]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - gl_rmain.c
better support non-GLSL130 in offsetmapping
[xonotic/darkplaces.git] / gl_rmain.c
index 7dde3795497368044675567c7842bd003b11a431..790dbca4ad9457461078fbeb72658556c3836c50 100644 (file)
@@ -25,6 +25,14 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #include "polygon.h"
 #include "image.h"
 #include "ft2.h"
+#include "csprogs.h"
+#include "cl_video.h"
+#include "dpsoftrast.h"
+
+#ifdef SUPPORTD3D
+#include <d3d9.h>
+extern LPDIRECT3DDEVICE9 vid_d3d9dev;
+#endif
 
 mempool_t *r_main_mempool;
 rtexturepool_t *r_main_texturepool;
@@ -74,7 +82,10 @@ cvar_t r_showcollisionbrushes_polygonoffset = {0, "r_showcollisionbrushes_polygo
 cvar_t r_showdisabledepthtest = {0, "r_showdisabledepthtest", "0", "disables depth testing on r_show* cvars, allowing you to see what hidden geometry the graphics card is processing"};
 cvar_t r_drawportals = {0, "r_drawportals", "0", "shows portals (separating polygons) in world interior in quake1 maps"};
 cvar_t r_drawentities = {0, "r_drawentities","1", "draw entities (doors, players, projectiles, etc)"};
+cvar_t r_draw2d = {0, "r_draw2d","1", "draw 2D stuff (dangerous to turn off)"};
+cvar_t r_drawworld = {0, "r_drawworld","1", "draw world (most static stuff)"};
 cvar_t r_drawviewmodel = {0, "r_drawviewmodel","1", "draw your weapon model"};
+cvar_t r_drawexteriormodel = {0, "r_drawexteriormodel","1", "draw your player model (e.g. in chase cam, reflections)"};
 cvar_t r_cullentities_trace = {0, "r_cullentities_trace", "1", "probabistically cull invisible entities"};
 cvar_t r_cullentities_trace_samples = {0, "r_cullentities_trace_samples", "2", "number of samples to test for entity culling (in addition to center sample)"};
 cvar_t r_cullentities_trace_tempentitysamples = {0, "r_cullentities_trace_tempentitysamples", "-1", "number of samples to test for entity culling of temp entities (including all CSQC entities), -1 disables trace culling on these entities to prevent flicker (pvs still applies)"};
@@ -82,6 +93,11 @@ cvar_t r_cullentities_trace_enlarge = {0, "r_cullentities_trace_enlarge", "0", "
 cvar_t r_cullentities_trace_delay = {0, "r_cullentities_trace_delay", "1", "number of seconds until the entity gets actually culled"};
 cvar_t r_speeds = {0, "r_speeds","0", "displays rendering statistics and per-subsystem timings"};
 cvar_t r_fullbright = {0, "r_fullbright","0", "makes map very bright and renders faster"};
+
+cvar_t r_fakelight = {0, "r_fakelight","0", "render 'fake' lighting instead of real lightmaps"};
+cvar_t r_fakelight_intensity = {0, "r_fakelight_intensity","0.75", "fakelight intensity modifier"};
+#define FAKELIGHT_ENABLED (r_fakelight.integer >= 2 || (r_fakelight.integer && r_refdef.scene.worldmodel && !r_refdef.scene.worldmodel->lit))
+
 cvar_t r_wateralpha = {CVAR_SAVE, "r_wateralpha","1", "opacity of water polygons"};
 cvar_t r_dynamic = {CVAR_SAVE, "r_dynamic","1", "enables dynamic lights (rocket glow and such)"};
 cvar_t r_fullbrights = {CVAR_SAVE, "r_fullbrights", "1", "enables glowing pixels in quake textures (changes need r_restart to take effect)"};
@@ -91,6 +107,8 @@ cvar_t r_shadows_throwdistance = {CVAR_SAVE, "r_shadows_throwdistance", "500", "
 cvar_t r_shadows_throwdirection = {CVAR_SAVE, "r_shadows_throwdirection", "0 0 -1", "override throwing direction for r_shadows 2"};
 cvar_t r_shadows_drawafterrtlighting = {CVAR_SAVE, "r_shadows_drawafterrtlighting", "0", "draw fake shadows AFTER realtime lightning is drawn. May be useful for simulating fast sunlight on large outdoor maps with only one noshadow rtlight. The price is less realistic appearance of dynamic light shadows."};
 cvar_t r_shadows_castfrombmodels = {CVAR_SAVE, "r_shadows_castfrombmodels", "0", "do cast shadows from bmodels"};
+cvar_t r_shadows_focus = {CVAR_SAVE, "r_shadows_focus", "0 0 0", "offset the shadowed area focus"};
+cvar_t r_shadows_shadowmapscale = {CVAR_SAVE, "r_shadows_shadowmapscale", "1", "increases shadowmap quality (multiply global shadowmap precision) for fake shadows. Needs shadowmapping ON."};
 cvar_t r_q1bsp_skymasking = {0, "r_q1bsp_skymasking", "1", "allows sky polygons in quake1 maps to obscure other geometry"};
 cvar_t r_polygonoffset_submodel_factor = {0, "r_polygonoffset_submodel_factor", "0", "biases depth values of world submodels such as doors, to prevent z-fighting artifacts in Quake maps"};
 cvar_t r_polygonoffset_submodel_offset = {0, "r_polygonoffset_submodel_offset", "14", "biases depth values of world submodels such as doors, to prevent z-fighting artifacts in Quake maps"};
@@ -112,6 +130,12 @@ cvar_t gl_skyclip = {0, "gl_skyclip", "4608", "nehahra farclip distance - the re
 cvar_t r_texture_dds_load = {CVAR_SAVE, "r_texture_dds_load", "0", "load compressed dds/filename.dds texture instead of filename.tga, if the file exists (requires driver support)"};
 cvar_t r_texture_dds_save = {CVAR_SAVE, "r_texture_dds_save", "0", "save compressed dds/filename.dds texture when filename.tga is loaded, so that it can be loaded instead next time"};
 
+cvar_t r_texture_convertsRGB_2d = {0, "r_texture_convertsRGB_2d", "0", "load textures as sRGB and convert to linear for proper shading"};
+cvar_t r_texture_convertsRGB_skin = {0, "r_texture_convertsRGB_skin", "0", "load textures as sRGB and convert to linear for proper shading"};
+cvar_t r_texture_convertsRGB_cubemap = {0, "r_texture_convertsRGB_cubemap", "0", "load textures as sRGB and convert to linear for proper shading"};
+cvar_t r_texture_convertsRGB_skybox = {0, "r_texture_convertsRGB_skybox", "0", "load textures as sRGB and convert to linear for proper shading"};
+cvar_t r_texture_convertsRGB_particles = {0, "r_texture_convertsRGB_particles", "0", "load textures as sRGB and convert to linear for proper shading"};
+
 cvar_t r_textureunits = {0, "r_textureunits", "32", "number of texture units to use in GL 1.1 and GL 1.3 rendering paths"};
 static cvar_t gl_combine = {CVAR_READONLY, "gl_combine", "1", "indicates whether the OpenGL 1.3 rendering path is active"};
 static cvar_t r_glsl = {CVAR_READONLY, "r_glsl", "1", "indicates whether the OpenGL 2.0 rendering path is active"};
@@ -125,12 +149,17 @@ cvar_t r_glsl_postprocess_uservec1 = {CVAR_SAVE, "r_glsl_postprocess_uservec1",
 cvar_t r_glsl_postprocess_uservec2 = {CVAR_SAVE, "r_glsl_postprocess_uservec2", "0 0 0 0", "a 4-component vector to pass as uservec2 to the postprocessing shader (only useful if default.glsl has been customized)"};
 cvar_t r_glsl_postprocess_uservec3 = {CVAR_SAVE, "r_glsl_postprocess_uservec3", "0 0 0 0", "a 4-component vector to pass as uservec3 to the postprocessing shader (only useful if default.glsl has been customized)"};
 cvar_t r_glsl_postprocess_uservec4 = {CVAR_SAVE, "r_glsl_postprocess_uservec4", "0 0 0 0", "a 4-component vector to pass as uservec4 to the postprocessing shader (only useful if default.glsl has been customized)"};
+cvar_t r_glsl_postprocess_uservec1_enable = {CVAR_SAVE, "r_glsl_postprocess_uservec1_enable", "1", "enables postprocessing uservec1 usage, creates USERVEC1 define (only useful if default.glsl has been customized)"};
+cvar_t r_glsl_postprocess_uservec2_enable = {CVAR_SAVE, "r_glsl_postprocess_uservec2_enable", "1", "enables postprocessing uservec2 usage, creates USERVEC1 define (only useful if default.glsl has been customized)"};
+cvar_t r_glsl_postprocess_uservec3_enable = {CVAR_SAVE, "r_glsl_postprocess_uservec3_enable", "1", "enables postprocessing uservec3 usage, creates USERVEC1 define (only useful if default.glsl has been customized)"};
+cvar_t r_glsl_postprocess_uservec4_enable = {CVAR_SAVE, "r_glsl_postprocess_uservec4_enable", "1", "enables postprocessing uservec4 usage, creates USERVEC1 define (only useful if default.glsl has been customized)"};
 
 cvar_t r_water = {CVAR_SAVE, "r_water", "0", "whether to use reflections and refraction on water surfaces (note: r_wateralpha must be set below 1)"};
 cvar_t r_water_clippingplanebias = {CVAR_SAVE, "r_water_clippingplanebias", "1", "a rather technical setting which avoids black pixels around water edges"};
 cvar_t r_water_resolutionmultiplier = {CVAR_SAVE, "r_water_resolutionmultiplier", "0.5", "multiplier for screen resolution when rendering refracted/reflected scenes, 1 is full quality, lower values are faster"};
 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_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"};
@@ -142,13 +171,19 @@ cvar_t r_bloom_colorscale = {CVAR_SAVE, "r_bloom_colorscale", "1", "how bright t
 cvar_t r_bloom_brighten = {CVAR_SAVE, "r_bloom_brighten", "2", "how bright the glow is, after subtract/power"};
 cvar_t r_bloom_blur = {CVAR_SAVE, "r_bloom_blur", "4", "how large the glow is"};
 cvar_t r_bloom_resolution = {CVAR_SAVE, "r_bloom_resolution", "320", "what resolution to perform the bloom effect at (independent of screen resolution)"};
-cvar_t r_bloom_colorexponent = {CVAR_SAVE, "r_bloom_colorexponent", "1", "how exagerated the glow is"};
+cvar_t r_bloom_colorexponent = {CVAR_SAVE, "r_bloom_colorexponent", "1", "how exaggerated the glow is"};
 cvar_t r_bloom_colorsubtract = {CVAR_SAVE, "r_bloom_colorsubtract", "0.125", "reduces bloom colors by a certain amount"};
 
 cvar_t r_hdr = {CVAR_SAVE, "r_hdr", "0", "enables High Dynamic Range bloom effect (higher quality version of r_bloom)"};
 cvar_t r_hdr_scenebrightness = {CVAR_SAVE, "r_hdr_scenebrightness", "1", "global rendering brightness"};
 cvar_t r_hdr_glowintensity = {CVAR_SAVE, "r_hdr_glowintensity", "1", "how bright light emitting textures should appear"};
-cvar_t r_hdr_range = {CVAR_SAVE, "r_hdr_range", "4", "how much dynamic range to render bloom with (equivilant to multiplying r_bloom_brighten by this value and dividing r_bloom_colorscale by this value)"};
+cvar_t r_hdr_range = {CVAR_SAVE, "r_hdr_range", "4", "how much dynamic range to render bloom with (equivalent to multiplying r_bloom_brighten by this value and dividing r_bloom_colorscale by this value)"};
+cvar_t r_hdr_irisadaptation = {CVAR_SAVE, "r_hdr_irisadaptation", "0", "adjust scene brightness according to light intensity at player location"};
+cvar_t r_hdr_irisadaptation_multiplier = {CVAR_SAVE, "r_hdr_irisadaptation_multiplier", "2", "brightness at which value will be 1.0"};
+cvar_t r_hdr_irisadaptation_minvalue = {CVAR_SAVE, "r_hdr_irisadaptation_minvalue", "0.5", "minimum value that can result from multiplier / brightness"};
+cvar_t r_hdr_irisadaptation_maxvalue = {CVAR_SAVE, "r_hdr_irisadaptation_maxvalue", "4", "maximum value that can result from multiplier / brightness"};
+cvar_t r_hdr_irisadaptation_value = {0, "r_hdr_irisadaptation_value", "1", "current value as scenebrightness multiplier, changes continuously when irisadaptation is active"};
+cvar_t r_hdr_irisadaptation_fade = {CVAR_SAVE, "r_hdr_irisadaptation_fade", "1", "fade rate at which value adjusts"};
 
 cvar_t r_smoothnormals_areaweighting = {0, "r_smoothnormals_areaweighting", "1", "uses significantly faster (and supposedly higher quality) area-weighted vertex normals and tangent vectors rather than summing normalized triangle normals and tangents"};
 
@@ -157,17 +192,19 @@ cvar_t developer_texturelogging = {0, "developer_texturelogging", "0", "produces
 cvar_t gl_lightmaps = {0, "gl_lightmaps", "0", "draws only lightmaps, no texture (for level designers)"};
 
 cvar_t r_test = {0, "r_test", "0", "internal development use only, leave it alone (usually does nothing anyway)"};
-cvar_t r_batchmode = {0, "r_batchmode", "1", "selects method of rendering multiple surfaces with one driver call (values are 0, 1, 2, etc...)"};
 cvar_t r_track_sprites = {CVAR_SAVE, "r_track_sprites", "1", "track SPR_LABEL* sprites by putting them as indicator at the screen border to rotate to"};
-cvar_t r_track_sprites_flags = {CVAR_SAVE, "r_track_sprites_flags", "1", "1: Rotate sprites accodringly, 2: Make it a continuous rotation"};
+cvar_t r_track_sprites_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", "0.15", "fake perspective effect for SPR_OVERHEAD sprites"};
-cvar_t r_overheadsprites_pushback = {CVAR_SAVE, "r_overheadsprites_pushback", "16", "how far to pull the SPR_OVERHEAD sprites toward the eye (used to avoid intersections with 3D models)"};
+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"};
 
-cvar_t r_framedatasize = {CVAR_SAVE, "r_framedatasize", "1", "size of renderer data cache used during one frame (for skeletal animation caching, light processing, etc)"};
+cvar_t r_framedatasize = {CVAR_SAVE, "r_framedatasize", "0.5", "size of renderer data cache used during one frame (for skeletal animation caching, light processing, etc)"};
 
 extern cvar_t v_glslgamma;
 
@@ -208,6 +245,7 @@ rtexture_t *r_texture_notexture;
 rtexture_t *r_texture_whitecube;
 rtexture_t *r_texture_normalizationcube;
 rtexture_t *r_texture_fogattenuation;
+rtexture_t *r_texture_fogheighttexture;
 rtexture_t *r_texture_gammaramps;
 unsigned int r_texture_gammaramps_serial;
 //rtexture_t *r_texture_fogintensity;
@@ -239,6 +277,8 @@ static r_qwskincache_t *r_qwskincache;
 static int r_qwskincache_size;
 
 /// vertex coordinates for a quad that covers the screen exactly
+extern const float r_screenvertex3f[12];
+extern const float r_d3dscreenvertex3f[12];
 const float r_screenvertex3f[12] =
 {
        0, 0, 0,
@@ -246,8 +286,13 @@ const float r_screenvertex3f[12] =
        1, 1, 0,
        0, 1, 0
 };
-
-extern void R_DrawModelShadows(void);
+const float r_d3dscreenvertex3f[12] =
+{
+       0, 1, 0,
+       1, 1, 0,
+       1, 0, 0,
+       0, 0, 0
+};
 
 void R_ModulateColors(float *in, float *out, int verts, float r, float g, float b)
 {
@@ -296,6 +341,7 @@ void FOG_clear(void)
        r_refdef.fog_end = 16384;
        r_refdef.fog_height = 1<<30;
        r_refdef.fog_fadedepth = 128;
+       memset(r_refdef.fog_height_texturename, 0, sizeof(r_refdef.fog_height_texturename));
 }
 
 static void R_BuildBlankTextures(void)
@@ -305,22 +351,22 @@ static void R_BuildBlankTextures(void)
        data[1] = 128; // normal Y
        data[0] = 255; // normal Z
        data[3] = 128; // height
-       r_texture_blanknormalmap = R_LoadTexture2D(r_main_texturepool, "blankbump", 1, 1, data, TEXTYPE_BGRA, TEXF_PERSISTENT, NULL);
+       r_texture_blanknormalmap = R_LoadTexture2D(r_main_texturepool, "blankbump", 1, 1, data, TEXTYPE_BGRA, TEXF_PERSISTENT, -1, NULL);
        data[0] = 255;
        data[1] = 255;
        data[2] = 255;
        data[3] = 255;
-       r_texture_white = R_LoadTexture2D(r_main_texturepool, "blankwhite", 1, 1, data, TEXTYPE_BGRA, TEXF_PERSISTENT, NULL);
+       r_texture_white = R_LoadTexture2D(r_main_texturepool, "blankwhite", 1, 1, data, TEXTYPE_BGRA, TEXF_PERSISTENT, -1, NULL);
        data[0] = 128;
        data[1] = 128;
        data[2] = 128;
        data[3] = 255;
-       r_texture_grey128 = R_LoadTexture2D(r_main_texturepool, "blankgrey128", 1, 1, data, TEXTYPE_BGRA, TEXF_PERSISTENT, NULL);
+       r_texture_grey128 = R_LoadTexture2D(r_main_texturepool, "blankgrey128", 1, 1, data, TEXTYPE_BGRA, TEXF_PERSISTENT, -1, NULL);
        data[0] = 0;
        data[1] = 0;
        data[2] = 0;
        data[3] = 255;
-       r_texture_black = R_LoadTexture2D(r_main_texturepool, "blankblack", 1, 1, data, TEXTYPE_BGRA, TEXF_PERSISTENT, NULL);
+       r_texture_black = R_LoadTexture2D(r_main_texturepool, "blankblack", 1, 1, data, TEXTYPE_BGRA, TEXF_PERSISTENT, -1, NULL);
 }
 
 static void R_BuildNoTexture(void)
@@ -348,14 +394,14 @@ static void R_BuildNoTexture(void)
                        }
                }
        }
-       r_texture_notexture = R_LoadTexture2D(r_main_texturepool, "notexture", 16, 16, &pix[0][0][0], TEXTYPE_BGRA, TEXF_MIPMAP | TEXF_PERSISTENT, NULL);
+       r_texture_notexture = R_LoadTexture2D(r_main_texturepool, "notexture", 16, 16, &pix[0][0][0], TEXTYPE_BGRA, TEXF_MIPMAP | TEXF_PERSISTENT, -1, NULL);
 }
 
 static void R_BuildWhiteCube(void)
 {
        unsigned char data[6*1*1*4];
        memset(data, 255, sizeof(data));
-       r_texture_whitecube = R_LoadTextureCubeMap(r_main_texturepool, "whitecube", 1, data, TEXTYPE_BGRA, TEXF_CLAMP | TEXF_PERSISTENT, NULL);
+       r_texture_whitecube = R_LoadTextureCubeMap(r_main_texturepool, "whitecube", 1, data, TEXTYPE_BGRA, TEXF_CLAMP | TEXF_PERSISTENT, -1, NULL);
 }
 
 static void R_BuildNormalizationCube(void)
@@ -365,7 +411,7 @@ static void R_BuildNormalizationCube(void)
        vec_t s, t, intensity;
 #define NORMSIZE 64
        unsigned char *data;
-       data = Mem_Alloc(tempmempool, 6*NORMSIZE*NORMSIZE*4);
+       data = (unsigned char *)Mem_Alloc(tempmempool, 6*NORMSIZE*NORMSIZE*4);
        for (side = 0;side < 6;side++)
        {
                for (y = 0;y < NORMSIZE;y++)
@@ -416,7 +462,7 @@ static void R_BuildNormalizationCube(void)
                        }
                }
        }
-       r_texture_normalizationcube = R_LoadTextureCubeMap(r_main_texturepool, "normalcube", NORMSIZE, data, TEXTYPE_BGRA, TEXF_CLAMP | TEXF_PERSISTENT, NULL);
+       r_texture_normalizationcube = R_LoadTextureCubeMap(r_main_texturepool, "normalcube", NORMSIZE, data, TEXTYPE_BGRA, TEXF_CLAMP | TEXF_PERSISTENT, -1, NULL);
        Mem_Free(data);
 }
 
@@ -466,2702 +512,96 @@ static void R_BuildFogTexture(void)
        }
        if (r_texture_fogattenuation)
        {
-               R_UpdateTexture(r_texture_fogattenuation, &data1[0][0], 0, 0, FOGWIDTH, 1);
-               //R_UpdateTexture(r_texture_fogattenuation, &data2[0][0], 0, 0, FOGWIDTH, 1);
+               R_UpdateTexture(r_texture_fogattenuation, &data1[0][0], 0, 0, 0, FOGWIDTH, 1, 1);
+               //R_UpdateTexture(r_texture_fogattenuation, &data2[0][0], 0, 0, 0, FOGWIDTH, 1, 1);
        }
        else
        {
-               r_texture_fogattenuation = R_LoadTexture2D(r_main_texturepool, "fogattenuation", FOGWIDTH, 1, &data1[0][0], TEXTYPE_BGRA, TEXF_FORCELINEAR | TEXF_CLAMP | TEXF_PERSISTENT | TEXF_ALLOWUPDATES, NULL);
-               //r_texture_fogintensity = R_LoadTexture2D(r_main_texturepool, "fogintensity", FOGWIDTH, 1, &data2[0][0], TEXTYPE_BGRA, TEXF_FORCELINEAR | TEXF_CLAMP | TEXF_ALLOWUPDATES, NULL);
+               r_texture_fogattenuation = R_LoadTexture2D(r_main_texturepool, "fogattenuation", FOGWIDTH, 1, &data1[0][0], TEXTYPE_BGRA, TEXF_FORCELINEAR | TEXF_CLAMP | TEXF_PERSISTENT, -1, NULL);
+               //r_texture_fogintensity = R_LoadTexture2D(r_main_texturepool, "fogintensity", FOGWIDTH, 1, &data2[0][0], TEXTYPE_BGRA, TEXF_FORCELINEAR | TEXF_CLAMP, NULL);
+       }
+}
+
+static void R_BuildFogHeightTexture(void)
+{
+       unsigned char *inpixels;
+       int size;
+       int x;
+       int y;
+       int j;
+       float c[4];
+       float f;
+       inpixels = NULL;
+       strlcpy(r_refdef.fogheighttexturename, r_refdef.fog_height_texturename, sizeof(r_refdef.fogheighttexturename));
+       if (r_refdef.fogheighttexturename[0])
+               inpixels = loadimagepixelsbgra(r_refdef.fogheighttexturename, true, false, false, NULL);
+       if (!inpixels)
+       {
+               r_refdef.fog_height_tablesize = 0;
+               if (r_texture_fogheighttexture)
+                       R_FreeTexture(r_texture_fogheighttexture);
+               r_texture_fogheighttexture = NULL;
+               if (r_refdef.fog_height_table2d)
+                       Mem_Free(r_refdef.fog_height_table2d);
+               r_refdef.fog_height_table2d = NULL;
+               if (r_refdef.fog_height_table1d)
+                       Mem_Free(r_refdef.fog_height_table1d);
+               r_refdef.fog_height_table1d = NULL;
+               return;
+       }
+       size = image_width;
+       r_refdef.fog_height_tablesize = size;
+       r_refdef.fog_height_table1d = (unsigned char *)Mem_Alloc(r_main_mempool, size * 4);
+       r_refdef.fog_height_table2d = (unsigned char *)Mem_Alloc(r_main_mempool, size * size * 4);
+       memcpy(r_refdef.fog_height_table1d, inpixels, size * 4);
+       Mem_Free(inpixels);
+       // LordHavoc: now the magic - what is that table2d for?  it is a cooked
+       // average fog color table accounting for every fog layer between a point
+       // and the camera.  (Note: attenuation is handled separately!)
+       for (y = 0;y < size;y++)
+       {
+               for (x = 0;x < size;x++)
+               {
+                       Vector4Clear(c);
+                       f = 0;
+                       if (x < y)
+                       {
+                               for (j = x;j <= y;j++)
+                               {
+                                       Vector4Add(c, r_refdef.fog_height_table1d + j*4, c);
+                                       f++;
+                               }
+                       }
+                       else
+                       {
+                               for (j = x;j >= y;j--)
+                               {
+                                       Vector4Add(c, r_refdef.fog_height_table1d + j*4, c);
+                                       f++;
+                               }
+                       }
+                       f = 1.0f / f;
+                       r_refdef.fog_height_table2d[(y*size+x)*4+0] = (unsigned char)(c[0] * f);
+                       r_refdef.fog_height_table2d[(y*size+x)*4+1] = (unsigned char)(c[1] * f);
+                       r_refdef.fog_height_table2d[(y*size+x)*4+2] = (unsigned char)(c[2] * f);
+                       r_refdef.fog_height_table2d[(y*size+x)*4+3] = (unsigned char)(c[3] * f);
+               }
        }
+       r_texture_fogheighttexture = R_LoadTexture2D(r_main_texturepool, "fogheighttable", size, size, r_refdef.fog_height_table2d, TEXTYPE_BGRA, TEXF_ALPHA | TEXF_CLAMP, -1, NULL);
 }
 
 //=======================================================================================================================================================
 
 static const char *builtinshaderstring =
-"// ambient+diffuse+specular+normalmap+attenuation+cubemap+fog shader\n"
-"// written by Forest 'LordHavoc' Hale\n"
-"// shadowmapping enhancements by Lee 'eihrul' Salzman\n"
-"\n"
-"#if defined(USEFOGINSIDE) || defined(USEFOGOUTSIDE)\n"
-"# define USEFOG\n"
-"#endif\n"
-"#if defined(MODE_LIGHTMAP) || defined(MODE_LIGHTDIRECTIONMAP_MODELSPACE) || defined(MODE_LIGHTDIRECTIONMAP_TANGENTSPACE)\n"
-"#define USELIGHTMAP\n"
-"#endif\n"
-"#if defined(USESPECULAR) || defined(USEOFFSETMAPPING) || defined(USEREFLECTCUBE)\n"
-"#define USEEYEVECTOR\n"
-"#endif\n"
-"\n"
-"#if defined(USESHADOWMAPRECT) || defined(MODE_DEFERREDLIGHTSOURCE) || defined(USEDEFERREDLIGHTMAP)\n"
-"# extension GL_ARB_texture_rectangle : enable\n"
-"#endif\n"
-"\n"
-"#ifdef USESHADOWMAP2D\n"
-"# ifdef GL_EXT_gpu_shader4\n"
-"#   extension GL_EXT_gpu_shader4 : enable\n"
-"# endif\n"
-"# ifdef GL_ARB_texture_gather\n"
-"#   extension GL_ARB_texture_gather : enable\n"
-"# else\n"
-"#   ifdef GL_AMD_texture_texture4\n"
-"#     extension GL_AMD_texture_texture4 : enable\n"
-"#   endif\n"
-"# endif\n"
-"#endif\n"
-"\n"
-"#ifdef USESHADOWMAPCUBE\n"
-"# extension GL_EXT_gpu_shader4 : enable\n"
-"#endif\n"
-"\n"
-"//#ifdef USESHADOWSAMPLER\n"
-"//# extension GL_ARB_shadow : enable\n"
-"//#endif\n"
-"\n"
-"//#ifdef __GLSL_CG_DATA_TYPES\n"
-"//# define myhalf half\n"
-"//# define myhalf2 half2\n"
-"//# define myhalf3 half3\n"
-"//# define myhalf4 half4\n"
-"//#else\n"
-"# define myhalf float\n"
-"# define myhalf2 vec2\n"
-"# define myhalf3 vec3\n"
-"# define myhalf4 vec4\n"
-"//#endif\n"
-"\n"
-"#ifdef VERTEX_SHADER\n"
-"uniform mat4 ModelViewProjectionMatrix;\n"
-"#endif\n"
-"\n"
-"#ifdef MODE_DEPTH_OR_SHADOW\n"
-"#ifdef VERTEX_SHADER\n"
-"void main(void)\n"
-"{\n"
-"      gl_Position = ModelViewProjectionMatrix * gl_Vertex;\n"
-"}\n"
-"#endif\n"
-"#else // !MODE_DEPTH_ORSHADOW\n"
-"\n"
-"\n"
-"\n"
-"\n"
-"#ifdef MODE_SHOWDEPTH\n"
-"#ifdef VERTEX_SHADER\n"
-"void main(void)\n"
-"{\n"
-"      gl_Position = ModelViewProjectionMatrix * gl_Vertex;\n"
-"      gl_FrontColor = vec4(gl_Position.z, gl_Position.z, gl_Position.z, 1.0);\n"
-"}\n"
-"#endif\n"
-"\n"
-"#ifdef FRAGMENT_SHADER\n"
-"void main(void)\n"
-"{\n"
-"      gl_FragColor = gl_Color;\n"
-"}\n"
-"#endif\n"
-"#else // !MODE_SHOWDEPTH\n"
-"\n"
-"\n"
-"\n"
-"\n"
-"#ifdef MODE_POSTPROCESS\n"
-"varying vec2 TexCoord1;\n"
-"varying vec2 TexCoord2;\n"
-"\n"
-"#ifdef VERTEX_SHADER\n"
-"void main(void)\n"
-"{\n"
-"      gl_Position = ModelViewProjectionMatrix * gl_Vertex;\n"
-"      TexCoord1 = gl_MultiTexCoord0.xy;\n"
-"#ifdef USEBLOOM\n"
-"      TexCoord2 = gl_MultiTexCoord1.xy;\n"
-"#endif\n"
-"}\n"
-"#endif\n"
-"\n"
-"#ifdef FRAGMENT_SHADER\n"
-"uniform sampler2D Texture_First;\n"
-"#ifdef USEBLOOM\n"
-"uniform sampler2D Texture_Second;\n"
-"#endif\n"
-"#ifdef USEGAMMARAMPS\n"
-"uniform sampler2D Texture_GammaRamps;\n"
-"#endif\n"
-"#ifdef USESATURATION\n"
-"uniform float Saturation;\n"
-"#endif\n"
-"#ifdef USEVIEWTINT\n"
-"uniform vec4 ViewTintColor;\n"
-"#endif\n"
-"//uncomment these if you want to use them:\n"
-"uniform vec4 UserVec1;\n"
-"// uniform vec4 UserVec2;\n"
-"// uniform vec4 UserVec3;\n"
-"// uniform vec4 UserVec4;\n"
-"// uniform float ClientTime;\n"
-"uniform vec2 PixelSize;\n"
-"void main(void)\n"
-"{\n"
-"      gl_FragColor = texture2D(Texture_First, TexCoord1);\n"
-"#ifdef USEBLOOM\n"
-"      gl_FragColor += texture2D(Texture_Second, TexCoord2);\n"
-"#endif\n"
-"#ifdef USEVIEWTINT\n"
-"      gl_FragColor = mix(gl_FragColor, ViewTintColor, ViewTintColor.a);\n"
-"#endif\n"
-"\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 and blends it using the second component\n"
-"      gl_FragColor += texture2D(Texture_First, TexCoord1 + PixelSize*UserVec1.x*vec2(-0.987688, -0.156434)) * UserVec1.y;\n"
-"      gl_FragColor += texture2D(Texture_First, TexCoord1 + PixelSize*UserVec1.x*vec2(-0.156434, -0.891007)) * UserVec1.y;\n"
-"      gl_FragColor += texture2D(Texture_First, TexCoord1 + PixelSize*UserVec1.x*vec2( 0.891007, -0.453990)) * UserVec1.y;\n"
-"      gl_FragColor += texture2D(Texture_First, TexCoord1 + PixelSize*UserVec1.x*vec2( 0.707107,  0.707107)) * UserVec1.y;\n"
-"      gl_FragColor += texture2D(Texture_First, TexCoord1 + PixelSize*UserVec1.x*vec2(-0.453990,  0.891007)) * UserVec1.y;\n"
-"      gl_FragColor /= (1 + 5 * UserVec1.y);\n"
-"#endif\n"
-"\n"
-"#ifdef USESATURATION\n"
-"      //apply saturation BEFORE gamma ramps, so v_glslgamma value does not matter\n"
-"      float y = dot(gl_FragColor.rgb, vec3(0.299, 0.587, 0.114));\n"
-"      //gl_FragColor = vec3(y) + (gl_FragColor.rgb - vec3(y)) * Saturation;\n"
-"      gl_FragColor.rgb = mix(vec3(y), gl_FragColor.rgb, Saturation);\n"
-"#endif\n"
-"\n"
-"#ifdef USEGAMMARAMPS\n"
-"      gl_FragColor.r = texture2D(Texture_GammaRamps, vec2(gl_FragColor.r, 0)).r;\n"
-"      gl_FragColor.g = texture2D(Texture_GammaRamps, vec2(gl_FragColor.g, 0)).g;\n"
-"      gl_FragColor.b = texture2D(Texture_GammaRamps, vec2(gl_FragColor.b, 0)).b;\n"
-"#endif\n"
-"}\n"
-"#endif\n"
-"#else // !MODE_POSTPROCESS\n"
-"\n"
-"\n"
-"\n"
-"\n"
-"#ifdef MODE_GENERIC\n"
-"#ifdef USEDIFFUSE\n"
-"varying vec2 TexCoord1;\n"
-"#endif\n"
-"#ifdef USESPECULAR\n"
-"varying vec2 TexCoord2;\n"
-"#endif\n"
-"#ifdef VERTEX_SHADER\n"
-"void main(void)\n"
-"{\n"
-"      gl_FrontColor = gl_Color;\n"
-"#ifdef USEDIFFUSE\n"
-"      TexCoord1 = gl_MultiTexCoord0.xy;\n"
-"#endif\n"
-"#ifdef USESPECULAR\n"
-"      TexCoord2 = gl_MultiTexCoord1.xy;\n"
-"#endif\n"
-"      gl_Position = ModelViewProjectionMatrix * gl_Vertex;\n"
-"}\n"
-"#endif\n"
-"\n"
-"#ifdef FRAGMENT_SHADER\n"
-"#ifdef USEDIFFUSE\n"
-"uniform sampler2D Texture_First;\n"
-"#endif\n"
-"#ifdef USESPECULAR\n"
-"uniform sampler2D Texture_Second;\n"
-"#endif\n"
-"\n"
-"void main(void)\n"
-"{\n"
-"      gl_FragColor = gl_Color;\n"
-"#ifdef USEDIFFUSE\n"
-"      gl_FragColor *= texture2D(Texture_First, TexCoord1);\n"
-"#endif\n"
-"\n"
-"#ifdef USESPECULAR\n"
-"      vec4 tex2 = texture2D(Texture_Second, TexCoord2);\n"
-"# ifdef USECOLORMAPPING\n"
-"      gl_FragColor *= tex2;\n"
-"# endif\n"
-"# ifdef USEGLOW\n"
-"      gl_FragColor += tex2;\n"
-"# endif\n"
-"# ifdef USEVERTEXTEXTUREBLEND\n"
-"      gl_FragColor = mix(gl_FragColor, tex2, tex2.a);\n"
-"# endif\n"
-"#endif\n"
-"}\n"
-"#endif\n"
-"#else // !MODE_GENERIC\n"
-"\n"
-"\n"
-"\n"
-"\n"
-"#ifdef MODE_BLOOMBLUR\n"
-"varying TexCoord;\n"
-"#ifdef VERTEX_SHADER\n"
-"void main(void)\n"
-"{\n"
-"      gl_FrontColor = gl_Color;\n"
-"      TexCoord = gl_MultiTexCoord0.xy;\n"
-"      gl_Position = ModelViewProjectionMatrix * gl_Vertex;\n"
-"}\n"
-"#endif\n"
-"\n"
-"#ifdef FRAGMENT_SHADER\n"
-"uniform sampler2D Texture_First;\n"
-"uniform vec4 BloomBlur_Parameters;\n"
-"\n"
-"void main(void)\n"
-"{\n"
-"      int i;\n"
-"      vec2 tc = TexCoord;\n"
-"      vec3 color = texture2D(Texture_First, tc).rgb;\n"
-"      tc += BloomBlur_Parameters.xy;\n"
-"      for (i = 1;i < SAMPLES;i++)\n"
-"      {\n"
-"              color += texture2D(Texture_First, tc).rgb;\n"
-"              tc += BloomBlur_Parameters.xy;\n"
-"      }\n"
-"      gl_FragColor = vec4(color * BloomBlur_Parameters.z + vec3(BloomBlur_Parameters.w), 1);\n"
-"}\n"
-"#endif\n"
-"#else // !MODE_BLOOMBLUR\n"
-"#ifdef MODE_REFRACTION\n"
-"varying vec2 TexCoord;\n"
-"varying vec4 ModelViewProjectionPosition;\n"
-"uniform mat4 TexMatrix;\n"
-"#ifdef VERTEX_SHADER\n"
-"\n"
-"void main(void)\n"
-"{\n"
-"      TexCoord = vec2(TexMatrix * gl_MultiTexCoord0);\n"
-"      gl_Position = ModelViewProjectionMatrix * gl_Vertex;\n"
-"      ModelViewProjectionPosition = gl_Position;\n"
-"}\n"
-"#endif\n"
-"\n"
-"#ifdef FRAGMENT_SHADER\n"
-"uniform sampler2D Texture_Normal;\n"
-"uniform sampler2D Texture_Refraction;\n"
-"uniform sampler2D Texture_Reflection;\n"
-"\n"
-"uniform vec4 DistortScaleRefractReflect;\n"
-"uniform vec4 ScreenScaleRefractReflect;\n"
-"uniform vec4 ScreenCenterRefractReflect;\n"
-"uniform vec4 RefractColor;\n"
-"uniform vec4 ReflectColor;\n"
-"uniform float ReflectFactor;\n"
-"uniform float ReflectOffset;\n"
-"\n"
-"void main(void)\n"
-"{\n"
-"      vec2 ScreenScaleRefractReflectIW = ScreenScaleRefractReflect.xy * (1.0 / ModelViewProjectionPosition.w);\n"
-"      //vec2 ScreenTexCoord = (ModelViewProjectionPosition.xy + normalize(vec3(texture2D(Texture_Normal, TexCoord)) - vec3(0.5)).xy * DistortScaleRefractReflect.xy * 100) * ScreenScaleRefractReflectIW + ScreenCenterRefractReflect.xy;\n"
-"      vec2 SafeScreenTexCoord = ModelViewProjectionPosition.xy * ScreenScaleRefractReflectIW + ScreenCenterRefractReflect.xy;\n"
-"      vec2 ScreenTexCoord = SafeScreenTexCoord + vec2(normalize(vec3(texture2D(Texture_Normal, TexCoord)) - vec3(0.5))).xy * DistortScaleRefractReflect.xy;\n"
-"      // FIXME temporary hack to detect the case that the reflection\n"
-"      // gets blackened at edges due to leaving the area that contains actual\n"
-"      // content.\n"
-"      // Remove this 'ack once we have a better way to stop this thing from\n"
-"      // 'appening.\n"
-"      float f = min(1.0, length(texture2D(Texture_Refraction, ScreenTexCoord + vec2(0.01, 0.01)).rgb) / 0.05);\n"
-"      f      *= min(1.0, length(texture2D(Texture_Refraction, ScreenTexCoord + vec2(0.01, -0.01)).rgb) / 0.05);\n"
-"      f      *= min(1.0, length(texture2D(Texture_Refraction, ScreenTexCoord + vec2(-0.01, 0.01)).rgb) / 0.05);\n"
-"      f      *= min(1.0, length(texture2D(Texture_Refraction, ScreenTexCoord + vec2(-0.01, -0.01)).rgb) / 0.05);\n"
-"      ScreenTexCoord = mix(SafeScreenTexCoord, ScreenTexCoord, f);\n"
-"      gl_FragColor = texture2D(Texture_Refraction, ScreenTexCoord) * RefractColor;\n"
-"}\n"
-"#endif\n"
-"#else // !MODE_REFRACTION\n"
-"\n"
-"\n"
-"\n"
-"\n"
-"#ifdef MODE_WATER\n"
-"varying vec2 TexCoord;\n"
-"varying vec3 EyeVector;\n"
-"varying vec4 ModelViewProjectionPosition;\n"
-"#ifdef VERTEX_SHADER\n"
-"uniform vec3 EyePosition;\n"
-"uniform mat4 TexMatrix;\n"
-"\n"
-"void main(void)\n"
-"{\n"
-"      TexCoord = vec2(TexMatrix * gl_MultiTexCoord0);\n"
-"      vec3 EyeVectorModelSpace = EyePosition - gl_Vertex.xyz;\n"
-"      EyeVector.x = dot(EyeVectorModelSpace, gl_MultiTexCoord1.xyz);\n"
-"      EyeVector.y = dot(EyeVectorModelSpace, gl_MultiTexCoord2.xyz);\n"
-"      EyeVector.z = dot(EyeVectorModelSpace, gl_MultiTexCoord3.xyz);\n"
-"      gl_Position = ModelViewProjectionMatrix * gl_Vertex;\n"
-"      ModelViewProjectionPosition = gl_Position;\n"
-"}\n"
-"#endif\n"
-"\n"
-"#ifdef FRAGMENT_SHADER\n"
-"uniform sampler2D Texture_Normal;\n"
-"uniform sampler2D Texture_Refraction;\n"
-"uniform sampler2D Texture_Reflection;\n"
-"\n"
-"uniform vec4 DistortScaleRefractReflect;\n"
-"uniform vec4 ScreenScaleRefractReflect;\n"
-"uniform vec4 ScreenCenterRefractReflect;\n"
-"uniform vec4 RefractColor;\n"
-"uniform vec4 ReflectColor;\n"
-"uniform float ReflectFactor;\n"
-"uniform float ReflectOffset;\n"
-"\n"
-"void main(void)\n"
-"{\n"
-"      vec4 ScreenScaleRefractReflectIW = ScreenScaleRefractReflect * (1.0 / ModelViewProjectionPosition.w);\n"
-"      //vec4 ScreenTexCoord = (ModelViewProjectionPosition.xyxy + normalize(vec3(texture2D(Texture_Normal, TexCoord)) - vec3(0.5)).xyxy * DistortScaleRefractReflect * 100) * ScreenScaleRefractReflectIW + ScreenCenterRefractReflect;\n"
-"      vec4 SafeScreenTexCoord = ModelViewProjectionPosition.xyxy * ScreenScaleRefractReflectIW + ScreenCenterRefractReflect;\n"
-"      //SafeScreenTexCoord = gl_FragCoord.xyxy * vec4(1.0 / 1920.0, 1.0 / 1200.0, 1.0 / 1920.0, 1.0 / 1200.0);\n"
-"      vec4 ScreenTexCoord = SafeScreenTexCoord + vec2(normalize(vec3(texture2D(Texture_Normal, TexCoord)) - vec3(0.5))).xyxy * DistortScaleRefractReflect;\n"
-"      // FIXME temporary hack to detect the case that the reflection\n"
-"      // gets blackened at edges due to leaving the area that contains actual\n"
-"      // content.\n"
-"      // Remove this 'ack once we have a better way to stop this thing from\n"
-"      // 'appening.\n"
-"      float f = min(1.0, length(texture2D(Texture_Refraction, ScreenTexCoord.xy + vec2(0.01, 0.01)).rgb) / 0.05);\n"
-"      f      *= min(1.0, length(texture2D(Texture_Refraction, ScreenTexCoord.xy + vec2(0.01, -0.01)).rgb) / 0.05);\n"
-"      f      *= min(1.0, length(texture2D(Texture_Refraction, ScreenTexCoord.xy + vec2(-0.01, 0.01)).rgb) / 0.05);\n"
-"      f      *= min(1.0, length(texture2D(Texture_Refraction, ScreenTexCoord.xy + vec2(-0.01, -0.01)).rgb) / 0.05);\n"
-"      ScreenTexCoord.xy = mix(SafeScreenTexCoord.xy, ScreenTexCoord.xy, f);\n"
-"      f       = min(1.0, length(texture2D(Texture_Reflection, ScreenTexCoord.zw + vec2(0.01, 0.01)).rgb) / 0.05);\n"
-"      f      *= min(1.0, length(texture2D(Texture_Reflection, ScreenTexCoord.zw + vec2(0.01, -0.01)).rgb) / 0.05);\n"
-"      f      *= min(1.0, length(texture2D(Texture_Reflection, ScreenTexCoord.zw + vec2(-0.01, 0.01)).rgb) / 0.05);\n"
-"      f      *= min(1.0, length(texture2D(Texture_Reflection, ScreenTexCoord.zw + vec2(-0.01, -0.01)).rgb) / 0.05);\n"
-"      ScreenTexCoord.zw = mix(SafeScreenTexCoord.zw, ScreenTexCoord.zw, f);\n"
-"      float Fresnel = pow(min(1.0, 1.0 - float(normalize(EyeVector).z)), 2.0) * ReflectFactor + ReflectOffset;\n"
-"      gl_FragColor = mix(texture2D(Texture_Refraction, ScreenTexCoord.xy) * RefractColor, texture2D(Texture_Reflection, ScreenTexCoord.zw) * ReflectColor, Fresnel);\n"
-"}\n"
-"#endif\n"
-"#else // !MODE_WATER\n"
-"\n"
-"\n"
-"\n"
-"\n"
-"// common definitions between vertex shader and fragment shader:\n"
-"\n"
-"varying vec2 TexCoord;\n"
-"#ifdef USEVERTEXTEXTUREBLEND\n"
-"varying vec2 TexCoord2;\n"
-"#endif\n"
-"#ifdef USELIGHTMAP\n"
-"varying vec2 TexCoordLightmap;\n"
-"#endif\n"
-"\n"
-"#ifdef MODE_LIGHTSOURCE\n"
-"varying vec3 CubeVector;\n"
-"#endif\n"
-"\n"
-"#if (defined(MODE_LIGHTSOURCE) || defined(MODE_LIGHTDIRECTION)) && defined(USEDIFFUSE)\n"
-"varying vec3 LightVector;\n"
-"#endif\n"
-"\n"
-"#ifdef USEEYEVECTOR\n"
-"varying vec3 EyeVector;\n"
-"#endif\n"
-"#ifdef USEFOG\n"
-"varying vec4 EyeVectorModelSpaceFogPlaneVertexDist;\n"
-"#endif\n"
-"\n"
-"#if defined(MODE_LIGHTDIRECTIONMAP_MODELSPACE) || defined(MODE_DEFERREDGEOMETRY) || defined(USEREFLECTCUBE)\n"
-"varying vec3 VectorS; // direction of S texcoord (sometimes crudely called tangent)\n"
-"varying vec3 VectorT; // direction of T texcoord (sometimes crudely called binormal)\n"
-"varying vec3 VectorR; // direction of R texcoord (surface normal)\n"
-"#endif\n"
-"\n"
-"#ifdef USEREFLECTION\n"
-"varying vec4 ModelViewProjectionPosition;\n"
-"#endif\n"
-"#ifdef MODE_DEFERREDLIGHTSOURCE\n"
-"uniform vec3 LightPosition;\n"
-"varying vec4 ModelViewPosition;\n"
-"#endif\n"
-"\n"
-"#ifdef MODE_LIGHTSOURCE\n"
-"uniform vec3 LightPosition;\n"
-"#endif\n"
-"uniform vec3 EyePosition;\n"
-"#ifdef MODE_LIGHTDIRECTION\n"
-"uniform vec3 LightDir;\n"
-"#endif\n"
-"uniform vec4 FogPlane;\n"
-"\n"
-"\n"
-"\n"
-"\n"
-"\n"
-"// TODO: get rid of tangentt (texcoord2) and use a crossproduct to regenerate it from tangents (texcoord1) and normal (texcoord3), this would require sending a 4 component texcoord1 with W as 1 or -1 according to which side the texcoord2 should be on\n"
-"\n"
-"// fragment shader specific:\n"
-"#ifdef FRAGMENT_SHADER\n"
-"\n"
-"uniform sampler2D Texture_Normal;\n"
-"uniform sampler2D Texture_Color;\n"
-"uniform sampler2D Texture_Gloss;\n"
-"#ifdef USEGLOW\n"
-"uniform sampler2D Texture_Glow;\n"
-"#endif\n"
-"#ifdef USEVERTEXTEXTUREBLEND\n"
-"uniform sampler2D Texture_SecondaryNormal;\n"
-"uniform sampler2D Texture_SecondaryColor;\n"
-"uniform sampler2D Texture_SecondaryGloss;\n"
-"#ifdef USEGLOW\n"
-"uniform sampler2D Texture_SecondaryGlow;\n"
-"#endif\n"
-"#endif\n"
-"#ifdef USECOLORMAPPING\n"
-"uniform sampler2D Texture_Pants;\n"
-"uniform sampler2D Texture_Shirt;\n"
-"#endif\n"
-"#ifdef USEFOG\n"
-"uniform sampler2D Texture_FogMask;\n"
-"#endif\n"
-"#ifdef USELIGHTMAP\n"
-"uniform sampler2D Texture_Lightmap;\n"
-"#endif\n"
-"#if defined(MODE_LIGHTDIRECTIONMAP_MODELSPACE) || defined(MODE_LIGHTDIRECTIONMAP_TANGENTSPACE)\n"
-"uniform sampler2D Texture_Deluxemap;\n"
-"#endif\n"
-"#ifdef USEREFLECTION\n"
-"uniform sampler2D Texture_Reflection;\n"
-"#endif\n"
-"\n"
-"#ifdef MODE_DEFERREDLIGHTSOURCE\n"
-"uniform sampler2D Texture_ScreenDepth;\n"
-"uniform sampler2D Texture_ScreenNormalMap;\n"
-"#endif\n"
-"#ifdef USEDEFERREDLIGHTMAP\n"
-"uniform sampler2D Texture_ScreenDiffuse;\n"
-"uniform sampler2D Texture_ScreenSpecular;\n"
-"#endif\n"
-"\n"
-"uniform myhalf3 Color_Pants;\n"
-"uniform myhalf3 Color_Shirt;\n"
-"uniform myhalf3 FogColor;\n"
-"\n"
-"#ifdef USEFOG\n"
-"uniform float FogRangeRecip;\n"
-"uniform float FogPlaneViewDist;\n"
-"uniform float FogHeightFade;\n"
-"float FogVertex(void)\n"
-"{\n"
-"      vec3 EyeVectorModelSpace = EyeVectorModelSpaceFogPlaneVertexDist.xyz;\n"
-"      float FogPlaneVertexDist = EyeVectorModelSpaceFogPlaneVertexDist.w;\n"
-"      float fogfrac;\n"
-"#ifdef USEFOGOUTSIDE\n"
-"      fogfrac = min(0.0, FogPlaneVertexDist) / (FogPlaneVertexDist - FogPlaneViewDist) * min(1.0, min(0.0, FogPlaneVertexDist) * FogHeightFade);\n"
-"#else\n"
-"      fogfrac = FogPlaneViewDist / (FogPlaneViewDist - max(0.0, FogPlaneVertexDist)) * min(1.0, (min(0.0, FogPlaneVertexDist) + FogPlaneViewDist) * FogHeightFade);\n"
-"#endif\n"
-"      return float(texture2D(Texture_FogMask, myhalf2(length(EyeVectorModelSpace)*fogfrac*FogRangeRecip, 0.0)));\n"
-"}\n"
-"#endif\n"
-"\n"
-"#ifdef USEOFFSETMAPPING\n"
-"uniform float OffsetMapping_Scale;\n"
-"vec2 OffsetMapping(vec2 TexCoord)\n"
-"{\n"
-"#ifdef USEOFFSETMAPPING_RELIEFMAPPING\n"
-"      // 14 sample relief mapping: linear search and then binary search\n"
-"      // this basically steps forward a small amount repeatedly until it finds\n"
-"      // itself inside solid, then jitters forward and back using decreasing\n"
-"      // amounts to find the impact\n"
-"      //vec3 OffsetVector = vec3(EyeVector.xy * ((1.0 / EyeVector.z) * OffsetMapping_Scale) * vec2(-1, 1), -1);\n"
-"      //vec3 OffsetVector = vec3(normalize(EyeVector.xy) * OffsetMapping_Scale * vec2(-1, 1), -1);\n"
-"      vec3 OffsetVector = vec3(normalize(EyeVector).xy * OffsetMapping_Scale * vec2(-1, 1), -1);\n"
-"      vec3 RT = vec3(TexCoord, 1);\n"
-"      OffsetVector *= 0.1;\n"
-"      RT += OffsetVector *  step(texture2D(Texture_Normal, RT.xy).a, RT.z);\n"
-"      RT += OffsetVector *  step(texture2D(Texture_Normal, RT.xy).a, RT.z);\n"
-"      RT += OffsetVector *  step(texture2D(Texture_Normal, RT.xy).a, RT.z);\n"
-"      RT += OffsetVector *  step(texture2D(Texture_Normal, RT.xy).a, RT.z);\n"
-"      RT += OffsetVector *  step(texture2D(Texture_Normal, RT.xy).a, RT.z);\n"
-"      RT += OffsetVector *  step(texture2D(Texture_Normal, RT.xy).a, RT.z);\n"
-"      RT += OffsetVector *  step(texture2D(Texture_Normal, RT.xy).a, RT.z);\n"
-"      RT += OffsetVector *  step(texture2D(Texture_Normal, RT.xy).a, RT.z);\n"
-"      RT += OffsetVector *  step(texture2D(Texture_Normal, RT.xy).a, RT.z);\n"
-"      RT += OffsetVector * (step(texture2D(Texture_Normal, RT.xy).a, RT.z)          - 0.5);\n"
-"      RT += OffsetVector * (step(texture2D(Texture_Normal, RT.xy).a, RT.z) * 0.5    - 0.25);\n"
-"      RT += OffsetVector * (step(texture2D(Texture_Normal, RT.xy).a, RT.z) * 0.25   - 0.125);\n"
-"      RT += OffsetVector * (step(texture2D(Texture_Normal, RT.xy).a, RT.z) * 0.125  - 0.0625);\n"
-"      RT += OffsetVector * (step(texture2D(Texture_Normal, RT.xy).a, RT.z) * 0.0625 - 0.03125);\n"
-"      return RT.xy;\n"
-"#else\n"
-"      // 3 sample offset mapping (only 3 samples because of ATI Radeon 9500-9800/X300 limits)\n"
-"      // this basically moves forward the full distance, and then backs up based\n"
-"      // on height of samples\n"
-"      //vec2 OffsetVector = vec2(EyeVector.xy * ((1.0 / EyeVector.z) * OffsetMapping_Scale) * vec2(-1, 1));\n"
-"      //vec2 OffsetVector = vec2(normalize(EyeVector.xy) * OffsetMapping_Scale * vec2(-1, 1));\n"
-"      vec2 OffsetVector = vec2(normalize(EyeVector).xy * OffsetMapping_Scale * vec2(-1, 1));\n"
-"      TexCoord += OffsetVector;\n"
-"      OffsetVector *= 0.333;\n"
-"      TexCoord -= OffsetVector * texture2D(Texture_Normal, TexCoord).a;\n"
-"      TexCoord -= OffsetVector * texture2D(Texture_Normal, TexCoord).a;\n"
-"      TexCoord -= OffsetVector * texture2D(Texture_Normal, TexCoord).a;\n"
-"      return TexCoord;\n"
-"#endif\n"
-"}\n"
-"#endif // USEOFFSETMAPPING\n"
-"\n"
-"#if defined(MODE_LIGHTSOURCE) || defined(MODE_DEFERREDLIGHTSOURCE)\n"
-"uniform sampler2D Texture_Attenuation;\n"
-"uniform samplerCube Texture_Cube;\n"
-"\n"
-"#ifdef USESHADOWMAPRECT\n"
-"# ifdef USESHADOWSAMPLER\n"
-"uniform sampler2DRectShadow Texture_ShadowMapRect;\n"
-"# else\n"
-"uniform sampler2DRect Texture_ShadowMapRect;\n"
-"# endif\n"
-"#endif\n"
-"\n"
-"#ifdef USESHADOWMAP2D\n"
-"# ifdef USESHADOWSAMPLER\n"
-"uniform sampler2DShadow Texture_ShadowMap2D;\n"
-"# else\n"
-"uniform sampler2D Texture_ShadowMap2D;\n"
-"# endif\n"
-"#endif\n"
-"\n"
-"#ifdef USESHADOWMAPVSDCT\n"
-"uniform samplerCube Texture_CubeProjection;\n"
-"#endif\n"
-"\n"
-"#ifdef USESHADOWMAPCUBE\n"
-"# ifdef USESHADOWSAMPLER\n"
-"uniform samplerCubeShadow Texture_ShadowMapCube;\n"
-"# else\n"
-"uniform samplerCube Texture_ShadowMapCube;\n"
-"# endif\n"
-"#endif\n"
-"\n"
-"#if defined(USESHADOWMAPRECT) || defined(USESHADOWMAP2D) || defined(USESHADOWMAPCUBE)\n"
-"uniform vec2 ShadowMap_TextureScale;\n"
-"uniform vec4 ShadowMap_Parameters;\n"
-"#endif\n"
-"\n"
-"#if defined(USESHADOWMAPRECT) || defined(USESHADOWMAP2D)\n"
-"# ifndef USESHADOWMAPVSDCT\n"
-"vec3 GetShadowMapTC2D(vec3 dir)\n"
-"{\n"
-"      vec3 adir = abs(dir);\n"
-"      vec2 tc;\n"
-"      vec2 offset;\n"
-"      float ma;\n"
-"      if (adir.x > adir.y)\n"
-"      {\n"
-"              if (adir.x > adir.z) // X\n"
-"              {\n"
-"                      ma = adir.x;\n"
-"                      tc = dir.zy;\n"
-"                      offset = vec2(mix(0.5, 1.5, dir.x < 0.0), 0.5);\n"
-"              }\n"
-"              else // Z\n"
-"              {\n"
-"                      ma = adir.z;\n"
-"                      tc = dir.xy;\n"
-"                      offset = vec2(mix(0.5, 1.5, dir.z < 0.0), 2.5);\n"
-"              }\n"
-"      }\n"
-"      else\n"
-"      {\n"
-"              if (adir.y > adir.z) // Y\n"
-"              {\n"
-"                      ma = adir.y;\n"
-"                      tc = dir.xz;\n"
-"                      offset = vec2(mix(0.5, 1.5, dir.y < 0.0), 1.5);\n"
-"              }\n"
-"              else // Z\n"
-"              {\n"
-"                      ma = adir.z;\n"
-"                      tc = dir.xy;\n"
-"                      offset = vec2(mix(0.5, 1.5, dir.z < 0.0), 2.5);\n"
-"              }\n"
-"      }\n"
-"\n"
-"      vec3 stc = vec3(tc * ShadowMap_Parameters.x, ShadowMap_Parameters.w) / ma;\n"
-"      stc.xy += offset * ShadowMap_Parameters.y;\n"
-"      stc.z += ShadowMap_Parameters.z;\n"
-"      return stc;\n"
-"}\n"
-"# else\n"
-"vec3 GetShadowMapTC2D(vec3 dir)\n"
-"{\n"
-"      vec3 adir = abs(dir);\n"
-"      vec4 proj = textureCube(Texture_CubeProjection, dir);\n"
-"      float ma = max(max(adir.x, adir.y), adir.z);\n"
-"      vec3 stc = vec3(mix(dir.xy, dir.zz, proj.xy) * ShadowMap_Parameters.x, ShadowMap_Parameters.w) / ma;\n"
-"      stc.xy += proj.zw * ShadowMap_Parameters.y;\n"
-"      stc.z += ShadowMap_Parameters.z;\n"
-"      return stc;\n"
-"}\n"
-"# endif\n"
-"#endif // defined(USESHADOWMAPRECT) || defined(USESHADOWMAP2D)\n"
-"\n"
-"#ifdef USESHADOWMAPCUBE\n"
-"vec4 GetShadowMapTCCube(vec3 dir)\n"
-"{\n"
-"      vec3 adir = abs(dir);\n"
-"      return vec4(dir, ShadowMap_Parameters.z + ShadowMap_Parameters.w / max(max(adir.x, adir.y), adir.z));\n"
-"}\n"
-"#endif\n"
-"\n"
-"# ifdef USESHADOWMAPRECT\n"
-"float ShadowMapCompare(vec3 dir)\n"
-"{\n"
-"      vec3 shadowmaptc = GetShadowMapTC2D(dir);\n"
-"      float f;\n"
-"#  ifdef USESHADOWSAMPLER\n"
-"\n"
-"#    ifdef USESHADOWMAPPCF\n"
-"#      define texval(x, y) shadow2DRect(Texture_ShadowMapRect, shadowmaptc + vec3(x, y, 0.0)).r\n"
-"      f = dot(vec4(0.25), vec4(texval(-0.4, 1.0), texval(-1.0, -0.4), texval(0.4, -1.0), texval(1.0, 0.4)));\n"
-"#    else\n"
-"      f = shadow2DRect(Texture_ShadowMapRect, shadowmaptc).r;\n"
-"#    endif\n"
-"\n"
-"#  else\n"
-"\n"
-"#    ifdef USESHADOWMAPPCF\n"
-"#      if USESHADOWMAPPCF > 1\n"
-"#        define texval(x, y) texture2DRect(Texture_ShadowMapRect, center + vec2(x, y)).r\n"
-"      vec2 center = shadowmaptc.xy - 0.5, offset = fract(center);\n"
-"      vec4 row1 = step(shadowmaptc.z, vec4(texval(-1.0, -1.0), texval( 0.0, -1.0), texval( 1.0, -1.0), texval( 2.0, -1.0)));\n"
-"      vec4 row2 = step(shadowmaptc.z, vec4(texval(-1.0,  0.0), texval( 0.0,  0.0), texval( 1.0,  0.0), texval( 2.0,  0.0)));\n"
-"      vec4 row3 = step(shadowmaptc.z, vec4(texval(-1.0,  1.0), texval( 0.0,  1.0), texval( 1.0,  1.0), texval( 2.0,  1.0)));\n"
-"      vec4 row4 = step(shadowmaptc.z, vec4(texval(-1.0,  2.0), texval( 0.0,  2.0), texval( 1.0,  2.0), texval( 2.0,  2.0)));\n"
-"      vec4 cols = row2 + row3 + mix(row1, row4, offset.y);\n"
-"      f = dot(mix(cols.xyz, cols.yzw, offset.x), vec3(1.0/9.0));\n"
-"#      else\n"
-"#        define texval(x, y) texture2DRect(Texture_ShadowMapRect, shadowmaptc.xy + vec2(x, y)).r\n"
-"      vec2 offset = fract(shadowmaptc.xy);\n"
-"      vec3 row1 = step(shadowmaptc.z, vec3(texval(-1.0, -1.0), texval( 0.0, -1.0), texval( 1.0, -1.0)));\n"
-"      vec3 row2 = step(shadowmaptc.z, vec3(texval(-1.0,  0.0), texval( 0.0,  0.0), texval( 1.0,  0.0)));\n"
-"      vec3 row3 = step(shadowmaptc.z, vec3(texval(-1.0,  1.0), texval( 0.0,  1.0), texval( 1.0,  1.0)));\n"
-"      vec3 cols = row2 + mix(row1, row3, offset.y);\n"
-"      f = dot(mix(cols.xy, cols.yz, offset.x), vec2(0.25));\n"
-"#      endif\n"
-"#    else\n"
-"      f = step(shadowmaptc.z, texture2DRect(Texture_ShadowMapRect, shadowmaptc.xy).r);\n"
-"#    endif\n"
-"\n"
-"#  endif\n"
-"      return f;\n"
-"}\n"
-"# endif\n"
-"\n"
-"# ifdef USESHADOWMAP2D\n"
-"float ShadowMapCompare(vec3 dir)\n"
-"{\n"
-"      vec3 shadowmaptc = GetShadowMapTC2D(dir);\n"
-"      float f;\n"
-"\n"
-"#  ifdef USESHADOWSAMPLER\n"
-"#    ifdef USESHADOWMAPPCF\n"
-"#      define texval(x, y) shadow2D(Texture_ShadowMap2D, vec3(center + vec2(x, y)*ShadowMap_TextureScale, shadowmaptc.z)).r  \n"
-"      vec2 center = shadowmaptc.xy*ShadowMap_TextureScale;\n"
-"      f = dot(vec4(0.25), vec4(texval(-0.4, 1.0), texval(-1.0, -0.4), texval(0.4, -1.0), texval(1.0, 0.4)));\n"
-"#    else\n"
-"      f = shadow2D(Texture_ShadowMap2D, vec3(shadowmaptc.xy*ShadowMap_TextureScale, shadowmaptc.z)).r;\n"
-"#    endif\n"
-"#  else\n"
-"#    ifdef USESHADOWMAPPCF\n"
-"#     if defined(GL_ARB_texture_gather) || defined(GL_AMD_texture_texture4)\n"
-"#      ifdef GL_ARB_texture_gather\n"
-"#        define texval(x, y) textureGatherOffset(Texture_ShadowMap2D, center, ivec2(x, y))\n"
-"#      else\n"
-"#        define texval(x, y) texture4(Texture_ShadowMap2D, center + vec2(x, y)*ShadowMap_TextureScale)\n"
-"#      endif\n"
-"      vec2 center = shadowmaptc.xy - 0.5, offset = fract(center);\n"
-"      center *= ShadowMap_TextureScale;\n"
-"      vec4 group1 = step(shadowmaptc.z, texval(-1.0, -1.0));\n"
-"      vec4 group2 = step(shadowmaptc.z, texval( 1.0, -1.0));\n"
-"      vec4 group3 = step(shadowmaptc.z, texval(-1.0,  1.0));\n"
-"      vec4 group4 = step(shadowmaptc.z, texval( 1.0,  1.0));\n"
-"      vec4 cols = vec4(group1.rg, group2.rg) + vec4(group3.ab, group4.ab) +\n"
-"                              mix(vec4(group1.ab, group2.ab), vec4(group3.rg, group4.rg), offset.y);\n"
-"      f = dot(mix(cols.xyz, cols.yzw, offset.x), vec3(1.0/9.0));\n"
-"#     else\n"
-"#      ifdef GL_EXT_gpu_shader4\n"
-"#        define texval(x, y) texture2DOffset(Texture_ShadowMap2D, center, ivec2(x, y)).r\n"
-"#      else\n"
-"#        define texval(x, y) texture2D(Texture_ShadowMap2D, center + vec2(x, y)*ShadowMap_TextureScale).r  \n"
-"#      endif\n"
-"#      if USESHADOWMAPPCF > 1\n"
-"      vec2 center = shadowmaptc.xy - 0.5, offset = fract(center);\n"
-"      center *= ShadowMap_TextureScale;\n"
-"      vec4 row1 = step(shadowmaptc.z, vec4(texval(-1.0, -1.0), texval( 0.0, -1.0), texval( 1.0, -1.0), texval( 2.0, -1.0)));\n"
-"      vec4 row2 = step(shadowmaptc.z, vec4(texval(-1.0,  0.0), texval( 0.0,  0.0), texval( 1.0,  0.0), texval( 2.0,  0.0)));\n"
-"      vec4 row3 = step(shadowmaptc.z, vec4(texval(-1.0,  1.0), texval( 0.0,  1.0), texval( 1.0,  1.0), texval( 2.0,  1.0)));\n"
-"      vec4 row4 = step(shadowmaptc.z, vec4(texval(-1.0,  2.0), texval( 0.0,  2.0), texval( 1.0,  2.0), texval( 2.0,  2.0)));\n"
-"      vec4 cols = row2 + row3 + mix(row1, row4, offset.y);\n"
-"      f = dot(mix(cols.xyz, cols.yzw, offset.x), vec3(1.0/9.0));\n"
-"#      else\n"
-"      vec2 center = shadowmaptc.xy*ShadowMap_TextureScale, offset = fract(shadowmaptc.xy);\n"
-"      vec3 row1 = step(shadowmaptc.z, vec3(texval(-1.0, -1.0), texval( 0.0, -1.0), texval( 1.0, -1.0)));\n"
-"      vec3 row2 = step(shadowmaptc.z, vec3(texval(-1.0,  0.0), texval( 0.0,  0.0), texval( 1.0,  0.0)));\n"
-"      vec3 row3 = step(shadowmaptc.z, vec3(texval(-1.0,  1.0), texval( 0.0,  1.0), texval( 1.0,  1.0)));\n"
-"      vec3 cols = row2 + mix(row1, row3, offset.y);\n"
-"      f = dot(mix(cols.xy, cols.yz, offset.x), vec2(0.25));\n"
-"#      endif\n"
-"#     endif\n"
-"#    else\n"
-"      f = step(shadowmaptc.z, texture2D(Texture_ShadowMap2D, shadowmaptc.xy*ShadowMap_TextureScale).r);\n"
-"#    endif\n"
-"#  endif\n"
-"      return f;\n"
-"}\n"
-"# endif\n"
-"\n"
-"# ifdef USESHADOWMAPCUBE\n"
-"float ShadowMapCompare(vec3 dir)\n"
-"{\n"
-"      // apply depth texture cubemap as light filter\n"
-"      vec4 shadowmaptc = GetShadowMapTCCube(dir);\n"
-"      float f;\n"
-"#  ifdef USESHADOWSAMPLER\n"
-"      f = shadowCube(Texture_ShadowMapCube, shadowmaptc).r;\n"
-"#  else\n"
-"      f = step(shadowmaptc.w, textureCube(Texture_ShadowMapCube, shadowmaptc.xyz).r);\n"
-"#  endif\n"
-"      return f;\n"
-"}\n"
-"# endif\n"
-"#endif // !defined(MODE_LIGHTSOURCE) && !defined(MODE_DEFERREDLIGHTSOURCE)\n"
-"#endif // FRAGMENT_SHADER\n"
-"\n"
-"\n"
-"\n"
-"\n"
-"#ifdef MODE_DEFERREDGEOMETRY\n"
-"#ifdef VERTEX_SHADER\n"
-"uniform mat4 TexMatrix;\n"
-"#ifdef USEVERTEXTEXTUREBLEND\n"
-"uniform mat4 BackgroundTexMatrix;\n"
-"#endif\n"
-"uniform mat4 ModelViewMatrix;\n"
-"void main(void)\n"
-"{\n"
-"      TexCoord = vec2(TexMatrix * gl_MultiTexCoord0);\n"
-"#ifdef USEVERTEXTEXTUREBLEND\n"
-"      gl_FrontColor = gl_Color;\n"
-"      TexCoord2 = vec2(BackgroundTexMatrix * gl_MultiTexCoord0);\n"
-"#endif\n"
-"\n"
-"      // transform unnormalized eye direction into tangent space\n"
-"#ifdef USEOFFSETMAPPING\n"
-"      vec3 EyeVectorModelSpace = EyePosition - gl_Vertex.xyz;\n"
-"      EyeVector.x = dot(EyeVectorModelSpace, gl_MultiTexCoord1.xyz);\n"
-"      EyeVector.y = dot(EyeVectorModelSpace, gl_MultiTexCoord2.xyz);\n"
-"      EyeVector.z = dot(EyeVectorModelSpace, gl_MultiTexCoord3.xyz);\n"
-"#endif\n"
-"\n"
-"      VectorS = (ModelViewMatrix * vec4(gl_MultiTexCoord1.xyz, 0)).xyz;\n"
-"      VectorT = (ModelViewMatrix * vec4(gl_MultiTexCoord2.xyz, 0)).xyz;\n"
-"      VectorR = (ModelViewMatrix * vec4(gl_MultiTexCoord3.xyz, 0)).xyz;\n"
-"      gl_Position = ModelViewProjectionMatrix * gl_Vertex;\n"
-"}\n"
-"#endif // VERTEX_SHADER\n"
-"\n"
-"#ifdef FRAGMENT_SHADER\n"
-"void main(void)\n"
-"{\n"
-"#ifdef USEOFFSETMAPPING\n"
-"      // apply offsetmapping\n"
-"      vec2 TexCoordOffset = OffsetMapping(TexCoord);\n"
-"#define TexCoord TexCoordOffset\n"
-"#endif\n"
-"\n"
-"#ifdef USEALPHAKILL\n"
-"      if (texture2D(Texture_Color, TexCoord).a < 0.5)\n"
-"              discard;\n"
-"#endif\n"
-"\n"
-"#ifdef USEVERTEXTEXTUREBLEND\n"
-"      float alpha = texture2D(Texture_Color, TexCoord).a;\n"
-"      float terrainblend = clamp(float(gl_Color.a) * alpha * 2.0 - 0.5, float(0.0), float(1.0));\n"
-"      //float terrainblend = min(float(gl_Color.a) * alpha * 2.0, float(1.0));\n"
-"      //float terrainblend = float(gl_Color.a) * alpha > 0.5;\n"
-"#endif\n"
-"\n"
-"#ifdef USEVERTEXTEXTUREBLEND\n"
-"      vec3 surfacenormal = mix(vec3(texture2D(Texture_SecondaryNormal, TexCoord2)), vec3(texture2D(Texture_Normal, TexCoord)), terrainblend) - vec3(0.5, 0.5, 0.5);\n"
-"      float a = mix(texture2D(Texture_SecondaryGloss, TexCoord2).a, texture2D(Texture_Gloss, TexCoord).a, terrainblend);\n"
-"#else\n"
-"      vec3 surfacenormal = vec3(texture2D(Texture_Normal, TexCoord)) - vec3(0.5, 0.5, 0.5);\n"
-"      float a = texture2D(Texture_Gloss, TexCoord).a;\n"
-"#endif\n"
-"\n"
-"      gl_FragColor = vec4(normalize(surfacenormal.x * VectorS + surfacenormal.y * VectorT + surfacenormal.z * VectorR) * 0.5 + vec3(0.5, 0.5, 0.5), a);\n"
-"}\n"
-"#endif // FRAGMENT_SHADER\n"
-"#else // !MODE_DEFERREDGEOMETRY\n"
-"\n"
-"\n"
-"\n"
-"\n"
-"#ifdef MODE_DEFERREDLIGHTSOURCE\n"
-"#ifdef VERTEX_SHADER\n"
-"uniform mat4 ModelViewMatrix;\n"
-"void main(void)\n"
-"{\n"
-"      ModelViewPosition = ModelViewMatrix * gl_Vertex;\n"
-"      gl_Position = ModelViewProjectionMatrix * gl_Vertex;\n"
-"}\n"
-"#endif // VERTEX_SHADER\n"
-"\n"
-"#ifdef FRAGMENT_SHADER\n"
-"uniform mat4 ViewToLight;\n"
-"// ScreenToDepth = vec2(Far / (Far - Near), Far * Near / (Near - Far));\n"
-"uniform vec2 ScreenToDepth;\n"
-"uniform myhalf3 DeferredColor_Ambient;\n"
-"uniform myhalf3 DeferredColor_Diffuse;\n"
-"#ifdef USESPECULAR\n"
-"uniform myhalf3 DeferredColor_Specular;\n"
-"uniform myhalf SpecularPower;\n"
-"#endif\n"
-"uniform myhalf2 PixelToScreenTexCoord;\n"
-"void main(void)\n"
-"{\n"
-"      // calculate viewspace pixel position\n"
-"      vec2 ScreenTexCoord = gl_FragCoord.xy * PixelToScreenTexCoord;\n"
-"      vec3 position;\n"
-"      position.z = ScreenToDepth.y / (texture2D(Texture_ScreenDepth, ScreenTexCoord).r + ScreenToDepth.x);\n"
-"      position.xy = ModelViewPosition.xy * (position.z / ModelViewPosition.z);\n"
-"      // decode viewspace pixel normal\n"
-"      myhalf4 normalmap = texture2D(Texture_ScreenNormalMap, ScreenTexCoord);\n"
-"      myhalf3 surfacenormal = normalize(normalmap.rgb - myhalf3(0.5,0.5,0.5));\n"
-"      // surfacenormal = pixel normal in viewspace\n"
-"      // LightVector = pixel to light in viewspace\n"
-"      // CubeVector = position in lightspace\n"
-"      // eyevector = pixel to view in viewspace\n"
-"      vec3 CubeVector = vec3(ViewToLight * vec4(position,1));\n"
-"      myhalf fade = myhalf(texture2D(Texture_Attenuation, vec2(length(CubeVector), 0.0)));\n"
-"#ifdef USEDIFFUSE\n"
-"      // calculate diffuse shading\n"
-"      myhalf3 lightnormal = myhalf3(normalize(LightPosition - position));\n"
-"      myhalf diffuse = myhalf(max(float(dot(surfacenormal, lightnormal)), 0.0));\n"
-"#endif\n"
-"#ifdef USESPECULAR\n"
-"      // calculate directional shading\n"
-"      vec3 eyevector = position * -1.0;\n"
-"#  ifdef USEEXACTSPECULARMATH\n"
-"      myhalf specular = pow(myhalf(max(float(dot(reflect(lightnormal, surfacenormal), normalize(eyevector)))*-1.0, 0.0)), SpecularPower * normalmap.a);\n"
-"#  else\n"
-"      myhalf3 specularnormal = normalize(lightnormal + myhalf3(normalize(eyevector)));\n"
-"      myhalf specular = pow(myhalf(max(float(dot(surfacenormal, specularnormal)), 0.0)), SpecularPower * normalmap.a);\n"
-"#  endif\n"
-"#endif\n"
-"\n"
-"#if defined(USESHADOWMAPRECT) || defined(USESHADOWMAPCUBE) || defined(USESHADOWMAP2D)\n"
-"      fade *= ShadowMapCompare(CubeVector);\n"
-"#endif\n"
-"\n"
-"#ifdef USEDIFFUSE\n"
-"      gl_FragData[0] = vec4((DeferredColor_Ambient + DeferredColor_Diffuse * diffuse) * fade, 1.0);\n"
-"#else\n"
-"      gl_FragData[0] = vec4(DeferredColor_Ambient * fade, 1.0);\n"
-"#endif\n"
-"#ifdef USESPECULAR\n"
-"      gl_FragData[1] = vec4(DeferredColor_Specular * (specular * fade), 1.0);\n"
-"#else\n"
-"      gl_FragData[1] = vec4(0.0, 0.0, 0.0, 1.0);\n"
-"#endif\n"
-"\n"
-"# ifdef USECUBEFILTER\n"
-"      vec3 cubecolor = textureCube(Texture_Cube, CubeVector).rgb;\n"
-"      gl_FragData[0].rgb *= cubecolor;\n"
-"      gl_FragData[1].rgb *= cubecolor;\n"
-"# endif\n"
-"}\n"
-"#endif // FRAGMENT_SHADER\n"
-"#else // !MODE_DEFERREDLIGHTSOURCE\n"
-"\n"
-"\n"
-"\n"
-"\n"
-"#ifdef VERTEX_SHADER\n"
-"uniform mat4 TexMatrix;\n"
-"#ifdef USEVERTEXTEXTUREBLEND\n"
-"uniform mat4 BackgroundTexMatrix;\n"
-"#endif\n"
-"#ifdef MODE_LIGHTSOURCE\n"
-"uniform mat4 ModelToLight;\n"
-"#endif\n"
-"void main(void)\n"
-"{\n"
-"#if defined(MODE_VERTEXCOLOR) || defined(USEVERTEXTEXTUREBLEND)\n"
-"      gl_FrontColor = gl_Color;\n"
-"#endif\n"
-"      // copy the surface texcoord\n"
-"      TexCoord = vec2(TexMatrix * gl_MultiTexCoord0);\n"
-"#ifdef USEVERTEXTEXTUREBLEND\n"
-"      TexCoord2 = vec2(BackgroundTexMatrix * gl_MultiTexCoord0);\n"
-"#endif\n"
-"#ifdef USELIGHTMAP\n"
-"      TexCoordLightmap = vec2(gl_MultiTexCoord4);\n"
-"#endif\n"
-"\n"
-"#ifdef MODE_LIGHTSOURCE\n"
-"      // transform vertex position into light attenuation/cubemap space\n"
-"      // (-1 to +1 across the light box)\n"
-"      CubeVector = vec3(ModelToLight * gl_Vertex);\n"
-"\n"
-"# ifdef USEDIFFUSE\n"
-"      // transform unnormalized light direction into tangent space\n"
-"      // (we use unnormalized to ensure that it interpolates correctly and then\n"
-"      //  normalize it per pixel)\n"
-"      vec3 lightminusvertex = LightPosition - gl_Vertex.xyz;\n"
-"      LightVector.x = dot(lightminusvertex, gl_MultiTexCoord1.xyz);\n"
-"      LightVector.y = dot(lightminusvertex, gl_MultiTexCoord2.xyz);\n"
-"      LightVector.z = dot(lightminusvertex, gl_MultiTexCoord3.xyz);\n"
-"# endif\n"
-"#endif\n"
-"\n"
-"#if defined(MODE_LIGHTDIRECTION) && defined(USEDIFFUSE)\n"
-"      LightVector.x = dot(LightDir, gl_MultiTexCoord1.xyz);\n"
-"      LightVector.y = dot(LightDir, gl_MultiTexCoord2.xyz);\n"
-"      LightVector.z = dot(LightDir, gl_MultiTexCoord3.xyz);\n"
-"#endif\n"
-"\n"
-"      // transform unnormalized eye direction into tangent space\n"
-"#ifdef USEEYEVECTOR\n"
-"      vec3 EyeVectorModelSpace = EyePosition - gl_Vertex.xyz;\n"
-"      EyeVector.x = dot(EyeVectorModelSpace, gl_MultiTexCoord1.xyz);\n"
-"      EyeVector.y = dot(EyeVectorModelSpace, gl_MultiTexCoord2.xyz);\n"
-"      EyeVector.z = dot(EyeVectorModelSpace, gl_MultiTexCoord3.xyz);\n"
-"#endif\n"
-"\n"
-"#ifdef USEFOG\n"
-"      EyeVectorModelSpaceFogPlaneVertexDist.xyz = EyePosition - gl_Vertex.xyz;\n"
-"      EyeVectorModelSpaceFogPlaneVertexDist.w = dot(FogPlane, gl_Vertex);\n"
-"#endif\n"
-"\n"
-"#if defined(MODE_LIGHTDIRECTIONMAP_MODELSPACE) || defined(USEREFLECTCUBE)\n"
-"      VectorS = gl_MultiTexCoord1.xyz;\n"
-"      VectorT = gl_MultiTexCoord2.xyz;\n"
-"      VectorR = gl_MultiTexCoord3.xyz;\n"
-"#endif\n"
-"\n"
-"      // transform vertex to camera space, using ftransform to match non-VS rendering\n"
-"      gl_Position = ModelViewProjectionMatrix * gl_Vertex;\n"
-"\n"
-"#ifdef USEREFLECTION\n"
-"      ModelViewProjectionPosition = gl_Position;\n"
-"#endif\n"
-"}\n"
-"#endif // VERTEX_SHADER\n"
-"\n"
-"\n"
-"\n"
-"\n"
-"#ifdef FRAGMENT_SHADER\n"
-"#ifdef USEDEFERREDLIGHTMAP\n"
-"uniform myhalf2 PixelToScreenTexCoord;\n"
-"uniform myhalf3 DeferredMod_Diffuse;\n"
-"uniform myhalf3 DeferredMod_Specular;\n"
-"#endif\n"
-"uniform myhalf3 Color_Ambient;\n"
-"uniform myhalf3 Color_Diffuse;\n"
-"uniform myhalf3 Color_Specular;\n"
-"uniform myhalf SpecularPower;\n"
-"#ifdef USEGLOW\n"
-"uniform myhalf3 Color_Glow;\n"
-"#endif\n"
-"uniform myhalf Alpha;\n"
-"#ifdef USEREFLECTION\n"
-"uniform vec4 DistortScaleRefractReflect;\n"
-"uniform vec4 ScreenScaleRefractReflect;\n"
-"uniform vec4 ScreenCenterRefractReflect;\n"
-"uniform myhalf4 ReflectColor;\n"
-"#endif\n"
-"#ifdef USEREFLECTCUBE\n"
-"uniform mat4 ModelToReflectCube;\n"
-"uniform sampler2D Texture_ReflectMask;\n"
-"uniform samplerCube Texture_ReflectCube;\n"
-"#endif\n"
-"#ifdef MODE_LIGHTDIRECTION\n"
-"uniform myhalf3 LightColor;\n"
-"#endif\n"
-"#ifdef MODE_LIGHTSOURCE\n"
-"uniform myhalf3 LightColor;\n"
-"#endif\n"
-"void main(void)\n"
-"{\n"
-"#ifdef USEOFFSETMAPPING\n"
-"      // apply offsetmapping\n"
-"      vec2 TexCoordOffset = OffsetMapping(TexCoord);\n"
-"#define TexCoord TexCoordOffset\n"
-"#endif\n"
-"\n"
-"      // combine the diffuse textures (base, pants, shirt)\n"
-"      myhalf4 color = myhalf4(texture2D(Texture_Color, TexCoord));\n"
-"#ifdef USEALPHAKILL\n"
-"      if (color.a < 0.5)\n"
-"              discard;\n"
-"#endif\n"
-"      color.a *= Alpha;\n"
-"#ifdef USECOLORMAPPING\n"
-"      color.rgb += myhalf3(texture2D(Texture_Pants, TexCoord)) * Color_Pants + myhalf3(texture2D(Texture_Shirt, TexCoord)) * Color_Shirt;\n"
-"#endif\n"
-"#ifdef USEVERTEXTEXTUREBLEND\n"
-"      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, TexCoord2)), color.rgb, terrainblend);\n"
-"      color.a = 1.0;\n"
-"      //color = mix(myhalf4(1, 0, 0, 1), color, terrainblend);\n"
-"#endif\n"
-"\n"
-"      // get the surface normal\n"
-"#ifdef USEVERTEXTEXTUREBLEND\n"
-"      myhalf3 surfacenormal = normalize(mix(myhalf3(texture2D(Texture_SecondaryNormal, TexCoord2)), myhalf3(texture2D(Texture_Normal, TexCoord)), terrainblend) - myhalf3(0.5, 0.5, 0.5));\n"
-"#else\n"
-"      myhalf3 surfacenormal = normalize(myhalf3(texture2D(Texture_Normal, TexCoord)) - myhalf3(0.5, 0.5, 0.5));\n"
-"#endif\n"
-"\n"
-"      // get the material colors\n"
-"      myhalf3 diffusetex = color.rgb;\n"
-"#if defined(USESPECULAR) || defined(USEDEFERREDLIGHTMAP)\n"
-"# ifdef USEVERTEXTEXTUREBLEND\n"
-"      myhalf4 glosstex = mix(myhalf4(texture2D(Texture_SecondaryGloss, TexCoord2)), myhalf4(texture2D(Texture_Gloss, TexCoord)), terrainblend);\n"
-"# else\n"
-"      myhalf4 glosstex = myhalf4(texture2D(Texture_Gloss, TexCoord));\n"
-"# endif\n"
-"#endif\n"
-"\n"
-"#ifdef USEREFLECTCUBE\n"
-"      vec3 TangentReflectVector = reflect(-EyeVector, surfacenormal);\n"
-"      vec3 ModelReflectVector = TangentReflectVector.x * VectorS + TangentReflectVector.y * VectorT + TangentReflectVector.z * VectorR;\n"
-"      vec3 ReflectCubeTexCoord = vec3(ModelToReflectCube * vec4(ModelReflectVector, 0));\n"
-"      diffusetex += myhalf3(texture2D(Texture_ReflectMask, TexCoord)) * myhalf3(textureCube(Texture_ReflectCube, ReflectCubeTexCoord));\n"
-"#endif\n"
-"\n"
-"\n"
-"\n"
-"\n"
-"#ifdef MODE_LIGHTSOURCE\n"
-"      // light source\n"
-"#ifdef USEDIFFUSE\n"
-"      myhalf3 lightnormal = myhalf3(normalize(LightVector));\n"
-"      myhalf diffuse = myhalf(max(float(dot(surfacenormal, lightnormal)), 0.0));\n"
-"      color.rgb = diffusetex * (Color_Ambient + diffuse * Color_Diffuse);\n"
-"#ifdef USESPECULAR\n"
-"#ifdef USEEXACTSPECULARMATH\n"
-"      myhalf specular = pow(myhalf(max(float(dot(reflect(lightnormal, surfacenormal), normalize(EyeVector)))*-1.0, 0.0)), SpecularPower * glosstex.a);\n"
-"#else\n"
-"      myhalf3 specularnormal = normalize(lightnormal + myhalf3(normalize(EyeVector)));\n"
-"      myhalf specular = pow(myhalf(max(float(dot(surfacenormal, specularnormal)), 0.0)), SpecularPower * glosstex.a);\n"
-"#endif\n"
-"      color.rgb += glosstex.rgb * (specular * Color_Specular);\n"
-"#endif\n"
-"#else\n"
-"      color.rgb = diffusetex * Color_Ambient;\n"
-"#endif\n"
-"      color.rgb *= LightColor;\n"
-"      color.rgb *= myhalf(texture2D(Texture_Attenuation, vec2(length(CubeVector), 0.0)));\n"
-"#if defined(USESHADOWMAPRECT) || defined(USESHADOWMAPCUBE) || defined(USESHADOWMAP2D)\n"
-"      color.rgb *= ShadowMapCompare(CubeVector);\n"
-"#endif\n"
-"# ifdef USECUBEFILTER\n"
-"      color.rgb *= myhalf3(textureCube(Texture_Cube, CubeVector));\n"
-"# endif\n"
-"#endif // MODE_LIGHTSOURCE\n"
-"\n"
-"\n"
-"\n"
-"\n"
-"#ifdef MODE_LIGHTDIRECTION\n"
-"#define SHADING\n"
-"#ifdef USEDIFFUSE\n"
-"      myhalf3 lightnormal = myhalf3(normalize(LightVector));\n"
-"#endif\n"
-"#define lightcolor LightColor\n"
-"#endif // MODE_LIGHTDIRECTION\n"
-"#ifdef MODE_LIGHTDIRECTIONMAP_MODELSPACE\n"
-"#define SHADING\n"
-"      // deluxemap lightmapping using light vectors in modelspace (q3map2 -light -deluxe)\n"
-"      myhalf3 lightnormal_modelspace = myhalf3(texture2D(Texture_Deluxemap, TexCoordLightmap)) * 2.0 + myhalf3(-1.0, -1.0, -1.0);\n"
-"      myhalf3 lightcolor = myhalf3(texture2D(Texture_Lightmap, TexCoordLightmap));\n"
-"      // convert modelspace light vector to tangentspace\n"
-"      myhalf3 lightnormal;\n"
-"      lightnormal.x = dot(lightnormal_modelspace, myhalf3(VectorS));\n"
-"      lightnormal.y = dot(lightnormal_modelspace, myhalf3(VectorT));\n"
-"      lightnormal.z = dot(lightnormal_modelspace, myhalf3(VectorR));\n"
-"      // calculate directional shading (and undoing the existing angle attenuation on the lightmap by the division)\n"
-"      // note that q3map2 is too stupid to calculate proper surface normals when q3map_nonplanar\n"
-"      // is used (the lightmap and deluxemap coords correspond to virtually random coordinates\n"
-"      // on that luxel, and NOT to its center, because recursive triangle subdivision is used\n"
-"      // to map the luxels to coordinates on the draw surfaces), which also causes\n"
-"      // deluxemaps to be wrong because light contributions from the wrong side of the surface\n"
-"      // are added up. To prevent divisions by zero or strong exaggerations, a max()\n"
-"      // nudge is done here at expense of some additional fps. This is ONLY needed for\n"
-"      // deluxemaps, tangentspace deluxemap avoid this problem by design.\n"
-"      lightcolor *= 1.0 / max(0.25, lightnormal.z);\n"
-"#endif // MODE_LIGHTDIRECTIONMAP_MODELSPACE\n"
-"#ifdef MODE_LIGHTDIRECTIONMAP_TANGENTSPACE\n"
-"#define SHADING\n"
-"      // deluxemap lightmapping using light vectors in tangentspace (hmap2 -light)\n"
-"      myhalf3 lightnormal = myhalf3(texture2D(Texture_Deluxemap, TexCoordLightmap)) * 2.0 + myhalf3(-1.0, -1.0, -1.0);\n"
-"      myhalf3 lightcolor = myhalf3(texture2D(Texture_Lightmap, TexCoordLightmap));\n"
-"#endif\n"
-"\n"
-"\n"
-"\n"
-"\n"
-"#ifdef MODE_LIGHTMAP\n"
-"      color.rgb = diffusetex * (Color_Ambient + myhalf3(texture2D(Texture_Lightmap, TexCoordLightmap)) * Color_Diffuse);\n"
-"#endif // MODE_LIGHTMAP\n"
-"#ifdef MODE_VERTEXCOLOR\n"
-"      color.rgb = diffusetex * (Color_Ambient + myhalf3(gl_Color.rgb) * Color_Diffuse);\n"
-"#endif // MODE_VERTEXCOLOR\n"
-"#ifdef MODE_FLATCOLOR\n"
-"      color.rgb = diffusetex * Color_Ambient;\n"
-"#endif // MODE_FLATCOLOR\n"
-"\n"
-"\n"
-"\n"
-"\n"
-"#ifdef SHADING\n"
-"# ifdef USEDIFFUSE\n"
-"      myhalf diffuse = myhalf(max(float(dot(surfacenormal, lightnormal)), 0.0));\n"
-"#  ifdef USESPECULAR\n"
-"#   ifdef USEEXACTSPECULARMATH\n"
-"      myhalf specular = pow(myhalf(max(float(dot(reflect(lightnormal, surfacenormal), normalize(EyeVector)))*-1.0, 0.0)), SpecularPower * glosstex.a);\n"
-"#   else\n"
-"      myhalf3 specularnormal = normalize(lightnormal + myhalf3(normalize(EyeVector)));\n"
-"      myhalf specular = pow(myhalf(max(float(dot(surfacenormal, specularnormal)), 0.0)), SpecularPower * glosstex.a);\n"
-"#   endif\n"
-"      color.rgb = diffusetex * Color_Ambient + (diffusetex * Color_Diffuse * diffuse + glosstex.rgb * Color_Specular * specular) * lightcolor;\n"
-"#  else\n"
-"      color.rgb = diffusetex * (Color_Ambient + Color_Diffuse * diffuse * lightcolor);\n"
-"#  endif\n"
-"# else\n"
-"      color.rgb = diffusetex * Color_Ambient;\n"
-"# endif\n"
-"#endif\n"
-"\n"
-"#ifdef USEDEFERREDLIGHTMAP\n"
-"      vec2 ScreenTexCoord = gl_FragCoord.xy * PixelToScreenTexCoord;\n"
-"      color.rgb += diffusetex * myhalf3(texture2D(Texture_ScreenDiffuse, ScreenTexCoord)) * DeferredMod_Diffuse;\n"
-"      color.rgb += glosstex.rgb * myhalf3(texture2D(Texture_ScreenSpecular, ScreenTexCoord)) * DeferredMod_Specular;\n"
-"#endif\n"
-"\n"
-"#ifdef USEGLOW\n"
-"#ifdef USEVERTEXTEXTUREBLEND\n"
-"      color.rgb += mix(myhalf3(texture2D(Texture_SecondaryGlow, TexCoord2)), myhalf3(texture2D(Texture_Glow, TexCoord)), terrainblend) * Color_Glow;\n"
-"#else\n"
-"      color.rgb += myhalf3(texture2D(Texture_Glow, TexCoord)) * Color_Glow;\n"
-"#endif\n"
-"#endif\n"
-"\n"
-"#ifdef USEFOG\n"
-"#ifdef MODE_LIGHTSOURCE\n"
-"      color.rgb *= myhalf(FogVertex());\n"
-"#else\n"
-"      color.rgb = mix(FogColor, color.rgb, FogVertex());\n"
-"#endif\n"
-"#endif\n"
-"\n"
-"      // reflection must come last because it already contains exactly the correct fog (the reflection render preserves camera distance from the plane, it only flips the side) and ContrastBoost/SceneBrightness\n"
-"#ifdef USEREFLECTION\n"
-"      vec4 ScreenScaleRefractReflectIW = ScreenScaleRefractReflect * (1.0 / ModelViewProjectionPosition.w);\n"
-"      //vec4 ScreenTexCoord = (ModelViewProjectionPosition.xyxy + normalize(myhalf3(texture2D(Texture_Normal, TexCoord)) - myhalf3(0.5)).xyxy * DistortScaleRefractReflect * 100) * ScreenScaleRefractReflectIW + ScreenCenterRefractReflect;\n"
-"      vec2 SafeScreenTexCoord = ModelViewProjectionPosition.xy * ScreenScaleRefractReflectIW.zw + ScreenCenterRefractReflect.zw;\n"
-"      vec2 ScreenTexCoord = SafeScreenTexCoord + vec3(normalize(myhalf3(texture2D(Texture_Normal, TexCoord)) - myhalf3(0.5))).xy * DistortScaleRefractReflect.zw;\n"
-"      // FIXME temporary hack to detect the case that the reflection\n"
-"      // gets blackened at edges due to leaving the area that contains actual\n"
-"      // content.\n"
-"      // Remove this 'ack once we have a better way to stop this thing from\n"
-"      // 'appening.\n"
-"      float f = min(1.0, length(texture2D(Texture_Reflection, ScreenTexCoord + vec2(0.01, 0.01)).rgb) / 0.05);\n"
-"      f      *= min(1.0, length(texture2D(Texture_Reflection, ScreenTexCoord + vec2(0.01, -0.01)).rgb) / 0.05);\n"
-"      f      *= min(1.0, length(texture2D(Texture_Reflection, ScreenTexCoord + vec2(-0.01, 0.01)).rgb) / 0.05);\n"
-"      f      *= min(1.0, length(texture2D(Texture_Reflection, ScreenTexCoord + vec2(-0.01, -0.01)).rgb) / 0.05);\n"
-"      ScreenTexCoord = mix(SafeScreenTexCoord, ScreenTexCoord, f);\n"
-"      color.rgb = mix(color.rgb, myhalf3(texture2D(Texture_Reflection, ScreenTexCoord)) * ReflectColor.rgb, ReflectColor.a);\n"
-"#endif\n"
-"\n"
-"      gl_FragColor = vec4(color);\n"
-"}\n"
-"#endif // FRAGMENT_SHADER\n"
-"\n"
-"#endif // !MODE_DEFERREDLIGHTSOURCE\n"
-"#endif // !MODE_DEFERREDGEOMETRY\n"
-"#endif // !MODE_WATER\n"
-"#endif // !MODE_REFRACTION\n"
-"#endif // !MODE_BLOOMBLUR\n"
-"#endif // !MODE_GENERIC\n"
-"#endif // !MODE_POSTPROCESS\n"
-"#endif // !MODE_SHOWDEPTH\n"
-"#endif // !MODE_DEPTH_OR_SHADOW\n"
+#include "shader_glsl.h"
 ;
 
-/*
-=========================================================================================================================================================
-
-
-
-=========================================================================================================================================================
-
-
-
-=========================================================================================================================================================
-
-
-
-=========================================================================================================================================================
-
-
-
-=========================================================================================================================================================
-
-
-
-=========================================================================================================================================================
-
-
-
-=========================================================================================================================================================
-*/
-
-const char *builtincgshaderstring =
-"// ambient+diffuse+specular+normalmap+attenuation+cubemap+fog shader\n"
-"// written by Forest 'LordHavoc' Hale\n"
-"// shadowmapping enhancements by Lee 'eihrul' Salzman\n"
-"\n"
-"#if defined(USEFOGINSIDE) || defined(USEFOGOUTSIDE)\n"
-"# define USEFOG\n"
-"#endif\n"
-"#if defined(MODE_LIGHTMAP) || defined(MODE_LIGHTDIRECTIONMAP_MODELSPACE) || defined(MODE_LIGHTDIRECTIONMAP_TANGENTSPACE)\n"
-"#define USELIGHTMAP\n"
-"#endif\n"
-"#if defined(USESPECULAR) || defined(USEOFFSETMAPPING) || defined(USEREFLECTCUBE)\n"
-"#define USEEYEVECTOR\n"
-"#endif\n"
-"\n"
-"#ifdef MODE_DEPTH_OR_SHADOW\n"
-"#ifdef VERTEX_SHADER\n"
-"void main\n"
-"(\n"
-"float4 gl_Vertex : POSITION,\n"
-"uniform float4x4 ModelViewProjectionMatrix,\n"
-"out float4 gl_Position : POSITION\n"
-")\n"
-"{\n"
-"      gl_Position = mul(ModelViewProjectionMatrix, gl_Vertex);\n"
-"}\n"
-"#endif\n"
-"#else // !MODE_DEPTH_ORSHADOW\n"
-"\n"
-"\n"
-"\n"
-"\n"
-"#ifdef MODE_SHOWDEPTH\n"
-"#ifdef VERTEX_SHADER\n"
-"void main\n"
-"(\n"
-"float4 gl_Vertex : POSITION,\n"
-"uniform float4x4 ModelViewProjectionMatrix,\n"
-"out float4 gl_Position : POSITION,\n"
-"out float4 gl_FrontColor : COLOR0\n"
-")\n"
-"{\n"
-"      gl_Position = mul(ModelViewProjectionMatrix, gl_Vertex);\n"
-"      gl_FrontColor = float4(gl_Position.z, gl_Position.z, gl_Position.z, 1.0);\n"
-"}\n"
-"#endif\n"
-"\n"
-"#ifdef FRAGMENT_SHADER\n"
-"void main\n"
-"(\n"
-"float4 gl_FrontColor : COLOR0,\n"
-"out float4 gl_FragColor : COLOR\n"
-")\n"
-"{\n"
-"      gl_FragColor = gl_FrontColor;\n"
-"}\n"
-"#endif\n"
-"#else // !MODE_SHOWDEPTH\n"
-"\n"
-"\n"
-"\n"
-"\n"
-"#ifdef MODE_POSTPROCESS\n"
-"\n"
-"#ifdef VERTEX_SHADER\n"
-"void main\n"
-"(\n"
-"float4 gl_Vertex : POSITION,\n"
-"uniform float4x4 ModelViewProjectionMatrix,\n"
-"float4 gl_MultiTexCoord0 : TEXCOORD0,\n"
-"float4 gl_MultiTexCoord1 : TEXCOORD1,\n"
-"out float4 gl_Position : POSITION,\n"
-"out float2 TexCoord1 : TEXCOORD0,\n"
-"out float2 TexCoord2 : TEXCOORD1\n"
-")\n"
-"{\n"
-"      gl_Position = mul(ModelViewProjectionMatrix, gl_Vertex);\n"
-"      TexCoord1 = gl_MultiTexCoord0.xy;\n"
-"#ifdef USEBLOOM\n"
-"      TexCoord2 = gl_MultiTexCoord1.xy;\n"
-"#endif\n"
-"}\n"
-"#endif\n"
-"\n"
-"#ifdef FRAGMENT_SHADER\n"
-"void main\n"
-"(\n"
-"float2 TexCoord1 : TEXCOORD0,\n"
-"float2 TexCoord2 : TEXCOORD1,\n"
-"uniform sampler2D Texture_First,\n"
-"#ifdef USEBLOOM\n"
-"uniform sampler2D Texture_Second,\n"
-"#endif\n"
-"#ifdef USEGAMMARAMPS\n"
-"uniform sampler2D Texture_GammaRamps,\n"
-"#endif\n"
-"#ifdef USESATURATION\n"
-"uniform float Saturation,\n"
-"#endif\n"
-"#ifdef USEVIEWTINT\n"
-"uniform float4 ViewTintColor,\n"
-"#endif\n"
-"uniform float4 UserVec1,\n"
-"uniform float4 UserVec2,\n"
-"uniform float4 UserVec3,\n"
-"uniform float4 UserVec4,\n"
-"uniform float ClientTime,\n"
-"uniform float2 PixelSize,\n"
-"out float4 gl_FragColor : COLOR\n"
-")\n"
-"{\n"
-"      gl_FragColor = tex2D(Texture_First, TexCoord1);\n"
-"#ifdef USEBLOOM\n"
-"      gl_FragColor += tex2D(Texture_Second, TexCoord2);\n"
-"#endif\n"
-"#ifdef USEVIEWTINT\n"
-"      gl_FragColor = lerp(gl_FragColor, ViewTintColor, ViewTintColor.a);\n"
-"#endif\n"
-"\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 and blends it using the second component\n"
-"      gl_FragColor += tex2D(Texture_First, TexCoord1 + PixelSize*UserVec1.x*float2(-0.987688, -0.156434)) * UserVec1.y;\n"
-"      gl_FragColor += tex2D(Texture_First, TexCoord1 + PixelSize*UserVec1.x*float2(-0.156434, -0.891007)) * UserVec1.y;\n"
-"      gl_FragColor += tex2D(Texture_First, TexCoord1 + PixelSize*UserVec1.x*float2( 0.891007, -0.453990)) * UserVec1.y;\n"
-"      gl_FragColor += tex2D(Texture_First, TexCoord1 + PixelSize*UserVec1.x*float2( 0.707107,  0.707107)) * UserVec1.y;\n"
-"      gl_FragColor += tex2D(Texture_First, TexCoord1 + PixelSize*UserVec1.x*float2(-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"
-"      float y = dot(gl_FragColor.rgb, float3(0.299, 0.587, 0.114));\n"
-"      //gl_FragColor = float3(y) + (gl_FragColor.rgb - float3(y)) * Saturation;\n"
-"      gl_FragColor.rgb = lerp(float3(y), gl_FragColor.rgb, Saturation);\n"
-"#endif\n"
-"\n"
-"#ifdef USEGAMMARAMPS\n"
-"      gl_FragColor.r = tex2D(Texture_GammaRamps, float2(gl_FragColor.r, 0)).r;\n"
-"      gl_FragColor.g = tex2D(Texture_GammaRamps, float2(gl_FragColor.g, 0)).g;\n"
-"      gl_FragColor.b = tex2D(Texture_GammaRamps, float2(gl_FragColor.b, 0)).b;\n"
-"#endif\n"
-"}\n"
-"#endif\n"
-"#else // !MODE_POSTPROCESS\n"
-"\n"
-"\n"
-"\n"
-"\n"
-"#ifdef MODE_GENERIC\n"
-"#ifdef VERTEX_SHADER\n"
-"void main\n"
-"(\n"
-"float4 gl_Vertex : POSITION,\n"
-"uniform float4x4 ModelViewProjectionMatrix,\n"
-"float4 gl_Color : COLOR0,\n"
-"float4 gl_MultiTexCoord0 : TEXCOORD0,\n"
-"float4 gl_MultiTexCoord1 : TEXCOORD1,\n"
-"out float4 gl_Position : POSITION,\n"
-"out float4 gl_FrontColor : COLOR,\n"
-"out float2 TexCoord1 : TEXCOORD0,\n"
-"out float2 TexCoord2 : TEXCOORD1\n"
-")\n"
-"{\n"
-"      gl_FrontColor = gl_Color;\n"
-"#ifdef USEDIFFUSE\n"
-"      TexCoord1 = gl_MultiTexCoord0.xy;\n"
-"#endif\n"
-"#ifdef USESPECULAR\n"
-"      TexCoord2 = gl_MultiTexCoord1.xy;\n"
-"#endif\n"
-"      gl_Position = mul(ModelViewProjectionMatrix, gl_Vertex);\n"
-"}\n"
-"#endif\n"
-"\n"
-"#ifdef FRAGMENT_SHADER\n"
-"\n"
-"void main\n"
-"(\n"
-"float4 gl_FrontColor : COLOR,\n"
-"float2 TexCoord1 : TEXCOORD0,\n"
-"float2 TexCoord2 : TEXCOORD1,\n"
-"#ifdef USEDIFFUSE\n"
-"uniform sampler2D Texture_First,\n"
-"#endif\n"
-"#ifdef USESPECULAR\n"
-"uniform sampler2D Texture_Second,\n"
-"#endif\n"
-"out float4 gl_FragColor : COLOR\n"
-")\n"
-"{\n"
-"      gl_FragColor = gl_FrontColor;\n"
-"#ifdef USEDIFFUSE\n"
-"      gl_FragColor *= tex2D(Texture_First, TexCoord1);\n"
-"#endif\n"
-"\n"
-"#ifdef USESPECULAR\n"
-"      float4 tex2 = tex2D(Texture_Second, TexCoord2);\n"
-"# ifdef USECOLORMAPPING\n"
-"      gl_FragColor *= tex2;\n"
-"# endif\n"
-"# ifdef USEGLOW\n"
-"      gl_FragColor += tex2;\n"
-"# endif\n"
-"# ifdef USEVERTEXTEXTUREBLEND\n"
-"      gl_FragColor = lerp(gl_FragColor, tex2, tex2.a);\n"
-"# endif\n"
-"#endif\n"
-"}\n"
-"#endif\n"
-"#else // !MODE_GENERIC\n"
-"\n"
-"\n"
-"\n"
-"\n"
-"#ifdef MODE_BLOOMBLUR\n"
-"#ifdef VERTEX_SHADER\n"
-"void main\n"
-"(\n"
-"float4 gl_Vertex : POSITION,\n"
-"uniform float4x4 ModelViewProjectionMatrix,\n"
-"float4 gl_MultiTexCoord0 : TEXCOORD0,\n"
-"out float4 gl_Position : POSITION,\n"
-"out float2 TexCoord : TEXCOORD0\n"
-")\n"
-"{\n"
-"      TexCoord = gl_MultiTexCoord0.xy;\n"
-"      gl_Position = mul(ModelViewProjectionMatrix, gl_Vertex);\n"
-"}\n"
-"#endif\n"
-"\n"
-"#ifdef FRAGMENT_SHADER\n"
-"\n"
-"void main\n"
-"(\n"
-"float2 TexCoord : TEXCOORD0,\n"
-"uniform sampler2D Texture_First,\n"
-"uniform float4 BloomBlur_Parameters,\n"
-"out float4 gl_FragColor : COLOR\n"
-")\n"
-"{\n"
-"      int i;\n"
-"      float2 tc = TexCoord;\n"
-"      float3 color = tex2D(Texture_First, tc).rgb;\n"
-"      tc += BloomBlur_Parameters.xy;\n"
-"      for (i = 1;i < SAMPLES;i++)\n"
-"      {\n"
-"              color += tex2D(Texture_First, tc).rgb;\n"
-"              tc += BloomBlur_Parameters.xy;\n"
-"      }\n"
-"      gl_FragColor = float4(color * BloomBlur_Parameters.z + float3(BloomBlur_Parameters.w), 1);\n"
-"}\n"
-"#endif\n"
-"#else // !MODE_BLOOMBLUR\n"
-"#ifdef MODE_REFRACTION\n"
-"#ifdef VERTEX_SHADER\n"
-"void main\n"
-"(\n"
-"float4 gl_Vertex : POSITION,\n"
-"uniform float4x4 ModelViewProjectionMatrix,\n"
-"float4 gl_MultiTexCoord0 : TEXCOORD0,\n"
-"uniform float4x4 TexMatrix,\n"
-"uniform float3 EyePosition,\n"
-"out float4 gl_Position : POSITION,\n"
-"out float2 TexCoord : TEXCOORD0,\n"
-"out float3 EyeVector : TEXCOORD1,\n"
-"out float4 ModelViewProjectionPosition : TEXCOORD2\n"
-")\n"
-"{\n"
-"      TexCoord = float2(mul(TexMatrix, gl_MultiTexCoord0));\n"
-"      gl_Position = mul(ModelViewProjectionMatrix, gl_Vertex);\n"
-"      ModelViewProjectionPosition = gl_Position;\n"
-"}\n"
-"#endif\n"
-"\n"
-"#ifdef FRAGMENT_SHADER\n"
-"void main\n"
-"(\n"
-"float2 TexCoord : TEXCOORD0,\n"
-"float3 EyeVector : TEXCOORD1,\n"
-"float4 ModelViewProjectionPosition : TEXCOORD2,\n"
-"uniform sampler2D Texture_Normal,\n"
-"uniform sampler2D Texture_Refraction,\n"
-"uniform sampler2D Texture_Reflection,\n"
-"uniform float4 DistortScaleRefractReflect,\n"
-"uniform float4 ScreenScaleRefractReflect,\n"
-"uniform float4 ScreenCenterRefractReflect,\n"
-"uniform float4 RefractColor,\n"
-"out float4 gl_FragColor : COLOR\n"
-")\n"
-"{\n"
-"      float2 ScreenScaleRefractReflectIW = ScreenScaleRefractReflect.xy * (1.0 / ModelViewProjectionPosition.w);\n"
-"      //float2 ScreenTexCoord = (ModelViewProjectionPosition.xy + normalize(float3(tex2D(Texture_Normal, TexCoord)) - float3(0.5)).xy * DistortScaleRefractReflect.xy * 100) * ScreenScaleRefractReflectIW + ScreenCenterRefractReflect.xy;\n"
-"      float2 SafeScreenTexCoord = ModelViewProjectionPosition.xy * ScreenScaleRefractReflectIW + ScreenCenterRefractReflect.xy;\n"
-"      float2 ScreenTexCoord = SafeScreenTexCoord + float2(normalize(float3(tex2D(Texture_Normal, TexCoord)) - float3(0.5))).xy * DistortScaleRefractReflect.xy;\n"
-"      // FIXME temporary hack to detect the case that the reflection\n"
-"      // gets blackened at edges due to leaving the area that contains actual\n"
-"      // content.\n"
-"      // Remove this 'ack once we have a better way to stop this thing from\n"
-"      // 'appening.\n"
-"      float f = min(1.0, length(tex2D(Texture_Refraction, ScreenTexCoord + float2(0.01, 0.01)).rgb) / 0.05);\n"
-"      f      *= min(1.0, length(tex2D(Texture_Refraction, ScreenTexCoord + float2(0.01, -0.01)).rgb) / 0.05);\n"
-"      f      *= min(1.0, length(tex2D(Texture_Refraction, ScreenTexCoord + float2(-0.01, 0.01)).rgb) / 0.05);\n"
-"      f      *= min(1.0, length(tex2D(Texture_Refraction, ScreenTexCoord + float2(-0.01, -0.01)).rgb) / 0.05);\n"
-"      ScreenTexCoord = lerp(SafeScreenTexCoord, ScreenTexCoord, f);\n"
-"      gl_FragColor = tex2D(Texture_Refraction, ScreenTexCoord) * RefractColor;\n"
-"}\n"
-"#endif\n"
-"#else // !MODE_REFRACTION\n"
-"\n"
-"\n"
-"\n"
-"\n"
-"#ifdef MODE_WATER\n"
-"#ifdef VERTEX_SHADER\n"
-"\n"
-"void main\n"
-"(\n"
-"float4 gl_Vertex : POSITION,\n"
-"uniform float4x4 ModelViewProjectionMatrix,\n"
-"float4 gl_MultiTexCoord0 : TEXCOORD0,\n"
-"uniform float4x4 TexMatrix,\n"
-"uniform float3 EyePosition,\n"
-"out float4 gl_Position : POSITION,\n"
-"out float2 TexCoord : TEXCOORD0,\n"
-"out float3 EyeVector : TEXCOORD1,\n"
-"out float4 ModelViewProjectionPosition : TEXCOORD2\n"
-")\n"
-"{\n"
-"      TexCoord = float2(mul(TexMatrix, gl_MultiTexCoord0));\n"
-"      float3 EyeVectorModelSpace = EyePosition - gl_Vertex.xyz;\n"
-"      EyeVector.x = dot(EyeVectorModelSpace, gl_MultiTexCoord1.xyz);\n"
-"      EyeVector.y = dot(EyeVectorModelSpace, gl_MultiTexCoord2.xyz);\n"
-"      EyeVector.z = dot(EyeVectorModelSpace, gl_MultiTexCoord3.xyz);\n"
-"      gl_Position = mul(ModelViewProjectionMatrix, gl_Vertex);\n"
-"      ModelViewProjectionPosition = gl_Position;\n"
-"}\n"
-"#endif\n"
-"\n"
-"#ifdef FRAGMENT_SHADER\n"
-"void main\n"
-"(\n"
-"float2 TexCoord : TEXCOORD0,\n"
-"float3 EyeVector : TEXCOORD1,\n"
-"float4 ModelViewProjectionPosition : TEXCOORD2,\n"
-"uniform sampler2D Texture_Normal,\n"
-"uniform sampler2D Texture_Refraction,\n"
-"uniform sampler2D Texture_Reflection,\n"
-"uniform float4 DistortScaleRefractReflect,\n"
-"uniform float4 ScreenScaleRefractReflect,\n"
-"uniform float4 ScreenCenterRefractReflect,\n"
-"uniform float4 RefractColor,\n"
-"uniform float4 ReflectColor,\n"
-"uniform float ReflectFactor,\n"
-"uniform float ReflectOffset,\n"
-"out float4 gl_FragColor : COLOR\n"
-")\n"
-"{\n"
-"      float4 ScreenScaleRefractReflectIW = ScreenScaleRefractReflect * (1.0 / ModelViewProjectionPosition.w);\n"
-"      //float4 ScreenTexCoord = (ModelViewProjectionPosition.xyxy + normalize(float3(tex2D(Texture_Normal, TexCoord)) - float3(0.5)).xyxy * DistortScaleRefractReflect * 100) * ScreenScaleRefractReflectIW + ScreenCenterRefractReflect;\n"
-"      float4 SafeScreenTexCoord = ModelViewProjectionPosition.xyxy * ScreenScaleRefractReflectIW + ScreenCenterRefractReflect;\n"
-"      float4 ScreenTexCoord = SafeScreenTexCoord + float2(normalize(float3(tex2D(Texture_Normal, TexCoord)) - float3(0.5))).xyxy * DistortScaleRefractReflect;\n"
-"      // FIXME temporary hack to detect the case that the reflection\n"
-"      // gets blackened at edges due to leaving the area that contains actual\n"
-"      // content.\n"
-"      // Remove this 'ack once we have a better way to stop this thing from\n"
-"      // 'appening.\n"
-"      float f = min(1.0, length(tex2D(Texture_Refraction, ScreenTexCoord.xy + float2(0.01, 0.01)).rgb) / 0.05);\n"
-"      f      *= min(1.0, length(tex2D(Texture_Refraction, ScreenTexCoord.xy + float2(0.01, -0.01)).rgb) / 0.05);\n"
-"      f      *= min(1.0, length(tex2D(Texture_Refraction, ScreenTexCoord.xy + float2(-0.01, 0.01)).rgb) / 0.05);\n"
-"      f      *= min(1.0, length(tex2D(Texture_Refraction, ScreenTexCoord.xy + float2(-0.01, -0.01)).rgb) / 0.05);\n"
-"      ScreenTexCoord.xy = lerp(SafeScreenTexCoord.xy, ScreenTexCoord.xy, f);\n"
-"      f       = min(1.0, length(tex2D(Texture_Reflection, ScreenTexCoord.zw + float2(0.01, 0.01)).rgb) / 0.05);\n"
-"      f      *= min(1.0, length(tex2D(Texture_Reflection, ScreenTexCoord.zw + float2(0.01, -0.01)).rgb) / 0.05);\n"
-"      f      *= min(1.0, length(tex2D(Texture_Reflection, ScreenTexCoord.zw + float2(-0.01, 0.01)).rgb) / 0.05);\n"
-"      f      *= min(1.0, length(tex2D(Texture_Reflection, ScreenTexCoord.zw + float2(-0.01, -0.01)).rgb) / 0.05);\n"
-"      ScreenTexCoord.zw = lerp(SafeScreenTexCoord.zw, ScreenTexCoord.zw, f);\n"
-"      float Fresnel = pow(min(1.0, 1.0 - float(normalize(EyeVector).z)), 2.0) * ReflectFactor + ReflectOffset;\n"
-"      gl_FragColor = lerp(tex2D(Texture_Refraction, ScreenTexCoord.xy) * RefractColor, tex2D(Texture_Reflection, ScreenTexCoord.zw) * ReflectColor, Fresnel);\n"
-"}\n"
-"#endif\n"
-"#else // !MODE_WATER\n"
-"\n"
-"\n"
-"\n"
-"\n"
-"// TODO: get rid of tangentt (texcoord2) and use a crossproduct to regenerate it from tangents (texcoord1) and normal (texcoord3), this would require sending a 4 component texcoord1 with W as 1 or -1 according to which side the texcoord2 should be on\n"
-"\n"
-"// fragment shader specific:\n"
-"#ifdef FRAGMENT_SHADER\n"
-"\n"
-"#ifdef USEFOG\n"
-"float FogVertex(float3 EyeVectorModelSpace, float FogPlaneVertexDist, float FogRangeRecip, float FogPlaneViewDist, float FogHeightFade, sampler2D Texture_FogMask)\n"
-"{\n"
-"      float fogfrac;\n"
-"#ifdef USEFOGOUTSIDE\n"
-"      fogfrac = min(0.0, FogPlaneVertexDist) / (FogPlaneVertexDist - FogPlaneViewDist) * min(1.0, min(0.0, FogPlaneVertexDist) * FogHeightFade);\n"
-"#else\n"
-"      fogfrac = FogPlaneViewDist / (FogPlaneViewDist - max(0.0, FogPlaneVertexDist)) * min(1.0, (min(0.0, FogPlaneVertexDist) + FogPlaneViewDist) * FogHeightFade);\n"
-"#endif\n"
-"      return float(tex2D(Texture_FogMask, half2(length(EyeVectorModelSpace)*fogfrac*FogRangeRecip, 0.0)));\n"
-"}\n"
-"#endif\n"
-"\n"
-"#ifdef USEOFFSETMAPPING\n"
-"float2 OffsetMapping(float2 TexCoord, float OffsetMapping_Scale, float3 EyeVector, sampler2D Texture_Normal)\n"
-"{\n"
-"#ifdef USEOFFSETMAPPING_RELIEFMAPPING\n"
-"      // 14 sample relief mapping: linear search and then binary search\n"
-"      // this basically steps forward a small amount repeatedly until it finds\n"
-"      // itself inside solid, then jitters forward and back using decreasing\n"
-"      // amounts to find the impact\n"
-"      //float3 OffsetVector = float3(EyeVector.xy * ((1.0 / EyeVector.z) * OffsetMapping_Scale) * float2(-1, 1), -1);\n"
-"      //float3 OffsetVector = float3(normalize(EyeVector.xy) * OffsetMapping_Scale * float2(-1, 1), -1);\n"
-"      float3 OffsetVector = float3(normalize(EyeVector).xy * OffsetMapping_Scale * float2(-1, 1), -1);\n"
-"      float3 RT = float3(TexCoord, 1);\n"
-"      OffsetVector *= 0.1;\n"
-"      RT += OffsetVector *  step(tex2D(Texture_Normal, RT.xy).a, RT.z);\n"
-"      RT += OffsetVector *  step(tex2D(Texture_Normal, RT.xy).a, RT.z);\n"
-"      RT += OffsetVector *  step(tex2D(Texture_Normal, RT.xy).a, RT.z);\n"
-"      RT += OffsetVector *  step(tex2D(Texture_Normal, RT.xy).a, RT.z);\n"
-"      RT += OffsetVector *  step(tex2D(Texture_Normal, RT.xy).a, RT.z);\n"
-"      RT += OffsetVector *  step(tex2D(Texture_Normal, RT.xy).a, RT.z);\n"
-"      RT += OffsetVector *  step(tex2D(Texture_Normal, RT.xy).a, RT.z);\n"
-"      RT += OffsetVector *  step(tex2D(Texture_Normal, RT.xy).a, RT.z);\n"
-"      RT += OffsetVector *  step(tex2D(Texture_Normal, RT.xy).a, RT.z);\n"
-"      RT += OffsetVector * (step(tex2D(Texture_Normal, RT.xy).a, RT.z)          - 0.5);\n"
-"      RT += OffsetVector * (step(tex2D(Texture_Normal, RT.xy).a, RT.z) * 0.5    - 0.25);\n"
-"      RT += OffsetVector * (step(tex2D(Texture_Normal, RT.xy).a, RT.z) * 0.25   - 0.125);\n"
-"      RT += OffsetVector * (step(tex2D(Texture_Normal, RT.xy).a, RT.z) * 0.125  - 0.0625);\n"
-"      RT += OffsetVector * (step(tex2D(Texture_Normal, RT.xy).a, RT.z) * 0.0625 - 0.03125);\n"
-"      return RT.xy;\n"
-"#else\n"
-"      // 3 sample offset mapping (only 3 samples because of ATI Radeon 9500-9800/X300 limits)\n"
-"      // this basically moves forward the full distance, and then backs up based\n"
-"      // on height of samples\n"
-"      //float2 OffsetVector = float2(EyeVector.xy * ((1.0 / EyeVector.z) * OffsetMapping_Scale) * float2(-1, 1));\n"
-"      //float2 OffsetVector = float2(normalize(EyeVector.xy) * OffsetMapping_Scale * float2(-1, 1));\n"
-"      float2 OffsetVector = float2(normalize(EyeVector).xy * OffsetMapping_Scale * float2(-1, 1));\n"
-"      TexCoord += OffsetVector;\n"
-"      OffsetVector *= 0.333;\n"
-"      TexCoord -= OffsetVector * tex2D(Texture_Normal, TexCoord).a;\n"
-"      TexCoord -= OffsetVector * tex2D(Texture_Normal, TexCoord).a;\n"
-"      TexCoord -= OffsetVector * tex2D(Texture_Normal, TexCoord).a;\n"
-"      return TexCoord;\n"
-"#endif\n"
-"}\n"
-"#endif // USEOFFSETMAPPING\n"
-"\n"
-"#if defined(MODE_LIGHTSOURCE) || defined(MODE_DEFERREDLIGHTSOURCE)\n"
-"#if defined(USESHADOWMAPRECT) || defined(USESHADOWMAP2D)\n"
-"# ifndef USESHADOWMAPVSDCT\n"
-"float3 GetShadowMapTC2D(float3 dir, float4 ShadowMap_Parameters)\n"
-"{\n"
-"      float3 adir = abs(dir);\n"
-"      float2 tc;\n"
-"      float2 offset;\n"
-"      float ma;\n"
-"      if (adir.x > adir.y)\n"
-"      {\n"
-"              if (adir.x > adir.z) // X\n"
-"              {\n"
-"                      ma = adir.x;\n"
-"                      tc = dir.zy;\n"
-"                      offset = float2(lerp(0.5, 1.5, dir.x < 0.0), 0.5);\n"
-"              }\n"
-"              else // Z\n"
-"              {\n"
-"                      ma = adir.z;\n"
-"                      tc = dir.xy;\n"
-"                      offset = float2(lerp(0.5, 1.5, dir.z < 0.0), 2.5);\n"
-"              }\n"
-"      }\n"
-"      else\n"
-"      {\n"
-"              if (adir.y > adir.z) // Y\n"
-"              {\n"
-"                      ma = adir.y;\n"
-"                      tc = dir.xz;\n"
-"                      offset = float2(lerp(0.5, 1.5, dir.y < 0.0), 1.5);\n"
-"              }\n"
-"              else // Z\n"
-"              {\n"
-"                      ma = adir.z;\n"
-"                      tc = dir.xy;\n"
-"                      offset = float2(lerp(0.5, 1.5, dir.z < 0.0), 2.5);\n"
-"              }\n"
-"      }\n"
-"\n"
-"      float3 stc = float3(tc * ShadowMap_Parameters.x, ShadowMap_Parameters.w) / ma;\n"
-"      stc.xy += offset * ShadowMap_Parameters.y;\n"
-"      stc.z += ShadowMap_Parameters.z;\n"
-"      return stc;\n"
-"}\n"
-"# else\n"
-"float3 GetShadowMapTC2D(float3 dir, float4 ShadowMap_Parameters, samplerCUBE Texture_CubeProjection)\n"
-"{\n"
-"      float3 adir = abs(dir);\n"
-"      float4 proj = texCUBE(Texture_CubeProjection, dir);\n"
-"      float ma = max(max(adir.x, adir.y), adir.z);\n"
-"      float3 stc = float3(lerp(dir.xy, dir.zz, proj.xy) * ShadowMap_Parameters.x, ShadowMap_Parameters.w) / ma;\n"
-"      stc.xy += proj.zw * ShadowMap_Parameters.y;\n"
-"      stc.z += ShadowMap_Parameters.z;\n"
-"      return stc;\n"
-"}\n"
-"# endif\n"
-"#endif // defined(USESHADOWMAPRECT) || defined(USESHADOWMAP2D)\n"
-"\n"
-"#ifdef USESHADOWMAPCUBE\n"
-"float4 GetShadowMapTCCube(float3 dir, float4 ShadowMap_Parameters)\n"
-"{\n"
-"    float3 adir = abs(dir);\n"
-"    return float4(dir, ShadowMap_Parameters.z + ShadowMap_Parameters.w / max(max(adir.x, adir.y), adir.z));\n"
-"}\n"
-"#endif\n"
-"\n"
-"# ifdef USESHADOWMAPRECT\n"
-"#ifdef USESHADOWMAPVSDCT\n"
-"float ShadowMapCompare(float3 dir, samplerRECT Texture_ShadowMapRect, float4 ShadowMap_Parameters, samplerCUBE Texture_CubeProjection)\n"
-"#else\n"
-"float ShadowMapCompare(float3 dir, samplerRECT Texture_ShadowMapRect, float4 ShadowMap_Parameters)\n"
-"#endif\n"
-"{\n"
-"#ifdef USESHADOWMAPVSDCT\n"
-"      float3 shadowmaptc = GetShadowMapTC2D(dir, ShadowMap_Parameters, Texture_CubeProjection);\n"
-"#else\n"
-"      float3 shadowmaptc = GetShadowMapTC2D(dir, ShadowMap_Parameters);\n"
-"#endif\n"
-"      float f;\n"
-"#  ifdef USESHADOWSAMPLER\n"
-"\n"
-"#    ifdef USESHADOWMAPPCF\n"
-"#      define texval(x, y) shadow2DRect(Texture_ShadowMapRect, shadowmaptc + float3(x, y, 0.0)).r\n"
-"    f = dot(float4(0.25), float4(texval(-0.4, 1.0), texval(-1.0, -0.4), texval(0.4, -1.0), texval(1.0, 0.4)));\n"
-"#    else\n"
-"    f = shadow2DRect(Texture_ShadowMapRect, shadowmaptc).r;\n"
-"#    endif\n"
-"\n"
-"#  else\n"
-"\n"
-"#    ifdef USESHADOWMAPPCF\n"
-"#      if USESHADOWMAPPCF > 1\n"
-"#        define texval(x, y) texRECT(Texture_ShadowMapRect, center + float2(x, y)).r\n"
-"    float2 center = shadowmaptc.xy - 0.5, offset = frac(center);\n"
-"    float4 row1 = step(shadowmaptc.z, float4(texval(-1.0, -1.0), texval( 0.0, -1.0), texval( 1.0, -1.0), texval( 2.0, -1.0)));\n"
-"    float4 row2 = step(shadowmaptc.z, float4(texval(-1.0,  0.0), texval( 0.0,  0.0), texval( 1.0,  0.0), texval( 2.0,  0.0)));\n"
-"    float4 row3 = step(shadowmaptc.z, float4(texval(-1.0,  1.0), texval( 0.0,  1.0), texval( 1.0,  1.0), texval( 2.0,  1.0)));\n"
-"    float4 row4 = step(shadowmaptc.z, float4(texval(-1.0,  2.0), texval( 0.0,  2.0), texval( 1.0,  2.0), texval( 2.0,  2.0)));\n"
-"    float4 cols = row2 + row3 + lerp(row1, row4, offset.y);\n"
-"    f = dot(lerp(cols.xyz, cols.yzw, offset.x), float3(1.0/9.0));\n"
-"#      else\n"
-"#        define texval(x, y) texRECT(Texture_ShadowMapRect, shadowmaptc.xy + float2(x, y)).r\n"
-"    float2 offset = frac(shadowmaptc.xy);\n"
-"    float3 row1 = step(shadowmaptc.z, float3(texval(-1.0, -1.0), texval( 0.0, -1.0), texval( 1.0, -1.0)));\n"
-"    float3 row2 = step(shadowmaptc.z, float3(texval(-1.0,  0.0), texval( 0.0,  0.0), texval( 1.0,  0.0)));\n"
-"    float3 row3 = step(shadowmaptc.z, float3(texval(-1.0,  1.0), texval( 0.0,  1.0), texval( 1.0,  1.0)));\n"
-"    float3 cols = row2 + lerp(row1, row3, offset.y);\n"
-"    f = dot(lerp(cols.xy, cols.yz, offset.x), float2(0.25));\n"
-"#      endif\n"
-"#    else\n"
-"    f = step(shadowmaptc.z, texRECT(Texture_ShadowMapRect, shadowmaptc.xy).r);\n"
-"#    endif\n"
-"\n"
-"#  endif\n"
-"      return f;\n"
-"}\n"
-"# endif\n"
-"\n"
-"# ifdef USESHADOWMAP2D\n"
-"#ifdef USESHADOWMAPVSDCT\n"
-"float ShadowMapCompare(float3 dir, sampler2D Texture_ShadowMap2D, float4 ShadowMap_Parameters, float2 ShadowMap_TextureScale, samplerCUBE Texture_CubeProjection)\n"
-"#else\n"
-"float ShadowMapCompare(float3 dir, sampler2D Texture_ShadowMap2D, float4 ShadowMap_Parameters, float2 ShadowMap_TextureScale)\n"
-"#endif\n"
-"{\n"
-"#ifdef USESHADOWMAPVSDCT\n"
-"      float3 shadowmaptc = GetShadowMapTC2D(dir, ShadowMap_Parameters, Texture_CubeProjection);\n"
-"#else\n"
-"      float3 shadowmaptc = GetShadowMapTC2D(dir, ShadowMap_Parameters);\n"
-"#endif\n"
-"    float f;\n"
-"\n"
-"#  ifdef USESHADOWSAMPLER\n"
-"#    ifdef USESHADOWMAPPCF\n"
-"#      define texval(x, y) shadow2D(Texture_ShadowMap2D, float3(center + float2(x, y)*ShadowMap_TextureScale, shadowmaptc.z)).r  \n"
-"    float2 center = shadowmaptc.xy*ShadowMap_TextureScale;\n"
-"    f = dot(float4(0.25), float4(texval(-0.4, 1.0), texval(-1.0, -0.4), texval(0.4, -1.0), texval(1.0, 0.4)));\n"
-"#    else\n"
-"    f = shadow2D(Texture_ShadowMap2D, float3(shadowmaptc.xy*ShadowMap_TextureScale, shadowmaptc.z)).r;\n"
-"#    endif\n"
-"#  else\n"
-"#    ifdef USESHADOWMAPPCF\n"
-"#     if defined(GL_ARB_texture_gather) || defined(GL_AMD_texture_texture4)\n"
-"#      ifdef GL_ARB_texture_gather\n"
-"#        define texval(x, y) textureGatherOffset(Texture_ShadowMap2D, center, ivec(x, y))\n"
-"#      else\n"
-"#        define texval(x, y) texture4(Texture_ShadowMap2D, center + float2(x,y)*ShadowMap_TextureScale)\n"
-"#      endif\n"
-"    float2 center = shadowmaptc.xy - 0.5, offset = frac(center);\n"
-"    center *= ShadowMap_TextureScale;\n"
-"    float4 group1 = step(shadowmaptc.z, texval(-1.0, -1.0));\n"
-"    float4 group2 = step(shadowmaptc.z, texval( 1.0, -1.0));\n"
-"    float4 group3 = step(shadowmaptc.z, texval(-1.0,  1.0));\n"
-"    float4 group4 = step(shadowmaptc.z, texval( 1.0,  1.0));\n"
-"    float4 cols = float4(group1.rg, group2.rg) + float4(group3.ab, group4.ab) +\n"
-"                lerp(float4(group1.ab, group2.ab), float4(group3.rg, group4.rg), offset.y);\n"
-"    f = dot(lerp(cols.xyz, cols.yzw, offset.x), float3(1.0/9.0));\n"
-"#     else\n"
-"#        define texval(x, y) texDepth2D(Texture_ShadowMap2D, center + float2(x, y)*ShadowMap_TextureScale)  \n"
-"#      if USESHADOWMAPPCF > 1\n"
-"    float2 center = shadowmaptc.xy - 0.5, offset = frac(center);\n"
-"    center *= ShadowMap_TextureScale;\n"
-"    float4 row1 = step(shadowmaptc.z, float4(texval(-1.0, -1.0), texval( 0.0, -1.0), texval( 1.0, -1.0), texval( 2.0, -1.0)));\n"
-"    float4 row2 = step(shadowmaptc.z, float4(texval(-1.0,  0.0), texval( 0.0,  0.0), texval( 1.0,  0.0), texval( 2.0,  0.0)));\n"
-"    float4 row3 = step(shadowmaptc.z, float4(texval(-1.0,  1.0), texval( 0.0,  1.0), texval( 1.0,  1.0), texval( 2.0,  1.0)));\n"
-"    float4 row4 = step(shadowmaptc.z, float4(texval(-1.0,  2.0), texval( 0.0,  2.0), texval( 1.0,  2.0), texval( 2.0,  2.0)));\n"
-"    float4 cols = row2 + row3 + lerp(row1, row4, offset.y);\n"
-"    f = dot(lerp(cols.xyz, cols.yzw, offset.x), float3(1.0/9.0));\n"
-"#      else\n"
-"    float2 center = shadowmaptc.xy*ShadowMap_TextureScale, offset = frac(shadowmaptc.xy);\n"
-"    float3 row1 = step(shadowmaptc.z, float3(texval(-1.0, -1.0), texval( 0.0, -1.0), texval( 1.0, -1.0)));\n"
-"    float3 row2 = step(shadowmaptc.z, float3(texval(-1.0,  0.0), texval( 0.0,  0.0), texval( 1.0,  0.0)));\n"
-"    float3 row3 = step(shadowmaptc.z, float3(texval(-1.0,  1.0), texval( 0.0,  1.0), texval( 1.0,  1.0)));\n"
-"    float3 cols = row2 + lerp(row1, row3, offset.y);\n"
-"    f = dot(lerp(cols.xy, cols.yz, offset.x), float2(0.25));\n"
-"#      endif\n"
-"#     endif\n"
-"#    else\n"
-"    f = step(shadowmaptc.z, tex2D(Texture_ShadowMap2D, shadowmaptc.xy*ShadowMap_TextureScale).r);\n"
-"#    endif\n"
-"#  endif\n"
-"    return f;\n"
-"}\n"
-"# endif\n"
-"\n"
-"# ifdef USESHADOWMAPCUBE\n"
-"float ShadowMapCompare(float3 dir, samplerCUBE Texture_ShadowMapCube, float4 ShadowMap_Parameters)\n"
-"{\n"
-"    // apply depth texture cubemap as light filter\n"
-"    float4 shadowmaptc = GetShadowMapTCCube(dir, ShadowMap_Parameters);\n"
-"    float f;\n"
-"#  ifdef USESHADOWSAMPLER\n"
-"    f = shadowCube(Texture_ShadowMapCube, shadowmaptc).r;\n"
-"#  else\n"
-"    f = step(shadowmaptc.w, texCUBE(Texture_ShadowMapCube, shadowmaptc.xyz).r);\n"
-"#  endif\n"
-"    return f;\n"
-"}\n"
-"# endif\n"
-"#endif // !defined(MODE_LIGHTSOURCE) && !defined(MODE_DEFERREDLIGHTSOURCE)\n"
-"#endif // FRAGMENT_SHADER\n"
-"\n"
-"\n"
-"\n"
-"\n"
-"#ifdef MODE_DEFERREDGEOMETRY\n"
-"#ifdef VERTEX_SHADER\n"
-"void main\n"
-"(\n"
-"float4 gl_Vertex : POSITION,\n"
-"uniform float4x4 ModelViewProjectionMatrix,\n"
-"#ifdef USEVERTEXTEXTUREBLEND\n"
-"float4 gl_Color : COLOR0,\n"
-"#endif\n"
-"float4 gl_MultiTexCoord0 : TEXCOORD0,\n"
-"float4 gl_MultiTexCoord1 : TEXCOORD1,\n"
-"float4 gl_MultiTexCoord2 : TEXCOORD2,\n"
-"float4 gl_MultiTexCoord3 : TEXCOORD3,\n"
-"uniform float4x4 TexMatrix,\n"
-"#ifdef USEVERTEXTEXTUREBLEND\n"
-"uniform float4x4 BackgroundTexMatrix,\n"
-"#endif\n"
-"uniform float4x4 ModelViewMatrix,\n"
-"#ifdef USEOFFSETMAPPING\n"
-"uniform float3 EyePosition,\n"
-"#endif\n"
-"out float4 gl_Position : POSITION,\n"
-"out float4 gl_FrontColor : COLOR,\n"
-"out float4 TexCoordBoth : TEXCOORD0,\n"
-"#ifdef USEOFFSETMAPPING\n"
-"out float3 EyeVector : TEXCOORD2,\n"
-"#endif\n"
-"out float3 VectorS : TEXCOORD5, // direction of S texcoord (sometimes crudely called tangent)\n"
-"out float3 VectorT : TEXCOORD6, // direction of T texcoord (sometimes crudely called binormal)\n"
-"out float3 VectorR : TEXCOORD7 // direction of R texcoord (surface normal)\n"
-")\n"
-"{\n"
-"      TexCoordBoth = mul(TexMatrix, gl_MultiTexCoord0);\n"
-"#ifdef USEVERTEXTEXTUREBLEND\n"
-"      gl_FrontColor = gl_Color;\n"
-"      TexCoordBoth.zw = float2(Backgroundmul(TexMatrix, gl_MultiTexCoord0));\n"
-"#endif\n"
-"\n"
-"      // transform unnormalized eye direction into tangent space\n"
-"#ifdef USEOFFSETMAPPING\n"
-"      float3 EyeVectorModelSpace = EyePosition - gl_Vertex.xyz;\n"
-"      EyeVector.x = dot(EyeVectorModelSpace, gl_MultiTexCoord1.xyz);\n"
-"      EyeVector.y = dot(EyeVectorModelSpace, gl_MultiTexCoord2.xyz);\n"
-"      EyeVector.z = dot(EyeVectorModelSpace, gl_MultiTexCoord3.xyz);\n"
-"#endif\n"
-"\n"
-"      VectorS = mul(ModelViewMatrix, float4(gl_MultiTexCoord1.xyz, 0)).xyz;\n"
-"      VectorT = mul(ModelViewMatrix, float4(gl_MultiTexCoord2.xyz, 0)).xyz;\n"
-"      VectorR = mul(ModelViewMatrix, float4(gl_MultiTexCoord3.xyz, 0)).xyz;\n"
-"      gl_Position = mul(ModelViewProjectionMatrix, gl_Vertex);\n"
-"}\n"
-"#endif // VERTEX_SHADER\n"
-"\n"
-"#ifdef FRAGMENT_SHADER\n"
-"void main\n"
-"(\n"
-"float4 TexCoordBoth : TEXCOORD0,\n"
-"float3 EyeVector : TEXCOORD2,\n"
-"float3 VectorS : TEXCOORD5, // direction of S texcoord (sometimes crudely called tangent)\n"
-"float3 VectorT : TEXCOORD6, // direction of T texcoord (sometimes crudely called binormal)\n"
-"float3 VectorR : TEXCOORD7, // direction of R texcoord (surface normal)\n"
-"uniform sampler2D Texture_Normal,\n"
-"#ifdef USEALPHAKILL\n"
-"uniform sampler2D Texture_Color,\n"
-"#endif\n"
-"uniform sampler2D Texture_Gloss,\n"
-"#ifdef USEVERTEXTEXTUREBLEND\n"
-"uniform sampler2D Texture_SecondaryNormal,\n"
-"uniform sampler2D Texture_SecondaryGloss,\n"
-"#endif\n"
-"#ifdef USEOFFSETMAPPING\n"
-"uniform float OffsetMapping_Scale,\n"
-"#endif\n"
-"uniform half SpecularPower,\n"
-"out float4 gl_FragColor : COLOR\n"
-")\n"
-"{\n"
-"      float2 TexCoord = TexCoordBoth.xy;\n"
-"#ifdef USEOFFSETMAPPING\n"
-"      // apply offsetmapping\n"
-"      float2 TexCoordOffset = OffsetMapping(TexCoord, OffsetMapping_Scale, EyeVector, Texture_Normal);\n"
-"#define TexCoord TexCoordOffset\n"
-"#endif\n"
-"\n"
-"#ifdef USEALPHAKILL\n"
-"      if (tex2D(Texture_Color, TexCoord).a < 0.5)\n"
-"              discard;\n"
-"#endif\n"
-"\n"
-"#ifdef USEVERTEXTEXTUREBLEND\n"
-"      float alpha = tex2D(Texture_Color, TexCoord).a;\n"
-"      float terrainblend = clamp(float(gl_FrontColor.a) * alpha * 2.0 - 0.5, float(0.0), float(1.0));\n"
-"      //float terrainblend = min(float(gl_FrontColor.a) * alpha * 2.0, float(1.0));\n"
-"      //float terrainblend = float(gl_FrontColor.a) * alpha > 0.5;\n"
-"#endif\n"
-"\n"
-"#ifdef USEVERTEXTEXTUREBLEND\n"
-"      float3 surfacenormal = lerp(float3(tex2D(Texture_SecondaryNormal, TexCoord2)), float3(tex2D(Texture_Normal, TexCoord)), terrainblend) - float3(0.5, 0.5, 0.5);\n"
-"      float a = lerp(tex2D(Texture_SecondaryGloss, TexCoord2), tex2D(Texture_Gloss, TexCoord).a, terrainblend);\n"
-"#else\n"
-"      float3 surfacenormal = float3(tex2D(Texture_Normal, TexCoord)) - float3(0.5, 0.5, 0.5);\n"
-"      float a = tex2D(Texture_Gloss, TexCoord).a;\n"
-"#endif\n"
-"\n"
-"      gl_FragColor = float4(normalize(surfacenormal.x * VectorS + surfacenormal.y * VectorT + surfacenormal.z * VectorR) * 0.5 + float3(0.5, 0.5, 0.5), 1);\n"
-"}\n"
-"#endif // FRAGMENT_SHADER\n"
-"#else // !MODE_DEFERREDGEOMETRY\n"
-"\n"
-"\n"
-"\n"
-"\n"
-"#ifdef MODE_DEFERREDLIGHTSOURCE\n"
-"#ifdef VERTEX_SHADER\n"
-"void main\n"
-"(\n"
-"float4 gl_Vertex : POSITION,\n"
-"uniform float4x4 ModelViewProjectionMatrix,\n"
-"uniform float4x4 ModelViewMatrix,\n"
-"out float4 gl_Position : POSITION,\n"
-"out float4 ModelViewPosition : TEXCOORD0\n"
-")\n"
-"{\n"
-"      ModelViewPosition = mul(ModelViewMatrix, gl_Vertex);\n"
-"      gl_Position = mul(ModelViewProjectionMatrix, gl_Vertex);\n"
-"}\n"
-"#endif // VERTEX_SHADER\n"
-"\n"
-"#ifdef FRAGMENT_SHADER\n"
-"void main\n"
-"(\n"
-"float2 Pixel : WPOS,\n"
-"float4 ModelViewPosition : TEXCOORD0,\n"
-"uniform float4x4 ViewToLight,\n"
-"uniform float2 ScreenToDepth, // ScreenToDepth = float2(Far / (Far - Near), Far * Near / (Near - Far));\n"
-"uniform float3 LightPosition,\n"
-"uniform half2 PixelToScreenTexCoord,\n"
-"uniform half3 DeferredColor_Ambient,\n"
-"uniform half3 DeferredColor_Diffuse,\n"
-"#ifdef USESPECULAR\n"
-"uniform half3 DeferredColor_Specular,\n"
-"uniform half SpecularPower,\n"
-"#endif\n"
-"uniform sampler2D Texture_Attenuation,\n"
-"uniform sampler2D Texture_ScreenDepth,\n"
-"uniform sampler2D Texture_ScreenNormalMap,\n"
-"\n"
-"#ifdef USESHADOWMAPRECT\n"
-"# ifdef USESHADOWSAMPLER\n"
-"uniform samplerRECTShadow Texture_ShadowMapRect,\n"
-"# else\n"
-"uniform samplerRECT Texture_ShadowMapRect,\n"
-"# endif\n"
-"#endif\n"
-"\n"
-"#ifdef USESHADOWMAP2D\n"
-"# ifdef USESHADOWSAMPLER\n"
-"uniform sampler2DShadow Texture_ShadowMap2D,\n"
-"# else\n"
-"uniform sampler2D Texture_ShadowMap2D,\n"
-"# endif\n"
-"#endif\n"
-"\n"
-"#ifdef USESHADOWMAPVSDCT\n"
-"uniform samplerCUBE Texture_CubeProjection,\n"
-"#endif\n"
-"\n"
-"#ifdef USESHADOWMAPCUBE\n"
-"# ifdef USESHADOWSAMPLER\n"
-"uniform samplerCUBEShadow Texture_ShadowMapCube,\n"
-"# else\n"
-"uniform samplerCUBE Texture_ShadowMapCube,\n"
-"# endif\n"
-"#endif\n"
-"\n"
-"#if defined(USESHADOWMAPRECT) || defined(USESHADOWMAP2D) || defined(USESHADOWMAPCUBE)\n"
-"uniform float2 ShadowMap_TextureScale,\n"
-"uniform float4 ShadowMap_Parameters,\n"
-"#endif\n"
-"\n"
-"out float4 gl_FragData0 : COLOR0,\n"
-"out float4 gl_FragData1 : COLOR1\n"
-")\n"
-"{\n"
-"      // calculate viewspace pixel position\n"
-"      float2 ScreenTexCoord = Pixel * PixelToScreenTexCoord;\n"
-"      ScreenTexCoord.y = ScreenTexCoord.y * -1 + 1; // Cg is opposite?\n"
-"      float3 position;\n"
-"      position.z = ScreenToDepth.y / (texDepth2D(Texture_ScreenDepth, ScreenTexCoord) + ScreenToDepth.x);\n"
-"      position.xy = ModelViewPosition.xy * (position.z / ModelViewPosition.z);\n"
-"      // decode viewspace pixel normal\n"
-"      half4 normalmap = tex2D(Texture_ScreenNormalMap, ScreenTexCoord);\n"
-"      half3 surfacenormal = normalize(normalmap.rgb - half3(0.5,0.5,0.5));\n"
-"      // surfacenormal = pixel normal in viewspace\n"
-"      // LightVector = pixel to light in viewspace\n"
-"      // CubeVector = position in lightspace\n"
-"      // eyevector = pixel to view in viewspace\n"
-"      float3 CubeVector = float3(mul(ViewToLight, float4(position,1)));\n"
-"      half fade = half(tex2D(Texture_Attenuation, float2(length(CubeVector), 0.0)));\n"
-"#ifdef USEDIFFUSE\n"
-"      // calculate diffuse shading\n"
-"      half3 lightnormal = half3(normalize(LightPosition - position));\n"
-"      half diffuse = half(max(float(dot(surfacenormal, lightnormal)), 0.0));\n"
-"#endif\n"
-"#ifdef USESPECULAR\n"
-"      // calculate directional shading\n"
-"      float3 eyevector = position * -1.0;\n"
-"#  ifdef USEEXACTSPECULARMATH\n"
-"      half specular = pow(half(max(float(dot(reflect(lightnormal, surfacenormal), normalize(eyevector)))*-1.0, 0.0)), SpecularPower * normalmap.a);\n"
-"#  else\n"
-"      half3 specularnormal = normalize(lightnormal + half3(normalize(eyevector)));\n"
-"      half specular = pow(half(max(float(dot(surfacenormal, specularnormal)), 0.0)), SpecularPower * normalmap.a);\n"
-"#  endif\n"
-"#endif\n"
-"\n"
-"#if defined(USESHADOWMAP2D) || defined(USESHADOWMAPRECT) || defined(USESHADOWMAPCUBE)\n"
-"      fade *= ShadowMapCompare(CubeVector,\n"
-"# if defined(USESHADOWMAP2D)\n"
-"Texture_ShadowMap2D, ShadowMap_Parameters, ShadowMap_TextureScale\n"
-"# endif\n"
-"# if defined(USESHADOWMAPRECT)\n"
-"Texture_ShadowMapRect, ShadowMap_Parameters\n"
-"# endif\n"
-"# if defined(USESHADOWMAPCUBE)\n"
-"Texture_ShadowMapCube, ShadowMap_Parameters\n"
-"# endif\n"
-"\n"
-"#ifdef USESHADOWMAPVSDCT\n"
-", Texture_CubeProjection\n"
-"#endif\n"
-"      );\n"
-"#endif\n"
-"\n"
-"#ifdef USEDIFFUSE\n"
-"      gl_FragData0 = float4((DeferredColor_Ambient + DeferredColor_Diffuse * diffuse) * fade, 1.0);\n"
-"#else\n"
-"      gl_FragData0 = float4(DeferredColor_Ambient * fade, 1.0);\n"
-"#endif\n"
-"#ifdef USESPECULAR\n"
-"      gl_FragData1 = float4(DeferredColor_Specular * (specular * fade), 1.0);\n"
-"#else\n"
-"      gl_FragData1 = float4(0.0, 0.0, 0.0, 1.0);\n"
-"#endif\n"
-"\n"
-"# ifdef USECUBEFILTER\n"
-"      float3 cubecolor = texCUBE(Texture_Cube, CubeVector).rgb;\n"
-"      gl_FragData0.rgb *= cubecolor;\n"
-"      gl_FragData1.rgb *= cubecolor;\n"
-"# endif\n"
-"}\n"
-"#endif // FRAGMENT_SHADER\n"
-"#else // !MODE_DEFERREDLIGHTSOURCE\n"
-"\n"
-"\n"
-"\n"
-"\n"
-"#ifdef VERTEX_SHADER\n"
-"void main\n"
-"(\n"
-"float4 gl_Vertex : POSITION,\n"
-"uniform float4x4 ModelViewProjectionMatrix,\n"
-"#if defined(USEVERTEXTEXTUREBLEND) || defined(MODE_VERTEXCOLOR)\n"
-"float4 gl_Color : COLOR0,\n"
-"#endif\n"
-"float4 gl_MultiTexCoord0 : TEXCOORD0,\n"
-"float4 gl_MultiTexCoord1 : TEXCOORD1,\n"
-"float4 gl_MultiTexCoord2 : TEXCOORD2,\n"
-"float4 gl_MultiTexCoord3 : TEXCOORD3,\n"
-"float4 gl_MultiTexCoord4 : TEXCOORD4,\n"
-"\n"
-"uniform float3 EyePosition,\n"
-"uniform float4x4 TexMatrix,\n"
-"#ifdef USEVERTEXTEXTUREBLEND\n"
-"uniform float4x4 BackgroundTexMatrix,\n"
-"#endif\n"
-"#ifdef MODE_LIGHTSOURCE\n"
-"uniform float4x4 ModelToLight,\n"
-"#endif\n"
-"#ifdef MODE_LIGHTSOURCE\n"
-"uniform float3 LightPosition,\n"
-"#endif\n"
-"#ifdef MODE_LIGHTDIRECTION\n"
-"uniform float3 LightDir,\n"
-"#endif\n"
-"uniform float4 FogPlane,\n"
-"#ifdef MODE_DEFERREDLIGHTSOURCE\n"
-"uniform float3 LightPosition,\n"
-"#endif\n"
-"\n"
-"out float4 gl_FrontColor : COLOR,\n"
-"out float4 TexCoordBoth : TEXCOORD0,\n"
-"#ifdef USELIGHTMAP\n"
-"out float2 TexCoordLightmap : TEXCOORD1,\n"
-"#endif\n"
-"#ifdef USEEYEVECTOR\n"
-"out float3 EyeVector : TEXCOORD2,\n"
-"#endif\n"
-"#ifdef USEREFLECTION\n"
-"out float4 ModelViewProjectionPosition : TEXCOORD3,\n"
-"#endif\n"
-"#ifdef USEFOG\n"
-"out float4 EyeVectorModelSpaceFogPlaneVertexDist : TEXCOORD4,\n"
-"#endif\n"
-"#if defined(MODE_LIGHTSOURCE) || defined(MODE_LIGHTDIRECTION)\n"
-"out float3 LightVector : TEXCOORD5,\n"
-"#endif\n"
-"#ifdef MODE_LIGHTSOURCE\n"
-"out float3 CubeVector : TEXCOORD3,\n"
-"#endif\n"
-"#if defined(MODE_LIGHTDIRECTIONMAP_MODELSPACE) || defined(MODE_DEFERREDGEOMETRY) || defined(USEREFLECTCUBE)\n"
-"out float3 VectorS : TEXCOORD5, // direction of S texcoord (sometimes crudely called tangent)\n"
-"out float3 VectorT : TEXCOORD6, // direction of T texcoord (sometimes crudely called binormal)\n"
-"out float3 VectorR : TEXCOORD7, // direction of R texcoord (surface normal)\n"
-"#endif\n"
-"out float4 gl_Position : POSITION\n"
-")\n"
-"{\n"
-"#if defined(MODE_VERTEXCOLOR) || defined(USEVERTEXTEXTUREBLEND)\n"
-"      gl_FrontColor = gl_Color;\n"
-"#endif\n"
-"      // copy the surface texcoord\n"
-"      TexCoordBoth = mul(TexMatrix, gl_MultiTexCoord0);\n"
-"#ifdef USEVERTEXTEXTUREBLEND\n"
-"      TexCoordBoth.zw = mul(BackgroundTexMatrix, gl_MultiTexCoord0).xy;\n"
-"#endif\n"
-"#ifdef USELIGHTMAP\n"
-"      TexCoordLightmap = float2(gl_MultiTexCoord4);\n"
-"#endif\n"
-"\n"
-"#ifdef MODE_LIGHTSOURCE\n"
-"      // transform vertex position into light attenuation/cubemap space\n"
-"      // (-1 to +1 across the light box)\n"
-"      CubeVector = float3(mul(ModelToLight, gl_Vertex));\n"
-"\n"
-"# ifdef USEDIFFUSE\n"
-"      // transform unnormalized light direction into tangent space\n"
-"      // (we use unnormalized to ensure that it interpolates correctly and then\n"
-"      //  normalize it per pixel)\n"
-"      float3 lightminusvertex = LightPosition - gl_Vertex.xyz;\n"
-"      LightVector.x = dot(lightminusvertex, gl_MultiTexCoord1.xyz);\n"
-"      LightVector.y = dot(lightminusvertex, gl_MultiTexCoord2.xyz);\n"
-"      LightVector.z = dot(lightminusvertex, gl_MultiTexCoord3.xyz);\n"
-"# endif\n"
-"#endif\n"
-"\n"
-"#if defined(MODE_LIGHTDIRECTION) && defined(USEDIFFUSE)\n"
-"      LightVector.x = dot(LightDir, gl_MultiTexCoord1.xyz);\n"
-"      LightVector.y = dot(LightDir, gl_MultiTexCoord2.xyz);\n"
-"      LightVector.z = dot(LightDir, gl_MultiTexCoord3.xyz);\n"
-"#endif\n"
-"\n"
-"      // transform unnormalized eye direction into tangent space\n"
-"#ifdef USEEYEVECTOR\n"
-"      float3 EyeVectorModelSpace = EyePosition - gl_Vertex.xyz;\n"
-"      EyeVector.x = dot(EyeVectorModelSpace, gl_MultiTexCoord1.xyz);\n"
-"      EyeVector.y = dot(EyeVectorModelSpace, gl_MultiTexCoord2.xyz);\n"
-"      EyeVector.z = dot(EyeVectorModelSpace, gl_MultiTexCoord3.xyz);\n"
-"#endif\n"
-"\n"
-"#ifdef USEFOG\n"
-"      EyeVectorModelSpaceFogPlaneVertexDist.xyz = EyePosition - gl_Vertex.xyz;\n"
-"      EyeVectorModelSpaceFogPlaneVertexDist.w = dot(FogPlane, gl_Vertex);\n"
-"#endif\n"
-"\n"
-"#ifdef MODE_LIGHTDIRECTIONMAP_MODELSPACE\n"
-"      VectorS = gl_MultiTexCoord1.xyz;\n"
-"      VectorT = gl_MultiTexCoord2.xyz;\n"
-"      VectorR = gl_MultiTexCoord3.xyz;\n"
-"#endif\n"
-"\n"
-"      // transform vertex to camera space, using ftransform to match non-VS rendering\n"
-"      gl_Position = mul(ModelViewProjectionMatrix, gl_Vertex);\n"
-"\n"
-"#ifdef USEREFLECTION\n"
-"      ModelViewProjectionPosition = gl_Position;\n"
-"#endif\n"
-"}\n"
-"#endif // VERTEX_SHADER\n"
-"\n"
-"\n"
-"\n"
-"\n"
-"#ifdef FRAGMENT_SHADER\n"
-"void main\n"
-"(\n"
-"#ifdef USEDEFERREDLIGHTMAP\n"
-"float2 Pixel : WPOS,\n"
-"#endif\n"
-"float4 gl_FrontColor : COLOR,\n"
-"float4 TexCoordBoth : TEXCOORD0,\n"
-"#ifdef USELIGHTMAP\n"
-"float2 TexCoordLightmap : TEXCOORD1,\n"
-"#endif\n"
-"#ifdef USEEYEVECTOR\n"
-"float3 EyeVector : TEXCOORD2,\n"
-"#endif\n"
-"#ifdef USEREFLECTION\n"
-"float4 ModelViewProjectionPosition : TEXCOORD3,\n"
-"#endif\n"
-"#ifdef USEFOG\n"
-"float4 EyeVectorModelSpaceFogPlaneVertexDist : TEXCOORD4,\n"
-"#endif\n"
-"#if defined(MODE_LIGHTSOURCE) || defined(MODE_LIGHTDIRECTION)\n"
-"float3 LightVector : TEXCOORD5,\n"
-"#endif\n"
-"#ifdef MODE_LIGHTSOURCE\n"
-"float3 CubeVector : TEXCOORD3,\n"
-"#endif\n"
-"#ifdef MODE_DEFERREDLIGHTSOURCE\n"
-"float4 ModelViewPosition : TEXCOORD0,\n"
-"#endif\n"
-"#if defined(MODE_LIGHTDIRECTIONMAP_MODELSPACE) || defined(MODE_DEFERREDGEOMETRY) || defined(USEREFLECTCUBE)\n"
-"float3 VectorS : TEXCOORD5, // direction of S texcoord (sometimes crudely called tangent)\n"
-"float3 VectorT : TEXCOORD6, // direction of T texcoord (sometimes crudely called binormal)\n"
-"float3 VectorR : TEXCOORD7, // direction of R texcoord (surface normal)\n"
-"#endif\n"
-"\n"
-"uniform sampler2D Texture_Normal,\n"
-"uniform sampler2D Texture_Color,\n"
-"#if defined(USESPECULAR) || defined(USEDEFERREDLIGHTMAP)\n"
-"uniform sampler2D Texture_Gloss,\n"
-"#endif\n"
-"#ifdef USEGLOW\n"
-"uniform sampler2D Texture_Glow,\n"
-"#endif\n"
-"#ifdef USEVERTEXTEXTUREBLEND\n"
-"uniform sampler2D Texture_SecondaryNormal,\n"
-"uniform sampler2D Texture_SecondaryColor,\n"
-"#if defined(USESPECULAR) || defined(USEDEFERREDLIGHTMAP)\n"
-"uniform sampler2D Texture_SecondaryGloss,\n"
-"#endif\n"
-"#ifdef USEGLOW\n"
-"uniform sampler2D Texture_SecondaryGlow,\n"
-"#endif\n"
-"#endif\n"
-"#ifdef USECOLORMAPPING\n"
-"uniform sampler2D Texture_Pants,\n"
-"uniform sampler2D Texture_Shirt,\n"
-"#endif\n"
-"#ifdef USEFOG\n"
-"uniform sampler2D Texture_FogMask,\n"
-"#endif\n"
-"#ifdef USELIGHTMAP\n"
-"uniform sampler2D Texture_Lightmap,\n"
-"#endif\n"
-"#if defined(MODE_LIGHTDIRECTIONMAP_MODELSPACE) || defined(MODE_LIGHTDIRECTIONMAP_TANGENTSPACE)\n"
-"uniform sampler2D Texture_Deluxemap,\n"
-"#endif\n"
-"#ifdef USEREFLECTION\n"
-"uniform sampler2D Texture_Reflection,\n"
-"#endif\n"
-"\n"
-"#ifdef MODE_DEFERREDLIGHTSOURCE\n"
-"uniform sampler2D Texture_ScreenDepth,\n"
-"uniform sampler2D Texture_ScreenNormalMap,\n"
-"#endif\n"
-"#ifdef USEDEFERREDLIGHTMAP\n"
-"uniform sampler2D Texture_ScreenDiffuse,\n"
-"uniform sampler2D Texture_ScreenSpecular,\n"
-"#endif\n"
-"\n"
-"#ifdef USECOLORMAPPING\n"
-"uniform half3 Color_Pants,\n"
-"uniform half3 Color_Shirt,\n"
-"#endif\n"
-"#ifdef USEFOG\n"
-"uniform float3 FogColor,\n"
-"uniform float FogRangeRecip,\n"
-"uniform float FogPlaneViewDist,\n"
-"uniform float FogHeightFade,\n"
-"#endif\n"
-"\n"
-"#ifdef USEOFFSETMAPPING\n"
-"uniform float OffsetMapping_Scale,\n"
-"#endif\n"
-"\n"
-"#ifdef USEDEFERREDLIGHTMAP\n"
-"uniform half2 PixelToScreenTexCoord,\n"
-"uniform half3 DeferredMod_Diffuse,\n"
-"uniform half3 DeferredMod_Specular,\n"
-"#endif\n"
-"uniform half3 Color_Ambient,\n"
-"uniform half3 Color_Diffuse,\n"
-"uniform half3 Color_Specular,\n"
-"uniform half SpecularPower,\n"
-"#ifdef USEGLOW\n"
-"uniform half3 Color_Glow,\n"
-"#endif\n"
-"uniform half Alpha,\n"
-"#ifdef USEREFLECTION\n"
-"uniform float4 DistortScaleRefractReflect,\n"
-"uniform float4 ScreenScaleRefractReflect,\n"
-"uniform float4 ScreenCenterRefractReflect,\n"
-"uniform half4 ReflectColor,\n"
-"#endif\n"
-"#ifdef USEREFLECTCUBE\n"
-"uniform float4x4 ModelToReflectCube,\n"
-"uniform sampler2D Texture_ReflectMask,\n"
-"uniform samplerCUBE Texture_ReflectCube,\n"
-"#endif\n"
-"#ifdef MODE_LIGHTDIRECTION\n"
-"uniform half3 LightColor,\n"
-"#endif\n"
-"#ifdef MODE_LIGHTSOURCE\n"
-"uniform half3 LightColor,\n"
-"#endif\n"
-"\n"
-"#if defined(MODE_LIGHTSOURCE) || defined(MODE_DEFERREDLIGHTSOURCE)\n"
-"uniform sampler2D Texture_Attenuation,\n"
-"uniform samplerCUBE Texture_Cube,\n"
-"\n"
-"#ifdef USESHADOWMAPRECT\n"
-"# ifdef USESHADOWSAMPLER\n"
-"uniform samplerRECTShadow Texture_ShadowMapRect,\n"
-"# else\n"
-"uniform samplerRECT Texture_ShadowMapRect,\n"
-"# endif\n"
-"#endif\n"
-"\n"
-"#ifdef USESHADOWMAP2D\n"
-"# ifdef USESHADOWSAMPLER\n"
-"uniform sampler2DShadow Texture_ShadowMap2D,\n"
-"# else\n"
-"uniform sampler2D Texture_ShadowMap2D,\n"
-"# endif\n"
-"#endif\n"
-"\n"
-"#ifdef USESHADOWMAPVSDCT\n"
-"uniform samplerCUBE Texture_CubeProjection,\n"
-"#endif\n"
-"\n"
-"#ifdef USESHADOWMAPCUBE\n"
-"# ifdef USESHADOWSAMPLER\n"
-"uniform samplerCUBEShadow Texture_ShadowMapCube,\n"
-"# else\n"
-"uniform samplerCUBE Texture_ShadowMapCube,\n"
-"# endif\n"
-"#endif\n"
-"\n"
-"#if defined(USESHADOWMAPRECT) || defined(USESHADOWMAP2D) || defined(USESHADOWMAPCUBE)\n"
-"uniform float2 ShadowMap_TextureScale,\n"
-"uniform float4 ShadowMap_Parameters,\n"
-"#endif\n"
-"#endif // !defined(MODE_LIGHTSOURCE) && !defined(MODE_DEFERREDLIGHTSOURCE)\n"
-"\n"
-"out float4 gl_FragColor : COLOR\n"
-")\n"
-"{\n"
-"      float2 TexCoord = TexCoordBoth.xy;\n"
-"#ifdef USEVERTEXTEXTUREBLEND\n"
-"      float2 TexCoord2 = TexCoordBoth.zw;\n"
-"#endif\n"
-"#ifdef USEOFFSETMAPPING\n"
-"      // apply offsetmapping\n"
-"      float2 TexCoordOffset = OffsetMapping(TexCoord, OffsetMapping_Scale, EyeVector, Texture_Normal);\n"
-"#define TexCoord TexCoordOffset\n"
-"#endif\n"
-"\n"
-"      // combine the diffuse textures (base, pants, shirt)\n"
-"      half4 color = half4(tex2D(Texture_Color, TexCoord));\n"
-"#ifdef USEALPHAKILL\n"
-"      if (color.a < 0.5)\n"
-"              discard;\n"
-"#endif\n"
-"      color.a *= Alpha;\n"
-"#ifdef USECOLORMAPPING\n"
-"      color.rgb += half3(tex2D(Texture_Pants, TexCoord)) * Color_Pants + half3(tex2D(Texture_Shirt, TexCoord)) * Color_Shirt;\n"
-"#endif\n"
-"#ifdef USEVERTEXTEXTUREBLEND\n"
-"      float terrainblend = clamp(half(gl_FrontColor.a) * color.a * 2.0 - 0.5, half(0.0), half(1.0));\n"
-"      //half terrainblend = min(half(gl_FrontColor.a) * color.a * 2.0, half(1.0));\n"
-"      //half terrainblend = half(gl_FrontColor.a) * color.a > 0.5;\n"
-"      color.rgb = half3(lerp(float3(tex2D(Texture_SecondaryColor, TexCoord2)), float3(color.rgb), terrainblend));\n"
-"      color.a = 1.0;\n"
-"      //color = lerp(half4(1, 0, 0, 1), color, terrainblend);\n"
-"#endif\n"
-"\n"
-"      // get the surface normal\n"
-"#ifdef USEVERTEXTEXTUREBLEND\n"
-"      half3 surfacenormal = normalize(half3(lerp(float3(tex2D(Texture_SecondaryNormal, TexCoord2)), float3(tex2D(Texture_Normal, TexCoord)), terrainblend)) - half3(0.5, 0.5, 0.5));\n"
-"#else\n"
-"      half3 surfacenormal = normalize(half3(tex2D(Texture_Normal, TexCoord)) - half3(0.5, 0.5, 0.5));\n"
-"#endif\n"
-"\n"
-"      // get the material colors\n"
-"      half3 diffusetex = color.rgb;\n"
-"#if defined(USESPECULAR) || defined(USEDEFERREDLIGHTMAP)\n"
-"# ifdef USEVERTEXTEXTUREBLEND\n"
-"      half4 glosstex = half4(lerp(float4(tex2D(Texture_SecondaryGloss, TexCoord2)), float4(tex2D(Texture_Gloss, TexCoord)), terrainblend));\n"
-"# else\n"
-"      half4 glosstex = half4(tex2D(Texture_Gloss, TexCoord));\n"
-"# endif\n"
-"#endif\n"
-"\n"
-"#ifdef USEREFLECTCUBE\n"
-"      vec3 TangentReflectVector = reflect(-EyeVector, surfacenormal);\n"
-"      vec3 ModelReflectVector = TangentReflectVector.x * VectorS + TangentReflectVector.y * VectorT + TangentReflectVector.z * VectorR;\n"
-"      vec3 ReflectCubeTexCoord = float3(mul(ModelToReflectCube, float4(ModelReflectVector, 0)));\n"
-"      diffusetex += half3(tex2D(Texture_ReflectMask, TexCoord)) * half3(texCUBE(Texture_ReflectCube, ReflectCubeTexCoord));\n"
-"#endif\n"
-"\n"
-"\n"
-"\n"
-"\n"
-"#ifdef MODE_LIGHTSOURCE\n"
-"      // light source\n"
-"#ifdef USEDIFFUSE\n"
-"      half3 lightnormal = half3(normalize(LightVector));\n"
-"      half diffuse = half(max(float(dot(surfacenormal, lightnormal)), 0.0));\n"
-"      color.rgb = diffusetex * (Color_Ambient + diffuse * Color_Diffuse);\n"
-"#ifdef USESPECULAR\n"
-"#ifdef USEEXACTSPECULARMATH\n"
-"      half specular = pow(half(max(float(dot(reflect(lightnormal, surfacenormal), normalize(EyeVector)))*-1.0, 0.0)), SpecularPower * glosstex.a);\n"
-"#else\n"
-"      half3 specularnormal = normalize(lightnormal + half3(normalize(EyeVector)));\n"
-"      half specular = pow(half(max(float(dot(surfacenormal, specularnormal)), 0.0)), SpecularPower * glosstex.a);\n"
-"#endif\n"
-"      color.rgb += glosstex.rgb * (specular * Color_Specular);\n"
-"#endif\n"
-"#else\n"
-"      color.rgb = diffusetex * Color_Ambient;\n"
-"#endif\n"
-"      color.rgb *= LightColor;\n"
-"      color.rgb *= half(tex2D(Texture_Attenuation, float2(length(CubeVector), 0.0)));\n"
-"#if defined(USESHADOWMAPRECT) || defined(USESHADOWMAPCUBE) || defined(USESHADOWMAP2D)\n"
-"      color.rgb *= ShadowMapCompare(CubeVector,\n"
-"# if defined(USESHADOWMAP2D)\n"
-"Texture_ShadowMap2D, ShadowMap_Parameters, ShadowMap_TextureScale\n"
-"# endif\n"
-"# if defined(USESHADOWMAPRECT)\n"
-"Texture_ShadowMapRect, ShadowMap_Parameters\n"
-"# endif\n"
-"# if defined(USESHADOWMAPCUBE)\n"
-"Texture_ShadowMapCube, ShadowMap_Parameters\n"
-"# endif\n"
-"\n"
-"#ifdef USESHADOWMAPVSDCT\n"
-", Texture_CubeProjection\n"
-"#endif\n"
-"      );\n"
-"\n"
-"#endif\n"
-"# ifdef USECUBEFILTER\n"
-"      color.rgb *= half3(texCUBE(Texture_Cube, CubeVector));\n"
-"# endif\n"
-"#endif // MODE_LIGHTSOURCE\n"
-"\n"
-"\n"
-"\n"
-"\n"
-"#ifdef MODE_LIGHTDIRECTION\n"
-"#define SHADING\n"
-"#ifdef USEDIFFUSE\n"
-"      half3 lightnormal = half3(normalize(LightVector));\n"
-"#endif\n"
-"#define lightcolor LightColor\n"
-"#endif // MODE_LIGHTDIRECTION\n"
-"#ifdef MODE_LIGHTDIRECTIONMAP_MODELSPACE\n"
-"#define SHADING\n"
-"      // deluxemap lightmapping using light vectors in modelspace (q3map2 -light -deluxe)\n"
-"      half3 lightnormal_modelspace = half3(tex2D(Texture_Deluxemap, TexCoordLightmap)) * 2.0 + half3(-1.0, -1.0, -1.0);\n"
-"      half3 lightcolor = half3(tex2D(Texture_Lightmap, TexCoordLightmap));\n"
-"      // convert modelspace light vector to tangentspace\n"
-"      half3 lightnormal;\n"
-"      lightnormal.x = dot(lightnormal_modelspace, half3(VectorS));\n"
-"      lightnormal.y = dot(lightnormal_modelspace, half3(VectorT));\n"
-"      lightnormal.z = dot(lightnormal_modelspace, half3(VectorR));\n"
-"      // calculate directional shading (and undoing the existing angle attenuation on the lightmap by the division)\n"
-"      // note that q3map2 is too stupid to calculate proper surface normals when q3map_nonplanar\n"
-"      // is used (the lightmap and deluxemap coords correspond to virtually random coordinates\n"
-"      // on that luxel, and NOT to its center, because recursive triangle subdivision is used\n"
-"      // to map the luxels to coordinates on the draw surfaces), which also causes\n"
-"      // deluxemaps to be wrong because light contributions from the wrong side of the surface\n"
-"      // are added up. To prevent divisions by zero or strong exaggerations, a max()\n"
-"      // nudge is done here at expense of some additional fps. This is ONLY needed for\n"
-"      // deluxemaps, tangentspace deluxemap avoid this problem by design.\n"
-"      lightcolor *= 1.0 / max(0.25, lightnormal.z);\n"
-"#endif // MODE_LIGHTDIRECTIONMAP_MODELSPACE\n"
-"#ifdef MODE_LIGHTDIRECTIONMAP_TANGENTSPACE\n"
-"#define SHADING\n"
-"      // deluxemap lightmapping using light vectors in tangentspace (hmap2 -light)\n"
-"      half3 lightnormal = half3(tex2D(Texture_Deluxemap, TexCoordLightmap)) * 2.0 + half3(-1.0, -1.0, -1.0);\n"
-"      half3 lightcolor = half3(tex2D(Texture_Lightmap, TexCoordLightmap));\n"
-"#endif\n"
-"\n"
-"\n"
-"\n"
-"\n"
-"#ifdef MODE_LIGHTMAP\n"
-"      color.rgb = diffusetex * (Color_Ambient + half3(tex2D(Texture_Lightmap, TexCoordLightmap)) * Color_Diffuse);\n"
-"#endif // MODE_LIGHTMAP\n"
-"#ifdef MODE_VERTEXCOLOR\n"
-"      color.rgb = diffusetex * (Color_Ambient + half3(gl_FrontColor.rgb) * Color_Diffuse);\n"
-"#endif // MODE_VERTEXCOLOR\n"
-"#ifdef MODE_FLATCOLOR\n"
-"      color.rgb = diffusetex * Color_Ambient;\n"
-"#endif // MODE_FLATCOLOR\n"
-"\n"
-"\n"
-"\n"
-"\n"
-"#ifdef SHADING\n"
-"# ifdef USEDIFFUSE\n"
-"      half diffuse = half(max(float(dot(surfacenormal, lightnormal)), 0.0));\n"
-"#  ifdef USESPECULAR\n"
-"#   ifdef USEEXACTSPECULARMATH\n"
-"      half specular = pow(half(max(float(dot(reflect(lightnormal, surfacenormal), normalize(EyeVector)))*-1.0, 0.0)), SpecularPower * glosstex.a);\n"
-"#   else\n"
-"      half3 specularnormal = normalize(lightnormal + half3(normalize(EyeVector)));\n"
-"      half specular = pow(half(max(float(dot(surfacenormal, specularnormal)), 0.0)), SpecularPower * glosstex.a);\n"
-"#   endif\n"
-"      color.rgb = diffusetex * Color_Ambient + (diffusetex * Color_Diffuse * diffuse + glosstex.rgb * Color_Specular * specular) * lightcolor;\n"
-"#  else\n"
-"      color.rgb = diffusetex * (Color_Ambient + Color_Diffuse * diffuse * lightcolor);\n"
-"#  endif\n"
-"# else\n"
-"      color.rgb = diffusetex * Color_Ambient;\n"
-"# endif\n"
-"#endif\n"
-"\n"
-"#ifdef USEDEFERREDLIGHTMAP\n"
-"      float2 ScreenTexCoord = Pixel * PixelToScreenTexCoord;\n"
-"      color.rgb += diffusetex * half3(tex2D(Texture_ScreenDiffuse, ScreenTexCoord)) * DeferredMod_Diffuse;\n"
-"      color.rgb += glosstex.rgb * half3(tex2D(Texture_ScreenSpecular, ScreenTexCoord)) * DeferredMod_Specular;\n"
-"#endif\n"
-"\n"
-"#ifdef USEGLOW\n"
-"#ifdef USEVERTEXTEXTUREBLEND\n"
-"      color.rgb += lerp(half3(tex2D(Texture_SecondaryGlow, TexCoord2)), half3(tex2D(Texture_Glow, TexCoord)), terrainblend) * Color_Glow;\n"
-"#else\n"
-"      color.rgb += half3(tex2D(Texture_Glow, TexCoord)) * Color_Glow;\n"
-"#endif\n"
-"#endif\n"
-"\n"
-"#ifdef USEFOG\n"
-"#ifdef MODE_LIGHTSOURCE\n"
-"      color.rgb *= half(FogVertex(EyeVectorModelSpaceFogPlaneVertexDist.xyz, EyeVectorModelSpaceFogPlaneVertexDist.w, FogRangeRecip, FogPlaneViewDist, FogHeightFade, Texture_FogMask));\n"
-"#else\n"
-"      color.rgb = lerp(FogColor, float3(color.rgb), FogVertex(EyeVectorModelSpaceFogPlaneVertexDist.xyz, EyeVectorModelSpaceFogPlaneVertexDist.w, FogRangeRecip, FogPlaneViewDist, FogHeightFade, Texture_FogMask));\n"
-"#endif\n"
-"#endif\n"
-"\n"
-"      // reflection must come last because it already contains exactly the correct fog (the reflection render preserves camera distance from the plane, it only flips the side) and ContrastBoost/SceneBrightness\n"
-"#ifdef USEREFLECTION\n"
-"      float4 ScreenScaleRefractReflectIW = ScreenScaleRefractReflect * (1.0 / ModelViewProjectionPosition.w);\n"
-"      //float4 ScreenTexCoord = (ModelViewProjectionPosition.xyxy + normalize(half3(tex2D(Texture_Normal, TexCoord)) - half3(0.5)).xyxy * DistortScaleRefractReflect * 100) * ScreenScaleRefractReflectIW + ScreenCenterRefractReflect;\n"
-"      float2 SafeScreenTexCoord = ModelViewProjectionPosition.xy * ScreenScaleRefractReflectIW.zw + ScreenCenterRefractReflect.zw;\n"
-"      float2 ScreenTexCoord = SafeScreenTexCoord + float3(normalize(half3(tex2D(Texture_Normal, TexCoord)) - half3(0.5))).xy * DistortScaleRefractReflect.zw;\n"
-"      // FIXME temporary hack to detect the case that the reflection\n"
-"      // gets blackened at edges due to leaving the area that contains actual\n"
-"      // content.\n"
-"      // Remove this 'ack once we have a better way to stop this thing from\n"
-"      // 'appening.\n"
-"      float f = min(1.0, length(tex2D(Texture_Reflection, ScreenTexCoord + float2(0.01, 0.01)).rgb) / 0.05);\n"
-"      f      *= min(1.0, length(tex2D(Texture_Reflection, ScreenTexCoord + float2(0.01, -0.01)).rgb) / 0.05);\n"
-"      f      *= min(1.0, length(tex2D(Texture_Reflection, ScreenTexCoord + float2(-0.01, 0.01)).rgb) / 0.05);\n"
-"      f      *= min(1.0, length(tex2D(Texture_Reflection, ScreenTexCoord + float2(-0.01, -0.01)).rgb) / 0.05);\n"
-"      ScreenTexCoord = lerp(SafeScreenTexCoord, ScreenTexCoord, f);\n"
-"      color.rgb = lerp(color.rgb, half3(tex2D(Texture_Reflection, ScreenTexCoord)) * ReflectColor.rgb, ReflectColor.a);\n"
-"#endif\n"
-"\n"
-"      gl_FragColor = float4(color);\n"
-"}\n"
-"#endif // FRAGMENT_SHADER\n"
-"\n"
-"#endif // !MODE_DEFERREDLIGHTSOURCE\n"
-"#endif // !MODE_DEFERREDGEOMETRY\n"
-"#endif // !MODE_WATER\n"
-"#endif // !MODE_REFRACTION\n"
-"#endif // !MODE_BLOOMBLUR\n"
-"#endif // !MODE_GENERIC\n"
-"#endif // !MODE_POSTPROCESS\n"
-"#endif // !MODE_SHOWDEPTH\n"
-"#endif // !MODE_DEPTH_OR_SHADOW\n"
+const char *builtinhlslshaderstring =
+#include "shader_hlsl.h"
 ;
 
 char *glslshaderstring = NULL;
-char *cgshaderstring = NULL;
+char *hlslshaderstring = NULL;
 
 //=======================================================================================================================================================
 
@@ -3182,40 +622,6 @@ typedef struct shadermodeinfo_s
 }
 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_VIEWTINT = 1<<2, ///< view tint (postprocessing only)
-       SHADERPERMUTATION_COLORMAPPING = 1<<3, ///< indicates this is a colormapped skin
-       SHADERPERMUTATION_SATURATION = 1<<4, ///< saturation (postprocessing only)
-       SHADERPERMUTATION_FOGINSIDE = 1<<5, ///< tint the color by fog color or black if using additive blend mode
-       SHADERPERMUTATION_FOGOUTSIDE = 1<<6, ///< tint the color by fog color or black if using additive blend mode
-       SHADERPERMUTATION_GAMMARAMPS = 1<<7, ///< gamma (postprocessing only)
-       SHADERPERMUTATION_CUBEFILTER = 1<<8, ///< (lightsource) use cubemap light filter
-       SHADERPERMUTATION_GLOW = 1<<9, ///< (lightmap) blend in an additive glow texture
-       SHADERPERMUTATION_BLOOM = 1<<10, ///< bloom (postprocessing only)
-       SHADERPERMUTATION_SPECULAR = 1<<11, ///< (lightsource or deluxemapping) render specular effects
-       SHADERPERMUTATION_POSTPROCESSING = 1<<12, ///< user defined postprocessing (postprocessing only)
-       SHADERPERMUTATION_EXACTSPECULARMATH = 1<<13, ///< (lightsource or deluxemapping) use exact reflection map for specular effects, as opposed to the usual OpenGL approximation
-       SHADERPERMUTATION_REFLECTION = 1<<14, ///< normalmap-perturbed reflection of the scene infront of the surface, preformed as an overlay on the surface
-       SHADERPERMUTATION_OFFSETMAPPING = 1<<15, ///< adjust texcoords to roughly simulate a displacement mapped surface
-       SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING = 1<<16, ///< adjust texcoords to accurately simulate a displacement mapped surface (requires OFFSETMAPPING to also be set!)
-       SHADERPERMUTATION_SHADOWMAPRECT = 1<<17, ///< (lightsource) use shadowmap rectangle texture as light filter
-       SHADERPERMUTATION_SHADOWMAPCUBE = 1<<18, ///< (lightsource) use shadowmap cubemap texture as light filter
-       SHADERPERMUTATION_SHADOWMAP2D = 1<<19, ///< (lightsource) use shadowmap rectangle texture as light filter
-       SHADERPERMUTATION_SHADOWMAPPCF = 1<<20, ///< (lightsource) use percentage closer filtering on shadowmap test results
-       SHADERPERMUTATION_SHADOWMAPPCF2 = 1<<21, ///< (lightsource) use higher quality percentage closer filtering on shadowmap test results
-       SHADERPERMUTATION_SHADOWSAMPLER = 1<<22, ///< (lightsource) use hardware shadowmap test
-       SHADERPERMUTATION_SHADOWMAPVSDCT = 1<<23, ///< (lightsource) use virtual shadow depth cube texture for shadowmap indexing
-       SHADERPERMUTATION_DEFERREDLIGHTMAP = 1<<24, ///< (lightmap) read Texture_ScreenDiffuse/Specular textures and add them on top of lightmapping
-       SHADERPERMUTATION_ALPHAKILL = 1<<25, ///< (deferredgeometry) discard pixel if diffuse texture alpha below 0.5
-       SHADERPERMUTATION_REFLECTCUBE = 1<<26, ///< fake reflections using global cubemap (not HDRI light probe)
-       SHADERPERMUTATION_LIMIT = 1<<27, ///< size of permutations array
-       SHADERPERMUTATION_COUNT = 27 ///< size of shaderpermutationinfo array
-}
-shaderpermutation_t;
-
 // NOTE: MUST MATCH ORDER OF SHADERPERMUTATION_* DEFINES!
 shaderpermutationinfo_t shaderpermutationinfo[SHADERPERMUTATION_COUNT] =
 {
@@ -3226,50 +632,30 @@ shaderpermutationinfo_t shaderpermutationinfo[SHADERPERMUTATION_COUNT] =
        {"#define USESATURATION\n", " saturation"},
        {"#define USEFOGINSIDE\n", " foginside"},
        {"#define USEFOGOUTSIDE\n", " fogoutside"},
+       {"#define USEFOGHEIGHTTEXTURE\n", " fogheighttexture"},
+       {"#define USEFOGALPHAHACK\n", " fogalphahack"},
        {"#define USEGAMMARAMPS\n", " gammaramps"},
        {"#define USECUBEFILTER\n", " cubefilter"},
        {"#define USEGLOW\n", " glow"},
        {"#define USEBLOOM\n", " bloom"},
        {"#define USESPECULAR\n", " specular"},
        {"#define USEPOSTPROCESSING\n", " postprocessing"},
-       {"#define USEEXACTSPECULARMATH\n", " exactspecularmath"},
        {"#define USEREFLECTION\n", " reflection"},
        {"#define USEOFFSETMAPPING\n", " offsetmapping"},
        {"#define USEOFFSETMAPPING_RELIEFMAPPING\n", " reliefmapping"},
-       {"#define USESHADOWMAPRECT\n", " shadowmaprect"},
-       {"#define USESHADOWMAPCUBE\n", " shadowmapcube"},
        {"#define USESHADOWMAP2D\n", " shadowmap2d"},
        {"#define USESHADOWMAPPCF 1\n", " shadowmappcf"},
        {"#define USESHADOWMAPPCF 2\n", " shadowmappcf2"},
        {"#define USESHADOWSAMPLER\n", " shadowsampler"},
        {"#define USESHADOWMAPVSDCT\n", " shadowmapvsdct"},
+       {"#define USESHADOWMAPORTHO\n", " shadowmaportho"},
        {"#define USEDEFERREDLIGHTMAP\n", " deferredlightmap"},
        {"#define USEALPHAKILL\n", " alphakill"},
        {"#define USEREFLECTCUBE\n", " reflectcube"},
+       {"#define USENORMALMAPSCROLLBLEND\n", " normalmapscrollblend"},
+       {"#define USEBOUNCEGRID\n", " bouncegrid"},
 };
 
-/// 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_SHOWDEPTH, ///< (debugging) renders depth as color
-       SHADERMODE_DEFERREDGEOMETRY, ///< (deferred) render material properties to screenspace geometry buffers
-       SHADERMODE_DEFERREDLIGHTSOURCE, ///< (deferred) use directional pixel shading from light source (rtlight) on screenspace geometry buffers
-       SHADERMODE_COUNT
-}
-shadermode_t;
-
 // NOTE: MUST MATCH ORDER OF SHADERMODE_* ENUMS!
 shadermodeinfo_t glslshadermodeinfo[SHADERMODE_COUNT] =
 {
@@ -3279,6 +665,7 @@ shadermodeinfo_t glslshadermodeinfo[SHADERMODE_COUNT] =
        {"glsl/default.glsl", NULL, "glsl/default.glsl", "#define MODE_FLATCOLOR\n", " flatcolor"},
        {"glsl/default.glsl", NULL, "glsl/default.glsl", "#define MODE_VERTEXCOLOR\n", " vertexcolor"},
        {"glsl/default.glsl", NULL, "glsl/default.glsl", "#define MODE_LIGHTMAP\n", " lightmap"},
+       {"glsl/default.glsl", NULL, "glsl/default.glsl", "#define MODE_FAKELIGHT\n", " fakelight"},
        {"glsl/default.glsl", NULL, "glsl/default.glsl", "#define MODE_LIGHTDIRECTIONMAP_MODELSPACE\n", " lightdirectionmap_modelspace"},
        {"glsl/default.glsl", NULL, "glsl/default.glsl", "#define MODE_LIGHTDIRECTIONMAP_TANGENTSPACE\n", " lightdirectionmap_tangentspace"},
        {"glsl/default.glsl", NULL, "glsl/default.glsl", "#define MODE_LIGHTDIRECTION\n", " lightdirection"},
@@ -3290,26 +677,25 @@ shadermodeinfo_t glslshadermodeinfo[SHADERMODE_COUNT] =
        {"glsl/default.glsl", NULL, "glsl/default.glsl", "#define MODE_DEFERREDLIGHTSOURCE\n", " deferredlightsource"},
 };
 
-#ifdef SUPPORTCG
-shadermodeinfo_t cgshadermodeinfo[SHADERMODE_COUNT] =
-{
-       {"cg/default.cg", NULL, "cg/default.cg", "#define MODE_GENERIC\n", " generic"},
-       {"cg/default.cg", NULL, "cg/default.cg", "#define MODE_POSTPROCESS\n", " postprocess"},
-       {"cg/default.cg", NULL, NULL           , "#define MODE_DEPTH_OR_SHADOW\n", " depth"},
-       {"cg/default.cg", NULL, "cg/default.cg", "#define MODE_FLATCOLOR\n", " flatcolor"},
-       {"cg/default.cg", NULL, "cg/default.cg", "#define MODE_VERTEXCOLOR\n", " vertexcolor"},
-       {"cg/default.cg", NULL, "cg/default.cg", "#define MODE_LIGHTMAP\n", " lightmap"},
-       {"cg/default.cg", NULL, "cg/default.cg", "#define MODE_LIGHTDIRECTIONMAP_MODELSPACE\n", " lightdirectionmap_modelspace"},
-       {"cg/default.cg", NULL, "cg/default.cg", "#define MODE_LIGHTDIRECTIONMAP_TANGENTSPACE\n", " lightdirectionmap_tangentspace"},
-       {"cg/default.cg", NULL, "cg/default.cg", "#define MODE_LIGHTDIRECTION\n", " lightdirection"},
-       {"cg/default.cg", NULL, "cg/default.cg", "#define MODE_LIGHTSOURCE\n", " lightsource"},
-       {"cg/default.cg", NULL, "cg/default.cg", "#define MODE_REFRACTION\n", " refraction"},
-       {"cg/default.cg", NULL, "cg/default.cg", "#define MODE_WATER\n", " water"},
-       {"cg/default.cg", NULL, "cg/default.cg", "#define MODE_SHOWDEPTH\n", " showdepth"},
-       {"cg/default.cg", NULL, "cg/default.cg", "#define MODE_DEFERREDGEOMETRY\n", " deferredgeometry"},
-       {"cg/default.cg", NULL, "cg/default.cg", "#define MODE_DEFERREDLIGHTSOURCE\n", " deferredlightsource"},
+shadermodeinfo_t hlslshadermodeinfo[SHADERMODE_COUNT] =
+{
+       {"hlsl/default.hlsl", NULL, "hlsl/default.hlsl", "#define MODE_GENERIC\n", " generic"},
+       {"hlsl/default.hlsl", NULL, "hlsl/default.hlsl", "#define MODE_POSTPROCESS\n", " postprocess"},
+       {"hlsl/default.hlsl", NULL, "hlsl/default.hlsl", "#define MODE_DEPTH_OR_SHADOW\n", " depth/shadow"},
+       {"hlsl/default.hlsl", NULL, "hlsl/default.hlsl", "#define MODE_FLATCOLOR\n", " flatcolor"},
+       {"hlsl/default.hlsl", NULL, "hlsl/default.hlsl", "#define MODE_VERTEXCOLOR\n", " vertexcolor"},
+       {"hlsl/default.hlsl", NULL, "hlsl/default.hlsl", "#define MODE_LIGHTMAP\n", " lightmap"},
+       {"hlsl/default.hlsl", NULL, "hlsl/default.hlsl", "#define MODE_FAKELIGHT\n", " fakelight"},
+       {"hlsl/default.hlsl", NULL, "hlsl/default.hlsl", "#define MODE_LIGHTDIRECTIONMAP_MODELSPACE\n", " lightdirectionmap_modelspace"},
+       {"hlsl/default.hlsl", NULL, "hlsl/default.hlsl", "#define MODE_LIGHTDIRECTIONMAP_TANGENTSPACE\n", " lightdirectionmap_tangentspace"},
+       {"hlsl/default.hlsl", NULL, "hlsl/default.hlsl", "#define MODE_LIGHTDIRECTION\n", " lightdirection"},
+       {"hlsl/default.hlsl", NULL, "hlsl/default.hlsl", "#define MODE_LIGHTSOURCE\n", " lightsource"},
+       {"hlsl/default.hlsl", NULL, "hlsl/default.hlsl", "#define MODE_REFRACTION\n", " refraction"},
+       {"hlsl/default.hlsl", NULL, "hlsl/default.hlsl", "#define MODE_WATER\n", " water"},
+       {"hlsl/default.hlsl", NULL, "hlsl/default.hlsl", "#define MODE_SHOWDEPTH\n", " showdepth"},
+       {"hlsl/default.hlsl", NULL, "hlsl/default.hlsl", "#define MODE_DEFERREDGEOMETRY\n", " deferredgeometry"},
+       {"hlsl/default.hlsl", NULL, "hlsl/default.hlsl", "#define MODE_DEFERREDLIGHTSOURCE\n", " deferredlightsource"},
 };
-#endif
 
 struct r_glsl_permutation_s;
 typedef struct r_glsl_permutation_s
@@ -3323,6 +709,37 @@ typedef struct r_glsl_permutation_s
        qboolean compiled;
        /// 0 if compilation failed
        int program;
+       // texture units assigned to each detected uniform
+       int tex_Texture_First;
+       int tex_Texture_Second;
+       int tex_Texture_GammaRamps;
+       int tex_Texture_Normal;
+       int tex_Texture_Color;
+       int tex_Texture_Gloss;
+       int tex_Texture_Glow;
+       int tex_Texture_SecondaryNormal;
+       int tex_Texture_SecondaryColor;
+       int tex_Texture_SecondaryGloss;
+       int tex_Texture_SecondaryGlow;
+       int tex_Texture_Pants;
+       int tex_Texture_Shirt;
+       int tex_Texture_FogHeightTexture;
+       int tex_Texture_FogMask;
+       int tex_Texture_Lightmap;
+       int tex_Texture_Deluxemap;
+       int tex_Texture_Attenuation;
+       int tex_Texture_Cube;
+       int tex_Texture_Refraction;
+       int tex_Texture_Reflection;
+       int tex_Texture_ShadowMap2D;
+       int tex_Texture_CubeProjection;
+       int tex_Texture_ScreenDepth;
+       int tex_Texture_ScreenNormalMap;
+       int tex_Texture_ScreenDiffuse;
+       int tex_Texture_ScreenSpecular;
+       int tex_Texture_ReflectMask;
+       int tex_Texture_ReflectCube;
+       int tex_Texture_BounceGrid;
        /// locations of detected uniforms in program object, or -1 if not found
        int loc_Texture_First;
        int loc_Texture_Second;
@@ -3337,6 +754,7 @@ typedef struct r_glsl_permutation_s
        int loc_Texture_SecondaryGlow;
        int loc_Texture_Pants;
        int loc_Texture_Shirt;
+       int loc_Texture_FogHeightTexture;
        int loc_Texture_FogMask;
        int loc_Texture_Lightmap;
        int loc_Texture_Deluxemap;
@@ -3344,8 +762,6 @@ typedef struct r_glsl_permutation_s
        int loc_Texture_Cube;
        int loc_Texture_Refraction;
        int loc_Texture_Reflection;
-       int loc_Texture_ShadowMapRect;
-       int loc_Texture_ShadowMapCube;
        int loc_Texture_ShadowMap2D;
        int loc_Texture_CubeProjection;
        int loc_Texture_ScreenDepth;
@@ -3354,6 +770,7 @@ typedef struct r_glsl_permutation_s
        int loc_Texture_ScreenSpecular;
        int loc_Texture_ReflectMask;
        int loc_Texture_ReflectCube;
+       int loc_Texture_BounceGrid;
        int loc_Alpha;
        int loc_BloomBlur_Parameters;
        int loc_ClientTime;
@@ -3404,11 +821,78 @@ typedef struct r_glsl_permutation_s
        int loc_ModelViewMatrix;
        int loc_PixelToScreenTexCoord;
        int loc_ModelToReflectCube;
+       int loc_ShadowMapMatrix;
+       int loc_BloomColorSubtract;
+       int loc_NormalmapScrollBlend;
+       int loc_BounceGridMatrix;
+       int loc_BounceGridIntensity;
 }
 r_glsl_permutation_t;
 
 #define SHADERPERMUTATION_HASHSIZE 256
 
+
+// non-degradable "lightweight" shader parameters to keep the permutations simpler
+// these can NOT degrade! only use for simple stuff
+enum
+{
+       SHADERSTATICPARM_SATURATION_REDCOMPENSATE = 0, ///< red compensation filter for saturation
+       SHADERSTATICPARM_EXACTSPECULARMATH = 1, ///< (lightsource or deluxemapping) use exact reflection map for specular effects, as opposed to the usual OpenGL approximation
+       SHADERSTATICPARM_POSTPROCESS_USERVEC1 = 2, ///< postprocess uservec1 is enabled
+       SHADERSTATICPARM_POSTPROCESS_USERVEC2 = 3, ///< postprocess uservec2 is enabled
+       SHADERSTATICPARM_POSTPROCESS_USERVEC3 = 4, ///< postprocess uservec3 is enabled
+       SHADERSTATICPARM_POSTPROCESS_USERVEC4 = 5  ///< postprocess uservec4 is enabled
+};
+#define SHADERSTATICPARMS_COUNT 6
+
+static const char *shaderstaticparmstrings_list[SHADERSTATICPARMS_COUNT];
+static int shaderstaticparms_count = 0;
+
+static unsigned int r_compileshader_staticparms[(SHADERSTATICPARMS_COUNT + 0x1F) >> 5] = {0};
+#define R_COMPILESHADER_STATICPARM_ENABLE(p) r_compileshader_staticparms[(p) >> 5] |= (1 << ((p) & 0x1F))
+qboolean R_CompileShader_CheckStaticParms(void)
+{
+       static int r_compileshader_staticparms_save[1];
+       memcpy(r_compileshader_staticparms_save, r_compileshader_staticparms, sizeof(r_compileshader_staticparms));
+       memset(r_compileshader_staticparms, 0, sizeof(r_compileshader_staticparms));
+
+       // detect all
+       if (r_glsl_saturation_redcompensate.integer)
+               R_COMPILESHADER_STATICPARM_ENABLE(SHADERSTATICPARM_SATURATION_REDCOMPENSATE);
+       if (r_shadow_glossexact.integer)
+               R_COMPILESHADER_STATICPARM_ENABLE(SHADERSTATICPARM_EXACTSPECULARMATH);
+       if (r_glsl_postprocess.integer)
+       {
+               if (r_glsl_postprocess_uservec1_enable.integer)
+                       R_COMPILESHADER_STATICPARM_ENABLE(SHADERSTATICPARM_POSTPROCESS_USERVEC1);
+               if (r_glsl_postprocess_uservec2_enable.integer)
+                       R_COMPILESHADER_STATICPARM_ENABLE(SHADERSTATICPARM_POSTPROCESS_USERVEC2);
+               if (r_glsl_postprocess_uservec3_enable.integer)
+                       R_COMPILESHADER_STATICPARM_ENABLE(SHADERSTATICPARM_POSTPROCESS_USERVEC3);
+               if (r_glsl_postprocess_uservec4_enable.integer)
+                       R_COMPILESHADER_STATICPARM_ENABLE(SHADERSTATICPARM_POSTPROCESS_USERVEC4);
+       }
+       return memcmp(r_compileshader_staticparms, r_compileshader_staticparms_save, sizeof(r_compileshader_staticparms)) != 0;
+}
+
+#define R_COMPILESHADER_STATICPARM_EMIT(p, n) \
+       if(r_compileshader_staticparms[(p) >> 5] & (1 << ((p) & 0x1F))) \
+               shaderstaticparmstrings_list[shaderstaticparms_count++] = "#define " n "\n"; \
+       else \
+               shaderstaticparmstrings_list[shaderstaticparms_count++] = "\n"
+void R_CompileShader_AddStaticParms(unsigned int mode, unsigned int permutation)
+{
+       shaderstaticparms_count = 0;
+
+       // emit all
+       R_COMPILESHADER_STATICPARM_EMIT(SHADERSTATICPARM_SATURATION_REDCOMPENSATE, "SATURATION_REDCOMPENSATE");
+       R_COMPILESHADER_STATICPARM_EMIT(SHADERSTATICPARM_EXACTSPECULARMATH, "USEEXACTSPECULARMATH");
+       R_COMPILESHADER_STATICPARM_EMIT(SHADERSTATICPARM_POSTPROCESS_USERVEC1, "USERVEC1");
+       R_COMPILESHADER_STATICPARM_EMIT(SHADERSTATICPARM_POSTPROCESS_USERVEC2, "USERVEC2");
+       R_COMPILESHADER_STATICPARM_EMIT(SHADERSTATICPARM_POSTPROCESS_USERVEC3, "USERVEC3");
+       R_COMPILESHADER_STATICPARM_EMIT(SHADERSTATICPARM_POSTPROCESS_USERVEC4, "USERVEC4");
+}
+
 /// information about each possible shader permutation
 r_glsl_permutation_t *r_glsl_permutationhash[SHADERMODE_COUNT][SHADERPERMUTATION_HASHSIZE];
 /// currently selected permutation
@@ -3473,15 +957,16 @@ static char *R_GLSL_GetText(const char *filename, qboolean printfromdisknotice)
 static void R_GLSL_CompilePermutation(r_glsl_permutation_t *p, unsigned int mode, unsigned int permutation)
 {
        int i;
+       int sampler;
        shadermodeinfo_t *modeinfo = glslshadermodeinfo + mode;
+       char *vertexstring, *geometrystring, *fragmentstring;
+       char permutationname[256];
        int vertstrings_count = 0;
        int geomstrings_count = 0;
        int fragstrings_count = 0;
-       char *vertexstring, *geometrystring, *fragmentstring;
-       const char *vertstrings_list[32+3];
-       const char *geomstrings_list[32+3];
-       const char *fragstrings_list[32+3];
-       char permutationname[256];
+       const char *vertstrings_list[32+3+SHADERSTATICPARMS_COUNT+1];
+       const char *geomstrings_list[32+3+SHADERSTATICPARMS_COUNT+1];
+       const char *fragstrings_list[32+3+SHADERSTATICPARMS_COUNT+1];
 
        if (p->compiled)
                return;
@@ -3495,6 +980,15 @@ static void R_GLSL_CompilePermutation(r_glsl_permutation_t *p, unsigned int mode
 
        strlcat(permutationname, modeinfo->vertexfilename, sizeof(permutationname));
 
+       // the first pretext is which type of shader to compile as
+       // (later these will all be bound together as a program object)
+       if(qglBindFragDataLocation && (permutation & SHADERPERMUTATION_OFFSETMAPPING)) // we use textureGrad() which is glsl 1.30
+       {
+               vertstrings_list[vertstrings_count++] = "#version 130\n";
+               geomstrings_list[geomstrings_count++] = "#version 130\n";
+               fragstrings_list[fragstrings_count++] = "#version 130\n";
+       }
+
        // the first pretext is which type of shader to compile as
        // (later these will all be bound together as a program object)
        vertstrings_list[vertstrings_count++] = "#define VERTEX_SHADER\n";
@@ -3526,6 +1020,15 @@ static void R_GLSL_CompilePermutation(r_glsl_permutation_t *p, unsigned int mode
                }
        }
 
+       // add static parms
+       R_CompileShader_AddStaticParms(mode, permutation);
+       memcpy((char *)(vertstrings_list + vertstrings_count), shaderstaticparmstrings_list, sizeof(*vertstrings_list) * shaderstaticparms_count);
+       vertstrings_count += shaderstaticparms_count;
+       memcpy((char *)(geomstrings_list + geomstrings_count), shaderstaticparmstrings_list, sizeof(*vertstrings_list) * shaderstaticparms_count);
+       geomstrings_count += shaderstaticparms_count;
+       memcpy((char *)(fragstrings_list + fragstrings_count), shaderstaticparmstrings_list, sizeof(*vertstrings_list) * shaderstaticparms_count);
+       fragstrings_count += shaderstaticparms_count;
+
        // now append the shader text itself
        vertstrings_list[vertstrings_count++] = vertexstring;
        geomstrings_list[geomstrings_count++] = geometrystring;
@@ -3545,123 +1048,159 @@ static void R_GLSL_CompilePermutation(r_glsl_permutation_t *p, unsigned int mode
        if (p->program)
        {
                CHECKGLERROR
-               qglUseProgramObjectARB(p->program);CHECKGLERROR
+               qglUseProgram(p->program);CHECKGLERROR
                // look up all the uniform variable names we care about, so we don't
                // have to look them up every time we set them
 
-               p->loc_Texture_First              = qglGetUniformLocationARB(p->program, "Texture_First");
-               p->loc_Texture_Second             = qglGetUniformLocationARB(p->program, "Texture_Second");
-               p->loc_Texture_GammaRamps         = qglGetUniformLocationARB(p->program, "Texture_GammaRamps");
-               p->loc_Texture_Normal             = qglGetUniformLocationARB(p->program, "Texture_Normal");
-               p->loc_Texture_Color              = qglGetUniformLocationARB(p->program, "Texture_Color");
-               p->loc_Texture_Gloss              = qglGetUniformLocationARB(p->program, "Texture_Gloss");
-               p->loc_Texture_Glow               = qglGetUniformLocationARB(p->program, "Texture_Glow");
-               p->loc_Texture_SecondaryNormal    = qglGetUniformLocationARB(p->program, "Texture_SecondaryNormal");
-               p->loc_Texture_SecondaryColor     = qglGetUniformLocationARB(p->program, "Texture_SecondaryColor");
-               p->loc_Texture_SecondaryGloss     = qglGetUniformLocationARB(p->program, "Texture_SecondaryGloss");
-               p->loc_Texture_SecondaryGlow      = qglGetUniformLocationARB(p->program, "Texture_SecondaryGlow");
-               p->loc_Texture_Pants              = qglGetUniformLocationARB(p->program, "Texture_Pants");
-               p->loc_Texture_Shirt              = qglGetUniformLocationARB(p->program, "Texture_Shirt");
-               p->loc_Texture_FogMask            = qglGetUniformLocationARB(p->program, "Texture_FogMask");
-               p->loc_Texture_Lightmap           = qglGetUniformLocationARB(p->program, "Texture_Lightmap");
-               p->loc_Texture_Deluxemap          = qglGetUniformLocationARB(p->program, "Texture_Deluxemap");
-               p->loc_Texture_Attenuation        = qglGetUniformLocationARB(p->program, "Texture_Attenuation");
-               p->loc_Texture_Cube               = qglGetUniformLocationARB(p->program, "Texture_Cube");
-               p->loc_Texture_Refraction         = qglGetUniformLocationARB(p->program, "Texture_Refraction");
-               p->loc_Texture_Reflection         = qglGetUniformLocationARB(p->program, "Texture_Reflection");
-               p->loc_Texture_ShadowMapRect      = qglGetUniformLocationARB(p->program, "Texture_ShadowMapRect");
-               p->loc_Texture_ShadowMapCube      = qglGetUniformLocationARB(p->program, "Texture_ShadowMapCube");
-               p->loc_Texture_ShadowMap2D        = qglGetUniformLocationARB(p->program, "Texture_ShadowMap2D");
-               p->loc_Texture_CubeProjection     = qglGetUniformLocationARB(p->program, "Texture_CubeProjection");
-               p->loc_Texture_ScreenDepth        = qglGetUniformLocationARB(p->program, "Texture_ScreenDepth");
-               p->loc_Texture_ScreenNormalMap    = qglGetUniformLocationARB(p->program, "Texture_ScreenNormalMap");
-               p->loc_Texture_ScreenDiffuse      = qglGetUniformLocationARB(p->program, "Texture_ScreenDiffuse");
-               p->loc_Texture_ScreenSpecular     = qglGetUniformLocationARB(p->program, "Texture_ScreenSpecular");
-               p->loc_Texture_ReflectMask        = qglGetUniformLocationARB(p->program, "Texture_ReflectMask");
-               p->loc_Texture_ReflectCube        = qglGetUniformLocationARB(p->program, "Texture_ReflectCube");
-               p->loc_Alpha                      = qglGetUniformLocationARB(p->program, "Alpha");
-               p->loc_BloomBlur_Parameters       = qglGetUniformLocationARB(p->program, "BloomBlur_Parameters");
-               p->loc_ClientTime                 = qglGetUniformLocationARB(p->program, "ClientTime");
-               p->loc_Color_Ambient              = qglGetUniformLocationARB(p->program, "Color_Ambient");
-               p->loc_Color_Diffuse              = qglGetUniformLocationARB(p->program, "Color_Diffuse");
-               p->loc_Color_Specular             = qglGetUniformLocationARB(p->program, "Color_Specular");
-               p->loc_Color_Glow                 = qglGetUniformLocationARB(p->program, "Color_Glow");
-               p->loc_Color_Pants                = qglGetUniformLocationARB(p->program, "Color_Pants");
-               p->loc_Color_Shirt                = qglGetUniformLocationARB(p->program, "Color_Shirt");
-               p->loc_DeferredColor_Ambient      = qglGetUniformLocationARB(p->program, "DeferredColor_Ambient");
-               p->loc_DeferredColor_Diffuse      = qglGetUniformLocationARB(p->program, "DeferredColor_Diffuse");
-               p->loc_DeferredColor_Specular     = qglGetUniformLocationARB(p->program, "DeferredColor_Specular");
-               p->loc_DeferredMod_Diffuse        = qglGetUniformLocationARB(p->program, "DeferredMod_Diffuse");
-               p->loc_DeferredMod_Specular       = qglGetUniformLocationARB(p->program, "DeferredMod_Specular");
-               p->loc_DistortScaleRefractReflect = qglGetUniformLocationARB(p->program, "DistortScaleRefractReflect");
-               p->loc_EyePosition                = qglGetUniformLocationARB(p->program, "EyePosition");
-               p->loc_FogColor                   = qglGetUniformLocationARB(p->program, "FogColor");
-               p->loc_FogHeightFade              = qglGetUniformLocationARB(p->program, "FogHeightFade");
-               p->loc_FogPlane                   = qglGetUniformLocationARB(p->program, "FogPlane");
-               p->loc_FogPlaneViewDist           = qglGetUniformLocationARB(p->program, "FogPlaneViewDist");
-               p->loc_FogRangeRecip              = qglGetUniformLocationARB(p->program, "FogRangeRecip");
-               p->loc_LightColor                 = qglGetUniformLocationARB(p->program, "LightColor");
-               p->loc_LightDir                   = qglGetUniformLocationARB(p->program, "LightDir");
-               p->loc_LightPosition              = qglGetUniformLocationARB(p->program, "LightPosition");
-               p->loc_OffsetMapping_Scale        = qglGetUniformLocationARB(p->program, "OffsetMapping_Scale");
-               p->loc_PixelSize                  = qglGetUniformLocationARB(p->program, "PixelSize");
-               p->loc_ReflectColor               = qglGetUniformLocationARB(p->program, "ReflectColor");
-               p->loc_ReflectFactor              = qglGetUniformLocationARB(p->program, "ReflectFactor");
-               p->loc_ReflectOffset              = qglGetUniformLocationARB(p->program, "ReflectOffset");
-               p->loc_RefractColor               = qglGetUniformLocationARB(p->program, "RefractColor");
-               p->loc_Saturation                 = qglGetUniformLocationARB(p->program, "Saturation");
-               p->loc_ScreenCenterRefractReflect = qglGetUniformLocationARB(p->program, "ScreenCenterRefractReflect");
-               p->loc_ScreenScaleRefractReflect  = qglGetUniformLocationARB(p->program, "ScreenScaleRefractReflect");
-               p->loc_ScreenToDepth              = qglGetUniformLocationARB(p->program, "ScreenToDepth");
-               p->loc_ShadowMap_Parameters       = qglGetUniformLocationARB(p->program, "ShadowMap_Parameters");
-               p->loc_ShadowMap_TextureScale     = qglGetUniformLocationARB(p->program, "ShadowMap_TextureScale");
-               p->loc_SpecularPower              = qglGetUniformLocationARB(p->program, "SpecularPower");
-               p->loc_UserVec1                   = qglGetUniformLocationARB(p->program, "UserVec1");
-               p->loc_UserVec2                   = qglGetUniformLocationARB(p->program, "UserVec2");
-               p->loc_UserVec3                   = qglGetUniformLocationARB(p->program, "UserVec3");
-               p->loc_UserVec4                   = qglGetUniformLocationARB(p->program, "UserVec4");
-               p->loc_ViewTintColor              = qglGetUniformLocationARB(p->program, "ViewTintColor");
-               p->loc_ViewToLight                = qglGetUniformLocationARB(p->program, "ViewToLight");
-               p->loc_ModelToLight               = qglGetUniformLocationARB(p->program, "ModelToLight");
-               p->loc_TexMatrix                  = qglGetUniformLocationARB(p->program, "TexMatrix");
-               p->loc_BackgroundTexMatrix        = qglGetUniformLocationARB(p->program, "BackgroundTexMatrix");
-               p->loc_ModelViewMatrix            = qglGetUniformLocationARB(p->program, "ModelViewMatrix");
-               p->loc_ModelViewProjectionMatrix  = qglGetUniformLocationARB(p->program, "ModelViewProjectionMatrix");
-               p->loc_PixelToScreenTexCoord      = qglGetUniformLocationARB(p->program, "PixelToScreenTexCoord");
-               p->loc_ModelToReflectCube         = qglGetUniformLocationARB(p->program, "ModelToReflectCube");
+               p->loc_Texture_First              = qglGetUniformLocation(p->program, "Texture_First");
+               p->loc_Texture_Second             = qglGetUniformLocation(p->program, "Texture_Second");
+               p->loc_Texture_GammaRamps         = qglGetUniformLocation(p->program, "Texture_GammaRamps");
+               p->loc_Texture_Normal             = qglGetUniformLocation(p->program, "Texture_Normal");
+               p->loc_Texture_Color              = qglGetUniformLocation(p->program, "Texture_Color");
+               p->loc_Texture_Gloss              = qglGetUniformLocation(p->program, "Texture_Gloss");
+               p->loc_Texture_Glow               = qglGetUniformLocation(p->program, "Texture_Glow");
+               p->loc_Texture_SecondaryNormal    = qglGetUniformLocation(p->program, "Texture_SecondaryNormal");
+               p->loc_Texture_SecondaryColor     = qglGetUniformLocation(p->program, "Texture_SecondaryColor");
+               p->loc_Texture_SecondaryGloss     = qglGetUniformLocation(p->program, "Texture_SecondaryGloss");
+               p->loc_Texture_SecondaryGlow      = qglGetUniformLocation(p->program, "Texture_SecondaryGlow");
+               p->loc_Texture_Pants              = qglGetUniformLocation(p->program, "Texture_Pants");
+               p->loc_Texture_Shirt              = qglGetUniformLocation(p->program, "Texture_Shirt");
+               p->loc_Texture_FogHeightTexture   = qglGetUniformLocation(p->program, "Texture_FogHeightTexture");
+               p->loc_Texture_FogMask            = qglGetUniformLocation(p->program, "Texture_FogMask");
+               p->loc_Texture_Lightmap           = qglGetUniformLocation(p->program, "Texture_Lightmap");
+               p->loc_Texture_Deluxemap          = qglGetUniformLocation(p->program, "Texture_Deluxemap");
+               p->loc_Texture_Attenuation        = qglGetUniformLocation(p->program, "Texture_Attenuation");
+               p->loc_Texture_Cube               = qglGetUniformLocation(p->program, "Texture_Cube");
+               p->loc_Texture_Refraction         = qglGetUniformLocation(p->program, "Texture_Refraction");
+               p->loc_Texture_Reflection         = qglGetUniformLocation(p->program, "Texture_Reflection");
+               p->loc_Texture_ShadowMap2D        = qglGetUniformLocation(p->program, "Texture_ShadowMap2D");
+               p->loc_Texture_CubeProjection     = qglGetUniformLocation(p->program, "Texture_CubeProjection");
+               p->loc_Texture_ScreenDepth        = qglGetUniformLocation(p->program, "Texture_ScreenDepth");
+               p->loc_Texture_ScreenNormalMap    = qglGetUniformLocation(p->program, "Texture_ScreenNormalMap");
+               p->loc_Texture_ScreenDiffuse      = qglGetUniformLocation(p->program, "Texture_ScreenDiffuse");
+               p->loc_Texture_ScreenSpecular     = qglGetUniformLocation(p->program, "Texture_ScreenSpecular");
+               p->loc_Texture_ReflectMask        = qglGetUniformLocation(p->program, "Texture_ReflectMask");
+               p->loc_Texture_ReflectCube        = qglGetUniformLocation(p->program, "Texture_ReflectCube");
+               p->loc_Texture_BounceGrid         = qglGetUniformLocation(p->program, "Texture_BounceGrid");
+               p->loc_Alpha                      = qglGetUniformLocation(p->program, "Alpha");
+               p->loc_BloomBlur_Parameters       = qglGetUniformLocation(p->program, "BloomBlur_Parameters");
+               p->loc_ClientTime                 = qglGetUniformLocation(p->program, "ClientTime");
+               p->loc_Color_Ambient              = qglGetUniformLocation(p->program, "Color_Ambient");
+               p->loc_Color_Diffuse              = qglGetUniformLocation(p->program, "Color_Diffuse");
+               p->loc_Color_Specular             = qglGetUniformLocation(p->program, "Color_Specular");
+               p->loc_Color_Glow                 = qglGetUniformLocation(p->program, "Color_Glow");
+               p->loc_Color_Pants                = qglGetUniformLocation(p->program, "Color_Pants");
+               p->loc_Color_Shirt                = qglGetUniformLocation(p->program, "Color_Shirt");
+               p->loc_DeferredColor_Ambient      = qglGetUniformLocation(p->program, "DeferredColor_Ambient");
+               p->loc_DeferredColor_Diffuse      = qglGetUniformLocation(p->program, "DeferredColor_Diffuse");
+               p->loc_DeferredColor_Specular     = qglGetUniformLocation(p->program, "DeferredColor_Specular");
+               p->loc_DeferredMod_Diffuse        = qglGetUniformLocation(p->program, "DeferredMod_Diffuse");
+               p->loc_DeferredMod_Specular       = qglGetUniformLocation(p->program, "DeferredMod_Specular");
+               p->loc_DistortScaleRefractReflect = qglGetUniformLocation(p->program, "DistortScaleRefractReflect");
+               p->loc_EyePosition                = qglGetUniformLocation(p->program, "EyePosition");
+               p->loc_FogColor                   = qglGetUniformLocation(p->program, "FogColor");
+               p->loc_FogHeightFade              = qglGetUniformLocation(p->program, "FogHeightFade");
+               p->loc_FogPlane                   = qglGetUniformLocation(p->program, "FogPlane");
+               p->loc_FogPlaneViewDist           = qglGetUniformLocation(p->program, "FogPlaneViewDist");
+               p->loc_FogRangeRecip              = qglGetUniformLocation(p->program, "FogRangeRecip");
+               p->loc_LightColor                 = qglGetUniformLocation(p->program, "LightColor");
+               p->loc_LightDir                   = qglGetUniformLocation(p->program, "LightDir");
+               p->loc_LightPosition              = qglGetUniformLocation(p->program, "LightPosition");
+               p->loc_OffsetMapping_Scale        = qglGetUniformLocation(p->program, "OffsetMapping_Scale");
+               p->loc_PixelSize                  = qglGetUniformLocation(p->program, "PixelSize");
+               p->loc_ReflectColor               = qglGetUniformLocation(p->program, "ReflectColor");
+               p->loc_ReflectFactor              = qglGetUniformLocation(p->program, "ReflectFactor");
+               p->loc_ReflectOffset              = qglGetUniformLocation(p->program, "ReflectOffset");
+               p->loc_RefractColor               = qglGetUniformLocation(p->program, "RefractColor");
+               p->loc_Saturation                 = qglGetUniformLocation(p->program, "Saturation");
+               p->loc_ScreenCenterRefractReflect = qglGetUniformLocation(p->program, "ScreenCenterRefractReflect");
+               p->loc_ScreenScaleRefractReflect  = qglGetUniformLocation(p->program, "ScreenScaleRefractReflect");
+               p->loc_ScreenToDepth              = qglGetUniformLocation(p->program, "ScreenToDepth");
+               p->loc_ShadowMap_Parameters       = qglGetUniformLocation(p->program, "ShadowMap_Parameters");
+               p->loc_ShadowMap_TextureScale     = qglGetUniformLocation(p->program, "ShadowMap_TextureScale");
+               p->loc_SpecularPower              = qglGetUniformLocation(p->program, "SpecularPower");
+               p->loc_UserVec1                   = qglGetUniformLocation(p->program, "UserVec1");
+               p->loc_UserVec2                   = qglGetUniformLocation(p->program, "UserVec2");
+               p->loc_UserVec3                   = qglGetUniformLocation(p->program, "UserVec3");
+               p->loc_UserVec4                   = qglGetUniformLocation(p->program, "UserVec4");
+               p->loc_ViewTintColor              = qglGetUniformLocation(p->program, "ViewTintColor");
+               p->loc_ViewToLight                = qglGetUniformLocation(p->program, "ViewToLight");
+               p->loc_ModelToLight               = qglGetUniformLocation(p->program, "ModelToLight");
+               p->loc_TexMatrix                  = qglGetUniformLocation(p->program, "TexMatrix");
+               p->loc_BackgroundTexMatrix        = qglGetUniformLocation(p->program, "BackgroundTexMatrix");
+               p->loc_ModelViewMatrix            = qglGetUniformLocation(p->program, "ModelViewMatrix");
+               p->loc_ModelViewProjectionMatrix  = qglGetUniformLocation(p->program, "ModelViewProjectionMatrix");
+               p->loc_PixelToScreenTexCoord      = qglGetUniformLocation(p->program, "PixelToScreenTexCoord");
+               p->loc_ModelToReflectCube         = qglGetUniformLocation(p->program, "ModelToReflectCube");
+               p->loc_ShadowMapMatrix            = qglGetUniformLocation(p->program, "ShadowMapMatrix");
+               p->loc_BloomColorSubtract         = qglGetUniformLocation(p->program, "BloomColorSubtract");
+               p->loc_NormalmapScrollBlend       = qglGetUniformLocation(p->program, "NormalmapScrollBlend");
+               p->loc_BounceGridMatrix           = qglGetUniformLocation(p->program, "BounceGridMatrix");
+               p->loc_BounceGridIntensity        = qglGetUniformLocation(p->program, "BounceGridIntensity");
                // 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);
-               if (p->loc_Texture_GammaRamps      >= 0) qglUniform1iARB(p->loc_Texture_GammaRamps     , GL20TU_GAMMARAMPS);
-               if (p->loc_Texture_Normal          >= 0) qglUniform1iARB(p->loc_Texture_Normal         , GL20TU_NORMAL);
-               if (p->loc_Texture_Color           >= 0) qglUniform1iARB(p->loc_Texture_Color          , GL20TU_COLOR);
-               if (p->loc_Texture_Gloss           >= 0) qglUniform1iARB(p->loc_Texture_Gloss          , GL20TU_GLOSS);
-               if (p->loc_Texture_Glow            >= 0) qglUniform1iARB(p->loc_Texture_Glow           , GL20TU_GLOW);
-               if (p->loc_Texture_SecondaryNormal >= 0) qglUniform1iARB(p->loc_Texture_SecondaryNormal, GL20TU_SECONDARY_NORMAL);
-               if (p->loc_Texture_SecondaryColor  >= 0) qglUniform1iARB(p->loc_Texture_SecondaryColor , GL20TU_SECONDARY_COLOR);
-               if (p->loc_Texture_SecondaryGloss  >= 0) qglUniform1iARB(p->loc_Texture_SecondaryGloss , GL20TU_SECONDARY_GLOSS);
-               if (p->loc_Texture_SecondaryGlow   >= 0) qglUniform1iARB(p->loc_Texture_SecondaryGlow  , GL20TU_SECONDARY_GLOW);
-               if (p->loc_Texture_Pants           >= 0) qglUniform1iARB(p->loc_Texture_Pants          , GL20TU_PANTS);
-               if (p->loc_Texture_Shirt           >= 0) qglUniform1iARB(p->loc_Texture_Shirt          , GL20TU_SHIRT);
-               if (p->loc_Texture_FogMask         >= 0) qglUniform1iARB(p->loc_Texture_FogMask        , GL20TU_FOGMASK);
-               if (p->loc_Texture_Lightmap        >= 0) qglUniform1iARB(p->loc_Texture_Lightmap       , GL20TU_LIGHTMAP);
-               if (p->loc_Texture_Deluxemap       >= 0) qglUniform1iARB(p->loc_Texture_Deluxemap      , GL20TU_DELUXEMAP);
-               if (p->loc_Texture_Attenuation     >= 0) qglUniform1iARB(p->loc_Texture_Attenuation    , GL20TU_ATTENUATION);
-               if (p->loc_Texture_Cube            >= 0) qglUniform1iARB(p->loc_Texture_Cube           , GL20TU_CUBE);
-               if (p->loc_Texture_Refraction      >= 0) qglUniform1iARB(p->loc_Texture_Refraction     , GL20TU_REFRACTION);
-               if (p->loc_Texture_Reflection      >= 0) qglUniform1iARB(p->loc_Texture_Reflection     , GL20TU_REFLECTION);
-               if (p->loc_Texture_ShadowMapRect   >= 0) qglUniform1iARB(p->loc_Texture_ShadowMapRect  , GL20TU_SHADOWMAPRECT);
-               if (p->loc_Texture_ShadowMapCube   >= 0) qglUniform1iARB(p->loc_Texture_ShadowMapCube  , GL20TU_SHADOWMAPCUBE);
-               if (p->loc_Texture_ShadowMap2D     >= 0) qglUniform1iARB(p->loc_Texture_ShadowMap2D    , GL20TU_SHADOWMAP2D);
-               if (p->loc_Texture_CubeProjection  >= 0) qglUniform1iARB(p->loc_Texture_CubeProjection , GL20TU_CUBEPROJECTION);
-               if (p->loc_Texture_ScreenDepth     >= 0) qglUniform1iARB(p->loc_Texture_ScreenDepth    , GL20TU_SCREENDEPTH);
-               if (p->loc_Texture_ScreenNormalMap >= 0) qglUniform1iARB(p->loc_Texture_ScreenNormalMap, GL20TU_SCREENNORMALMAP);
-               if (p->loc_Texture_ScreenDiffuse   >= 0) qglUniform1iARB(p->loc_Texture_ScreenDiffuse  , GL20TU_SCREENDIFFUSE);
-               if (p->loc_Texture_ScreenSpecular  >= 0) qglUniform1iARB(p->loc_Texture_ScreenSpecular , GL20TU_SCREENSPECULAR);
-               if (p->loc_Texture_ReflectMask     >= 0) qglUniform1iARB(p->loc_Texture_ReflectMask    , GL20TU_REFLECTMASK);
-               if (p->loc_Texture_ReflectCube     >= 0) qglUniform1iARB(p->loc_Texture_ReflectCube    , GL20TU_REFLECTCUBE);
+               p->tex_Texture_First = -1;
+               p->tex_Texture_Second = -1;
+               p->tex_Texture_GammaRamps = -1;
+               p->tex_Texture_Normal = -1;
+               p->tex_Texture_Color = -1;
+               p->tex_Texture_Gloss = -1;
+               p->tex_Texture_Glow = -1;
+               p->tex_Texture_SecondaryNormal = -1;
+               p->tex_Texture_SecondaryColor = -1;
+               p->tex_Texture_SecondaryGloss = -1;
+               p->tex_Texture_SecondaryGlow = -1;
+               p->tex_Texture_Pants = -1;
+               p->tex_Texture_Shirt = -1;
+               p->tex_Texture_FogHeightTexture = -1;
+               p->tex_Texture_FogMask = -1;
+               p->tex_Texture_Lightmap = -1;
+               p->tex_Texture_Deluxemap = -1;
+               p->tex_Texture_Attenuation = -1;
+               p->tex_Texture_Cube = -1;
+               p->tex_Texture_Refraction = -1;
+               p->tex_Texture_Reflection = -1;
+               p->tex_Texture_ShadowMap2D = -1;
+               p->tex_Texture_CubeProjection = -1;
+               p->tex_Texture_ScreenDepth = -1;
+               p->tex_Texture_ScreenNormalMap = -1;
+               p->tex_Texture_ScreenDiffuse = -1;
+               p->tex_Texture_ScreenSpecular = -1;
+               p->tex_Texture_ReflectMask = -1;
+               p->tex_Texture_ReflectCube = -1;
+               p->tex_Texture_BounceGrid = -1;
+               sampler = 0;
+               if (p->loc_Texture_First           >= 0) {p->tex_Texture_First            = sampler;qglUniform1i(p->loc_Texture_First           , sampler);sampler++;}
+               if (p->loc_Texture_Second          >= 0) {p->tex_Texture_Second           = sampler;qglUniform1i(p->loc_Texture_Second          , sampler);sampler++;}
+               if (p->loc_Texture_GammaRamps      >= 0) {p->tex_Texture_GammaRamps       = sampler;qglUniform1i(p->loc_Texture_GammaRamps      , sampler);sampler++;}
+               if (p->loc_Texture_Normal          >= 0) {p->tex_Texture_Normal           = sampler;qglUniform1i(p->loc_Texture_Normal          , sampler);sampler++;}
+               if (p->loc_Texture_Color           >= 0) {p->tex_Texture_Color            = sampler;qglUniform1i(p->loc_Texture_Color           , sampler);sampler++;}
+               if (p->loc_Texture_Gloss           >= 0) {p->tex_Texture_Gloss            = sampler;qglUniform1i(p->loc_Texture_Gloss           , sampler);sampler++;}
+               if (p->loc_Texture_Glow            >= 0) {p->tex_Texture_Glow             = sampler;qglUniform1i(p->loc_Texture_Glow            , sampler);sampler++;}
+               if (p->loc_Texture_SecondaryNormal >= 0) {p->tex_Texture_SecondaryNormal  = sampler;qglUniform1i(p->loc_Texture_SecondaryNormal , sampler);sampler++;}
+               if (p->loc_Texture_SecondaryColor  >= 0) {p->tex_Texture_SecondaryColor   = sampler;qglUniform1i(p->loc_Texture_SecondaryColor  , sampler);sampler++;}
+               if (p->loc_Texture_SecondaryGloss  >= 0) {p->tex_Texture_SecondaryGloss   = sampler;qglUniform1i(p->loc_Texture_SecondaryGloss  , sampler);sampler++;}
+               if (p->loc_Texture_SecondaryGlow   >= 0) {p->tex_Texture_SecondaryGlow    = sampler;qglUniform1i(p->loc_Texture_SecondaryGlow   , sampler);sampler++;}
+               if (p->loc_Texture_Pants           >= 0) {p->tex_Texture_Pants            = sampler;qglUniform1i(p->loc_Texture_Pants           , sampler);sampler++;}
+               if (p->loc_Texture_Shirt           >= 0) {p->tex_Texture_Shirt            = sampler;qglUniform1i(p->loc_Texture_Shirt           , sampler);sampler++;}
+               if (p->loc_Texture_FogHeightTexture>= 0) {p->tex_Texture_FogHeightTexture = sampler;qglUniform1i(p->loc_Texture_FogHeightTexture, sampler);sampler++;}
+               if (p->loc_Texture_FogMask         >= 0) {p->tex_Texture_FogMask          = sampler;qglUniform1i(p->loc_Texture_FogMask         , sampler);sampler++;}
+               if (p->loc_Texture_Lightmap        >= 0) {p->tex_Texture_Lightmap         = sampler;qglUniform1i(p->loc_Texture_Lightmap        , sampler);sampler++;}
+               if (p->loc_Texture_Deluxemap       >= 0) {p->tex_Texture_Deluxemap        = sampler;qglUniform1i(p->loc_Texture_Deluxemap       , sampler);sampler++;}
+               if (p->loc_Texture_Attenuation     >= 0) {p->tex_Texture_Attenuation      = sampler;qglUniform1i(p->loc_Texture_Attenuation     , sampler);sampler++;}
+               if (p->loc_Texture_Cube            >= 0) {p->tex_Texture_Cube             = sampler;qglUniform1i(p->loc_Texture_Cube            , sampler);sampler++;}
+               if (p->loc_Texture_Refraction      >= 0) {p->tex_Texture_Refraction       = sampler;qglUniform1i(p->loc_Texture_Refraction      , sampler);sampler++;}
+               if (p->loc_Texture_Reflection      >= 0) {p->tex_Texture_Reflection       = sampler;qglUniform1i(p->loc_Texture_Reflection      , sampler);sampler++;}
+               if (p->loc_Texture_ShadowMap2D     >= 0) {p->tex_Texture_ShadowMap2D      = sampler;qglUniform1i(p->loc_Texture_ShadowMap2D     , sampler);sampler++;}
+               if (p->loc_Texture_CubeProjection  >= 0) {p->tex_Texture_CubeProjection   = sampler;qglUniform1i(p->loc_Texture_CubeProjection  , sampler);sampler++;}
+               if (p->loc_Texture_ScreenDepth     >= 0) {p->tex_Texture_ScreenDepth      = sampler;qglUniform1i(p->loc_Texture_ScreenDepth     , sampler);sampler++;}
+               if (p->loc_Texture_ScreenNormalMap >= 0) {p->tex_Texture_ScreenNormalMap  = sampler;qglUniform1i(p->loc_Texture_ScreenNormalMap , sampler);sampler++;}
+               if (p->loc_Texture_ScreenDiffuse   >= 0) {p->tex_Texture_ScreenDiffuse    = sampler;qglUniform1i(p->loc_Texture_ScreenDiffuse   , sampler);sampler++;}
+               if (p->loc_Texture_ScreenSpecular  >= 0) {p->tex_Texture_ScreenSpecular   = sampler;qglUniform1i(p->loc_Texture_ScreenSpecular  , sampler);sampler++;}
+               if (p->loc_Texture_ReflectMask     >= 0) {p->tex_Texture_ReflectMask      = sampler;qglUniform1i(p->loc_Texture_ReflectMask     , sampler);sampler++;}
+               if (p->loc_Texture_ReflectCube     >= 0) {p->tex_Texture_ReflectCube      = sampler;qglUniform1i(p->loc_Texture_ReflectCube     , sampler);sampler++;}
+               if (p->loc_Texture_BounceGrid      >= 0) {p->tex_Texture_BounceGrid       = sampler;qglUniform1i(p->loc_Texture_BounceGrid      , sampler);sampler++;}
                CHECKGLERROR
-               Con_DPrintf("^5GLSL shader %s compiled.\n", permutationname);
+               Con_DPrintf("^5GLSL shader %s compiled (%i textures).\n", permutationname, sampler);
        }
        else
                Con_Printf("^1GLSL shader %s failed!  some features may not work properly.\n", permutationname);
@@ -3706,173 +1245,160 @@ void R_SetupShader_SetPermutationGLSL(unsigned int mode, unsigned int permutatio
                                {
                                        //Con_Printf("Could not find a working OpenGL 2.0 shader for permutation %s %s\n", shadermodeinfo[mode].vertexfilename, shadermodeinfo[mode].pretext);
                                        r_glsl_permutation = R_GLSL_FindPermutation(mode, permutation);
-                                       qglUseProgramObjectARB(0);CHECKGLERROR
+                                       qglUseProgram(0);CHECKGLERROR
                                        return; // no bit left to clear, entire mode is broken
                                }
                        }
                }
                CHECKGLERROR
-               qglUseProgramObjectARB(r_glsl_permutation->program);CHECKGLERROR
+               qglUseProgram(r_glsl_permutation->program);CHECKGLERROR
        }
-       if (r_glsl_permutation->loc_ModelViewProjectionMatrix >= 0) qglUniformMatrix4fvARB(r_glsl_permutation->loc_ModelViewProjectionMatrix, 1, false, gl_modelviewprojection16f);
-       if (r_glsl_permutation->loc_ModelViewMatrix >= 0) qglUniformMatrix4fvARB(r_glsl_permutation->loc_ModelViewMatrix, 1, false, gl_modelview16f);
+       if (r_glsl_permutation->loc_ModelViewProjectionMatrix >= 0) qglUniformMatrix4fv(r_glsl_permutation->loc_ModelViewProjectionMatrix, 1, false, gl_modelviewprojection16f);
+       if (r_glsl_permutation->loc_ModelViewMatrix >= 0) qglUniformMatrix4fv(r_glsl_permutation->loc_ModelViewMatrix, 1, false, gl_modelview16f);
+       if (r_glsl_permutation->loc_ClientTime >= 0) qglUniform1f(r_glsl_permutation->loc_ClientTime, cl.time);
 }
 
-#ifdef SUPPORTCG
-#include <Cg/cgGL.h>
-struct r_cg_permutation_s;
-typedef struct r_cg_permutation_s
+#ifdef SUPPORTD3D
+
+#ifdef SUPPORTD3D
+#include <d3d9.h>
+extern LPDIRECT3DDEVICE9 vid_d3d9dev;
+extern D3DCAPS9 vid_d3d9caps;
+#endif
+
+struct r_hlsl_permutation_s;
+typedef struct r_hlsl_permutation_s
 {
        /// hash lookup data
-       struct r_cg_permutation_s *hashnext;
+       struct r_hlsl_permutation_s *hashnext;
        unsigned int mode;
        unsigned int permutation;
 
        /// indicates if we have tried compiling this permutation already
        qboolean compiled;
-       /// 0 if compilation failed
-       CGprogram vprogram;
-       CGprogram fprogram;
-       /// locations of detected parameters in programs, or NULL if not found
-       CGparameter vp_EyePosition;
-       CGparameter vp_FogPlane;
-       CGparameter vp_LightDir;
-       CGparameter vp_LightPosition;
-       CGparameter vp_ModelToLight;
-       CGparameter vp_TexMatrix;
-       CGparameter vp_BackgroundTexMatrix;
-       CGparameter vp_ModelViewProjectionMatrix;
-       CGparameter vp_ModelViewMatrix;
-
-       CGparameter fp_Texture_First;
-       CGparameter fp_Texture_Second;
-       CGparameter fp_Texture_GammaRamps;
-       CGparameter fp_Texture_Normal;
-       CGparameter fp_Texture_Color;
-       CGparameter fp_Texture_Gloss;
-       CGparameter fp_Texture_Glow;
-       CGparameter fp_Texture_SecondaryNormal;
-       CGparameter fp_Texture_SecondaryColor;
-       CGparameter fp_Texture_SecondaryGloss;
-       CGparameter fp_Texture_SecondaryGlow;
-       CGparameter fp_Texture_Pants;
-       CGparameter fp_Texture_Shirt;
-       CGparameter fp_Texture_FogMask;
-       CGparameter fp_Texture_Lightmap;
-       CGparameter fp_Texture_Deluxemap;
-       CGparameter fp_Texture_Attenuation;
-       CGparameter fp_Texture_Cube;
-       CGparameter fp_Texture_Refraction;
-       CGparameter fp_Texture_Reflection;
-       CGparameter fp_Texture_ShadowMapRect;
-       CGparameter fp_Texture_ShadowMapCube;
-       CGparameter fp_Texture_ShadowMap2D;
-       CGparameter fp_Texture_CubeProjection;
-       CGparameter fp_Texture_ScreenDepth;
-       CGparameter fp_Texture_ScreenNormalMap;
-       CGparameter fp_Texture_ScreenDiffuse;
-       CGparameter fp_Texture_ScreenSpecular;
-       CGparameter fp_Texture_ReflectMask;
-       CGparameter fp_Texture_ReflectCube;
-       CGparameter fp_Alpha;
-       CGparameter fp_BloomBlur_Parameters;
-       CGparameter fp_ClientTime;
-       CGparameter fp_Color_Ambient;
-       CGparameter fp_Color_Diffuse;
-       CGparameter fp_Color_Specular;
-       CGparameter fp_Color_Glow;
-       CGparameter fp_Color_Pants;
-       CGparameter fp_Color_Shirt;
-       CGparameter fp_DeferredColor_Ambient;
-       CGparameter fp_DeferredColor_Diffuse;
-       CGparameter fp_DeferredColor_Specular;
-       CGparameter fp_DeferredMod_Diffuse;
-       CGparameter fp_DeferredMod_Specular;
-       CGparameter fp_DistortScaleRefractReflect;
-       CGparameter fp_EyePosition;
-       CGparameter fp_FogColor;
-       CGparameter fp_FogHeightFade;
-       CGparameter fp_FogPlane;
-       CGparameter fp_FogPlaneViewDist;
-       CGparameter fp_FogRangeRecip;
-       CGparameter fp_LightColor;
-       CGparameter fp_LightDir;
-       CGparameter fp_LightPosition;
-       CGparameter fp_OffsetMapping_Scale;
-       CGparameter fp_PixelSize;
-       CGparameter fp_ReflectColor;
-       CGparameter fp_ReflectFactor;
-       CGparameter fp_ReflectOffset;
-       CGparameter fp_RefractColor;
-       CGparameter fp_Saturation;
-       CGparameter fp_ScreenCenterRefractReflect;
-       CGparameter fp_ScreenScaleRefractReflect;
-       CGparameter fp_ScreenToDepth;
-       CGparameter fp_ShadowMap_Parameters;
-       CGparameter fp_ShadowMap_TextureScale;
-       CGparameter fp_SpecularPower;
-       CGparameter fp_UserVec1;
-       CGparameter fp_UserVec2;
-       CGparameter fp_UserVec3;
-       CGparameter fp_UserVec4;
-       CGparameter fp_ViewTintColor;
-       CGparameter fp_ViewToLight;
-       CGparameter fp_PixelToScreenTexCoord;
-       CGparameter fp_ModelToReflectCube;
-}
-r_cg_permutation_t;
+       /// NULL if compilation failed
+       IDirect3DVertexShader9 *vertexshader;
+       IDirect3DPixelShader9 *pixelshader;
+}
+r_hlsl_permutation_t;
+
+typedef enum D3DVSREGISTER_e
+{
+       D3DVSREGISTER_TexMatrix = 0, // float4x4
+       D3DVSREGISTER_BackgroundTexMatrix = 4, // float4x4
+       D3DVSREGISTER_ModelViewProjectionMatrix = 8, // float4x4
+       D3DVSREGISTER_ModelViewMatrix = 12, // float4x4
+       D3DVSREGISTER_ShadowMapMatrix = 16, // float4x4
+       D3DVSREGISTER_ModelToLight = 20, // float4x4
+       D3DVSREGISTER_EyePosition = 24,
+       D3DVSREGISTER_FogPlane = 25,
+       D3DVSREGISTER_LightDir = 26,
+       D3DVSREGISTER_LightPosition = 27,
+}
+D3DVSREGISTER_t;
+
+typedef enum D3DPSREGISTER_e
+{
+       D3DPSREGISTER_Alpha = 0,
+       D3DPSREGISTER_BloomBlur_Parameters = 1,
+       D3DPSREGISTER_ClientTime = 2,
+       D3DPSREGISTER_Color_Ambient = 3,
+       D3DPSREGISTER_Color_Diffuse = 4,
+       D3DPSREGISTER_Color_Specular = 5,
+       D3DPSREGISTER_Color_Glow = 6,
+       D3DPSREGISTER_Color_Pants = 7,
+       D3DPSREGISTER_Color_Shirt = 8,
+       D3DPSREGISTER_DeferredColor_Ambient = 9,
+       D3DPSREGISTER_DeferredColor_Diffuse = 10,
+       D3DPSREGISTER_DeferredColor_Specular = 11,
+       D3DPSREGISTER_DeferredMod_Diffuse = 12,
+       D3DPSREGISTER_DeferredMod_Specular = 13,
+       D3DPSREGISTER_DistortScaleRefractReflect = 14,
+       D3DPSREGISTER_EyePosition = 15, // unused
+       D3DPSREGISTER_FogColor = 16,
+       D3DPSREGISTER_FogHeightFade = 17,
+       D3DPSREGISTER_FogPlane = 18,
+       D3DPSREGISTER_FogPlaneViewDist = 19,
+       D3DPSREGISTER_FogRangeRecip = 20,
+       D3DPSREGISTER_LightColor = 21,
+       D3DPSREGISTER_LightDir = 22, // unused
+       D3DPSREGISTER_LightPosition = 23,
+       D3DPSREGISTER_OffsetMapping_Scale = 24,
+       D3DPSREGISTER_PixelSize = 25,
+       D3DPSREGISTER_ReflectColor = 26,
+       D3DPSREGISTER_ReflectFactor = 27,
+       D3DPSREGISTER_ReflectOffset = 28,
+       D3DPSREGISTER_RefractColor = 29,
+       D3DPSREGISTER_Saturation = 30,
+       D3DPSREGISTER_ScreenCenterRefractReflect = 31,
+       D3DPSREGISTER_ScreenScaleRefractReflect = 32,
+       D3DPSREGISTER_ScreenToDepth = 33,
+       D3DPSREGISTER_ShadowMap_Parameters = 34,
+       D3DPSREGISTER_ShadowMap_TextureScale = 35,
+       D3DPSREGISTER_SpecularPower = 36,
+       D3DPSREGISTER_UserVec1 = 37,
+       D3DPSREGISTER_UserVec2 = 38,
+       D3DPSREGISTER_UserVec3 = 39,
+       D3DPSREGISTER_UserVec4 = 40,
+       D3DPSREGISTER_ViewTintColor = 41,
+       D3DPSREGISTER_PixelToScreenTexCoord = 42,
+       D3DPSREGISTER_BloomColorSubtract = 43,
+       D3DPSREGISTER_ViewToLight = 44, // float4x4
+       D3DPSREGISTER_ModelToReflectCube = 48, // float4x4
+       D3DPSREGISTER_NormalmapScrollBlend = 52,
+       // next at 53
+}
+D3DPSREGISTER_t;
 
 /// information about each possible shader permutation
-r_cg_permutation_t *r_cg_permutationhash[SHADERMODE_COUNT][SHADERPERMUTATION_HASHSIZE];
+r_hlsl_permutation_t *r_hlsl_permutationhash[SHADERMODE_COUNT][SHADERPERMUTATION_HASHSIZE];
 /// currently selected permutation
-r_cg_permutation_t *r_cg_permutation;
+r_hlsl_permutation_t *r_hlsl_permutation;
 /// storage for permutations linked in the hash table
-memexpandablearray_t r_cg_permutationarray;
-
-#define CHECKCGERROR {CGerror err = cgGetError(), err2 = err;if (err){Con_Printf("%s:%i CG error %i: %s : %s\n", __FILE__, __LINE__, err, cgGetErrorString(err), cgGetLastErrorString(&err2));if (err == 1) Con_Printf("last listing:\n%s\n", cgGetLastListing(vid.cgcontext));}}
+memexpandablearray_t r_hlsl_permutationarray;
 
-static r_cg_permutation_t *R_CG_FindPermutation(unsigned int mode, unsigned int permutation)
+static r_hlsl_permutation_t *R_HLSL_FindPermutation(unsigned int mode, unsigned int permutation)
 {
        //unsigned int hashdepth = 0;
        unsigned int hashindex = (permutation * 0x1021) & (SHADERPERMUTATION_HASHSIZE - 1);
-       r_cg_permutation_t *p;
-       for (p = r_cg_permutationhash[mode][hashindex];p;p = p->hashnext)
+       r_hlsl_permutation_t *p;
+       for (p = r_hlsl_permutationhash[mode][hashindex];p;p = p->hashnext)
        {
                if (p->mode == mode && p->permutation == permutation)
                {
                        //if (hashdepth > 10)
-                       //      Con_Printf("R_CG_FindPermutation: Warning: %i:%i has hashdepth %i\n", mode, permutation, hashdepth);
+                       //      Con_Printf("R_HLSL_FindPermutation: Warning: %i:%i has hashdepth %i\n", mode, permutation, hashdepth);
                        return p;
                }
                //hashdepth++;
        }
-       p = (r_cg_permutation_t*)Mem_ExpandableArray_AllocRecord(&r_cg_permutationarray);
+       p = (r_hlsl_permutation_t*)Mem_ExpandableArray_AllocRecord(&r_hlsl_permutationarray);
        p->mode = mode;
        p->permutation = permutation;
-       p->hashnext = r_cg_permutationhash[mode][hashindex];
-       r_cg_permutationhash[mode][hashindex] = p;
+       p->hashnext = r_hlsl_permutationhash[mode][hashindex];
+       r_hlsl_permutationhash[mode][hashindex] = p;
        //if (hashdepth > 10)
-       //      Con_Printf("R_CG_FindPermutation: Warning: %i:%i has hashdepth %i\n", mode, permutation, hashdepth);
+       //      Con_Printf("R_HLSL_FindPermutation: Warning: %i:%i has hashdepth %i\n", mode, permutation, hashdepth);
        return p;
 }
 
-static char *R_CG_GetText(const char *filename, qboolean printfromdisknotice)
+static char *R_HLSL_GetText(const char *filename, qboolean printfromdisknotice)
 {
        char *shaderstring;
        if (!filename || !filename[0])
                return NULL;
-       if (!strcmp(filename, "cg/default.cg"))
+       if (!strcmp(filename, "hlsl/default.hlsl"))
        {
-               if (!cgshaderstring)
+               if (!hlslshaderstring)
                {
-                       cgshaderstring = (char *)FS_LoadFile(filename, r_main_mempool, false, NULL);
-                       if (cgshaderstring)
+                       hlslshaderstring = (char *)FS_LoadFile(filename, r_main_mempool, false, NULL);
+                       if (hlslshaderstring)
                                Con_DPrintf("Loading shaders from file %s...\n", filename);
                        else
-                               cgshaderstring = (char *)builtincgshaderstring;
+                               hlslshaderstring = (char *)builtinhlslshaderstring;
                }
-               shaderstring = (char *) Mem_Alloc(r_main_mempool, strlen(cgshaderstring) + 1);
-               memcpy(shaderstring, cgshaderstring, strlen(cgshaderstring) + 1);
+               shaderstring = (char *) Mem_Alloc(r_main_mempool, strlen(hlslshaderstring) + 1);
+               memcpy(shaderstring, hlslshaderstring, strlen(hlslshaderstring) + 1);
                return shaderstring;
        }
        shaderstring = (char *)FS_LoadFile(filename, r_main_mempool, false, NULL);
@@ -3885,43 +1411,190 @@ static char *R_CG_GetText(const char *filename, qboolean printfromdisknotice)
        return shaderstring;
 }
 
-static void R_CG_CacheShader(r_cg_permutation_t *p, const char *cachename, const char *vertstring, const char *fragstring)
-{
-       // TODO: load or create .fp and .vp shader files
+#include <d3dx9.h>
+//#include <d3dx9shader.h>
+//#include <d3dx9mesh.h>
+
+static void R_HLSL_CacheShader(r_hlsl_permutation_t *p, const char *cachename, const char *vertstring, const char *fragstring)
+{
+       DWORD *vsbin = NULL;
+       DWORD *psbin = NULL;
+       fs_offset_t vsbinsize;
+       fs_offset_t psbinsize;
+//     IDirect3DVertexShader9 *vs = NULL;
+//     IDirect3DPixelShader9 *ps = NULL;
+       ID3DXBuffer *vslog = NULL;
+       ID3DXBuffer *vsbuffer = NULL;
+       ID3DXConstantTable *vsconstanttable = NULL;
+       ID3DXBuffer *pslog = NULL;
+       ID3DXBuffer *psbuffer = NULL;
+       ID3DXConstantTable *psconstanttable = NULL;
+       int vsresult = 0;
+       int psresult = 0;
+       char temp[MAX_INPUTLINE];
+       const char *vsversion = "vs_3_0", *psversion = "ps_3_0";
+       qboolean debugshader = gl_paranoid.integer != 0;
+       if (p->permutation & SHADERPERMUTATION_OFFSETMAPPING) {vsversion = "vs_3_0";psversion = "ps_3_0";}
+       if (p->permutation & SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING) {vsversion = "vs_3_0";psversion = "ps_3_0";}
+       if (!debugshader)
+       {
+               vsbin = (DWORD *)FS_LoadFile(va("%s.vsbin", cachename), r_main_mempool, true, &vsbinsize);
+               psbin = (DWORD *)FS_LoadFile(va("%s.psbin", cachename), r_main_mempool, true, &psbinsize);
+       }
+       if ((!vsbin && vertstring) || (!psbin && fragstring))
+       {
+               const char* dllnames_d3dx9 [] =
+               {
+                       "d3dx9_43.dll",
+                       "d3dx9_42.dll",
+                       "d3dx9_41.dll",
+                       "d3dx9_40.dll",
+                       "d3dx9_39.dll",
+                       "d3dx9_38.dll",
+                       "d3dx9_37.dll",
+                       "d3dx9_36.dll",
+                       "d3dx9_35.dll",
+                       "d3dx9_34.dll",
+                       "d3dx9_33.dll",
+                       "d3dx9_32.dll",
+                       "d3dx9_31.dll",
+                       "d3dx9_30.dll",
+                       "d3dx9_29.dll",
+                       "d3dx9_28.dll",
+                       "d3dx9_27.dll",
+                       "d3dx9_26.dll",
+                       "d3dx9_25.dll",
+                       "d3dx9_24.dll",
+                       NULL
+               };
+               dllhandle_t d3dx9_dll = NULL;
+               HRESULT (WINAPI *qD3DXCompileShaderFromFileA)(LPCSTR pSrcFile, CONST D3DXMACRO* pDefines, LPD3DXINCLUDE pInclude, LPCSTR pFunctionName, LPCSTR pProfile, DWORD Flags, LPD3DXBUFFER* ppShader, LPD3DXBUFFER* ppErrorMsgs, LPD3DXCONSTANTTABLE* ppConstantTable);
+               HRESULT (WINAPI *qD3DXPreprocessShader)(LPCSTR pSrcData, UINT SrcDataSize, CONST D3DXMACRO* pDefines, LPD3DXINCLUDE pInclude, LPD3DXBUFFER* ppShaderText, LPD3DXBUFFER* ppErrorMsgs);
+               HRESULT (WINAPI *qD3DXCompileShader)(LPCSTR pSrcData, UINT SrcDataLen, CONST D3DXMACRO* pDefines, LPD3DXINCLUDE pInclude, LPCSTR pFunctionName, LPCSTR pProfile, DWORD Flags, LPD3DXBUFFER* ppShader, LPD3DXBUFFER* ppErrorMsgs, LPD3DXCONSTANTTABLE* ppConstantTable);
+               dllfunction_t d3dx9_dllfuncs[] =
+               {
+                       {"D3DXCompileShaderFromFileA",  (void **) &qD3DXCompileShaderFromFileA},
+                       {"D3DXPreprocessShader",                (void **) &qD3DXPreprocessShader},
+                       {"D3DXCompileShader",                   (void **) &qD3DXCompileShader},
+                       {NULL, NULL}
+               };
+               if (Sys_LoadLibrary(dllnames_d3dx9, &d3dx9_dll, d3dx9_dllfuncs))
+               {
+                       DWORD shaderflags = 0;
+                       if (debugshader)
+                               shaderflags = D3DXSHADER_DEBUG | D3DXSHADER_SKIPOPTIMIZATION;
+                       vsbin = (DWORD *)Mem_Realloc(tempmempool, vsbin, 0);
+                       psbin = (DWORD *)Mem_Realloc(tempmempool, psbin, 0);
+                       if (vertstring && vertstring[0])
+                       {
+                               if (debugshader)
+                               {
+//                                     vsresult = qD3DXPreprocessShader(vertstring, strlen(vertstring), NULL, NULL, &vsbuffer, &vslog);
+//                                     FS_WriteFile(va("%s_vs.fx", cachename), vsbuffer->GetBufferPointer(), vsbuffer->GetBufferSize());
+                                       FS_WriteFile(va("%s_vs.fx", cachename), vertstring, strlen(vertstring));
+                                       vsresult = qD3DXCompileShaderFromFileA(va("%s/%s_vs.fx", fs_gamedir, cachename), NULL, NULL, "main", vsversion, shaderflags, &vsbuffer, &vslog, &vsconstanttable);
+                               }
+                               else
+                                       vsresult = qD3DXCompileShader(vertstring, strlen(vertstring), NULL, NULL, "main", vsversion, shaderflags, &vsbuffer, &vslog, &vsconstanttable);
+                               if (vsbuffer)
+                               {
+                                       vsbinsize = vsbuffer->GetBufferSize();
+                                       vsbin = (DWORD *)Mem_Alloc(tempmempool, vsbinsize);
+                                       memcpy(vsbin, vsbuffer->GetBufferPointer(), vsbinsize);
+                                       vsbuffer->Release();
+                               }
+                               if (vslog)
+                               {
+                                       strlcpy(temp, (const char *)vslog->GetBufferPointer(), min(sizeof(temp), vslog->GetBufferSize()));
+                                       Con_Printf("HLSL vertex shader compile output for %s follows:\n%s\n", cachename, temp);
+                                       vslog->Release();
+                               }
+                       }
+                       if (fragstring && fragstring[0])
+                       {
+                               if (debugshader)
+                               {
+//                                     psresult = qD3DXPreprocessShader(fragstring, strlen(fragstring), NULL, NULL, &psbuffer, &pslog);
+//                                     FS_WriteFile(va("%s_ps.fx", cachename), psbuffer->GetBufferPointer(), psbuffer->GetBufferSize());
+                                       FS_WriteFile(va("%s_ps.fx", cachename), fragstring, strlen(fragstring));
+                                       psresult = qD3DXCompileShaderFromFileA(va("%s/%s_ps.fx", fs_gamedir, cachename), NULL, NULL, "main", psversion, shaderflags, &psbuffer, &pslog, &psconstanttable);
+                               }
+                               else
+                                       psresult = qD3DXCompileShader(fragstring, strlen(fragstring), NULL, NULL, "main", psversion, shaderflags, &psbuffer, &pslog, &psconstanttable);
+                               if (psbuffer)
+                               {
+                                       psbinsize = psbuffer->GetBufferSize();
+                                       psbin = (DWORD *)Mem_Alloc(tempmempool, psbinsize);
+                                       memcpy(psbin, psbuffer->GetBufferPointer(), psbinsize);
+                                       psbuffer->Release();
+                               }
+                               if (pslog)
+                               {
+                                       strlcpy(temp, (const char *)pslog->GetBufferPointer(), min(sizeof(temp), pslog->GetBufferSize()));
+                                       Con_Printf("HLSL pixel shader compile output for %s follows:\n%s\n", cachename, temp);
+                                       pslog->Release();
+                               }
+                       }
+                       Sys_UnloadLibrary(&d3dx9_dll);
+               }
+               else
+                       Con_Printf("Unable to compile shader - D3DXCompileShader function not found\n");
+       }
+       if (vsbin && psbin)
+       {
+               vsresult = IDirect3DDevice9_CreateVertexShader(vid_d3d9dev, vsbin, &p->vertexshader);
+               if (FAILED(vsresult))
+                       Con_Printf("HLSL CreateVertexShader failed for %s (hresult = %8x)\n", cachename, vsresult);
+               psresult = IDirect3DDevice9_CreatePixelShader(vid_d3d9dev, psbin, &p->pixelshader);
+               if (FAILED(psresult))
+                       Con_Printf("HLSL CreatePixelShader failed for %s (hresult = %8x)\n", cachename, psresult);
+       }
+       // free the shader data
+       vsbin = (DWORD *)Mem_Realloc(tempmempool, vsbin, 0);
+       psbin = (DWORD *)Mem_Realloc(tempmempool, psbin, 0);
 }
 
-static void R_CG_CompilePermutation(r_cg_permutation_t *p, unsigned int mode, unsigned int permutation)
+static void R_HLSL_CompilePermutation(r_hlsl_permutation_t *p, unsigned int mode, unsigned int permutation)
 {
        int i;
-       shadermodeinfo_t *modeinfo = cgshadermodeinfo + mode;
-       int vertstrings_count = 0, vertstring_length = 0;
-       int geomstrings_count = 0, geomstring_length = 0;
-       int fragstrings_count = 0, fragstring_length = 0;
+       shadermodeinfo_t *modeinfo = hlslshadermodeinfo + mode;
+       int vertstring_length = 0;
+       int geomstring_length = 0;
+       int fragstring_length = 0;
        char *t;
        char *vertexstring, *geometrystring, *fragmentstring;
        char *vertstring, *geomstring, *fragstring;
-       const char *vertstrings_list[32+3];
-       const char *geomstrings_list[32+3];
-       const char *fragstrings_list[32+3];
        char permutationname[256];
        char cachename[256];
-       CGprofile vertexProfile;
-       CGprofile fragmentProfile;
+       int vertstrings_count = 0;
+       int geomstrings_count = 0;
+       int fragstrings_count = 0;
+       const char *vertstrings_list[32+3+SHADERSTATICPARMS_COUNT+1];
+       const char *geomstrings_list[32+3+SHADERSTATICPARMS_COUNT+1];
+       const char *fragstrings_list[32+3+SHADERSTATICPARMS_COUNT+1];
 
        if (p->compiled)
                return;
        p->compiled = true;
-       p->vprogram = NULL;
-       p->fprogram = NULL;
+       p->vertexshader = NULL;
+       p->pixelshader = NULL;
 
        permutationname[0] = 0;
        cachename[0] = 0;
-       vertexstring   = R_CG_GetText(modeinfo->vertexfilename, true);
-       geometrystring = R_CG_GetText(modeinfo->geometryfilename, false);
-       fragmentstring = R_CG_GetText(modeinfo->fragmentfilename, false);
+       vertexstring   = R_HLSL_GetText(modeinfo->vertexfilename, true);
+       geometrystring = R_HLSL_GetText(modeinfo->geometryfilename, false);
+       fragmentstring = R_HLSL_GetText(modeinfo->fragmentfilename, false);
 
        strlcat(permutationname, modeinfo->vertexfilename, sizeof(permutationname));
-       strlcat(cachename, "cg/", sizeof(cachename));
+       strlcat(cachename, "hlsl/", sizeof(cachename));
+
+       // define HLSL so that the shader can tell apart the HLSL compiler and the Cg compiler
+       vertstrings_count = 0;
+       geomstrings_count = 0;
+       fragstrings_count = 0;
+       vertstrings_list[vertstrings_count++] = "#define HLSL\n";
+       geomstrings_list[geomstrings_count++] = "#define HLSL\n";
+       fragstrings_list[fragstrings_count++] = "#define HLSL\n";
 
        // the first pretext is which type of shader to compile as
        // (later these will all be bound together as a program object)
@@ -3956,6 +1629,15 @@ static void R_CG_CompilePermutation(r_cg_permutation_t *p, unsigned int mode, un
                }
        }
 
+       // add static parms
+       R_CompileShader_AddStaticParms(mode, permutation);
+       memcpy(vertstrings_list + vertstrings_count, shaderstaticparmstrings_list, sizeof(*vertstrings_list) * shaderstaticparms_count);
+       vertstrings_count += shaderstaticparms_count;
+       memcpy(geomstrings_list + geomstrings_count, shaderstaticparmstrings_list, sizeof(*vertstrings_list) * shaderstaticparms_count);
+       geomstrings_count += shaderstaticparms_count;
+       memcpy(fragstrings_list + fragstrings_count, shaderstaticparmstrings_list, sizeof(*vertstrings_list) * shaderstaticparms_count);
+       fragstrings_count += shaderstaticparms_count;
+
        // replace spaces in the cachename with _ characters
        for (i = 0;cachename[i];i++)
                if (cachename[i] == ' ')
@@ -3977,152 +1659,31 @@ static void R_CG_CompilePermutation(r_cg_permutation_t *p, unsigned int mode, un
        vertstring_length = 0;
        for (i = 0;i < vertstrings_count;i++)
                vertstring_length += strlen(vertstrings_list[i]);
-       vertstring = t = Mem_Alloc(tempmempool, vertstring_length + 1);
+       vertstring = t = (char *)Mem_Alloc(tempmempool, vertstring_length + 1);
        for (i = 0;i < vertstrings_count;t += strlen(vertstrings_list[i]), i++)
                memcpy(t, vertstrings_list[i], strlen(vertstrings_list[i]));
 
        geomstring_length = 0;
        for (i = 0;i < geomstrings_count;i++)
                geomstring_length += strlen(geomstrings_list[i]);
-       geomstring = t = Mem_Alloc(tempmempool, geomstring_length + 1);
+       geomstring = t = (char *)Mem_Alloc(tempmempool, geomstring_length + 1);
        for (i = 0;i < geomstrings_count;t += strlen(geomstrings_list[i]), i++)
                memcpy(t, geomstrings_list[i], strlen(geomstrings_list[i]));
 
        fragstring_length = 0;
        for (i = 0;i < fragstrings_count;i++)
                fragstring_length += strlen(fragstrings_list[i]);
-       fragstring = t = Mem_Alloc(tempmempool, fragstring_length + 1);
+       fragstring = t = (char *)Mem_Alloc(tempmempool, fragstring_length + 1);
        for (i = 0;i < fragstrings_count;t += strlen(fragstrings_list[i]), i++)
                memcpy(t, fragstrings_list[i], strlen(fragstrings_list[i]));
 
-       CHECKGLERROR
-       CHECKCGERROR
-       //vertexProfile = CG_PROFILE_ARBVP1;
-       //fragmentProfile = CG_PROFILE_ARBFP1;
-       vertexProfile = cgGLGetLatestProfile(CG_GL_VERTEX);CHECKCGERROR
-       fragmentProfile = cgGLGetLatestProfile(CG_GL_FRAGMENT);CHECKCGERROR
-       //cgGLSetOptimalOptions(vertexProfile);CHECKCGERROR
-       //cgGLSetOptimalOptions(fragmentProfile);CHECKCGERROR
-       //cgSetAutoCompile(vid.cgcontext, CG_COMPILE_MANUAL);CHECKCGERROR
-       CHECKGLERROR
-
        // try to load the cached shader, or generate one
-       R_CG_CacheShader(p, cachename, vertstring, fragstring);
-
-       // if caching failed, do a dynamic compile for now
-       CHECKCGERROR
-       if (vertstring[0] && !p->vprogram)
-               p->vprogram = cgCreateProgram(vid.cgcontext, CG_SOURCE, vertstring, vertexProfile, NULL, NULL);
-       CHECKCGERROR
-       if (fragstring[0] && !p->fprogram)
-               p->fprogram = cgCreateProgram(vid.cgcontext, CG_SOURCE, fragstring, fragmentProfile, NULL, NULL);
-       CHECKCGERROR
-
-       // look up all the uniform variable names we care about, so we don't
-       // have to look them up every time we set them
-       if (p->vprogram)
-       {
-               CHECKCGERROR
-               cgGLLoadProgram(p->vprogram);CHECKCGERROR CHECKGLERROR
-               cgGLEnableProfile(vertexProfile);CHECKCGERROR CHECKGLERROR
-               p->vp_EyePosition                = cgGetNamedParameter(p->vprogram, "EyePosition");
-               p->vp_FogPlane                   = cgGetNamedParameter(p->vprogram, "FogPlane");
-               p->vp_LightDir                   = cgGetNamedParameter(p->vprogram, "LightDir");
-               p->vp_LightPosition              = cgGetNamedParameter(p->vprogram, "LightPosition");
-               p->vp_ModelToLight               = cgGetNamedParameter(p->vprogram, "ModelToLight");
-               p->vp_TexMatrix                  = cgGetNamedParameter(p->vprogram, "TexMatrix");
-               p->vp_BackgroundTexMatrix        = cgGetNamedParameter(p->vprogram, "BackgroundTexMatrix");
-               p->vp_ModelViewProjectionMatrix  = cgGetNamedParameter(p->vprogram, "ModelViewProjectionMatrix");
-               p->vp_ModelViewMatrix            = cgGetNamedParameter(p->vprogram, "ModelViewMatrix");
-               CHECKCGERROR
-       }
-       if (p->fprogram)
-       {
-               CHECKCGERROR
-               cgGLLoadProgram(p->fprogram);CHECKCGERROR CHECKGLERROR
-               cgGLEnableProfile(fragmentProfile);CHECKCGERROR CHECKGLERROR
-               p->fp_Texture_First              = cgGetNamedParameter(p->fprogram, "Texture_First");
-               p->fp_Texture_Second             = cgGetNamedParameter(p->fprogram, "Texture_Second");
-               p->fp_Texture_GammaRamps         = cgGetNamedParameter(p->fprogram, "Texture_GammaRamps");
-               p->fp_Texture_Normal             = cgGetNamedParameter(p->fprogram, "Texture_Normal");
-               p->fp_Texture_Color              = cgGetNamedParameter(p->fprogram, "Texture_Color");
-               p->fp_Texture_Gloss              = cgGetNamedParameter(p->fprogram, "Texture_Gloss");
-               p->fp_Texture_Glow               = cgGetNamedParameter(p->fprogram, "Texture_Glow");
-               p->fp_Texture_SecondaryNormal    = cgGetNamedParameter(p->fprogram, "Texture_SecondaryNormal");
-               p->fp_Texture_SecondaryColor     = cgGetNamedParameter(p->fprogram, "Texture_SecondaryColor");
-               p->fp_Texture_SecondaryGloss     = cgGetNamedParameter(p->fprogram, "Texture_SecondaryGloss");
-               p->fp_Texture_SecondaryGlow      = cgGetNamedParameter(p->fprogram, "Texture_SecondaryGlow");
-               p->fp_Texture_Pants              = cgGetNamedParameter(p->fprogram, "Texture_Pants");
-               p->fp_Texture_Shirt              = cgGetNamedParameter(p->fprogram, "Texture_Shirt");
-               p->fp_Texture_FogMask            = cgGetNamedParameter(p->fprogram, "Texture_FogMask");
-               p->fp_Texture_Lightmap           = cgGetNamedParameter(p->fprogram, "Texture_Lightmap");
-               p->fp_Texture_Deluxemap          = cgGetNamedParameter(p->fprogram, "Texture_Deluxemap");
-               p->fp_Texture_Attenuation        = cgGetNamedParameter(p->fprogram, "Texture_Attenuation");
-               p->fp_Texture_Cube               = cgGetNamedParameter(p->fprogram, "Texture_Cube");
-               p->fp_Texture_Refraction         = cgGetNamedParameter(p->fprogram, "Texture_Refraction");
-               p->fp_Texture_Reflection         = cgGetNamedParameter(p->fprogram, "Texture_Reflection");
-               p->fp_Texture_ShadowMapRect      = cgGetNamedParameter(p->fprogram, "Texture_ShadowMapRect");
-               p->fp_Texture_ShadowMapCube      = cgGetNamedParameter(p->fprogram, "Texture_ShadowMapCube");
-               p->fp_Texture_ShadowMap2D        = cgGetNamedParameter(p->fprogram, "Texture_ShadowMap2D");
-               p->fp_Texture_CubeProjection     = cgGetNamedParameter(p->fprogram, "Texture_CubeProjection");
-               p->fp_Texture_ScreenDepth        = cgGetNamedParameter(p->fprogram, "Texture_ScreenDepth");
-               p->fp_Texture_ScreenNormalMap    = cgGetNamedParameter(p->fprogram, "Texture_ScreenNormalMap");
-               p->fp_Texture_ScreenDiffuse      = cgGetNamedParameter(p->fprogram, "Texture_ScreenDiffuse");
-               p->fp_Texture_ScreenSpecular     = cgGetNamedParameter(p->fprogram, "Texture_ScreenSpecular");
-               p->fp_Texture_ReflectMask        = cgGetNamedParameter(p->fprogram, "Texture_ReflectMask");
-               p->fp_Texture_ReflectCube        = qgGetNamedParameter(p->fprogram, "Texture_ReflectCube");
-               p->fp_Alpha                      = cgGetNamedParameter(p->fprogram, "Alpha");
-               p->fp_BloomBlur_Parameters       = cgGetNamedParameter(p->fprogram, "BloomBlur_Parameters");
-               p->fp_ClientTime                 = cgGetNamedParameter(p->fprogram, "ClientTime");
-               p->fp_Color_Ambient              = cgGetNamedParameter(p->fprogram, "Color_Ambient");
-               p->fp_Color_Diffuse              = cgGetNamedParameter(p->fprogram, "Color_Diffuse");
-               p->fp_Color_Specular             = cgGetNamedParameter(p->fprogram, "Color_Specular");
-               p->fp_Color_Glow                 = cgGetNamedParameter(p->fprogram, "Color_Glow");
-               p->fp_Color_Pants                = cgGetNamedParameter(p->fprogram, "Color_Pants");
-               p->fp_Color_Shirt                = cgGetNamedParameter(p->fprogram, "Color_Shirt");
-               p->fp_DeferredColor_Ambient      = cgGetNamedParameter(p->fprogram, "DeferredColor_Ambient");
-               p->fp_DeferredColor_Diffuse      = cgGetNamedParameter(p->fprogram, "DeferredColor_Diffuse");
-               p->fp_DeferredColor_Specular     = cgGetNamedParameter(p->fprogram, "DeferredColor_Specular");
-               p->fp_DeferredMod_Diffuse        = cgGetNamedParameter(p->fprogram, "DeferredMod_Diffuse");
-               p->fp_DeferredMod_Specular       = cgGetNamedParameter(p->fprogram, "DeferredMod_Specular");
-               p->fp_DistortScaleRefractReflect = cgGetNamedParameter(p->fprogram, "DistortScaleRefractReflect");
-               p->fp_EyePosition                = cgGetNamedParameter(p->fprogram, "EyePosition");
-               p->fp_FogColor                   = cgGetNamedParameter(p->fprogram, "FogColor");
-               p->fp_FogHeightFade              = cgGetNamedParameter(p->fprogram, "FogHeightFade");
-               p->fp_FogPlane                   = cgGetNamedParameter(p->fprogram, "FogPlane");
-               p->fp_FogPlaneViewDist           = cgGetNamedParameter(p->fprogram, "FogPlaneViewDist");
-               p->fp_FogRangeRecip              = cgGetNamedParameter(p->fprogram, "FogRangeRecip");
-               p->fp_LightColor                 = cgGetNamedParameter(p->fprogram, "LightColor");
-               p->fp_LightDir                   = cgGetNamedParameter(p->fprogram, "LightDir");
-               p->fp_LightPosition              = cgGetNamedParameter(p->fprogram, "LightPosition");
-               p->fp_OffsetMapping_Scale        = cgGetNamedParameter(p->fprogram, "OffsetMapping_Scale");
-               p->fp_PixelSize                  = cgGetNamedParameter(p->fprogram, "PixelSize");
-               p->fp_ReflectColor               = cgGetNamedParameter(p->fprogram, "ReflectColor");
-               p->fp_ReflectFactor              = cgGetNamedParameter(p->fprogram, "ReflectFactor");
-               p->fp_ReflectOffset              = cgGetNamedParameter(p->fprogram, "ReflectOffset");
-               p->fp_RefractColor               = cgGetNamedParameter(p->fprogram, "RefractColor");
-               p->fp_Saturation                 = cgGetNamedParameter(p->fprogram, "Saturation");
-               p->fp_ScreenCenterRefractReflect = cgGetNamedParameter(p->fprogram, "ScreenCenterRefractReflect");
-               p->fp_ScreenScaleRefractReflect  = cgGetNamedParameter(p->fprogram, "ScreenScaleRefractReflect");
-               p->fp_ScreenToDepth              = cgGetNamedParameter(p->fprogram, "ScreenToDepth");
-               p->fp_ShadowMap_Parameters       = cgGetNamedParameter(p->fprogram, "ShadowMap_Parameters");
-               p->fp_ShadowMap_TextureScale     = cgGetNamedParameter(p->fprogram, "ShadowMap_TextureScale");
-               p->fp_SpecularPower              = cgGetNamedParameter(p->fprogram, "SpecularPower");
-               p->fp_UserVec1                   = cgGetNamedParameter(p->fprogram, "UserVec1");
-               p->fp_UserVec2                   = cgGetNamedParameter(p->fprogram, "UserVec2");
-               p->fp_UserVec3                   = cgGetNamedParameter(p->fprogram, "UserVec3");
-               p->fp_UserVec4                   = cgGetNamedParameter(p->fprogram, "UserVec4");
-               p->fp_ViewTintColor              = cgGetNamedParameter(p->fprogram, "ViewTintColor");
-               p->fp_ViewToLight                = cgGetNamedParameter(p->fprogram, "ViewToLight");
-               p->fp_PixelToScreenTexCoord      = cgGetNamedParameter(p->fprogram, "PixelToScreenTexCoord");
-               p->fp_ModelToReflectCube         = cgGetNamedParameter(p->fprogram, "ModelToReflectCube");
-               CHECKCGERROR
-       }
-
-       if ((p->vprogram || !vertstring[0]) && (p->fprogram || !fragstring[0]))
-               Con_DPrintf("^5CG shader %s compiled.\n", permutationname);
+       R_HLSL_CacheShader(p, cachename, vertstring, fragstring);
+
+       if ((p->vertexshader || !vertstring[0]) && (p->pixelshader || !fragstring[0]))
+               Con_DPrintf("^5HLSL shader %s compiled.\n", permutationname);
        else
-               Con_Printf("^1CG shader %s failed!  some features may not work properly.\n", permutationname);
+               Con_Printf("^1HLSL shader %s failed!  some features may not work properly.\n", permutationname);
 
        // free the strings
        if (vertstring)
@@ -4139,19 +1700,31 @@ static void R_CG_CompilePermutation(r_cg_permutation_t *p, unsigned int mode, un
                Mem_Free(fragmentstring);
 }
 
-void R_SetupShader_SetPermutationCG(unsigned int mode, unsigned int permutation)
+static inline void hlslVSSetParameter16f(D3DVSREGISTER_t r, const float *a) {IDirect3DDevice9_SetVertexShaderConstantF(vid_d3d9dev, r, a, 4);}
+static inline void hlslVSSetParameter4fv(D3DVSREGISTER_t r, const float *a) {IDirect3DDevice9_SetVertexShaderConstantF(vid_d3d9dev, r, a, 1);}
+static inline void hlslVSSetParameter4f(D3DVSREGISTER_t r, float x, float y, float z, float w) {float temp[4];Vector4Set(temp, x, y, z, w);IDirect3DDevice9_SetVertexShaderConstantF(vid_d3d9dev, r, temp, 1);}
+static inline void hlslVSSetParameter3f(D3DVSREGISTER_t r, float x, float y, float z) {float temp[4];Vector4Set(temp, x, y, z, 0);IDirect3DDevice9_SetVertexShaderConstantF(vid_d3d9dev, r, temp, 1);}
+static inline void hlslVSSetParameter2f(D3DVSREGISTER_t r, float x, float y) {float temp[4];Vector4Set(temp, x, y, 0, 0);IDirect3DDevice9_SetVertexShaderConstantF(vid_d3d9dev, r, temp, 1);}
+static inline void hlslVSSetParameter1f(D3DVSREGISTER_t r, float x) {float temp[4];Vector4Set(temp, x, 0, 0, 0);IDirect3DDevice9_SetVertexShaderConstantF(vid_d3d9dev, r, temp, 1);}
+
+static inline void hlslPSSetParameter16f(D3DPSREGISTER_t r, const float *a) {IDirect3DDevice9_SetPixelShaderConstantF(vid_d3d9dev, r, a, 4);}
+static inline void hlslPSSetParameter4fv(D3DPSREGISTER_t r, const float *a) {IDirect3DDevice9_SetPixelShaderConstantF(vid_d3d9dev, r, a, 1);}
+static inline void hlslPSSetParameter4f(D3DPSREGISTER_t r, float x, float y, float z, float w) {float temp[4];Vector4Set(temp, x, y, z, w);IDirect3DDevice9_SetPixelShaderConstantF(vid_d3d9dev, r, temp, 1);}
+static inline void hlslPSSetParameter3f(D3DPSREGISTER_t r, float x, float y, float z) {float temp[4];Vector4Set(temp, x, y, z, 0);IDirect3DDevice9_SetPixelShaderConstantF(vid_d3d9dev, r, temp, 1);}
+static inline void hlslPSSetParameter2f(D3DPSREGISTER_t r, float x, float y) {float temp[4];Vector4Set(temp, x, y, 0, 0);IDirect3DDevice9_SetPixelShaderConstantF(vid_d3d9dev, r, temp, 1);}
+static inline void hlslPSSetParameter1f(D3DPSREGISTER_t r, float x) {float temp[4];Vector4Set(temp, x, 0, 0, 0);IDirect3DDevice9_SetPixelShaderConstantF(vid_d3d9dev, r, temp, 1);}
+
+void R_SetupShader_SetPermutationHLSL(unsigned int mode, unsigned int permutation)
 {
-       r_cg_permutation_t *perm = R_CG_FindPermutation(mode, permutation);
-       CHECKGLERROR
-       CHECKCGERROR
-       if (r_cg_permutation != perm)
+       r_hlsl_permutation_t *perm = R_HLSL_FindPermutation(mode, permutation);
+       if (r_hlsl_permutation != perm)
        {
-               r_cg_permutation = perm;
-               if (!r_cg_permutation->vprogram && !r_cg_permutation->fprogram)
+               r_hlsl_permutation = perm;
+               if (!r_hlsl_permutation->vertexshader && !r_hlsl_permutation->pixelshader)
                {
-                       if (!r_cg_permutation->compiled)
-                               R_CG_CompilePermutation(perm, mode, permutation);
-                       if (!r_cg_permutation->vprogram && !r_cg_permutation->fprogram)
+                       if (!r_hlsl_permutation->compiled)
+                               R_HLSL_CompilePermutation(perm, mode, permutation);
+                       if (!r_hlsl_permutation->vertexshader && !r_hlsl_permutation->pixelshader)
                        {
                                // remove features until we find a valid permutation
                                int i;
@@ -4162,56 +1735,36 @@ void R_SetupShader_SetPermutationCG(unsigned int mode, unsigned int permutation)
                                        if (!(permutation & j))
                                                continue;
                                        permutation -= j;
-                                       r_cg_permutation = R_CG_FindPermutation(mode, permutation);
-                                       if (!r_cg_permutation->compiled)
-                                               R_CG_CompilePermutation(perm, mode, permutation);
-                                       if (r_cg_permutation->vprogram || r_cg_permutation->fprogram)
+                                       r_hlsl_permutation = R_HLSL_FindPermutation(mode, permutation);
+                                       if (!r_hlsl_permutation->compiled)
+                                               R_HLSL_CompilePermutation(perm, mode, permutation);
+                                       if (r_hlsl_permutation->vertexshader || r_hlsl_permutation->pixelshader)
                                                break;
                                }
                                if (i >= SHADERPERMUTATION_COUNT)
                                {
-                                       //Con_Printf("Could not find a working Cg shader for permutation %s %s\n", shadermodeinfo[mode].vertexfilename, shadermodeinfo[mode].pretext);
-                                       r_cg_permutation = R_CG_FindPermutation(mode, permutation);
+                                       //Con_Printf("Could not find a working HLSL shader for permutation %s %s\n", shadermodeinfo[mode].vertexfilename, shadermodeinfo[mode].pretext);
+                                       r_hlsl_permutation = R_HLSL_FindPermutation(mode, permutation);
                                        return; // no bit left to clear, entire mode is broken
                                }
                        }
                }
-               CHECKGLERROR
-               CHECKCGERROR
-               if (r_cg_permutation->vprogram)
-               {
-                       cgGLLoadProgram(r_cg_permutation->vprogram);CHECKCGERROR CHECKGLERROR
-                       cgGLBindProgram(r_cg_permutation->vprogram);CHECKCGERROR CHECKGLERROR
-                       cgGLEnableProfile(cgGLGetLatestProfile(CG_GL_VERTEX));CHECKCGERROR CHECKGLERROR
-               }
-               else
-               {
-                       cgGLDisableProfile(cgGLGetLatestProfile(CG_GL_VERTEX));CHECKCGERROR CHECKGLERROR
-                       cgGLUnbindProgram(cgGLGetLatestProfile(CG_GL_VERTEX));CHECKCGERROR CHECKGLERROR
-               }
-               if (r_cg_permutation->fprogram)
-               {
-                       cgGLLoadProgram(r_cg_permutation->fprogram);CHECKCGERROR CHECKGLERROR
-                       cgGLBindProgram(r_cg_permutation->fprogram);CHECKCGERROR CHECKGLERROR
-                       cgGLEnableProfile(cgGLGetLatestProfile(CG_GL_FRAGMENT));CHECKCGERROR CHECKGLERROR
-               }
-               else
-               {
-                       cgGLDisableProfile(cgGLGetLatestProfile(CG_GL_FRAGMENT));CHECKCGERROR CHECKGLERROR
-                       cgGLUnbindProgram(cgGLGetLatestProfile(CG_GL_FRAGMENT));CHECKCGERROR CHECKGLERROR
-               }
+               IDirect3DDevice9_SetVertexShader(vid_d3d9dev, r_hlsl_permutation->vertexshader);
+               IDirect3DDevice9_SetPixelShader(vid_d3d9dev, r_hlsl_permutation->pixelshader);
        }
-       CHECKCGERROR
-       if (r_cg_permutation->vp_ModelViewProjectionMatrix) cgGLSetMatrixParameterfc(r_cg_permutation->vp_ModelViewProjectionMatrix, gl_modelviewprojection16f);CHECKCGERROR
-       if (r_cg_permutation->vp_ModelViewMatrix) cgGLSetMatrixParameterfc(r_cg_permutation->vp_ModelViewMatrix, gl_modelview16f);CHECKCGERROR
+       hlslVSSetParameter16f(D3DVSREGISTER_ModelViewProjectionMatrix, gl_modelviewprojection16f);
+       hlslVSSetParameter16f(D3DVSREGISTER_ModelViewMatrix, gl_modelview16f);
+       hlslPSSetParameter1f(D3DPSREGISTER_ClientTime, cl.time);
 }
+#endif
 
-void CG_BindTexture(CGparameter param, rtexture_t *tex)
+void R_SetupShader_SetPermutationSoft(unsigned int mode, unsigned int permutation)
 {
-       cgGLSetTextureParameter(param, R_GetTexture(tex));
-       cgGLEnableTextureParameter(param);
+       DPSOFTRAST_SetShader(mode, permutation);
+       DPSOFTRAST_UniformMatrix4fv(DPSOFTRAST_UNIFORM_ModelViewProjectionMatrixM1, 1, false, gl_modelviewprojection16f);
+       DPSOFTRAST_UniformMatrix4fv(DPSOFTRAST_UNIFORM_ModelViewMatrixM1, 1, false, gl_modelview16f);
+       DPSOFTRAST_Uniform1f(DPSOFTRAST_UNIFORM_ClientTime, cl.time);
 }
-#endif
 
 void R_GLSL_Restart_f(void)
 {
@@ -4219,12 +1772,40 @@ void R_GLSL_Restart_f(void)
        if (glslshaderstring && glslshaderstring != builtinshaderstring)
                Mem_Free(glslshaderstring);
        glslshaderstring = NULL;
-       if (cgshaderstring && cgshaderstring != builtincgshaderstring)
-               Mem_Free(cgshaderstring);
-       cgshaderstring = NULL;
+       if (hlslshaderstring && hlslshaderstring != builtinhlslshaderstring)
+               Mem_Free(hlslshaderstring);
+       hlslshaderstring = NULL;
        switch(vid.renderpath)
        {
+       case RENDERPATH_D3D9:
+#ifdef SUPPORTD3D
+               {
+                       r_hlsl_permutation_t *p;
+                       r_hlsl_permutation = NULL;
+                       limit = Mem_ExpandableArray_IndexRange(&r_hlsl_permutationarray);
+                       for (i = 0;i < limit;i++)
+                       {
+                               if ((p = (r_hlsl_permutation_t*)Mem_ExpandableArray_RecordAtIndex(&r_hlsl_permutationarray, i)))
+                               {
+                                       if (p->vertexshader)
+                                               IDirect3DVertexShader9_Release(p->vertexshader);
+                                       if (p->pixelshader)
+                                               IDirect3DPixelShader9_Release(p->pixelshader);
+                                       Mem_ExpandableArray_FreeRecord(&r_hlsl_permutationarray, (void*)p);
+                               }
+                       }
+                       memset(r_hlsl_permutationhash, 0, sizeof(r_hlsl_permutationhash));
+               }
+#endif
+               break;
+       case RENDERPATH_D3D10:
+               Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               break;
+       case RENDERPATH_D3D11:
+               Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               break;
        case RENDERPATH_GL20:
+       case RENDERPATH_GLES2:
                {
                        r_glsl_permutation_t *p;
                        r_glsl_permutation = NULL;
@@ -4240,34 +1821,11 @@ void R_GLSL_Restart_f(void)
                        memset(r_glsl_permutationhash, 0, sizeof(r_glsl_permutationhash));
                }
                break;
-       case RENDERPATH_CGGL:
-#ifdef SUPPORTCG
-               {
-                       r_cg_permutation_t *p;
-                       r_cg_permutation = NULL;
-                       cgGLDisableProfile(cgGLGetLatestProfile(CG_GL_VERTEX));CHECKCGERROR CHECKGLERROR
-                       cgGLUnbindProgram(cgGLGetLatestProfile(CG_GL_VERTEX));CHECKCGERROR CHECKGLERROR
-                       cgGLDisableProfile(cgGLGetLatestProfile(CG_GL_FRAGMENT));CHECKCGERROR CHECKGLERROR
-                       cgGLUnbindProgram(cgGLGetLatestProfile(CG_GL_FRAGMENT));CHECKCGERROR CHECKGLERROR
-                       limit = Mem_ExpandableArray_IndexRange(&r_cg_permutationarray);
-                       for (i = 0;i < limit;i++)
-                       {
-                               if ((p = (r_cg_permutation_t*)Mem_ExpandableArray_RecordAtIndex(&r_cg_permutationarray, i)))
-                               {
-                                       if (p->vprogram)
-                                               cgDestroyProgram(p->vprogram);
-                                       if (p->fprogram)
-                                               cgDestroyProgram(p->fprogram);
-                                       Mem_ExpandableArray_FreeRecord(&r_cg_permutationarray, (void*)p);
-                               }
-                       }
-                       memset(r_cg_permutationhash, 0, sizeof(r_cg_permutationhash));
-               }
-               break;
-#endif
        case RENDERPATH_GL13:
        case RENDERPATH_GL11:
                break;
+       case RENDERPATH_SOFT:
+               break;
        }
 }
 
@@ -4293,24 +1851,22 @@ void R_GLSL_DumpShader_f(void)
        else
                Con_Printf("failed to write to glsl/default.glsl\n");
 
-#ifdef SUPPORTCG
-       file = FS_OpenRealFile("cg/default.cg", "w", false);
+       file = FS_OpenRealFile("hlsl/default.hlsl", "w", false);
        if (file)
        {
                FS_Print(file, "/* The engine may define the following macros:\n");
                FS_Print(file, "#define VERTEX_SHADER\n#define GEOMETRY_SHADER\n#define FRAGMENT_SHADER\n");
                for (i = 0;i < SHADERMODE_COUNT;i++)
-                       FS_Print(file, cgshadermodeinfo[i].pretext);
+                       FS_Print(file, hlslshadermodeinfo[i].pretext);
                for (i = 0;i < SHADERPERMUTATION_COUNT;i++)
                        FS_Print(file, shaderpermutationinfo[i].pretext);
                FS_Print(file, "*/\n");
-               FS_Print(file, builtincgshaderstring);
+               FS_Print(file, builtinhlslshaderstring);
                FS_Close(file);
-               Con_Printf("cg/default.cg written\n");
+               Con_Printf("hlsl/default.hlsl written\n");
        }
        else
-               Con_Printf("failed to write to cg/default.cg\n");
-#endif
+               Con_Printf("failed to write to hlsl/default.hlsl\n");
 }
 
 void R_SetupShader_Generic(rtexture_t *first, rtexture_t *second, int texturemode, int rgbscale)
@@ -4319,19 +1875,25 @@ void R_SetupShader_Generic(rtexture_t *first, rtexture_t *second, int texturemod
                texturemode = GL_MODULATE;
        switch (vid.renderpath)
        {
-       case RENDERPATH_GL20:
-               R_SetupShader_SetPermutationGLSL(SHADERMODE_GENERIC, (first ? SHADERPERMUTATION_DIFFUSE : 0) | (second ? SHADERPERMUTATION_SPECULAR : 0) | (r_shadow_glossexact.integer ? SHADERPERMUTATION_EXACTSPECULARMATH : 0) | (texturemode == GL_MODULATE ? SHADERPERMUTATION_COLORMAPPING : (texturemode == GL_ADD ? SHADERPERMUTATION_GLOW : (texturemode == GL_DECAL ? SHADERPERMUTATION_VERTEXTEXTUREBLEND : 0))));
-               if (r_glsl_permutation->loc_Texture_First ) R_Mesh_TexBind(GL20TU_FIRST , first );
-               if (r_glsl_permutation->loc_Texture_Second) R_Mesh_TexBind(GL20TU_SECOND, second);
-               break;
-       case RENDERPATH_CGGL:
-#ifdef SUPPORTCG
-               CHECKCGERROR
-               R_SetupShader_SetPermutationCG(SHADERMODE_GENERIC, (first ? SHADERPERMUTATION_DIFFUSE : 0) | (second ? SHADERPERMUTATION_SPECULAR : 0) | (r_shadow_glossexact.integer ? SHADERPERMUTATION_EXACTSPECULARMATH : 0) | (texturemode == GL_MODULATE ? SHADERPERMUTATION_COLORMAPPING : (texturemode == GL_ADD ? SHADERPERMUTATION_GLOW : (texturemode == GL_DECAL ? SHADERPERMUTATION_VERTEXTEXTUREBLEND : 0))));
-               if (r_cg_permutation->fp_Texture_First ) CG_BindTexture(r_cg_permutation->fp_Texture_First , first );CHECKCGERROR
-               if (r_cg_permutation->fp_Texture_Second) CG_BindTexture(r_cg_permutation->fp_Texture_Second, second);CHECKCGERROR
+       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_Mesh_TexBind(GL20TU_FIRST , first );
+               R_Mesh_TexBind(GL20TU_SECOND, second);
 #endif
                break;
+       case RENDERPATH_D3D10:
+               Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               break;
+       case RENDERPATH_D3D11:
+               Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               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_Mesh_TexBind(r_glsl_permutation->tex_Texture_First , first );
+               R_Mesh_TexBind(r_glsl_permutation->tex_Texture_Second, second);
+               break;
        case RENDERPATH_GL13:
                R_Mesh_TexBind(0, first );
                R_Mesh_TexCombine(0, GL_MODULATE, GL_MODULATE, 1, 1);
@@ -4342,6 +1904,11 @@ void R_SetupShader_Generic(rtexture_t *first, rtexture_t *second, int texturemod
        case RENDERPATH_GL11:
                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_Mesh_TexBind(GL20TU_FIRST , first );
+               R_Mesh_TexBind(GL20TU_SECOND, second);
+               break;
        }
 }
 
@@ -4349,14 +1916,21 @@ void R_SetupShader_DepthOrShadow(void)
 {
        switch (vid.renderpath)
        {
+       case RENDERPATH_D3D9:
+#ifdef SUPPORTD3D
+               R_SetupShader_SetPermutationHLSL(SHADERMODE_DEPTH_OR_SHADOW, 0);
+#endif
+               break;
+       case RENDERPATH_D3D10:
+               Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               break;
+       case RENDERPATH_D3D11:
+               Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               break;
        case RENDERPATH_GL20:
+       case RENDERPATH_GLES2:
                R_SetupShader_SetPermutationGLSL(SHADERMODE_DEPTH_OR_SHADOW, 0);
                break;
-       case RENDERPATH_CGGL:
-#ifdef SUPPORTCG
-               R_SetupShader_SetPermutationCG(SHADERMODE_DEPTH_OR_SHADOW, 0);
-#endif
-               break;
        case RENDERPATH_GL13:
                R_Mesh_TexBind(0, 0);
                R_Mesh_TexBind(1, 0);
@@ -4364,6 +1938,9 @@ void R_SetupShader_DepthOrShadow(void)
        case RENDERPATH_GL11:
                R_Mesh_TexBind(0, 0);
                break;
+       case RENDERPATH_SOFT:
+               R_SetupShader_SetPermutationSoft(SHADERMODE_DEPTH_OR_SHADOW, 0);
+               break;
        }
 }
 
@@ -4371,18 +1948,28 @@ void R_SetupShader_ShowDepth(void)
 {
        switch (vid.renderpath)
        {
+       case RENDERPATH_D3D9:
+#ifdef SUPPORTHLSL
+               R_SetupShader_SetPermutationHLSL(SHADERMODE_SHOWDEPTH, 0);
+#endif
+               break;
+       case RENDERPATH_D3D10:
+               Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               break;
+       case RENDERPATH_D3D11:
+               Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               break;
        case RENDERPATH_GL20:
+       case RENDERPATH_GLES2:
                R_SetupShader_SetPermutationGLSL(SHADERMODE_SHOWDEPTH, 0);
                break;
-       case RENDERPATH_CGGL:
-#ifdef SUPPORTCG
-               R_SetupShader_SetPermutationCG(SHADERMODE_SHOWDEPTH, 0);
-#endif
-               break;
        case RENDERPATH_GL13:
                break;
        case RENDERPATH_GL11:
                break;
+       case RENDERPATH_SOFT:
+               R_SetupShader_SetPermutationSoft(SHADERMODE_SHOWDEPTH, 0);
+               break;
        }
 }
 
@@ -4391,26 +1978,73 @@ extern cvar_t r_shadow_deferred_8bitrange;
 extern rtexture_t *r_shadow_attenuationgradienttexture;
 extern rtexture_t *r_shadow_attenuation2dtexture;
 extern rtexture_t *r_shadow_attenuation3dtexture;
-extern qboolean r_shadow_usingshadowmaprect;
-extern qboolean r_shadow_usingshadowmapcube;
 extern qboolean r_shadow_usingshadowmap2d;
+extern qboolean r_shadow_usingshadowmaportho;
 extern float r_shadow_shadowmap_texturescale[2];
 extern float r_shadow_shadowmap_parameters[4];
 extern qboolean r_shadow_shadowmapvsdct;
 extern qboolean r_shadow_shadowmapsampler;
 extern int r_shadow_shadowmappcf;
-extern rtexture_t *r_shadow_shadowmaprectangletexture;
 extern rtexture_t *r_shadow_shadowmap2dtexture;
-extern rtexture_t *r_shadow_shadowmapcubetexture[R_SHADOW_SHADOWMAP_NUMCUBEMAPS];
+extern rtexture_t *r_shadow_shadowmap2dcolortexture;
 extern rtexture_t *r_shadow_shadowmapvsdcttexture;
+extern matrix4x4_t r_shadow_shadowmapmatrix;
 extern int r_shadow_shadowmaplod; // changes for each light based on distance
 extern int r_shadow_prepass_width;
 extern int r_shadow_prepass_height;
 extern rtexture_t *r_shadow_prepassgeometrydepthtexture;
 extern rtexture_t *r_shadow_prepassgeometrynormalmaptexture;
+extern rtexture_t *r_shadow_prepassgeometrydepthcolortexture;
 extern rtexture_t *r_shadow_prepasslightingdiffusetexture;
 extern rtexture_t *r_shadow_prepasslightingspeculartexture;
-void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting, float ambientscale, float diffusescale, float specularscale, rsurfacepass_t rsurfacepass)
+
+#define BLENDFUNC_ALLOWS_COLORMOD      1
+#define BLENDFUNC_ALLOWS_FOG           2
+#define BLENDFUNC_ALLOWS_FOG_HACK0     4
+#define BLENDFUNC_ALLOWS_FOG_HACKALPHA 8
+#define BLENDFUNC_ALLOWS_ANYFOG        (BLENDFUNC_ALLOWS_FOG | BLENDFUNC_ALLOWS_FOG_HACK0 | BLENDFUNC_ALLOWS_FOG_HACKALPHA)
+static int R_BlendFuncFlags(int src, int dst)
+{
+       int r = 0;
+
+       // a blendfunc allows colormod if:
+       // a) it can never keep the destination pixel invariant, or
+       // b) it can keep the destination pixel invariant, and still can do so if colormodded
+       // this is to prevent unintended side effects from colormod
+
+       // a blendfunc allows fog if:
+       // blend(fog(src), fog(dst)) == fog(blend(src, dst))
+       // this is to prevent unintended side effects from fog
+
+       // these checks are the output of fogeval.pl
+
+       r |= BLENDFUNC_ALLOWS_COLORMOD;
+       if(src == GL_DST_ALPHA && dst == GL_ONE) r |= BLENDFUNC_ALLOWS_FOG_HACK0;
+       if(src == GL_DST_ALPHA && dst == GL_ONE_MINUS_DST_ALPHA) r |= BLENDFUNC_ALLOWS_FOG;
+       if(src == GL_DST_COLOR && dst == GL_ONE_MINUS_SRC_ALPHA) r &= ~BLENDFUNC_ALLOWS_COLORMOD;
+       if(src == GL_DST_COLOR && dst == GL_ONE_MINUS_SRC_COLOR) r |= BLENDFUNC_ALLOWS_FOG;
+       if(src == GL_DST_COLOR && dst == GL_SRC_ALPHA) r &= ~BLENDFUNC_ALLOWS_COLORMOD;
+       if(src == GL_DST_COLOR && dst == GL_SRC_COLOR) r &= ~BLENDFUNC_ALLOWS_COLORMOD;
+       if(src == GL_DST_COLOR && dst == GL_ZERO) r &= ~BLENDFUNC_ALLOWS_COLORMOD;
+       if(src == GL_ONE && dst == GL_ONE) r |= BLENDFUNC_ALLOWS_FOG_HACK0;
+       if(src == GL_ONE && dst == GL_ONE_MINUS_SRC_ALPHA) r |= BLENDFUNC_ALLOWS_FOG_HACKALPHA;
+       if(src == GL_ONE && dst == GL_ZERO) r |= BLENDFUNC_ALLOWS_FOG;
+       if(src == GL_ONE_MINUS_DST_ALPHA && dst == GL_DST_ALPHA) r |= BLENDFUNC_ALLOWS_FOG;
+       if(src == GL_ONE_MINUS_DST_ALPHA && dst == GL_ONE) r |= BLENDFUNC_ALLOWS_FOG_HACK0;
+       if(src == GL_ONE_MINUS_DST_COLOR && dst == GL_SRC_COLOR) r |= BLENDFUNC_ALLOWS_FOG;
+       if(src == GL_ONE_MINUS_SRC_ALPHA && dst == GL_ONE) r |= BLENDFUNC_ALLOWS_FOG_HACK0;
+       if(src == GL_ONE_MINUS_SRC_ALPHA && dst == GL_SRC_ALPHA) r |= BLENDFUNC_ALLOWS_FOG;
+       if(src == GL_ONE_MINUS_SRC_ALPHA && dst == GL_SRC_COLOR) r &= ~BLENDFUNC_ALLOWS_COLORMOD;
+       if(src == GL_ONE_MINUS_SRC_COLOR && dst == GL_SRC_COLOR) r &= ~BLENDFUNC_ALLOWS_COLORMOD;
+       if(src == GL_SRC_ALPHA && dst == GL_ONE) r |= BLENDFUNC_ALLOWS_FOG_HACK0;
+       if(src == GL_SRC_ALPHA && dst == GL_ONE_MINUS_SRC_ALPHA) r |= BLENDFUNC_ALLOWS_FOG;
+       if(src == GL_ZERO && dst == GL_ONE) r |= BLENDFUNC_ALLOWS_FOG;
+       if(src == GL_ZERO && dst == GL_SRC_COLOR) r &= ~BLENDFUNC_ALLOWS_COLORMOD;
+
+       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)
 {
        // select a permutation of the lighting shader appropriate to this
        // combination of texture, entity, light source, and fogging, only use the
@@ -4418,58 +2052,87 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
        // fragment shader on features that are not being used
        unsigned int permutation = 0;
        unsigned int mode = 0;
+       int blendfuncflags;
+       static float dummy_colormod[3] = {1, 1, 1};
+       float *colormod = rsurface.colormod;
        float m16f[16];
+       matrix4x4_t tempmatrix;
+       r_waterstate_waterplane_t *waterplane = (r_waterstate_waterplane_t *)surfacewaterplane;
+       if (rsurface.texture->currentmaterialflags & MATERIALFLAG_ALPHATEST)
+               permutation |= SHADERPERMUTATION_ALPHAKILL;
        if (rsurfacepass == RSURFPASS_BACKGROUND)
        {
                // distorted background
                if (rsurface.texture->currentmaterialflags & MATERIALFLAG_WATERSHADER)
+               {
                        mode = SHADERMODE_WATER;
-               else
+                       if (rsurface.texture->r_water_waterscroll[0] && rsurface.texture->r_water_waterscroll[1])
+                               permutation |= SHADERPERMUTATION_NORMALMAPSCROLLBLEND;
+                       if((r_wateralpha.value < 1) && (rsurface.texture->currentmaterialflags & MATERIALFLAG_WATERALPHA))
+                       {
+                               // this is the right thing to do for wateralpha
+                               GL_BlendFunc(GL_ONE, GL_ZERO);
+                               blendfuncflags = R_BlendFuncFlags(GL_ONE, GL_ZERO);
+                       }
+                       else
+                       {
+                               // this is the right thing to do for entity alpha
+                               GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+                               blendfuncflags = R_BlendFuncFlags(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+                       }
+               }
+               else if (rsurface.texture->currentmaterialflags & MATERIALFLAG_REFRACTION)
+               {
                        mode = SHADERMODE_REFRACTION;
-               R_Mesh_TexCoordPointer(0, 2, rsurface.texcoordtexture2f, rsurface.texcoordtexture2f_bufferobject, rsurface.texcoordtexture2f_bufferoffset);
-               R_Mesh_TexCoordPointer(1, 3, rsurface.svector3f, rsurface.svector3f_bufferobject, rsurface.svector3f_bufferoffset);
-               R_Mesh_TexCoordPointer(2, 3, rsurface.tvector3f, rsurface.tvector3f_bufferobject, rsurface.tvector3f_bufferoffset);
-               R_Mesh_TexCoordPointer(3, 3, rsurface.normal3f, rsurface.normal3f_bufferobject, rsurface.normal3f_bufferoffset);
-               R_Mesh_TexCoordPointer(4, 0, NULL, 0, 0);
-               R_Mesh_ColorPointer(NULL, 0, 0);
-               GL_AlphaTest(false);
-               GL_BlendFunc(GL_ONE, GL_ZERO);
+                       GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+                       blendfuncflags = R_BlendFuncFlags(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+               }
+               else
+               {
+                       mode = SHADERMODE_GENERIC;
+                       permutation |= SHADERPERMUTATION_DIFFUSE;
+                       GL_BlendFunc(GL_ONE, GL_ZERO);
+                       blendfuncflags = R_BlendFuncFlags(GL_ONE, GL_ZERO);
+               }
        }
        else if (rsurfacepass == RSURFPASS_DEFERREDGEOMETRY)
        {
                if (r_glsl_offsetmapping.integer)
                {
-                       permutation |= SHADERPERMUTATION_OFFSETMAPPING;
-                       if (r_glsl_offsetmapping_reliefmapping.integer)
-                               permutation |= SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING;
+                       if (rsurface.texture->offsetmapping == OFFSETMAPPING_LINEAR)
+                               permutation |= SHADERPERMUTATION_OFFSETMAPPING;
+                       else if (rsurface.texture->offsetmapping == OFFSETMAPPING_RELIEF)
+                               permutation |= SHADERPERMUTATION_OFFSETMAPPING | SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING;
+                       else if (rsurface.texture->offsetmapping != OFFSETMAPPING_OFF)
+                       {
+                               permutation |= SHADERPERMUTATION_OFFSETMAPPING;
+                               if (r_glsl_offsetmapping_reliefmapping.integer)
+                                       permutation |= SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING;
+                       }
                }
                if (rsurface.texture->currentmaterialflags & MATERIALFLAG_VERTEXTEXTUREBLEND)
                        permutation |= SHADERPERMUTATION_VERTEXTEXTUREBLEND;
-               if (rsurface.texture->currentmaterialflags & MATERIALFLAG_ALPHATEST)
-                       permutation |= SHADERPERMUTATION_ALPHAKILL;
                // normalmap (deferred prepass), may use alpha test on diffuse
                mode = SHADERMODE_DEFERREDGEOMETRY;
                if (rsurface.texture->currentmaterialflags & MATERIALFLAG_VERTEXTEXTUREBLEND)
                        permutation |= SHADERPERMUTATION_VERTEXTEXTUREBLEND;
-               R_Mesh_TexCoordPointer(0, 2, rsurface.texcoordtexture2f, rsurface.texcoordtexture2f_bufferobject, rsurface.texcoordtexture2f_bufferoffset);
-               R_Mesh_TexCoordPointer(1, 3, rsurface.svector3f, rsurface.svector3f_bufferobject, rsurface.svector3f_bufferoffset);
-               R_Mesh_TexCoordPointer(2, 3, rsurface.tvector3f, rsurface.tvector3f_bufferobject, rsurface.tvector3f_bufferoffset);
-               R_Mesh_TexCoordPointer(3, 3, rsurface.normal3f, rsurface.normal3f_bufferobject, rsurface.normal3f_bufferoffset);
-               R_Mesh_TexCoordPointer(4, 0, NULL, 0, 0);
-               if (permutation & SHADERPERMUTATION_VERTEXTEXTUREBLEND)
-                       R_Mesh_ColorPointer(rsurface.modellightmapcolor4f, rsurface.modellightmapcolor4f_bufferobject, rsurface.modellightmapcolor4f_bufferoffset);
-               else
-                       R_Mesh_ColorPointer(NULL, 0, 0);
-               GL_AlphaTest(false);
                GL_BlendFunc(GL_ONE, GL_ZERO);
+               blendfuncflags = R_BlendFuncFlags(GL_ONE, GL_ZERO);
        }
        else if (rsurfacepass == RSURFPASS_RTLIGHT)
        {
                if (r_glsl_offsetmapping.integer)
                {
-                       permutation |= SHADERPERMUTATION_OFFSETMAPPING;
-                       if (r_glsl_offsetmapping_reliefmapping.integer)
-                               permutation |= SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING;
+                       if (rsurface.texture->offsetmapping == OFFSETMAPPING_LINEAR)
+                               permutation |= SHADERPERMUTATION_OFFSETMAPPING;
+                       else if (rsurface.texture->offsetmapping == OFFSETMAPPING_RELIEF)
+                               permutation |= SHADERPERMUTATION_OFFSETMAPPING | SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING;
+                       else if (rsurface.texture->offsetmapping != OFFSETMAPPING_OFF)
+                       {
+                               permutation |= SHADERPERMUTATION_OFFSETMAPPING;
+                               if (r_glsl_offsetmapping_reliefmapping.integer)
+                                       permutation |= SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING;
+                       }
                }
                if (rsurface.texture->currentmaterialflags & MATERIALFLAG_VERTEXTEXTUREBLEND)
                        permutation |= SHADERPERMUTATION_VERTEXTEXTUREBLEND;
@@ -4482,24 +2145,15 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                if (diffusescale > 0)
                        permutation |= SHADERPERMUTATION_DIFFUSE;
                if (specularscale > 0)
-               {
                        permutation |= SHADERPERMUTATION_SPECULAR | SHADERPERMUTATION_DIFFUSE;
-                       if (r_shadow_glossexact.integer)
-                               permutation |= SHADERPERMUTATION_EXACTSPECULARMATH;
-               }
                if (r_refdef.fogenabled)
-                       permutation |= r_refdef.fogplaneviewabove ? SHADERPERMUTATION_FOGOUTSIDE : SHADERPERMUTATION_FOGINSIDE;
+                       permutation |= r_texture_fogheighttexture ? SHADERPERMUTATION_FOGHEIGHTTEXTURE : (r_refdef.fogplaneviewabove ? SHADERPERMUTATION_FOGOUTSIDE : SHADERPERMUTATION_FOGINSIDE);
                if (rsurface.texture->colormapping)
                        permutation |= SHADERPERMUTATION_COLORMAPPING;
-               if (r_shadow_usingshadowmaprect || r_shadow_usingshadowmap2d || r_shadow_usingshadowmapcube)
-               {
-                       if (r_shadow_usingshadowmaprect)
-                               permutation |= SHADERPERMUTATION_SHADOWMAPRECT;
-                       if (r_shadow_usingshadowmap2d)
-                               permutation |= SHADERPERMUTATION_SHADOWMAP2D;
-                       if (r_shadow_usingshadowmapcube)
-                               permutation |= SHADERPERMUTATION_SHADOWMAPCUBE;
-                       else if(r_shadow_shadowmapvsdct)
+               if (r_shadow_usingshadowmap2d)
+               {
+                       permutation |= SHADERPERMUTATION_SHADOWMAP2D;
+                       if(r_shadow_shadowmapvsdct)
                                permutation |= SHADERPERMUTATION_SHADOWMAPVSDCT;
 
                        if (r_shadow_shadowmapsampler)
@@ -4511,34 +2165,23 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                }
                if (rsurface.texture->reflectmasktexture)
                        permutation |= SHADERPERMUTATION_REFLECTCUBE;
-               R_Mesh_TexCoordPointer(0, 2, rsurface.texcoordtexture2f, rsurface.texcoordtexture2f_bufferobject, rsurface.texcoordtexture2f_bufferoffset);
-               if (true || permutation & (SHADERPERMUTATION_DIFFUSE | SHADERPERMUTATION_SPECULAR | SHADERPERMUTATION_OFFSETMAPPING))
-               {
-                       R_Mesh_TexCoordPointer(1, 3, rsurface.svector3f, rsurface.svector3f_bufferobject, rsurface.svector3f_bufferoffset);
-                       R_Mesh_TexCoordPointer(2, 3, rsurface.tvector3f, rsurface.tvector3f_bufferobject, rsurface.tvector3f_bufferoffset);
-                       R_Mesh_TexCoordPointer(3, 3, rsurface.normal3f, rsurface.normal3f_bufferobject, rsurface.normal3f_bufferoffset);
-               }
-               else
-               {
-                       R_Mesh_TexCoordPointer(1, 0, NULL, 0, 0);
-                       R_Mesh_TexCoordPointer(2, 0, NULL, 0, 0);
-                       R_Mesh_TexCoordPointer(3, 0, NULL, 0, 0);
-               }
-               //R_Mesh_TexCoordPointer(4, 0, NULL, 0, 0);
-               if (permutation & SHADERPERMUTATION_VERTEXTEXTUREBLEND)
-                       R_Mesh_ColorPointer(rsurface.modellightmapcolor4f, rsurface.modellightmapcolor4f_bufferobject, rsurface.modellightmapcolor4f_bufferoffset);
-               else
-                       R_Mesh_ColorPointer(NULL, 0, 0);
-               GL_AlphaTest((rsurface.texture->currentmaterialflags & MATERIALFLAG_ALPHATEST) != 0);
                GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
+               blendfuncflags = R_BlendFuncFlags(GL_SRC_ALPHA, GL_ONE);
        }
        else if (rsurface.texture->currentmaterialflags & MATERIALFLAG_FULLBRIGHT)
        {
                if (r_glsl_offsetmapping.integer)
                {
-                       permutation |= SHADERPERMUTATION_OFFSETMAPPING;
-                       if (r_glsl_offsetmapping_reliefmapping.integer)
-                               permutation |= SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING;
+                       if (rsurface.texture->offsetmapping == OFFSETMAPPING_LINEAR)
+                               permutation |= SHADERPERMUTATION_OFFSETMAPPING;
+                       else if (rsurface.texture->offsetmapping == OFFSETMAPPING_RELIEF)
+                               permutation |= SHADERPERMUTATION_OFFSETMAPPING | SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING;
+                       else if (rsurface.texture->offsetmapping != OFFSETMAPPING_OFF)
+                       {
+                               permutation |= SHADERPERMUTATION_OFFSETMAPPING;
+                               if (r_glsl_offsetmapping_reliefmapping.integer)
+                                       permutation |= SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING;
+                       }
                }
                if (rsurface.texture->currentmaterialflags & MATERIALFLAG_VERTEXTEXTUREBLEND)
                        permutation |= SHADERPERMUTATION_VERTEXTEXTUREBLEND;
@@ -4548,41 +2191,42 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                if (rsurface.texture->glowtexture && r_hdr_glowintensity.value > 0 && !gl_lightmaps.integer)
                        permutation |= SHADERPERMUTATION_GLOW;
                if (r_refdef.fogenabled)
-                       permutation |= r_refdef.fogplaneviewabove ? SHADERPERMUTATION_FOGOUTSIDE : SHADERPERMUTATION_FOGINSIDE;
+                       permutation |= r_texture_fogheighttexture ? SHADERPERMUTATION_FOGHEIGHTTEXTURE : (r_refdef.fogplaneviewabove ? SHADERPERMUTATION_FOGOUTSIDE : SHADERPERMUTATION_FOGINSIDE);
                if (rsurface.texture->colormapping)
                        permutation |= SHADERPERMUTATION_COLORMAPPING;
+               if (r_shadow_usingshadowmaportho && !(rsurface.ent_flags & RENDER_NOSELFSHADOW))
+               {
+                       permutation |= SHADERPERMUTATION_SHADOWMAPORTHO;
+                       permutation |= SHADERPERMUTATION_SHADOWMAP2D;
+
+                       if (r_shadow_shadowmapsampler)
+                               permutation |= SHADERPERMUTATION_SHADOWSAMPLER;
+                       if (r_shadow_shadowmappcf > 1)
+                               permutation |= SHADERPERMUTATION_SHADOWMAPPCF2;
+                       else if (r_shadow_shadowmappcf)
+                               permutation |= SHADERPERMUTATION_SHADOWMAPPCF;
+               }
                if (rsurface.texture->currentmaterialflags & MATERIALFLAG_REFLECTION)
                        permutation |= SHADERPERMUTATION_REFLECTION;
                if (rsurface.texture->reflectmasktexture)
                        permutation |= SHADERPERMUTATION_REFLECTCUBE;
-               R_Mesh_TexCoordPointer(0, 2, rsurface.texcoordtexture2f, rsurface.texcoordtexture2f_bufferobject, rsurface.texcoordtexture2f_bufferoffset);
-               if (true || permutation & (SHADERPERMUTATION_DIFFUSE | SHADERPERMUTATION_SPECULAR | SHADERPERMUTATION_OFFSETMAPPING))
-               {
-                       R_Mesh_TexCoordPointer(1, 3, rsurface.svector3f, rsurface.svector3f_bufferobject, rsurface.svector3f_bufferoffset);
-                       R_Mesh_TexCoordPointer(2, 3, rsurface.tvector3f, rsurface.tvector3f_bufferobject, rsurface.tvector3f_bufferoffset);
-                       R_Mesh_TexCoordPointer(3, 3, rsurface.normal3f, rsurface.normal3f_bufferobject, rsurface.normal3f_bufferoffset);
-               }
-               else
-               {
-                       R_Mesh_TexCoordPointer(1, 0, NULL, 0, 0);
-                       R_Mesh_TexCoordPointer(2, 0, NULL, 0, 0);
-                       R_Mesh_TexCoordPointer(3, 0, NULL, 0, 0);
-               }
-               R_Mesh_TexCoordPointer(4, 0, NULL, 0, 0);
-               if (permutation & SHADERPERMUTATION_VERTEXTEXTUREBLEND)
-                       R_Mesh_ColorPointer(rsurface.modellightmapcolor4f, rsurface.modellightmapcolor4f_bufferobject, rsurface.modellightmapcolor4f_bufferoffset);
-               else
-                       R_Mesh_ColorPointer(NULL, 0, 0);
-               GL_AlphaTest((rsurface.texture->currentmaterialflags & MATERIALFLAG_ALPHATEST) != 0);
                GL_BlendFunc(rsurface.texture->currentlayers[0].blendfunc1, rsurface.texture->currentlayers[0].blendfunc2);
+               blendfuncflags = R_BlendFuncFlags(rsurface.texture->currentlayers[0].blendfunc1, rsurface.texture->currentlayers[0].blendfunc2);
        }
        else if (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT_DIRECTIONAL)
        {
                if (r_glsl_offsetmapping.integer)
                {
-                       permutation |= SHADERPERMUTATION_OFFSETMAPPING;
-                       if (r_glsl_offsetmapping_reliefmapping.integer)
-                               permutation |= SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING;
+                       if (rsurface.texture->offsetmapping == OFFSETMAPPING_LINEAR)
+                               permutation |= SHADERPERMUTATION_OFFSETMAPPING;
+                       else if (rsurface.texture->offsetmapping == OFFSETMAPPING_RELIEF)
+                               permutation |= SHADERPERMUTATION_OFFSETMAPPING | SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING;
+                       else if (rsurface.texture->offsetmapping != OFFSETMAPPING_OFF)
+                       {
+                               permutation |= SHADERPERMUTATION_OFFSETMAPPING;
+                               if (r_glsl_offsetmapping_reliefmapping.integer)
+                                       permutation |= SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING;
+                       }
                }
                if (rsurface.texture->currentmaterialflags & MATERIALFLAG_VERTEXTEXTUREBLEND)
                        permutation |= SHADERPERMUTATION_VERTEXTEXTUREBLEND;
@@ -4592,46 +2236,48 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                        permutation |= SHADERPERMUTATION_GLOW;
                permutation |= SHADERPERMUTATION_DIFFUSE;
                if (specularscale > 0)
-               {
                        permutation |= SHADERPERMUTATION_SPECULAR;
-                       if (r_shadow_glossexact.integer)
-                               permutation |= SHADERPERMUTATION_EXACTSPECULARMATH;
-               }
                if (r_refdef.fogenabled)
-                       permutation |= r_refdef.fogplaneviewabove ? SHADERPERMUTATION_FOGOUTSIDE : SHADERPERMUTATION_FOGINSIDE;
+                       permutation |= r_texture_fogheighttexture ? SHADERPERMUTATION_FOGHEIGHTTEXTURE : (r_refdef.fogplaneviewabove ? SHADERPERMUTATION_FOGOUTSIDE : SHADERPERMUTATION_FOGINSIDE);
                if (rsurface.texture->colormapping)
                        permutation |= SHADERPERMUTATION_COLORMAPPING;
+               if (r_shadow_usingshadowmaportho && !(rsurface.ent_flags & RENDER_NOSELFSHADOW))
+               {
+                       permutation |= SHADERPERMUTATION_SHADOWMAPORTHO;
+                       permutation |= SHADERPERMUTATION_SHADOWMAP2D;
+
+                       if (r_shadow_shadowmapsampler)
+                               permutation |= SHADERPERMUTATION_SHADOWSAMPLER;
+                       if (r_shadow_shadowmappcf > 1)
+                               permutation |= SHADERPERMUTATION_SHADOWMAPPCF2;
+                       else if (r_shadow_shadowmappcf)
+                               permutation |= SHADERPERMUTATION_SHADOWMAPPCF;
+               }
                if (rsurface.texture->currentmaterialflags & MATERIALFLAG_REFLECTION)
                        permutation |= SHADERPERMUTATION_REFLECTION;
                if (r_shadow_usingdeferredprepass && !(rsurface.texture->currentmaterialflags & MATERIALFLAG_BLENDED))
                        permutation |= SHADERPERMUTATION_DEFERREDLIGHTMAP;
                if (rsurface.texture->reflectmasktexture)
                        permutation |= SHADERPERMUTATION_REFLECTCUBE;
-               R_Mesh_TexCoordPointer(0, 2, rsurface.texcoordtexture2f, rsurface.texcoordtexture2f_bufferobject, rsurface.texcoordtexture2f_bufferoffset);
-               if (true || permutation & (SHADERPERMUTATION_DIFFUSE | SHADERPERMUTATION_SPECULAR | SHADERPERMUTATION_OFFSETMAPPING))
-               {
-                       R_Mesh_TexCoordPointer(1, 3, rsurface.svector3f, rsurface.svector3f_bufferobject, rsurface.svector3f_bufferoffset);
-                       R_Mesh_TexCoordPointer(2, 3, rsurface.tvector3f, rsurface.tvector3f_bufferobject, rsurface.tvector3f_bufferoffset);
-                       R_Mesh_TexCoordPointer(3, 3, rsurface.normal3f, rsurface.normal3f_bufferobject, rsurface.normal3f_bufferoffset);
-               }
-               else
-               {
-                       R_Mesh_TexCoordPointer(1, 0, NULL, 0, 0);
-                       R_Mesh_TexCoordPointer(2, 0, NULL, 0, 0);
-                       R_Mesh_TexCoordPointer(3, 0, NULL, 0, 0);
-               }
-               R_Mesh_TexCoordPointer(4, 0, NULL, 0, 0);
-               R_Mesh_ColorPointer(NULL, 0, 0);
-               GL_AlphaTest((rsurface.texture->currentmaterialflags & MATERIALFLAG_ALPHATEST) != 0);
+               if (r_shadow_bouncegridtexture)
+                       permutation |= SHADERPERMUTATION_BOUNCEGRID;
                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);
        }
        else if (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT)
        {
                if (r_glsl_offsetmapping.integer)
                {
-                       permutation |= SHADERPERMUTATION_OFFSETMAPPING;
-                       if (r_glsl_offsetmapping_reliefmapping.integer)
-                               permutation |= SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING;
+                       if (rsurface.texture->offsetmapping == OFFSETMAPPING_LINEAR)
+                               permutation |= SHADERPERMUTATION_OFFSETMAPPING;
+                       else if (rsurface.texture->offsetmapping == OFFSETMAPPING_RELIEF)
+                               permutation |= SHADERPERMUTATION_OFFSETMAPPING | SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING;
+                       else if (rsurface.texture->offsetmapping != OFFSETMAPPING_OFF)
+                       {
+                               permutation |= SHADERPERMUTATION_OFFSETMAPPING;
+                               if (r_glsl_offsetmapping_reliefmapping.integer)
+                                       permutation |= SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING;
+                       }
                }
                if (rsurface.texture->currentmaterialflags & MATERIALFLAG_VERTEXTEXTUREBLEND)
                        permutation |= SHADERPERMUTATION_VERTEXTEXTUREBLEND;
@@ -4640,40 +2286,46 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                if (rsurface.texture->glowtexture && r_hdr_glowintensity.value > 0 && !gl_lightmaps.integer)
                        permutation |= SHADERPERMUTATION_GLOW;
                if (r_refdef.fogenabled)
-                       permutation |= r_refdef.fogplaneviewabove ? SHADERPERMUTATION_FOGOUTSIDE : SHADERPERMUTATION_FOGINSIDE;
+                       permutation |= r_texture_fogheighttexture ? SHADERPERMUTATION_FOGHEIGHTTEXTURE : (r_refdef.fogplaneviewabove ? SHADERPERMUTATION_FOGOUTSIDE : SHADERPERMUTATION_FOGINSIDE);
                if (rsurface.texture->colormapping)
                        permutation |= SHADERPERMUTATION_COLORMAPPING;
-               if (rsurface.texture->currentmaterialflags & MATERIALFLAG_REFLECTION)
-                       permutation |= SHADERPERMUTATION_REFLECTION;
+               if (r_shadow_usingshadowmaportho && !(rsurface.ent_flags & RENDER_NOSELFSHADOW))
+               {
+                       permutation |= SHADERPERMUTATION_SHADOWMAPORTHO;
+                       permutation |= SHADERPERMUTATION_SHADOWMAP2D;
+
+                       if (r_shadow_shadowmapsampler)
+                               permutation |= SHADERPERMUTATION_SHADOWSAMPLER;
+                       if (r_shadow_shadowmappcf > 1)
+                               permutation |= SHADERPERMUTATION_SHADOWMAPPCF2;
+                       else if (r_shadow_shadowmappcf)
+                               permutation |= SHADERPERMUTATION_SHADOWMAPPCF;
+               }
+               if (rsurface.texture->currentmaterialflags & MATERIALFLAG_REFLECTION)
+                       permutation |= SHADERPERMUTATION_REFLECTION;
                if (r_shadow_usingdeferredprepass && !(rsurface.texture->currentmaterialflags & MATERIALFLAG_BLENDED))
                        permutation |= SHADERPERMUTATION_DEFERREDLIGHTMAP;
                if (rsurface.texture->reflectmasktexture)
                        permutation |= SHADERPERMUTATION_REFLECTCUBE;
-               R_Mesh_TexCoordPointer(0, 2, rsurface.texcoordtexture2f, rsurface.texcoordtexture2f_bufferobject, rsurface.texcoordtexture2f_bufferoffset);
-               if (true || permutation & (SHADERPERMUTATION_DIFFUSE | SHADERPERMUTATION_SPECULAR | SHADERPERMUTATION_OFFSETMAPPING))
-               {
-                       R_Mesh_TexCoordPointer(1, 3, rsurface.svector3f, rsurface.svector3f_bufferobject, rsurface.svector3f_bufferoffset);
-                       R_Mesh_TexCoordPointer(2, 3, rsurface.tvector3f, rsurface.tvector3f_bufferobject, rsurface.tvector3f_bufferoffset);
-                       R_Mesh_TexCoordPointer(3, 3, rsurface.normal3f, rsurface.normal3f_bufferobject, rsurface.normal3f_bufferoffset);
-               }
-               else
-               {
-                       R_Mesh_TexCoordPointer(1, 0, NULL, 0, 0);
-                       R_Mesh_TexCoordPointer(2, 0, NULL, 0, 0);
-                       R_Mesh_TexCoordPointer(3, 0, NULL, 0, 0);
-               }
-               R_Mesh_TexCoordPointer(4, 0, NULL, 0, 0);
-               R_Mesh_ColorPointer(NULL, 0, 0);
-               GL_AlphaTest((rsurface.texture->currentmaterialflags & MATERIALFLAG_ALPHATEST) != 0);
+               if (r_shadow_bouncegridtexture)
+                       permutation |= SHADERPERMUTATION_BOUNCEGRID;
                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);
        }
        else
        {
                if (r_glsl_offsetmapping.integer)
                {
-                       permutation |= SHADERPERMUTATION_OFFSETMAPPING;
-                       if (r_glsl_offsetmapping_reliefmapping.integer)
-                               permutation |= SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING;
+                       if (rsurface.texture->offsetmapping == OFFSETMAPPING_LINEAR)
+                               permutation |= SHADERPERMUTATION_OFFSETMAPPING;
+                       else if (rsurface.texture->offsetmapping == OFFSETMAPPING_RELIEF)
+                               permutation |= SHADERPERMUTATION_OFFSETMAPPING | SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING;
+                       else if (rsurface.texture->offsetmapping != OFFSETMAPPING_OFF)
+                       {
+                               permutation |= SHADERPERMUTATION_OFFSETMAPPING;
+                               if (r_glsl_offsetmapping_reliefmapping.integer)
+                                       permutation |= SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING;
+                       }
                }
                if (rsurface.texture->currentmaterialflags & MATERIALFLAG_VERTEXTEXTUREBLEND)
                        permutation |= SHADERPERMUTATION_VERTEXTEXTUREBLEND;
@@ -4681,16 +2333,36 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                if (rsurface.texture->glowtexture && r_hdr_glowintensity.value > 0 && !gl_lightmaps.integer)
                        permutation |= SHADERPERMUTATION_GLOW;
                if (r_refdef.fogenabled)
-                       permutation |= r_refdef.fogplaneviewabove ? SHADERPERMUTATION_FOGOUTSIDE : SHADERPERMUTATION_FOGINSIDE;
+                       permutation |= r_texture_fogheighttexture ? SHADERPERMUTATION_FOGHEIGHTTEXTURE : (r_refdef.fogplaneviewabove ? SHADERPERMUTATION_FOGOUTSIDE : SHADERPERMUTATION_FOGINSIDE);
                if (rsurface.texture->colormapping)
                        permutation |= SHADERPERMUTATION_COLORMAPPING;
+               if (r_shadow_usingshadowmaportho && !(rsurface.ent_flags & RENDER_NOSELFSHADOW))
+               {
+                       permutation |= SHADERPERMUTATION_SHADOWMAPORTHO;
+                       permutation |= SHADERPERMUTATION_SHADOWMAP2D;
+
+                       if (r_shadow_shadowmapsampler)
+                               permutation |= SHADERPERMUTATION_SHADOWSAMPLER;
+                       if (r_shadow_shadowmappcf > 1)
+                               permutation |= SHADERPERMUTATION_SHADOWMAPPCF2;
+                       else if (r_shadow_shadowmappcf)
+                               permutation |= SHADERPERMUTATION_SHADOWMAPPCF;
+               }
                if (rsurface.texture->currentmaterialflags & MATERIALFLAG_REFLECTION)
                        permutation |= SHADERPERMUTATION_REFLECTION;
                if (r_shadow_usingdeferredprepass && !(rsurface.texture->currentmaterialflags & MATERIALFLAG_BLENDED))
                        permutation |= SHADERPERMUTATION_DEFERREDLIGHTMAP;
                if (rsurface.texture->reflectmasktexture)
                        permutation |= SHADERPERMUTATION_REFLECTCUBE;
-               if (r_glsl_deluxemapping.integer >= 1 && rsurface.uselightmaptexture && r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brushq3.deluxemapping)
+               if (FAKELIGHT_ENABLED)
+               {
+                       // fake lightmapping (q1bsp, q3bsp, fullbright map)
+                       mode = SHADERMODE_FAKELIGHT;
+                       permutation |= SHADERPERMUTATION_DIFFUSE;
+                       if (specularscale > 0)
+                               permutation |= SHADERPERMUTATION_SPECULAR | SHADERPERMUTATION_DIFFUSE;
+               }
+               else if (r_glsl_deluxemapping.integer >= 1 && rsurface.uselightmaptexture && r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brushq3.deluxemapping)
                {
                        // deluxemapping (light direction texture)
                        if (rsurface.uselightmaptexture && r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brushq3.deluxemapping && r_refdef.scene.worldmodel->brushq3.deluxemapping_modelspace)
@@ -4699,337 +2371,470 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                                mode = SHADERMODE_LIGHTDIRECTIONMAP_TANGENTSPACE;
                        permutation |= SHADERPERMUTATION_DIFFUSE;
                        if (specularscale > 0)
-                       {
                                permutation |= SHADERPERMUTATION_SPECULAR | SHADERPERMUTATION_DIFFUSE;
-                               if (r_shadow_glossexact.integer)
-                                       permutation |= SHADERPERMUTATION_EXACTSPECULARMATH;
-                       }
-                       R_Mesh_TexCoordPointer(4, 2, rsurface.modeltexcoordlightmap2f, rsurface.modeltexcoordlightmap2f_bufferobject, rsurface.modeltexcoordlightmap2f_bufferoffset);
-                       if (permutation & SHADERPERMUTATION_VERTEXTEXTUREBLEND)
-                               R_Mesh_ColorPointer(rsurface.modellightmapcolor4f, rsurface.modellightmapcolor4f_bufferobject, rsurface.modellightmapcolor4f_bufferoffset);
-                       else
-                               R_Mesh_ColorPointer(NULL, 0, 0);
                }
-               else if (r_glsl_deluxemapping.integer >= 2)
+               else if (r_glsl_deluxemapping.integer >= 2 && rsurface.uselightmaptexture)
                {
                        // fake deluxemapping (uniform light direction in tangentspace)
                        mode = SHADERMODE_LIGHTDIRECTIONMAP_TANGENTSPACE;
                        permutation |= SHADERPERMUTATION_DIFFUSE;
                        if (specularscale > 0)
-                       {
                                permutation |= SHADERPERMUTATION_SPECULAR | SHADERPERMUTATION_DIFFUSE;
-                               if (r_shadow_glossexact.integer)
-                                       permutation |= SHADERPERMUTATION_EXACTSPECULARMATH;
-                       }
-                       R_Mesh_TexCoordPointer(4, 2, rsurface.modeltexcoordlightmap2f, rsurface.modeltexcoordlightmap2f_bufferobject, rsurface.modeltexcoordlightmap2f_bufferoffset);
-                       if (permutation & SHADERPERMUTATION_VERTEXTEXTUREBLEND)
-                               R_Mesh_ColorPointer(rsurface.modellightmapcolor4f, rsurface.modellightmapcolor4f_bufferobject, rsurface.modellightmapcolor4f_bufferoffset);
-                       else
-                               R_Mesh_ColorPointer(NULL, 0, 0);
                }
                else if (rsurface.uselightmaptexture)
                {
                        // ordinary lightmapping (q1bsp, q3bsp)
                        mode = SHADERMODE_LIGHTMAP;
-                       R_Mesh_TexCoordPointer(4, 2, rsurface.modeltexcoordlightmap2f, rsurface.modeltexcoordlightmap2f_bufferobject, rsurface.modeltexcoordlightmap2f_bufferoffset);
-                       if (permutation & SHADERPERMUTATION_VERTEXTEXTUREBLEND)
-                               R_Mesh_ColorPointer(rsurface.modellightmapcolor4f, rsurface.modellightmapcolor4f_bufferobject, rsurface.modellightmapcolor4f_bufferoffset);
-                       else
-                               R_Mesh_ColorPointer(NULL, 0, 0);
                }
                else
                {
                        // ordinary vertex coloring (q3bsp)
                        mode = SHADERMODE_VERTEXCOLOR;
-                       R_Mesh_TexCoordPointer(4, 0, NULL, 0, 0);
-                       R_Mesh_ColorPointer(rsurface.modellightmapcolor4f, rsurface.modellightmapcolor4f_bufferobject, rsurface.modellightmapcolor4f_bufferoffset);
                }
-               R_Mesh_TexCoordPointer(0, 2, rsurface.texcoordtexture2f, rsurface.texcoordtexture2f_bufferobject, rsurface.texcoordtexture2f_bufferoffset);
-               if (true || permutation & (SHADERPERMUTATION_DIFFUSE | SHADERPERMUTATION_SPECULAR | SHADERPERMUTATION_OFFSETMAPPING))
+               if (r_shadow_bouncegridtexture)
+                       permutation |= SHADERPERMUTATION_BOUNCEGRID;
+               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);
+       }
+       if(!(blendfuncflags & BLENDFUNC_ALLOWS_COLORMOD))
+               colormod = dummy_colormod;
+       if(!(blendfuncflags & BLENDFUNC_ALLOWS_ANYFOG))
+               permutation &= ~(SHADERPERMUTATION_FOGHEIGHTTEXTURE | SHADERPERMUTATION_FOGOUTSIDE | SHADERPERMUTATION_FOGINSIDE);
+       if(blendfuncflags & BLENDFUNC_ALLOWS_FOG_HACKALPHA)
+               permutation |= SHADERPERMUTATION_FOGALPHAHACK;
+       switch(vid.renderpath)
+       {
+       case RENDERPATH_D3D9:
+#ifdef SUPPORTD3D
+               RSurf_PrepareVerticesForBatch(BATCHNEED_VERTEXMESH_VERTEX | BATCHNEED_VERTEXMESH_NORMAL | BATCHNEED_VERTEXMESH_VECTOR | (rsurface.modellightmapcolor4f ? BATCHNEED_VERTEXMESH_VERTEXCOLOR : 0) | BATCHNEED_VERTEXMESH_TEXCOORD | (rsurface.uselightmaptexture ? BATCHNEED_VERTEXMESH_LIGHTMAP : 0), texturenumsurfaces, texturesurfacelist);
+               R_Mesh_PrepareVertices_Mesh(rsurface.batchnumvertices, rsurface.batchvertexmesh, rsurface.batchvertexmeshbuffer);
+               R_SetupShader_SetPermutationHLSL(mode, permutation);
+               Matrix4x4_ToArrayFloatGL(&rsurface.matrix, m16f);hlslPSSetParameter16f(D3DPSREGISTER_ModelToReflectCube, m16f);
+               if (mode == SHADERMODE_LIGHTSOURCE)
                {
-                       R_Mesh_TexCoordPointer(1, 3, rsurface.svector3f, rsurface.svector3f_bufferobject, rsurface.svector3f_bufferoffset);
-                       R_Mesh_TexCoordPointer(2, 3, rsurface.tvector3f, rsurface.tvector3f_bufferobject, rsurface.tvector3f_bufferoffset);
-                       R_Mesh_TexCoordPointer(3, 3, rsurface.normal3f, rsurface.normal3f_bufferobject, rsurface.normal3f_bufferoffset);
+                       Matrix4x4_ToArrayFloatGL(&rsurface.entitytolight, m16f);hlslVSSetParameter16f(D3DVSREGISTER_ModelToLight, m16f);
+                       hlslVSSetParameter3f(D3DVSREGISTER_LightPosition, rsurface.entitylightorigin[0], rsurface.entitylightorigin[1], rsurface.entitylightorigin[2]);
                }
                else
                {
-                       R_Mesh_TexCoordPointer(1, 0, NULL, 0, 0);
-                       R_Mesh_TexCoordPointer(2, 0, NULL, 0, 0);
-                       R_Mesh_TexCoordPointer(3, 0, NULL, 0, 0);
+                       if (mode == SHADERMODE_LIGHTDIRECTION)
+                       {
+                               hlslVSSetParameter3f(D3DVSREGISTER_LightDir, rsurface.modellight_lightdir[0], rsurface.modellight_lightdir[1], rsurface.modellight_lightdir[2]);
+                       }
+               }
+               Matrix4x4_ToArrayFloatGL(&rsurface.texture->currenttexmatrix, m16f);hlslVSSetParameter16f(D3DVSREGISTER_TexMatrix, m16f);
+               Matrix4x4_ToArrayFloatGL(&rsurface.texture->currentbackgroundtexmatrix, m16f);hlslVSSetParameter16f(D3DVSREGISTER_BackgroundTexMatrix, m16f);
+               Matrix4x4_ToArrayFloatGL(&r_shadow_shadowmapmatrix, m16f);hlslVSSetParameter16f(D3DVSREGISTER_ShadowMapMatrix, m16f);
+               hlslVSSetParameter3f(D3DVSREGISTER_EyePosition, rsurface.localvieworigin[0], rsurface.localvieworigin[1], rsurface.localvieworigin[2]);
+               hlslVSSetParameter4f(D3DVSREGISTER_FogPlane, rsurface.fogplane[0], rsurface.fogplane[1], rsurface.fogplane[2], rsurface.fogplane[3]);
+
+               if (mode == SHADERMODE_LIGHTSOURCE)
+               {
+                       hlslPSSetParameter3f(D3DPSREGISTER_LightPosition, rsurface.entitylightorigin[0], rsurface.entitylightorigin[1], rsurface.entitylightorigin[2]);
+                       hlslPSSetParameter3f(D3DPSREGISTER_LightColor, lightcolorbase[0], lightcolorbase[1], lightcolorbase[2]);
+                       hlslPSSetParameter3f(D3DPSREGISTER_Color_Ambient, colormod[0] * ambientscale, colormod[1] * ambientscale, colormod[2] * ambientscale);
+                       hlslPSSetParameter3f(D3DPSREGISTER_Color_Diffuse, colormod[0] * diffusescale, colormod[1] * diffusescale, colormod[2] * diffusescale);
+                       hlslPSSetParameter3f(D3DPSREGISTER_Color_Specular, r_refdef.view.colorscale * specularscale, r_refdef.view.colorscale * specularscale, r_refdef.view.colorscale * specularscale);
+
+                       // additive passes are only darkened by fog, not tinted
+                       hlslPSSetParameter3f(D3DPSREGISTER_FogColor, 0, 0, 0);
+                       hlslPSSetParameter1f(D3DPSREGISTER_SpecularPower, rsurface.texture->specularpower * (r_shadow_glossexact.integer ? 0.25f : 1.0f));
                }
-               GL_AlphaTest((rsurface.texture->currentmaterialflags & MATERIALFLAG_ALPHATEST) != 0);
-               GL_BlendFunc(rsurface.texture->currentlayers[0].blendfunc1, rsurface.texture->currentlayers[0].blendfunc2);
-       }
-       switch(vid.renderpath)
-       {
+               else
+               {
+                       if (mode == SHADERMODE_FLATCOLOR)
+                       {
+                               hlslPSSetParameter3f(D3DPSREGISTER_Color_Ambient, colormod[0], colormod[1], colormod[2]);
+                       }
+                       else if (mode == SHADERMODE_LIGHTDIRECTION)
+                       {
+                               hlslPSSetParameter3f(D3DPSREGISTER_Color_Ambient, (r_refdef.scene.ambient + rsurface.modellight_ambient[0] * r_refdef.lightmapintensity) * colormod[0], (r_refdef.scene.ambient + rsurface.modellight_ambient[1] * r_refdef.lightmapintensity) * colormod[1], (r_refdef.scene.ambient + rsurface.modellight_ambient[2] * r_refdef.lightmapintensity) * colormod[2]);
+                               hlslPSSetParameter3f(D3DPSREGISTER_Color_Diffuse, r_refdef.lightmapintensity * colormod[0], r_refdef.lightmapintensity * colormod[1], r_refdef.lightmapintensity * colormod[2]);
+                               hlslPSSetParameter3f(D3DPSREGISTER_Color_Specular, r_refdef.lightmapintensity * r_refdef.view.colorscale * specularscale, r_refdef.lightmapintensity * r_refdef.view.colorscale * specularscale, r_refdef.lightmapintensity * r_refdef.view.colorscale * specularscale);
+                               hlslPSSetParameter3f(D3DPSREGISTER_DeferredMod_Diffuse, colormod[0] * r_shadow_deferred_8bitrange.value, colormod[1] * r_shadow_deferred_8bitrange.value, colormod[2] * r_shadow_deferred_8bitrange.value);
+                               hlslPSSetParameter3f(D3DPSREGISTER_DeferredMod_Specular, specularscale * r_shadow_deferred_8bitrange.value, specularscale * r_shadow_deferred_8bitrange.value, specularscale * r_shadow_deferred_8bitrange.value);
+                               hlslPSSetParameter3f(D3DPSREGISTER_LightColor, rsurface.modellight_diffuse[0], rsurface.modellight_diffuse[1], rsurface.modellight_diffuse[2]);
+                               hlslPSSetParameter3f(D3DPSREGISTER_LightDir, rsurface.modellight_lightdir[0], rsurface.modellight_lightdir[1], rsurface.modellight_lightdir[2]);
+                       }
+                       else
+                       {
+                               hlslPSSetParameter3f(D3DPSREGISTER_Color_Ambient, r_refdef.scene.ambient * colormod[0], r_refdef.scene.ambient * colormod[1], r_refdef.scene.ambient * colormod[2]);
+                               hlslPSSetParameter3f(D3DPSREGISTER_Color_Diffuse, rsurface.texture->lightmapcolor[0], rsurface.texture->lightmapcolor[1], rsurface.texture->lightmapcolor[2]);
+                               hlslPSSetParameter3f(D3DPSREGISTER_Color_Specular, r_refdef.lightmapintensity * r_refdef.view.colorscale * specularscale, r_refdef.lightmapintensity * r_refdef.view.colorscale * specularscale, r_refdef.lightmapintensity * r_refdef.view.colorscale * specularscale);
+                               hlslPSSetParameter3f(D3DPSREGISTER_DeferredMod_Diffuse, colormod[0] * diffusescale * r_shadow_deferred_8bitrange.value, colormod[1] * diffusescale * r_shadow_deferred_8bitrange.value, colormod[2] * diffusescale * r_shadow_deferred_8bitrange.value);
+                               hlslPSSetParameter3f(D3DPSREGISTER_DeferredMod_Specular, specularscale * r_shadow_deferred_8bitrange.value, specularscale * r_shadow_deferred_8bitrange.value, specularscale * r_shadow_deferred_8bitrange.value);
+                       }
+                       // additive passes are only darkened by fog, not tinted
+                       if(blendfuncflags & BLENDFUNC_ALLOWS_FOG_HACK0)
+                               hlslPSSetParameter3f(D3DPSREGISTER_FogColor, 0, 0, 0);
+                       else
+                               hlslPSSetParameter3f(D3DPSREGISTER_FogColor, r_refdef.fogcolor[0], r_refdef.fogcolor[1], r_refdef.fogcolor[2]);
+                       hlslPSSetParameter4f(D3DPSREGISTER_DistortScaleRefractReflect, r_water_refractdistort.value * rsurface.texture->refractfactor, r_water_refractdistort.value * rsurface.texture->refractfactor, r_water_reflectdistort.value * rsurface.texture->reflectfactor, r_water_reflectdistort.value * rsurface.texture->reflectfactor);
+                       hlslPSSetParameter4f(D3DPSREGISTER_ScreenScaleRefractReflect, r_waterstate.screenscale[0], r_waterstate.screenscale[1], r_waterstate.screenscale[0], r_waterstate.screenscale[1]);
+                       hlslPSSetParameter4f(D3DPSREGISTER_ScreenCenterRefractReflect, r_waterstate.screencenter[0], r_waterstate.screencenter[1], r_waterstate.screencenter[0], r_waterstate.screencenter[1]);
+                       hlslPSSetParameter4f(D3DPSREGISTER_RefractColor, rsurface.texture->refractcolor4f[0], rsurface.texture->refractcolor4f[1], rsurface.texture->refractcolor4f[2], rsurface.texture->refractcolor4f[3] * rsurface.texture->lightmapcolor[3]);
+                       hlslPSSetParameter4f(D3DPSREGISTER_ReflectColor, rsurface.texture->reflectcolor4f[0], rsurface.texture->reflectcolor4f[1], rsurface.texture->reflectcolor4f[2], rsurface.texture->reflectcolor4f[3] * rsurface.texture->lightmapcolor[3]);
+                       hlslPSSetParameter1f(D3DPSREGISTER_ReflectFactor, rsurface.texture->reflectmax - rsurface.texture->reflectmin);
+                       hlslPSSetParameter1f(D3DPSREGISTER_ReflectOffset, rsurface.texture->reflectmin);
+                       hlslPSSetParameter1f(D3DPSREGISTER_SpecularPower, rsurface.texture->specularpower * (r_shadow_glossexact.integer ? 0.25f : 1.0f));
+                       if (mode == SHADERMODE_WATER)
+                               hlslPSSetParameter2f(D3DPSREGISTER_NormalmapScrollBlend, rsurface.texture->r_water_waterscroll[0], rsurface.texture->r_water_waterscroll[1]);
+               }
+               hlslPSSetParameter2f(D3DPSREGISTER_ShadowMap_TextureScale, r_shadow_shadowmap_texturescale[0], r_shadow_shadowmap_texturescale[1]);
+               hlslPSSetParameter4f(D3DPSREGISTER_ShadowMap_Parameters, r_shadow_shadowmap_parameters[0], r_shadow_shadowmap_parameters[1], r_shadow_shadowmap_parameters[2], r_shadow_shadowmap_parameters[3]);
+               hlslPSSetParameter3f(D3DPSREGISTER_Color_Glow, rsurface.glowmod[0], rsurface.glowmod[1], rsurface.glowmod[2]);
+               hlslPSSetParameter1f(D3DPSREGISTER_Alpha, rsurface.texture->lightmapcolor[3] * ((rsurface.texture->basematerialflags & MATERIALFLAG_WATERSHADER && r_waterstate.enabled && !r_refdef.view.isoverlay) ? rsurface.texture->r_water_wateralpha : 1));
+               hlslPSSetParameter3f(D3DPSREGISTER_EyePosition, rsurface.localvieworigin[0], rsurface.localvieworigin[1], rsurface.localvieworigin[2]);
+               if (rsurface.texture->pantstexture)
+                       hlslPSSetParameter3f(D3DPSREGISTER_Color_Pants, rsurface.colormap_pantscolor[0], rsurface.colormap_pantscolor[1], rsurface.colormap_pantscolor[2]);
+               else
+                       hlslPSSetParameter3f(D3DPSREGISTER_Color_Pants, 0, 0, 0);
+               if (rsurface.texture->shirttexture)
+                       hlslPSSetParameter3f(D3DPSREGISTER_Color_Shirt, rsurface.colormap_shirtcolor[0], rsurface.colormap_shirtcolor[1], rsurface.colormap_shirtcolor[2]);
+               else
+                       hlslPSSetParameter3f(D3DPSREGISTER_Color_Shirt, 0, 0, 0);
+               hlslPSSetParameter4f(D3DPSREGISTER_FogPlane, rsurface.fogplane[0], rsurface.fogplane[1], rsurface.fogplane[2], rsurface.fogplane[3]);
+               hlslPSSetParameter1f(D3DPSREGISTER_FogPlaneViewDist, rsurface.fogplaneviewdist);
+               hlslPSSetParameter1f(D3DPSREGISTER_FogRangeRecip, rsurface.fograngerecip);
+               hlslPSSetParameter1f(D3DPSREGISTER_FogHeightFade, rsurface.fogheightfade);
+               hlslPSSetParameter1f(D3DPSREGISTER_OffsetMapping_Scale, r_glsl_offsetmapping_scale.value);
+               hlslPSSetParameter2f(D3DPSREGISTER_ScreenToDepth, r_refdef.view.viewport.screentodepth[0], r_refdef.view.viewport.screentodepth[1]);
+               hlslPSSetParameter2f(D3DPSREGISTER_PixelToScreenTexCoord, 1.0f/vid.width, 1.0/vid.height);
+
+               R_Mesh_TexBind(GL20TU_NORMAL            , rsurface.texture->nmaptexture                       );
+               R_Mesh_TexBind(GL20TU_COLOR             , rsurface.texture->basetexture                       );
+               R_Mesh_TexBind(GL20TU_GLOSS             , rsurface.texture->glosstexture                      );
+               R_Mesh_TexBind(GL20TU_GLOW              , rsurface.texture->glowtexture                       );
+               if (permutation & SHADERPERMUTATION_VERTEXTEXTUREBLEND) R_Mesh_TexBind(GL20TU_SECONDARY_NORMAL  , rsurface.texture->backgroundnmaptexture             );
+               if (permutation & SHADERPERMUTATION_VERTEXTEXTUREBLEND) R_Mesh_TexBind(GL20TU_SECONDARY_COLOR   , rsurface.texture->backgroundbasetexture             );
+               if (permutation & SHADERPERMUTATION_VERTEXTEXTUREBLEND) R_Mesh_TexBind(GL20TU_SECONDARY_GLOSS   , rsurface.texture->backgroundglosstexture            );
+               if (permutation & SHADERPERMUTATION_VERTEXTEXTUREBLEND) R_Mesh_TexBind(GL20TU_SECONDARY_GLOW    , rsurface.texture->backgroundglowtexture             );
+               if (permutation & SHADERPERMUTATION_COLORMAPPING) R_Mesh_TexBind(GL20TU_PANTS             , rsurface.texture->pantstexture                      );
+               if (permutation & SHADERPERMUTATION_COLORMAPPING) R_Mesh_TexBind(GL20TU_SHIRT             , rsurface.texture->shirttexture                      );
+               if (permutation & SHADERPERMUTATION_REFLECTCUBE) R_Mesh_TexBind(GL20TU_REFLECTMASK       , rsurface.texture->reflectmasktexture                );
+               if (permutation & SHADERPERMUTATION_REFLECTCUBE) R_Mesh_TexBind(GL20TU_REFLECTCUBE       , rsurface.texture->reflectcubetexture ? rsurface.texture->reflectcubetexture : r_texture_whitecube);
+               if (permutation & SHADERPERMUTATION_FOGHEIGHTTEXTURE) R_Mesh_TexBind(GL20TU_FOGHEIGHTTEXTURE  , r_texture_fogheighttexture                          );
+               if (permutation & (SHADERPERMUTATION_FOGINSIDE | SHADERPERMUTATION_FOGOUTSIDE)) R_Mesh_TexBind(GL20TU_FOGMASK           , r_texture_fogattenuation                            );
+               R_Mesh_TexBind(GL20TU_LIGHTMAP          , rsurface.lightmaptexture ? rsurface.lightmaptexture : r_texture_white);
+               R_Mesh_TexBind(GL20TU_DELUXEMAP         , rsurface.deluxemaptexture ? rsurface.deluxemaptexture : r_texture_blanknormalmap);
+               if (rsurface.rtlight                                  ) R_Mesh_TexBind(GL20TU_ATTENUATION       , r_shadow_attenuationgradienttexture                 );
+               if (rsurfacepass == RSURFPASS_BACKGROUND)
+               {
+                       R_Mesh_TexBind(GL20TU_REFRACTION        , waterplane->texture_refraction ? waterplane->texture_refraction : r_texture_black);
+                       if(mode == SHADERMODE_GENERIC) R_Mesh_TexBind(GL20TU_FIRST             , waterplane->texture_camera ? waterplane->texture_camera : r_texture_black);
+                       R_Mesh_TexBind(GL20TU_REFLECTION        , waterplane->texture_reflection ? waterplane->texture_reflection : r_texture_black);
+               }
+               else
+               {
+                       if (permutation & SHADERPERMUTATION_REFLECTION        ) R_Mesh_TexBind(GL20TU_REFLECTION        , waterplane->texture_reflection ? waterplane->texture_reflection : r_texture_black);
+               }
+//             if (rsurfacepass == RSURFPASS_DEFERREDLIGHT           ) R_Mesh_TexBind(GL20TU_SCREENDEPTH       , r_shadow_prepassgeometrydepthtexture                );
+//             if (rsurfacepass == RSURFPASS_DEFERREDLIGHT           ) R_Mesh_TexBind(GL20TU_SCREENNORMALMAP   , r_shadow_prepassgeometrynormalmaptexture            );
+               if (permutation & SHADERPERMUTATION_DEFERREDLIGHTMAP  ) R_Mesh_TexBind(GL20TU_SCREENDIFFUSE     , r_shadow_prepasslightingdiffusetexture              );
+               if (permutation & SHADERPERMUTATION_DEFERREDLIGHTMAP  ) R_Mesh_TexBind(GL20TU_SCREENSPECULAR    , r_shadow_prepasslightingspeculartexture             );
+               if (rsurface.rtlight || (r_shadow_usingshadowmaportho && !(rsurface.ent_flags & RENDER_NOSELFSHADOW)))
+               {
+                       R_Mesh_TexBind(GL20TU_SHADOWMAP2D, r_shadow_shadowmap2dcolortexture);
+                       if (rsurface.rtlight)
+                       {
+                               if (permutation & SHADERPERMUTATION_CUBEFILTER        ) R_Mesh_TexBind(GL20TU_CUBE              , rsurface.rtlight->currentcubemap                    );
+                               if (permutation & SHADERPERMUTATION_SHADOWMAPVSDCT    ) R_Mesh_TexBind(GL20TU_CUBEPROJECTION    , r_shadow_shadowmapvsdcttexture                      );
+                       }
+               }
+#endif
+               break;
+       case RENDERPATH_D3D10:
+               Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               break;
+       case RENDERPATH_D3D11:
+               Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               break;
        case RENDERPATH_GL20:
+       case RENDERPATH_GLES2:
+               if (!vid.useinterleavedarrays)
+               {
+                       RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_NORMAL | BATCHNEED_ARRAY_VECTOR | (rsurface.modellightmapcolor4f ? BATCHNEED_ARRAY_VERTEXCOLOR : 0) | BATCHNEED_ARRAY_TEXCOORD | (rsurface.uselightmaptexture ? BATCHNEED_ARRAY_LIGHTMAP : 0), texturenumsurfaces, texturesurfacelist);
+                       R_Mesh_VertexPointer(     3, GL_FLOAT, sizeof(float[3]), rsurface.batchvertex3f, rsurface.batchvertex3f_vertexbuffer, rsurface.batchvertex3f_bufferoffset);
+                       R_Mesh_ColorPointer(      4, GL_FLOAT, sizeof(float[4]), rsurface.batchlightmapcolor4f, rsurface.batchlightmapcolor4f_vertexbuffer, rsurface.batchlightmapcolor4f_bufferoffset);
+                       R_Mesh_TexCoordPointer(0, 2, GL_FLOAT, sizeof(float[2]), rsurface.batchtexcoordtexture2f, rsurface.batchtexcoordtexture2f_vertexbuffer, rsurface.batchtexcoordtexture2f_bufferoffset);
+                       R_Mesh_TexCoordPointer(1, 3, GL_FLOAT, sizeof(float[3]), rsurface.batchsvector3f, rsurface.batchsvector3f_vertexbuffer, rsurface.batchsvector3f_bufferoffset);
+                       R_Mesh_TexCoordPointer(2, 3, GL_FLOAT, sizeof(float[3]), rsurface.batchtvector3f, rsurface.batchtvector3f_vertexbuffer, rsurface.batchtvector3f_bufferoffset);
+                       R_Mesh_TexCoordPointer(3, 3, GL_FLOAT, sizeof(float[3]), rsurface.batchnormal3f, rsurface.batchnormal3f_vertexbuffer, rsurface.batchnormal3f_bufferoffset);
+                       R_Mesh_TexCoordPointer(4, 2, GL_FLOAT, sizeof(float[2]), rsurface.batchtexcoordlightmap2f, rsurface.batchtexcoordlightmap2f_vertexbuffer, rsurface.batchtexcoordlightmap2f_bufferoffset);
+               }
+               else
+               {
+                       RSurf_PrepareVerticesForBatch(BATCHNEED_VERTEXMESH_VERTEX | BATCHNEED_VERTEXMESH_NORMAL | BATCHNEED_VERTEXMESH_VECTOR | (rsurface.modellightmapcolor4f ? BATCHNEED_VERTEXMESH_VERTEXCOLOR : 0) | BATCHNEED_VERTEXMESH_TEXCOORD | (rsurface.uselightmaptexture ? BATCHNEED_VERTEXMESH_LIGHTMAP : 0), texturenumsurfaces, texturesurfacelist);
+                       R_Mesh_PrepareVertices_Mesh(rsurface.batchnumvertices, rsurface.batchvertexmesh, rsurface.batchvertexmeshbuffer);
+               }
                R_SetupShader_SetPermutationGLSL(mode, permutation);
-               if (r_glsl_permutation->loc_ModelToReflectCube >= 0) {Matrix4x4_ToArrayFloatGL(&rsurface.matrix, m16f);qglUniformMatrix4fvARB(r_glsl_permutation->loc_ModelToReflectCube, 1, false, m16f);}
+               if (r_glsl_permutation->loc_ModelToReflectCube >= 0) {Matrix4x4_ToArrayFloatGL(&rsurface.matrix, m16f);qglUniformMatrix4fv(r_glsl_permutation->loc_ModelToReflectCube, 1, false, m16f);}
                if (mode == SHADERMODE_LIGHTSOURCE)
                {
-                       if (r_glsl_permutation->loc_ModelToLight >= 0) {Matrix4x4_ToArrayFloatGL(&rsurface.entitytolight, m16f);qglUniformMatrix4fvARB(r_glsl_permutation->loc_ModelToLight, 1, false, m16f);}
-                       if (r_glsl_permutation->loc_LightPosition >= 0) qglUniform3fARB(r_glsl_permutation->loc_LightPosition, rsurface.entitylightorigin[0], rsurface.entitylightorigin[1], rsurface.entitylightorigin[2]);
-                       if (r_glsl_permutation->loc_LightColor >= 0) qglUniform3fARB(r_glsl_permutation->loc_LightColor, lightcolorbase[0], lightcolorbase[1], lightcolorbase[2]);
-                       if (r_glsl_permutation->loc_Color_Ambient >= 0) qglUniform3fARB(r_glsl_permutation->loc_Color_Ambient, rsurface.colormod[0] * ambientscale, rsurface.colormod[1] * ambientscale, rsurface.colormod[2] * ambientscale);
-                       if (r_glsl_permutation->loc_Color_Diffuse >= 0) qglUniform3fARB(r_glsl_permutation->loc_Color_Diffuse, rsurface.colormod[0] * diffusescale, rsurface.colormod[1] * diffusescale, rsurface.colormod[2] * diffusescale);
-                       if (r_glsl_permutation->loc_Color_Specular >= 0) qglUniform3fARB(r_glsl_permutation->loc_Color_Specular, r_refdef.view.colorscale * specularscale, r_refdef.view.colorscale * specularscale, r_refdef.view.colorscale * specularscale);
+                       if (r_glsl_permutation->loc_ModelToLight >= 0) {Matrix4x4_ToArrayFloatGL(&rsurface.entitytolight, m16f);qglUniformMatrix4fv(r_glsl_permutation->loc_ModelToLight, 1, false, m16f);}
+                       if (r_glsl_permutation->loc_LightPosition >= 0) qglUniform3f(r_glsl_permutation->loc_LightPosition, rsurface.entitylightorigin[0], rsurface.entitylightorigin[1], rsurface.entitylightorigin[2]);
+                       if (r_glsl_permutation->loc_LightColor >= 0) qglUniform3f(r_glsl_permutation->loc_LightColor, lightcolorbase[0], lightcolorbase[1], lightcolorbase[2]);
+                       if (r_glsl_permutation->loc_Color_Ambient >= 0) qglUniform3f(r_glsl_permutation->loc_Color_Ambient, colormod[0] * ambientscale, colormod[1] * ambientscale, colormod[2] * ambientscale);
+                       if (r_glsl_permutation->loc_Color_Diffuse >= 0) qglUniform3f(r_glsl_permutation->loc_Color_Diffuse, colormod[0] * diffusescale, colormod[1] * diffusescale, colormod[2] * diffusescale);
+                       if (r_glsl_permutation->loc_Color_Specular >= 0) qglUniform3f(r_glsl_permutation->loc_Color_Specular, r_refdef.view.colorscale * specularscale, r_refdef.view.colorscale * specularscale, r_refdef.view.colorscale * specularscale);
        
                        // additive passes are only darkened by fog, not tinted
                        if (r_glsl_permutation->loc_FogColor >= 0)
-                               qglUniform3fARB(r_glsl_permutation->loc_FogColor, 0, 0, 0);
-                       if (r_glsl_permutation->loc_ShadowMap_TextureScale >= 0) qglUniform2fARB(r_glsl_permutation->loc_ShadowMap_TextureScale, r_shadow_shadowmap_texturescale[0], r_shadow_shadowmap_texturescale[1]);
-                       if (r_glsl_permutation->loc_ShadowMap_Parameters >= 0) qglUniform4fARB(r_glsl_permutation->loc_ShadowMap_Parameters, r_shadow_shadowmap_parameters[0], r_shadow_shadowmap_parameters[1], r_shadow_shadowmap_parameters[2], r_shadow_shadowmap_parameters[3]);
-                       if (r_glsl_permutation->loc_SpecularPower >= 0) qglUniform1fARB(r_glsl_permutation->loc_SpecularPower, rsurface.texture->specularpower * ((permutation & SHADERPERMUTATION_EXACTSPECULARMATH) ? 0.25f : 1.0f));
+                               qglUniform3f(r_glsl_permutation->loc_FogColor, 0, 0, 0);
+                       if (r_glsl_permutation->loc_SpecularPower >= 0) qglUniform1f(r_glsl_permutation->loc_SpecularPower, rsurface.texture->specularpower * (r_shadow_glossexact.integer ? 0.25f : 1.0f));
                }
                else
                {
                        if (mode == SHADERMODE_FLATCOLOR)
                        {
-                               if (r_glsl_permutation->loc_Color_Ambient >= 0) qglUniform3fARB(r_glsl_permutation->loc_Color_Ambient, rsurface.colormod[0], rsurface.colormod[1], rsurface.colormod[2]);
+                               if (r_glsl_permutation->loc_Color_Ambient >= 0) qglUniform3f(r_glsl_permutation->loc_Color_Ambient, colormod[0], colormod[1], colormod[2]);
                        }
                        else if (mode == SHADERMODE_LIGHTDIRECTION)
                        {
-                               if (r_glsl_permutation->loc_Color_Ambient >= 0) qglUniform3fARB(r_glsl_permutation->loc_Color_Ambient, (r_refdef.scene.ambient + rsurface.modellight_ambient[0] * r_refdef.lightmapintensity) * rsurface.colormod[0], (r_refdef.scene.ambient + rsurface.modellight_ambient[1] * r_refdef.lightmapintensity) * rsurface.colormod[1], (r_refdef.scene.ambient + rsurface.modellight_ambient[2] * r_refdef.lightmapintensity) * rsurface.colormod[2]);
-                               if (r_glsl_permutation->loc_Color_Diffuse >= 0) qglUniform3fARB(r_glsl_permutation->loc_Color_Diffuse, r_refdef.lightmapintensity * rsurface.colormod[0], r_refdef.lightmapintensity * rsurface.colormod[1], r_refdef.lightmapintensity * rsurface.colormod[2]);
-                               if (r_glsl_permutation->loc_Color_Specular >= 0) qglUniform3fARB(r_glsl_permutation->loc_Color_Specular, r_refdef.lightmapintensity * r_refdef.view.colorscale * specularscale, r_refdef.lightmapintensity * r_refdef.view.colorscale * specularscale, r_refdef.lightmapintensity * r_refdef.view.colorscale * specularscale);
-                               if (r_glsl_permutation->loc_DeferredMod_Diffuse >= 0) qglUniform3fARB(r_glsl_permutation->loc_DeferredMod_Diffuse, rsurface.colormod[0] * r_shadow_deferred_8bitrange.value, rsurface.colormod[1] * r_shadow_deferred_8bitrange.value, rsurface.colormod[2] * r_shadow_deferred_8bitrange.value);
-                               if (r_glsl_permutation->loc_DeferredMod_Specular >= 0) qglUniform3fARB(r_glsl_permutation->loc_DeferredMod_Specular, specularscale * r_shadow_deferred_8bitrange.value, specularscale * r_shadow_deferred_8bitrange.value, specularscale * r_shadow_deferred_8bitrange.value);
-                               if (r_glsl_permutation->loc_LightColor >= 0) qglUniform3fARB(r_glsl_permutation->loc_LightColor, rsurface.modellight_diffuse[0], rsurface.modellight_diffuse[1], rsurface.modellight_diffuse[2]);
-                               if (r_glsl_permutation->loc_LightDir >= 0) qglUniform3fARB(r_glsl_permutation->loc_LightDir, rsurface.modellight_lightdir[0], rsurface.modellight_lightdir[1], rsurface.modellight_lightdir[2]);
+                               if (r_glsl_permutation->loc_Color_Ambient >= 0) qglUniform3f(r_glsl_permutation->loc_Color_Ambient, (r_refdef.scene.ambient + rsurface.modellight_ambient[0] * r_refdef.lightmapintensity * r_refdef.scene.rtlightstylevalue[0]) * colormod[0], (r_refdef.scene.ambient + rsurface.modellight_ambient[1] * r_refdef.lightmapintensity * r_refdef.scene.rtlightstylevalue[0]) * colormod[1], (r_refdef.scene.ambient + rsurface.modellight_ambient[2] * r_refdef.lightmapintensity * r_refdef.scene.rtlightstylevalue[0]) * colormod[2]);
+                               if (r_glsl_permutation->loc_Color_Diffuse >= 0) qglUniform3f(r_glsl_permutation->loc_Color_Diffuse, r_refdef.lightmapintensity * colormod[0], r_refdef.lightmapintensity * colormod[1], r_refdef.lightmapintensity * colormod[2]);
+                               if (r_glsl_permutation->loc_Color_Specular >= 0) qglUniform3f(r_glsl_permutation->loc_Color_Specular, r_refdef.lightmapintensity * r_refdef.view.colorscale * specularscale, r_refdef.lightmapintensity * r_refdef.view.colorscale * specularscale, r_refdef.lightmapintensity * r_refdef.view.colorscale * specularscale);
+                               if (r_glsl_permutation->loc_DeferredMod_Diffuse >= 0) qglUniform3f(r_glsl_permutation->loc_DeferredMod_Diffuse, colormod[0] * r_shadow_deferred_8bitrange.value, colormod[1] * r_shadow_deferred_8bitrange.value, colormod[2] * r_shadow_deferred_8bitrange.value);
+                               if (r_glsl_permutation->loc_DeferredMod_Specular >= 0) qglUniform3f(r_glsl_permutation->loc_DeferredMod_Specular, specularscale * r_shadow_deferred_8bitrange.value, specularscale * r_shadow_deferred_8bitrange.value, specularscale * r_shadow_deferred_8bitrange.value);
+                               if (r_glsl_permutation->loc_LightColor >= 0) qglUniform3f(r_glsl_permutation->loc_LightColor, rsurface.modellight_diffuse[0] * r_refdef.scene.rtlightstylevalue[0], rsurface.modellight_diffuse[1] * r_refdef.scene.rtlightstylevalue[0], rsurface.modellight_diffuse[2] * r_refdef.scene.rtlightstylevalue[0]);
+                               if (r_glsl_permutation->loc_LightDir >= 0) qglUniform3f(r_glsl_permutation->loc_LightDir, rsurface.modellight_lightdir[0], rsurface.modellight_lightdir[1], rsurface.modellight_lightdir[2]);
                        }
                        else
                        {
-                               if (r_glsl_permutation->loc_Color_Ambient >= 0) qglUniform3fARB(r_glsl_permutation->loc_Color_Ambient, r_refdef.scene.ambient * rsurface.colormod[0], r_refdef.scene.ambient * rsurface.colormod[1], r_refdef.scene.ambient * rsurface.colormod[2]);
-                               if (r_glsl_permutation->loc_Color_Diffuse >= 0) qglUniform3fARB(r_glsl_permutation->loc_Color_Diffuse, rsurface.texture->lightmapcolor[0], rsurface.texture->lightmapcolor[1], rsurface.texture->lightmapcolor[2]);
-                               if (r_glsl_permutation->loc_Color_Specular >= 0) qglUniform3fARB(r_glsl_permutation->loc_Color_Specular, r_refdef.lightmapintensity * r_refdef.view.colorscale * specularscale, r_refdef.lightmapintensity * r_refdef.view.colorscale * specularscale, r_refdef.lightmapintensity * r_refdef.view.colorscale * specularscale);
-                               if (r_glsl_permutation->loc_DeferredMod_Diffuse >= 0) qglUniform3fARB(r_glsl_permutation->loc_DeferredMod_Diffuse, rsurface.colormod[0] * diffusescale * r_shadow_deferred_8bitrange.value, rsurface.colormod[1] * diffusescale * r_shadow_deferred_8bitrange.value, rsurface.colormod[2] * diffusescale * r_shadow_deferred_8bitrange.value);
-                               if (r_glsl_permutation->loc_DeferredMod_Specular >= 0) qglUniform3fARB(r_glsl_permutation->loc_DeferredMod_Specular, specularscale * r_shadow_deferred_8bitrange.value, specularscale * r_shadow_deferred_8bitrange.value, specularscale * r_shadow_deferred_8bitrange.value);
+                               if (r_glsl_permutation->loc_Color_Ambient >= 0) qglUniform3f(r_glsl_permutation->loc_Color_Ambient, r_refdef.scene.ambient * colormod[0], r_refdef.scene.ambient * colormod[1], r_refdef.scene.ambient * colormod[2]);
+                               if (r_glsl_permutation->loc_Color_Diffuse >= 0) qglUniform3f(r_glsl_permutation->loc_Color_Diffuse, rsurface.texture->lightmapcolor[0], rsurface.texture->lightmapcolor[1], rsurface.texture->lightmapcolor[2]);
+                               if (r_glsl_permutation->loc_Color_Specular >= 0) qglUniform3f(r_glsl_permutation->loc_Color_Specular, r_refdef.lightmapintensity * r_refdef.view.colorscale * specularscale, r_refdef.lightmapintensity * r_refdef.view.colorscale * specularscale, r_refdef.lightmapintensity * r_refdef.view.colorscale * specularscale);
+                               if (r_glsl_permutation->loc_DeferredMod_Diffuse >= 0) qglUniform3f(r_glsl_permutation->loc_DeferredMod_Diffuse, colormod[0] * diffusescale * r_shadow_deferred_8bitrange.value, colormod[1] * diffusescale * r_shadow_deferred_8bitrange.value, colormod[2] * diffusescale * r_shadow_deferred_8bitrange.value);
+                               if (r_glsl_permutation->loc_DeferredMod_Specular >= 0) qglUniform3f(r_glsl_permutation->loc_DeferredMod_Specular, specularscale * r_shadow_deferred_8bitrange.value, specularscale * r_shadow_deferred_8bitrange.value, specularscale * r_shadow_deferred_8bitrange.value);
                        }
                        // additive passes are only darkened by fog, not tinted
                        if (r_glsl_permutation->loc_FogColor >= 0)
                        {
-                               if (rsurface.texture->currentmaterialflags & MATERIALFLAG_ADD)
-                                       qglUniform3fARB(r_glsl_permutation->loc_FogColor, 0, 0, 0);
+                               if(blendfuncflags & BLENDFUNC_ALLOWS_FOG_HACK0)
+                                       qglUniform3f(r_glsl_permutation->loc_FogColor, 0, 0, 0);
                                else
-                                       qglUniform3fARB(r_glsl_permutation->loc_FogColor, r_refdef.fogcolor[0], r_refdef.fogcolor[1], r_refdef.fogcolor[2]);
+                                       qglUniform3f(r_glsl_permutation->loc_FogColor, r_refdef.fogcolor[0], r_refdef.fogcolor[1], r_refdef.fogcolor[2]);
                        }
-                       if (r_glsl_permutation->loc_DistortScaleRefractReflect >= 0) qglUniform4fARB(r_glsl_permutation->loc_DistortScaleRefractReflect, r_water_refractdistort.value * rsurface.texture->refractfactor, r_water_refractdistort.value * rsurface.texture->refractfactor, r_water_reflectdistort.value * rsurface.texture->reflectfactor, r_water_reflectdistort.value * rsurface.texture->reflectfactor);
-                       if (r_glsl_permutation->loc_ScreenScaleRefractReflect >= 0) qglUniform4fARB(r_glsl_permutation->loc_ScreenScaleRefractReflect, r_waterstate.screenscale[0], r_waterstate.screenscale[1], r_waterstate.screenscale[0], r_waterstate.screenscale[1]);
-                       if (r_glsl_permutation->loc_ScreenCenterRefractReflect >= 0) qglUniform4fARB(r_glsl_permutation->loc_ScreenCenterRefractReflect, r_waterstate.screencenter[0], r_waterstate.screencenter[1], r_waterstate.screencenter[0], r_waterstate.screencenter[1]);
-                       if (r_glsl_permutation->loc_RefractColor >= 0) qglUniform4fvARB(r_glsl_permutation->loc_RefractColor, 1, rsurface.texture->refractcolor4f);
-                       if (r_glsl_permutation->loc_ReflectColor >= 0) qglUniform4fvARB(r_glsl_permutation->loc_ReflectColor, 1, rsurface.texture->reflectcolor4f);
-                       if (r_glsl_permutation->loc_ReflectFactor >= 0) qglUniform1fARB(r_glsl_permutation->loc_ReflectFactor, rsurface.texture->reflectmax - rsurface.texture->reflectmin);
-                       if (r_glsl_permutation->loc_ReflectOffset >= 0) qglUniform1fARB(r_glsl_permutation->loc_ReflectOffset, rsurface.texture->reflectmin);
-                       if (r_glsl_permutation->loc_SpecularPower >= 0) qglUniform1fARB(r_glsl_permutation->loc_SpecularPower, rsurface.texture->specularpower * ((permutation & SHADERPERMUTATION_EXACTSPECULARMATH) ? 0.25f : 1.0f));
-               }
-               if (r_glsl_permutation->loc_TexMatrix >= 0) {Matrix4x4_ToArrayFloatGL(&rsurface.texture->currenttexmatrix, m16f);qglUniformMatrix4fvARB(r_glsl_permutation->loc_TexMatrix, 1, false, m16f);}
-               if (r_glsl_permutation->loc_BackgroundTexMatrix >= 0) {Matrix4x4_ToArrayFloatGL(&rsurface.texture->currentbackgroundtexmatrix, m16f);qglUniformMatrix4fvARB(r_glsl_permutation->loc_BackgroundTexMatrix, 1, false, m16f);}
-               if (r_glsl_permutation->loc_Color_Glow >= 0) qglUniform3fARB(r_glsl_permutation->loc_Color_Glow, rsurface.glowmod[0], rsurface.glowmod[1], rsurface.glowmod[2]);
-               if (r_glsl_permutation->loc_Alpha >= 0) qglUniform1fARB(r_glsl_permutation->loc_Alpha, rsurface.texture->lightmapcolor[3]);
-               if (r_glsl_permutation->loc_EyePosition >= 0) qglUniform3fARB(r_glsl_permutation->loc_EyePosition, rsurface.localvieworigin[0], rsurface.localvieworigin[1], rsurface.localvieworigin[2]);
+                       if (r_glsl_permutation->loc_DistortScaleRefractReflect >= 0) qglUniform4f(r_glsl_permutation->loc_DistortScaleRefractReflect, r_water_refractdistort.value * rsurface.texture->refractfactor, r_water_refractdistort.value * rsurface.texture->refractfactor, r_water_reflectdistort.value * rsurface.texture->reflectfactor, r_water_reflectdistort.value * rsurface.texture->reflectfactor);
+                       if (r_glsl_permutation->loc_ScreenScaleRefractReflect >= 0) qglUniform4f(r_glsl_permutation->loc_ScreenScaleRefractReflect, r_waterstate.screenscale[0], r_waterstate.screenscale[1], r_waterstate.screenscale[0], r_waterstate.screenscale[1]);
+                       if (r_glsl_permutation->loc_ScreenCenterRefractReflect >= 0) qglUniform4f(r_glsl_permutation->loc_ScreenCenterRefractReflect, r_waterstate.screencenter[0], r_waterstate.screencenter[1], r_waterstate.screencenter[0], r_waterstate.screencenter[1]);
+                       if (r_glsl_permutation->loc_RefractColor >= 0) qglUniform4f(r_glsl_permutation->loc_RefractColor, rsurface.texture->refractcolor4f[0], rsurface.texture->refractcolor4f[1], rsurface.texture->refractcolor4f[2], rsurface.texture->refractcolor4f[3] * rsurface.texture->lightmapcolor[3]);
+                       if (r_glsl_permutation->loc_ReflectColor >= 0) qglUniform4f(r_glsl_permutation->loc_ReflectColor, rsurface.texture->reflectcolor4f[0], rsurface.texture->reflectcolor4f[1], rsurface.texture->reflectcolor4f[2], rsurface.texture->reflectcolor4f[3] * rsurface.texture->lightmapcolor[3]);
+                       if (r_glsl_permutation->loc_ReflectFactor >= 0) qglUniform1f(r_glsl_permutation->loc_ReflectFactor, rsurface.texture->reflectmax - rsurface.texture->reflectmin);
+                       if (r_glsl_permutation->loc_ReflectOffset >= 0) qglUniform1f(r_glsl_permutation->loc_ReflectOffset, rsurface.texture->reflectmin);
+                       if (r_glsl_permutation->loc_SpecularPower >= 0) qglUniform1f(r_glsl_permutation->loc_SpecularPower, rsurface.texture->specularpower * (r_shadow_glossexact.integer ? 0.25f : 1.0f));
+                       if (r_glsl_permutation->loc_NormalmapScrollBlend >= 0) qglUniform2f(r_glsl_permutation->loc_NormalmapScrollBlend, rsurface.texture->r_water_waterscroll[0], rsurface.texture->r_water_waterscroll[1]);
+               }
+               if (r_glsl_permutation->loc_TexMatrix >= 0) {Matrix4x4_ToArrayFloatGL(&rsurface.texture->currenttexmatrix, m16f);qglUniformMatrix4fv(r_glsl_permutation->loc_TexMatrix, 1, false, m16f);}
+               if (r_glsl_permutation->loc_BackgroundTexMatrix >= 0) {Matrix4x4_ToArrayFloatGL(&rsurface.texture->currentbackgroundtexmatrix, m16f);qglUniformMatrix4fv(r_glsl_permutation->loc_BackgroundTexMatrix, 1, false, m16f);}
+               if (r_glsl_permutation->loc_ShadowMapMatrix >= 0) {Matrix4x4_ToArrayFloatGL(&r_shadow_shadowmapmatrix, m16f);qglUniformMatrix4fv(r_glsl_permutation->loc_ShadowMapMatrix, 1, false, m16f);}
+               if (r_glsl_permutation->loc_ShadowMap_TextureScale >= 0) qglUniform2f(r_glsl_permutation->loc_ShadowMap_TextureScale, r_shadow_shadowmap_texturescale[0], r_shadow_shadowmap_texturescale[1]);
+               if (r_glsl_permutation->loc_ShadowMap_Parameters >= 0) qglUniform4f(r_glsl_permutation->loc_ShadowMap_Parameters, r_shadow_shadowmap_parameters[0], r_shadow_shadowmap_parameters[1], r_shadow_shadowmap_parameters[2], r_shadow_shadowmap_parameters[3]);
+
+               if (r_glsl_permutation->loc_Color_Glow >= 0) qglUniform3f(r_glsl_permutation->loc_Color_Glow, rsurface.glowmod[0], rsurface.glowmod[1], rsurface.glowmod[2]);
+               if (r_glsl_permutation->loc_Alpha >= 0) qglUniform1f(r_glsl_permutation->loc_Alpha, rsurface.texture->lightmapcolor[3] * ((rsurface.texture->basematerialflags & MATERIALFLAG_WATERSHADER && r_waterstate.enabled && !r_refdef.view.isoverlay) ? rsurface.texture->r_water_wateralpha : 1));
+               if (r_glsl_permutation->loc_EyePosition >= 0) qglUniform3f(r_glsl_permutation->loc_EyePosition, rsurface.localvieworigin[0], rsurface.localvieworigin[1], rsurface.localvieworigin[2]);
                if (r_glsl_permutation->loc_Color_Pants >= 0)
                {
                        if (rsurface.texture->pantstexture)
-                               qglUniform3fARB(r_glsl_permutation->loc_Color_Pants, rsurface.colormap_pantscolor[0], rsurface.colormap_pantscolor[1], rsurface.colormap_pantscolor[2]);
+                               qglUniform3f(r_glsl_permutation->loc_Color_Pants, rsurface.colormap_pantscolor[0], rsurface.colormap_pantscolor[1], rsurface.colormap_pantscolor[2]);
                        else
-                               qglUniform3fARB(r_glsl_permutation->loc_Color_Pants, 0, 0, 0);
+                               qglUniform3f(r_glsl_permutation->loc_Color_Pants, 0, 0, 0);
                }
                if (r_glsl_permutation->loc_Color_Shirt >= 0)
                {
                        if (rsurface.texture->shirttexture)
-                               qglUniform3fARB(r_glsl_permutation->loc_Color_Shirt, rsurface.colormap_shirtcolor[0], rsurface.colormap_shirtcolor[1], rsurface.colormap_shirtcolor[2]);
+                               qglUniform3f(r_glsl_permutation->loc_Color_Shirt, rsurface.colormap_shirtcolor[0], rsurface.colormap_shirtcolor[1], rsurface.colormap_shirtcolor[2]);
                        else
-                               qglUniform3fARB(r_glsl_permutation->loc_Color_Shirt, 0, 0, 0);
-               }
-               if (r_glsl_permutation->loc_FogPlane >= 0) qglUniform4fARB(r_glsl_permutation->loc_FogPlane, rsurface.fogplane[0], rsurface.fogplane[1], rsurface.fogplane[2], rsurface.fogplane[3]);
-               if (r_glsl_permutation->loc_FogPlaneViewDist >= 0) qglUniform1fARB(r_glsl_permutation->loc_FogPlaneViewDist, rsurface.fogplaneviewdist);
-               if (r_glsl_permutation->loc_FogRangeRecip >= 0) qglUniform1fARB(r_glsl_permutation->loc_FogRangeRecip, rsurface.fograngerecip);
-               if (r_glsl_permutation->loc_FogHeightFade >= 0) qglUniform1fARB(r_glsl_permutation->loc_FogHeightFade, rsurface.fogheightfade);
-               if (r_glsl_permutation->loc_OffsetMapping_Scale >= 0) qglUniform1fARB(r_glsl_permutation->loc_OffsetMapping_Scale, r_glsl_offsetmapping_scale.value);
-               if (r_glsl_permutation->loc_ScreenToDepth >= 0) qglUniform2fARB(r_glsl_permutation->loc_ScreenToDepth, r_refdef.view.viewport.screentodepth[0], r_refdef.view.viewport.screentodepth[1]);
-               if (r_glsl_permutation->loc_PixelToScreenTexCoord >= 0) qglUniform2fARB(r_glsl_permutation->loc_PixelToScreenTexCoord, 1.0f/vid.width, 1.0f/vid.height);
-
-       //      if (r_glsl_permutation->loc_Texture_First           >= 0) R_Mesh_TexBind(GL20TU_FIRST             , r_texture_white                                     );
-       //      if (r_glsl_permutation->loc_Texture_Second          >= 0) R_Mesh_TexBind(GL20TU_SECOND            , r_texture_white                                     );
-       //      if (r_glsl_permutation->loc_Texture_GammaRamps      >= 0) R_Mesh_TexBind(GL20TU_GAMMARAMPS        , r_texture_gammaramps                                );
-               if (r_glsl_permutation->loc_Texture_Normal          >= 0) R_Mesh_TexBind(GL20TU_NORMAL            , rsurface.texture->nmaptexture                       );
-               if (r_glsl_permutation->loc_Texture_Color           >= 0) R_Mesh_TexBind(GL20TU_COLOR             , rsurface.texture->basetexture                       );
-               if (r_glsl_permutation->loc_Texture_Gloss           >= 0) R_Mesh_TexBind(GL20TU_GLOSS             , rsurface.texture->glosstexture                      );
-               if (r_glsl_permutation->loc_Texture_Glow            >= 0) R_Mesh_TexBind(GL20TU_GLOW              , rsurface.texture->glowtexture                       );
-               if (r_glsl_permutation->loc_Texture_SecondaryNormal >= 0) R_Mesh_TexBind(GL20TU_SECONDARY_NORMAL  , rsurface.texture->backgroundnmaptexture             );
-               if (r_glsl_permutation->loc_Texture_SecondaryColor  >= 0) R_Mesh_TexBind(GL20TU_SECONDARY_COLOR   , rsurface.texture->backgroundbasetexture             );
-               if (r_glsl_permutation->loc_Texture_SecondaryGloss  >= 0) R_Mesh_TexBind(GL20TU_SECONDARY_GLOSS   , rsurface.texture->backgroundglosstexture            );
-               if (r_glsl_permutation->loc_Texture_SecondaryGlow   >= 0) R_Mesh_TexBind(GL20TU_SECONDARY_GLOW    , rsurface.texture->backgroundglowtexture             );
-               if (r_glsl_permutation->loc_Texture_Pants           >= 0) R_Mesh_TexBind(GL20TU_PANTS             , rsurface.texture->pantstexture                      );
-               if (r_glsl_permutation->loc_Texture_Shirt           >= 0) R_Mesh_TexBind(GL20TU_SHIRT             , rsurface.texture->shirttexture                      );
-               if (r_glsl_permutation->loc_Texture_ReflectMask     >= 0) R_Mesh_TexBind(GL20TU_REFLECTMASK       , rsurface.texture->reflectmasktexture                );
-               if (r_glsl_permutation->loc_Texture_ReflectCube     >= 0) R_Mesh_TexBind(GL20TU_REFLECTCUBE       , rsurface.texture->reflectcubetexture ? rsurface.texture->reflectcubetexture : r_texture_whitecube);
-               if (r_glsl_permutation->loc_Texture_FogMask         >= 0) R_Mesh_TexBind(GL20TU_FOGMASK           , r_texture_fogattenuation                            );
-               if (r_glsl_permutation->loc_Texture_Lightmap        >= 0) R_Mesh_TexBind(GL20TU_LIGHTMAP          , r_texture_white                                     );
-               if (r_glsl_permutation->loc_Texture_Deluxemap       >= 0) R_Mesh_TexBind(GL20TU_LIGHTMAP          , r_texture_blanknormalmap                            );
-               if (r_glsl_permutation->loc_Texture_Attenuation     >= 0) R_Mesh_TexBind(GL20TU_ATTENUATION       , r_shadow_attenuationgradienttexture                 );
-               if (r_glsl_permutation->loc_Texture_Refraction      >= 0) R_Mesh_TexBind(GL20TU_REFRACTION        , r_texture_white                                     );
-               if (r_glsl_permutation->loc_Texture_Reflection      >= 0) R_Mesh_TexBind(GL20TU_REFLECTION        , r_texture_white                                     );
-               if (r_glsl_permutation->loc_Texture_ScreenDepth     >= 0) R_Mesh_TexBind(GL20TU_SCREENDEPTH       , r_shadow_prepassgeometrydepthtexture                );
-               if (r_glsl_permutation->loc_Texture_ScreenNormalMap >= 0) R_Mesh_TexBind(GL20TU_SCREENNORMALMAP   , r_shadow_prepassgeometrynormalmaptexture            );
-               if (r_glsl_permutation->loc_Texture_ScreenDiffuse   >= 0) R_Mesh_TexBind(GL20TU_SCREENDIFFUSE     , r_shadow_prepasslightingdiffusetexture              );
-               if (r_glsl_permutation->loc_Texture_ScreenSpecular  >= 0) R_Mesh_TexBind(GL20TU_SCREENSPECULAR    , r_shadow_prepasslightingspeculartexture             );
-               if (rsurface.rtlight)
-               {
-                       if (r_glsl_permutation->loc_Texture_Cube            >= 0) R_Mesh_TexBind(GL20TU_CUBE              , rsurface.rtlight->currentcubemap                    );
-                       if (r_glsl_permutation->loc_Texture_ShadowMapRect   >= 0) R_Mesh_TexBind(GL20TU_SHADOWMAPRECT     , r_shadow_shadowmaprectangletexture                  );
-                       if (r_shadow_usingshadowmapcube)
-                               if (r_glsl_permutation->loc_Texture_ShadowMapCube   >= 0) R_Mesh_TexBind(GL20TU_SHADOWMAPCUBE     , r_shadow_shadowmapcubetexture[r_shadow_shadowmaplod]);
-                       if (r_glsl_permutation->loc_Texture_ShadowMap2D     >= 0) R_Mesh_TexBind(GL20TU_SHADOWMAP2D       , r_shadow_shadowmap2dtexture                         );
-                       if (r_glsl_permutation->loc_Texture_CubeProjection  >= 0) R_Mesh_TexBind(GL20TU_CUBEPROJECTION    , r_shadow_shadowmapvsdcttexture                      );
+                               qglUniform3f(r_glsl_permutation->loc_Color_Shirt, 0, 0, 0);
+               }
+               if (r_glsl_permutation->loc_FogPlane >= 0) qglUniform4f(r_glsl_permutation->loc_FogPlane, rsurface.fogplane[0], rsurface.fogplane[1], rsurface.fogplane[2], rsurface.fogplane[3]);
+               if (r_glsl_permutation->loc_FogPlaneViewDist >= 0) qglUniform1f(r_glsl_permutation->loc_FogPlaneViewDist, rsurface.fogplaneviewdist);
+               if (r_glsl_permutation->loc_FogRangeRecip >= 0) qglUniform1f(r_glsl_permutation->loc_FogRangeRecip, rsurface.fograngerecip);
+               if (r_glsl_permutation->loc_FogHeightFade >= 0) qglUniform1f(r_glsl_permutation->loc_FogHeightFade, rsurface.fogheightfade);
+               if (r_glsl_permutation->loc_OffsetMapping_Scale >= 0) qglUniform1f(r_glsl_permutation->loc_OffsetMapping_Scale, r_glsl_offsetmapping_scale.value*rsurface.texture->offsetscale);
+               if (r_glsl_permutation->loc_ScreenToDepth >= 0) qglUniform2f(r_glsl_permutation->loc_ScreenToDepth, r_refdef.view.viewport.screentodepth[0], r_refdef.view.viewport.screentodepth[1]);
+               if (r_glsl_permutation->loc_PixelToScreenTexCoord >= 0) qglUniform2f(r_glsl_permutation->loc_PixelToScreenTexCoord, 1.0f/vid.width, 1.0f/vid.height);
+               if (r_glsl_permutation->loc_BounceGridMatrix >= 0) {Matrix4x4_Concat(&tempmatrix, &r_shadow_bouncegridmatrix, &rsurface.matrix);Matrix4x4_ToArrayFloatGL(&tempmatrix, m16f);qglUniformMatrix4fv(r_glsl_permutation->loc_BounceGridMatrix, 1, false, m16f);}
+               if (r_glsl_permutation->loc_BounceGridIntensity >= 0) qglUniform1f(r_glsl_permutation->loc_BounceGridIntensity, r_shadow_bouncegridintensity);
+
+               if (r_glsl_permutation->tex_Texture_First           >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_First            , r_texture_white                                     );
+               if (r_glsl_permutation->tex_Texture_Second          >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_Second           , r_texture_white                                     );
+               if (r_glsl_permutation->tex_Texture_GammaRamps      >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_GammaRamps       , r_texture_gammaramps                                );
+               if (r_glsl_permutation->tex_Texture_Normal          >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_Normal           , rsurface.texture->nmaptexture                       );
+               if (r_glsl_permutation->tex_Texture_Color           >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_Color            , rsurface.texture->basetexture                       );
+               if (r_glsl_permutation->tex_Texture_Gloss           >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_Gloss            , rsurface.texture->glosstexture                      );
+               if (r_glsl_permutation->tex_Texture_Glow            >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_Glow             , rsurface.texture->glowtexture                       );
+               if (r_glsl_permutation->tex_Texture_SecondaryNormal >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_SecondaryNormal  , rsurface.texture->backgroundnmaptexture             );
+               if (r_glsl_permutation->tex_Texture_SecondaryColor  >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_SecondaryColor   , rsurface.texture->backgroundbasetexture             );
+               if (r_glsl_permutation->tex_Texture_SecondaryGloss  >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_SecondaryGloss   , rsurface.texture->backgroundglosstexture            );
+               if (r_glsl_permutation->tex_Texture_SecondaryGlow   >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_SecondaryGlow    , rsurface.texture->backgroundglowtexture             );
+               if (r_glsl_permutation->tex_Texture_Pants           >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_Pants            , rsurface.texture->pantstexture                      );
+               if (r_glsl_permutation->tex_Texture_Shirt           >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_Shirt            , rsurface.texture->shirttexture                      );
+               if (r_glsl_permutation->tex_Texture_ReflectMask     >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_ReflectMask      , rsurface.texture->reflectmasktexture                );
+               if (r_glsl_permutation->tex_Texture_ReflectCube     >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_ReflectCube      , rsurface.texture->reflectcubetexture ? rsurface.texture->reflectcubetexture : r_texture_whitecube);
+               if (r_glsl_permutation->tex_Texture_FogHeightTexture>= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_FogHeightTexture , r_texture_fogheighttexture                          );
+               if (r_glsl_permutation->tex_Texture_FogMask         >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_FogMask          , r_texture_fogattenuation                            );
+               if (r_glsl_permutation->tex_Texture_Lightmap        >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_Lightmap         , rsurface.lightmaptexture ? rsurface.lightmaptexture : r_texture_white);
+               if (r_glsl_permutation->tex_Texture_Deluxemap       >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_Deluxemap        , rsurface.deluxemaptexture ? rsurface.deluxemaptexture : r_texture_blanknormalmap);
+               if (r_glsl_permutation->tex_Texture_Attenuation     >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_Attenuation      , r_shadow_attenuationgradienttexture                 );
+               if (rsurfacepass == RSURFPASS_BACKGROUND)
+               {
+                       if (r_glsl_permutation->tex_Texture_Refraction  >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_Refraction        , waterplane->texture_refraction ? waterplane->texture_refraction : r_texture_black);
+                       if (r_glsl_permutation->tex_Texture_First       >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_First             , waterplane->texture_camera ? waterplane->texture_camera : r_texture_black);
+                       if (r_glsl_permutation->tex_Texture_Reflection  >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_Reflection        , waterplane->texture_reflection ? waterplane->texture_reflection : r_texture_black);
                }
-               CHECKGLERROR
-               break;
-       case RENDERPATH_CGGL:
-#ifdef SUPPORTCG
-               R_SetupShader_SetPermutationCG(mode, permutation);
-               if (r_cg_permutation->fp_ModelToReflectCube) {Matrix4x4_ToArrayFloatGL(&rsurface.matrix, m16f);cgGLSetMatrixParameterfc(r_cg_permutation->fp_ModelToReflectCube, m16f);}CHECKCGERROR
-               if (mode == SHADERMODE_LIGHTSOURCE)
+               else
                {
-                       if (r_cg_permutation->vp_ModelToLight) {Matrix4x4_ToArrayFloatGL(&rsurface.entitytolight, m16f);cgGLSetMatrixParameterfc(r_cg_permutation->vp_ModelToLight, m16f);}CHECKCGERROR
-                       if (r_cg_permutation->vp_LightPosition) cgGLSetParameter3f(r_cg_permutation->vp_LightPosition, rsurface.entitylightorigin[0], rsurface.entitylightorigin[1], rsurface.entitylightorigin[2]);CHECKCGERROR
+                       if (r_glsl_permutation->tex_Texture_Reflection >= 0 && waterplane) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_Reflection        , waterplane->texture_reflection ? waterplane->texture_reflection : r_texture_black);
                }
-               else
+               if (r_glsl_permutation->tex_Texture_ScreenDepth     >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_ScreenDepth       , r_shadow_prepassgeometrydepthtexture                );
+               if (r_glsl_permutation->tex_Texture_ScreenNormalMap >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_ScreenNormalMap   , r_shadow_prepassgeometrynormalmaptexture            );
+               if (r_glsl_permutation->tex_Texture_ScreenDiffuse   >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_ScreenDiffuse     , r_shadow_prepasslightingdiffusetexture              );
+               if (r_glsl_permutation->tex_Texture_ScreenSpecular  >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_ScreenSpecular    , r_shadow_prepasslightingspeculartexture             );
+               if (rsurface.rtlight || (r_shadow_usingshadowmaportho && !(rsurface.ent_flags & RENDER_NOSELFSHADOW)))
                {
-                       if (mode == SHADERMODE_LIGHTDIRECTION)
+                       if (r_glsl_permutation->tex_Texture_ShadowMap2D     >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_ShadowMap2D, r_shadow_shadowmap2dtexture                         );
+                       if (rsurface.rtlight)
                        {
-                               if (r_cg_permutation->vp_LightDir) cgGLSetParameter3f(r_cg_permutation->vp_LightDir, rsurface.modellight_lightdir[0], rsurface.modellight_lightdir[1], rsurface.modellight_lightdir[2]);CHECKCGERROR
+                               if (r_glsl_permutation->tex_Texture_Cube            >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_Cube              , rsurface.rtlight->currentcubemap                    );
+                               if (r_glsl_permutation->tex_Texture_CubeProjection  >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_CubeProjection    , r_shadow_shadowmapvsdcttexture                      );
                        }
                }
-               if (r_cg_permutation->vp_TexMatrix) {Matrix4x4_ToArrayFloatGL(&rsurface.texture->currenttexmatrix, m16f);cgGLSetMatrixParameterfc(r_cg_permutation->vp_TexMatrix, m16f);}CHECKCGERROR
-               if (r_cg_permutation->vp_BackgroundTexMatrix) {Matrix4x4_ToArrayFloatGL(&rsurface.texture->currentbackgroundtexmatrix, m16f);cgGLSetMatrixParameterfc(r_cg_permutation->vp_BackgroundTexMatrix, m16f);}CHECKCGERROR
-               if (r_cg_permutation->vp_EyePosition) cgGLSetParameter3f(r_cg_permutation->vp_EyePosition, rsurface.localvieworigin[0], rsurface.localvieworigin[1], rsurface.localvieworigin[2]);CHECKCGERROR
-               if (r_cg_permutation->vp_FogPlane) cgGLSetParameter4f(r_cg_permutation->vp_FogPlane, rsurface.fogplane[0], rsurface.fogplane[1], rsurface.fogplane[2], rsurface.fogplane[3]);CHECKCGERROR
+               if (r_glsl_permutation->tex_Texture_BounceGrid  >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_BounceGrid, r_shadow_bouncegridtexture);
                CHECKGLERROR
-
+               break;
+       case RENDERPATH_GL13:
+       case RENDERPATH_GL11:
+               break;
+       case RENDERPATH_SOFT:
+               RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_NORMAL | BATCHNEED_ARRAY_VECTOR | (rsurface.modellightmapcolor4f ? BATCHNEED_ARRAY_VERTEXCOLOR : 0) | BATCHNEED_ARRAY_TEXCOORD | (rsurface.uselightmaptexture ? BATCHNEED_ARRAY_LIGHTMAP : 0), texturenumsurfaces, texturesurfacelist);
+               R_Mesh_PrepareVertices_Mesh_Arrays(rsurface.batchnumvertices, rsurface.batchvertex3f, rsurface.batchsvector3f, rsurface.batchtvector3f, rsurface.batchnormal3f, rsurface.batchlightmapcolor4f, rsurface.batchtexcoordtexture2f, rsurface.batchtexcoordlightmap2f);
+               R_SetupShader_SetPermutationSoft(mode, permutation);
+               {Matrix4x4_ToArrayFloatGL(&rsurface.matrix, m16f);DPSOFTRAST_UniformMatrix4fv(DPSOFTRAST_UNIFORM_ModelToReflectCubeM1, 1, false, m16f);}
                if (mode == SHADERMODE_LIGHTSOURCE)
                {
-                       if (r_cg_permutation->fp_LightPosition) cgGLSetParameter3f(r_cg_permutation->fp_LightPosition, rsurface.entitylightorigin[0], rsurface.entitylightorigin[1], rsurface.entitylightorigin[2]);CHECKCGERROR
-                       if (r_cg_permutation->fp_LightColor) cgGLSetParameter3f(r_cg_permutation->fp_LightColor, lightcolorbase[0], lightcolorbase[1], lightcolorbase[2]);CHECKCGERROR
-                       if (r_cg_permutation->fp_Color_Ambient) cgGLSetParameter3f(r_cg_permutation->fp_Color_Ambient, rsurface.colormod[0] * ambientscale, rsurface.colormod[1] * ambientscale, rsurface.colormod[2] * ambientscale);CHECKCGERROR
-                       if (r_cg_permutation->fp_Color_Diffuse) cgGLSetParameter3f(r_cg_permutation->fp_Color_Diffuse, rsurface.colormod[0] * diffusescale, rsurface.colormod[1] * diffusescale, rsurface.colormod[2] * diffusescale);CHECKCGERROR
-                       if (r_cg_permutation->fp_Color_Specular) cgGLSetParameter3f(r_cg_permutation->fp_Color_Specular, r_refdef.view.colorscale * specularscale, r_refdef.view.colorscale * specularscale, r_refdef.view.colorscale * specularscale);CHECKCGERROR
-
+                       {Matrix4x4_ToArrayFloatGL(&rsurface.entitytolight, m16f);DPSOFTRAST_UniformMatrix4fv(DPSOFTRAST_UNIFORM_ModelToLightM1, 1, false, m16f);}
+                       DPSOFTRAST_Uniform3f(DPSOFTRAST_UNIFORM_LightPosition, rsurface.entitylightorigin[0], rsurface.entitylightorigin[1], rsurface.entitylightorigin[2]);
+                       DPSOFTRAST_Uniform3f(DPSOFTRAST_UNIFORM_LightColor, lightcolorbase[0], lightcolorbase[1], lightcolorbase[2]);
+                       DPSOFTRAST_Uniform3f(DPSOFTRAST_UNIFORM_Color_Ambient, colormod[0] * ambientscale, colormod[1] * ambientscale, colormod[2] * ambientscale);
+                       DPSOFTRAST_Uniform3f(DPSOFTRAST_UNIFORM_Color_Diffuse, colormod[0] * diffusescale, colormod[1] * diffusescale, colormod[2] * diffusescale);
+                       DPSOFTRAST_Uniform3f(DPSOFTRAST_UNIFORM_Color_Specular, r_refdef.view.colorscale * specularscale, r_refdef.view.colorscale * specularscale, r_refdef.view.colorscale * specularscale);
+       
                        // additive passes are only darkened by fog, not tinted
-                       if (r_cg_permutation->fp_FogColor) cgGLSetParameter3f(r_cg_permutation->fp_FogColor, 0, 0, 0);CHECKCGERROR
-                       if (r_cg_permutation->fp_ShadowMap_TextureScale) cgGLSetParameter2f(r_cg_permutation->fp_ShadowMap_TextureScale, r_shadow_shadowmap_texturescale[0], r_shadow_shadowmap_texturescale[1]);CHECKCGERROR
-                       if (r_cg_permutation->fp_ShadowMap_Parameters) cgGLSetParameter4f(r_cg_permutation->fp_ShadowMap_Parameters, r_shadow_shadowmap_parameters[0], r_shadow_shadowmap_parameters[1], r_shadow_shadowmap_parameters[2], r_shadow_shadowmap_parameters[3]);CHECKCGERROR
-                       if (r_cg_permutation->fp_SpecularPower) cgGLSetParameter1f(r_cg_permutation->fp_SpecularPower, rsurface.texture->specularpower * ((permutation & SHADERPERMUTATION_EXACTSPECULARMATH) ? 0.25f : 1.0f));CHECKCGERROR
+                       DPSOFTRAST_Uniform3f(DPSOFTRAST_UNIFORM_FogColor, 0, 0, 0);
+                       DPSOFTRAST_Uniform1f(DPSOFTRAST_UNIFORM_SpecularPower, rsurface.texture->specularpower * (r_shadow_glossexact.integer ? 0.25f : 1.0f));
                }
                else
                {
                        if (mode == SHADERMODE_FLATCOLOR)
                        {
-                               if (r_cg_permutation->fp_Color_Ambient) cgGLSetParameter3f(r_cg_permutation->fp_Color_Ambient, rsurface.colormod[0], rsurface.colormod[1], rsurface.colormod[2]);CHECKCGERROR
+                               DPSOFTRAST_Uniform3f(DPSOFTRAST_UNIFORM_Color_Ambient, colormod[0], colormod[1], colormod[2]);
                        }
                        else if (mode == SHADERMODE_LIGHTDIRECTION)
                        {
-                               if (r_cg_permutation->fp_Color_Ambient) cgGLSetParameter3f(r_cg_permutation->fp_Color_Ambient, (r_refdef.scene.ambient + rsurface.modellight_ambient[0] * r_refdef.lightmapintensity) * rsurface.colormod[0], (r_refdef.scene.ambient + rsurface.modellight_ambient[1] * r_refdef.lightmapintensity) * rsurface.colormod[1], (r_refdef.scene.ambient + rsurface.modellight_ambient[2] * r_refdef.lightmapintensity) * rsurface.colormod[2]);CHECKCGERROR
-                               if (r_cg_permutation->fp_Color_Diffuse) cgGLSetParameter3f(r_cg_permutation->fp_Color_Diffuse, r_refdef.lightmapintensity * rsurface.colormod[0], r_refdef.lightmapintensity * rsurface.colormod[1], r_refdef.lightmapintensity * rsurface.colormod[2]);CHECKCGERROR
-                               if (r_cg_permutation->fp_Color_Specular) cgGLSetParameter3f(r_cg_permutation->fp_Color_Specular, r_refdef.lightmapintensity * r_refdef.view.colorscale * specularscale, r_refdef.lightmapintensity * r_refdef.view.colorscale * specularscale, r_refdef.lightmapintensity * r_refdef.view.colorscale * specularscale);CHECKCGERROR
-                               if (r_cg_permutation->fp_DeferredMod_Diffuse) cgGLSetParameter3f(r_cg_permutation->fp_DeferredMod_Diffuse, rsurface.colormod[0] * r_shadow_deferred_8bitrange.value, rsurface.colormod[1] * r_shadow_deferred_8bitrange.value, rsurface.colormod[2] * r_shadow_deferred_8bitrange.value);CHECKCGERROR
-                               if (r_cg_permutation->fp_DeferredMod_Specular) cgGLSetParameter3f(r_cg_permutation->fp_DeferredMod_Specular, specularscale * r_shadow_deferred_8bitrange.value, specularscale * r_shadow_deferred_8bitrange.value, specularscale * r_shadow_deferred_8bitrange.value);CHECKCGERROR
-                               if (r_cg_permutation->fp_LightColor) cgGLSetParameter3f(r_cg_permutation->fp_LightColor, rsurface.modellight_diffuse[0], rsurface.modellight_diffuse[1], rsurface.modellight_diffuse[2]);CHECKCGERROR
-                               if (r_cg_permutation->fp_LightDir) cgGLSetParameter3f(r_cg_permutation->fp_LightDir, rsurface.modellight_lightdir[0], rsurface.modellight_lightdir[1], rsurface.modellight_lightdir[2]);CHECKCGERROR
+                               DPSOFTRAST_Uniform3f(DPSOFTRAST_UNIFORM_Color_Ambient, (r_refdef.scene.ambient + rsurface.modellight_ambient[0] * r_refdef.lightmapintensity * r_refdef.scene.rtlightstylevalue[0]) * colormod[0], (r_refdef.scene.ambient + rsurface.modellight_ambient[1] * r_refdef.lightmapintensity * r_refdef.scene.rtlightstylevalue[0]) * colormod[1], (r_refdef.scene.ambient + rsurface.modellight_ambient[2] * r_refdef.lightmapintensity * r_refdef.scene.rtlightstylevalue[0]) * colormod[2]);
+                               DPSOFTRAST_Uniform3f(DPSOFTRAST_UNIFORM_Color_Diffuse, r_refdef.lightmapintensity * colormod[0], r_refdef.lightmapintensity * colormod[1], r_refdef.lightmapintensity * colormod[2]);
+                               DPSOFTRAST_Uniform3f(DPSOFTRAST_UNIFORM_Color_Specular, r_refdef.lightmapintensity * r_refdef.view.colorscale * specularscale, r_refdef.lightmapintensity * r_refdef.view.colorscale * specularscale, r_refdef.lightmapintensity * r_refdef.view.colorscale * specularscale);
+                               DPSOFTRAST_Uniform3f(DPSOFTRAST_UNIFORM_DeferredMod_Diffuse, colormod[0] * r_shadow_deferred_8bitrange.value, colormod[1] * r_shadow_deferred_8bitrange.value, colormod[2] * r_shadow_deferred_8bitrange.value);
+                               DPSOFTRAST_Uniform3f(DPSOFTRAST_UNIFORM_DeferredMod_Specular, specularscale * r_shadow_deferred_8bitrange.value, specularscale * r_shadow_deferred_8bitrange.value, specularscale * r_shadow_deferred_8bitrange.value);
+                               DPSOFTRAST_Uniform3f(DPSOFTRAST_UNIFORM_LightColor, rsurface.modellight_diffuse[0] * r_refdef.scene.rtlightstylevalue[0], rsurface.modellight_diffuse[1] * r_refdef.scene.rtlightstylevalue[0], rsurface.modellight_diffuse[2] * r_refdef.scene.rtlightstylevalue[0]);
+                               DPSOFTRAST_Uniform3f(DPSOFTRAST_UNIFORM_LightDir, rsurface.modellight_lightdir[0], rsurface.modellight_lightdir[1], rsurface.modellight_lightdir[2]);
                        }
                        else
                        {
-                               if (r_cg_permutation->fp_Color_Ambient) cgGLSetParameter3f(r_cg_permutation->fp_Color_Ambient, r_refdef.scene.ambient * rsurface.colormod[0], r_refdef.scene.ambient * rsurface.colormod[1], r_refdef.scene.ambient * rsurface.colormod[2]);CHECKCGERROR
-                               if (r_cg_permutation->fp_Color_Diffuse) cgGLSetParameter3f(r_cg_permutation->fp_Color_Diffuse, rsurface.texture->lightmapcolor[0], rsurface.texture->lightmapcolor[1], rsurface.texture->lightmapcolor[2]);CHECKCGERROR
-                               if (r_cg_permutation->fp_Color_Specular) cgGLSetParameter3f(r_cg_permutation->fp_Color_Specular, r_refdef.lightmapintensity * r_refdef.view.colorscale * specularscale, r_refdef.lightmapintensity * r_refdef.view.colorscale * specularscale, r_refdef.lightmapintensity * r_refdef.view.colorscale * specularscale);CHECKCGERROR
-                               if (r_cg_permutation->fp_DeferredMod_Diffuse) cgGLSetParameter3f(r_cg_permutation->fp_DeferredMod_Diffuse, rsurface.colormod[0] * diffusescale * r_shadow_deferred_8bitrange.value, rsurface.colormod[1] * diffusescale * r_shadow_deferred_8bitrange.value, rsurface.colormod[2] * diffusescale * r_shadow_deferred_8bitrange.value);CHECKCGERROR
-                               if (r_cg_permutation->fp_DeferredMod_Specular) cgGLSetParameter3f(r_cg_permutation->fp_DeferredMod_Specular, specularscale * r_shadow_deferred_8bitrange.value, specularscale * r_shadow_deferred_8bitrange.value, specularscale * r_shadow_deferred_8bitrange.value);CHECKCGERROR
+                               DPSOFTRAST_Uniform3f(DPSOFTRAST_UNIFORM_Color_Ambient, r_refdef.scene.ambient * colormod[0], r_refdef.scene.ambient * colormod[1], r_refdef.scene.ambient * colormod[2]);
+                               DPSOFTRAST_Uniform3f(DPSOFTRAST_UNIFORM_Color_Diffuse, rsurface.texture->lightmapcolor[0], rsurface.texture->lightmapcolor[1], rsurface.texture->lightmapcolor[2]);
+                               DPSOFTRAST_Uniform3f(DPSOFTRAST_UNIFORM_Color_Specular, r_refdef.lightmapintensity * r_refdef.view.colorscale * specularscale, r_refdef.lightmapintensity * r_refdef.view.colorscale * specularscale, r_refdef.lightmapintensity * r_refdef.view.colorscale * specularscale);
+                               DPSOFTRAST_Uniform3f(DPSOFTRAST_UNIFORM_DeferredMod_Diffuse, colormod[0] * diffusescale * r_shadow_deferred_8bitrange.value, colormod[1] * diffusescale * r_shadow_deferred_8bitrange.value, colormod[2] * diffusescale * r_shadow_deferred_8bitrange.value);
+                               DPSOFTRAST_Uniform3f(DPSOFTRAST_UNIFORM_DeferredMod_Specular, specularscale * r_shadow_deferred_8bitrange.value, specularscale * r_shadow_deferred_8bitrange.value, specularscale * r_shadow_deferred_8bitrange.value);
                        }
                        // additive passes are only darkened by fog, not tinted
-                       if (r_cg_permutation->fp_FogColor)
-                       {
-                               if (rsurface.texture->currentmaterialflags & MATERIALFLAG_ADD)
-                                       cgGLSetParameter3f(r_cg_permutation->fp_FogColor, 0, 0, 0);
-                               else
-                                       cgGLSetParameter3f(r_cg_permutation->fp_FogColor, r_refdef.fogcolor[0], r_refdef.fogcolor[1], r_refdef.fogcolor[2]);
-                               CHECKCGERROR
-                       }
-                       if (r_cg_permutation->fp_DistortScaleRefractReflect) cgGLSetParameter4f(r_cg_permutation->fp_DistortScaleRefractReflect, r_water_refractdistort.value * rsurface.texture->refractfactor, r_water_refractdistort.value * rsurface.texture->refractfactor, r_water_reflectdistort.value * rsurface.texture->reflectfactor, r_water_reflectdistort.value * rsurface.texture->reflectfactor);CHECKCGERROR
-                       if (r_cg_permutation->fp_ScreenScaleRefractReflect) cgGLSetParameter4f(r_cg_permutation->fp_ScreenScaleRefractReflect, r_waterstate.screenscale[0], r_waterstate.screenscale[1], r_waterstate.screenscale[0], r_waterstate.screenscale[1]);CHECKCGERROR
-                       if (r_cg_permutation->fp_ScreenCenterRefractReflect) cgGLSetParameter4f(r_cg_permutation->fp_ScreenCenterRefractReflect, r_waterstate.screencenter[0], r_waterstate.screencenter[1], r_waterstate.screencenter[0], r_waterstate.screencenter[1]);CHECKCGERROR
-                       if (r_cg_permutation->fp_RefractColor) cgGLSetParameter4fv(r_cg_permutation->fp_RefractColor, rsurface.texture->refractcolor4f);CHECKCGERROR
-                       if (r_cg_permutation->fp_ReflectColor) cgGLSetParameter4fv(r_cg_permutation->fp_ReflectColor, rsurface.texture->reflectcolor4f);CHECKCGERROR
-                       if (r_cg_permutation->fp_ReflectFactor) cgGLSetParameter1f(r_cg_permutation->fp_ReflectFactor, rsurface.texture->reflectmax - rsurface.texture->reflectmin);CHECKCGERROR
-                       if (r_cg_permutation->fp_ReflectOffset) cgGLSetParameter1f(r_cg_permutation->fp_ReflectOffset, rsurface.texture->reflectmin);CHECKCGERROR
-                       if (r_cg_permutation->fp_SpecularPower) cgGLSetParameter1f(r_cg_permutation->fp_SpecularPower, rsurface.texture->specularpower * ((permutation & SHADERPERMUTATION_EXACTSPECULARMATH) ? 0.25f : 1.0f));CHECKCGERROR
-               }
-               if (r_cg_permutation->fp_Color_Glow) cgGLSetParameter3f(r_cg_permutation->fp_Color_Glow, rsurface.glowmod[0], rsurface.glowmod[1], rsurface.glowmod[2]);CHECKCGERROR
-               if (r_cg_permutation->fp_Alpha) cgGLSetParameter1f(r_cg_permutation->fp_Alpha, rsurface.texture->lightmapcolor[3]);CHECKCGERROR
-               if (r_cg_permutation->fp_EyePosition) cgGLSetParameter3f(r_cg_permutation->fp_EyePosition, rsurface.localvieworigin[0], rsurface.localvieworigin[1], rsurface.localvieworigin[2]);CHECKCGERROR
-               if (r_cg_permutation->fp_Color_Pants)
+                       if(blendfuncflags & BLENDFUNC_ALLOWS_FOG_HACK0)
+                               DPSOFTRAST_Uniform3f(DPSOFTRAST_UNIFORM_FogColor, 0, 0, 0);
+                       else
+                               DPSOFTRAST_Uniform3f(DPSOFTRAST_UNIFORM_FogColor, r_refdef.fogcolor[0], r_refdef.fogcolor[1], r_refdef.fogcolor[2]);
+                       DPSOFTRAST_Uniform4f(DPSOFTRAST_UNIFORM_DistortScaleRefractReflect, r_water_refractdistort.value * rsurface.texture->refractfactor, r_water_refractdistort.value * rsurface.texture->refractfactor, r_water_reflectdistort.value * rsurface.texture->reflectfactor, r_water_reflectdistort.value * rsurface.texture->reflectfactor);
+                       DPSOFTRAST_Uniform4f(DPSOFTRAST_UNIFORM_ScreenScaleRefractReflect, r_waterstate.screenscale[0], r_waterstate.screenscale[1], r_waterstate.screenscale[0], r_waterstate.screenscale[1]);
+                       DPSOFTRAST_Uniform4f(DPSOFTRAST_UNIFORM_ScreenCenterRefractReflect, r_waterstate.screencenter[0], r_waterstate.screencenter[1], r_waterstate.screencenter[0], r_waterstate.screencenter[1]);
+                       DPSOFTRAST_Uniform4f(DPSOFTRAST_UNIFORM_RefractColor, rsurface.texture->refractcolor4f[0], rsurface.texture->refractcolor4f[1], rsurface.texture->refractcolor4f[2], rsurface.texture->refractcolor4f[3] * rsurface.texture->lightmapcolor[3]);
+                       DPSOFTRAST_Uniform4f(DPSOFTRAST_UNIFORM_ReflectColor, rsurface.texture->reflectcolor4f[0], rsurface.texture->reflectcolor4f[1], rsurface.texture->reflectcolor4f[2], rsurface.texture->reflectcolor4f[3] * rsurface.texture->lightmapcolor[3]);
+                       DPSOFTRAST_Uniform1f(DPSOFTRAST_UNIFORM_ReflectFactor, rsurface.texture->reflectmax - rsurface.texture->reflectmin);
+                       DPSOFTRAST_Uniform1f(DPSOFTRAST_UNIFORM_ReflectOffset, rsurface.texture->reflectmin);
+                       DPSOFTRAST_Uniform1f(DPSOFTRAST_UNIFORM_SpecularPower, rsurface.texture->specularpower * (r_shadow_glossexact.integer ? 0.25f : 1.0f));
+                       DPSOFTRAST_Uniform2f(DPSOFTRAST_UNIFORM_NormalmapScrollBlend, rsurface.texture->r_water_waterscroll[0], rsurface.texture->r_water_waterscroll[1]);
+               }
+               {Matrix4x4_ToArrayFloatGL(&rsurface.texture->currenttexmatrix, m16f);DPSOFTRAST_UniformMatrix4fv(DPSOFTRAST_UNIFORM_TexMatrixM1, 1, false, m16f);}
+               {Matrix4x4_ToArrayFloatGL(&rsurface.texture->currentbackgroundtexmatrix, m16f);DPSOFTRAST_UniformMatrix4fv(DPSOFTRAST_UNIFORM_BackgroundTexMatrixM1, 1, false, m16f);}
+               {Matrix4x4_ToArrayFloatGL(&r_shadow_shadowmapmatrix, m16f);DPSOFTRAST_UniformMatrix4fv(DPSOFTRAST_UNIFORM_ShadowMapMatrixM1, 1, false, m16f);}
+               DPSOFTRAST_Uniform2f(DPSOFTRAST_UNIFORM_ShadowMap_TextureScale, r_shadow_shadowmap_texturescale[0], r_shadow_shadowmap_texturescale[1]);
+               DPSOFTRAST_Uniform4f(DPSOFTRAST_UNIFORM_ShadowMap_Parameters, r_shadow_shadowmap_parameters[0], r_shadow_shadowmap_parameters[1], r_shadow_shadowmap_parameters[2], r_shadow_shadowmap_parameters[3]);
+
+               DPSOFTRAST_Uniform3f(DPSOFTRAST_UNIFORM_Color_Glow, rsurface.glowmod[0], rsurface.glowmod[1], rsurface.glowmod[2]);
+               DPSOFTRAST_Uniform1f(DPSOFTRAST_UNIFORM_Alpha, rsurface.texture->lightmapcolor[3] * ((rsurface.texture->basematerialflags & MATERIALFLAG_WATERSHADER && r_waterstate.enabled && !r_refdef.view.isoverlay) ? rsurface.texture->r_water_wateralpha : 1));
+               DPSOFTRAST_Uniform3f(DPSOFTRAST_UNIFORM_EyePosition, rsurface.localvieworigin[0], rsurface.localvieworigin[1], rsurface.localvieworigin[2]);
+               if (DPSOFTRAST_UNIFORM_Color_Pants >= 0)
                {
                        if (rsurface.texture->pantstexture)
-                               cgGLSetParameter3f(r_cg_permutation->fp_Color_Pants, rsurface.colormap_pantscolor[0], rsurface.colormap_pantscolor[1], rsurface.colormap_pantscolor[2]);
+                               DPSOFTRAST_Uniform3f(DPSOFTRAST_UNIFORM_Color_Pants, rsurface.colormap_pantscolor[0], rsurface.colormap_pantscolor[1], rsurface.colormap_pantscolor[2]);
                        else
-                               cgGLSetParameter3f(r_cg_permutation->fp_Color_Pants, 0, 0, 0);
-                       CHECKCGERROR
+                               DPSOFTRAST_Uniform3f(DPSOFTRAST_UNIFORM_Color_Pants, 0, 0, 0);
                }
-               if (r_cg_permutation->fp_Color_Shirt)
+               if (DPSOFTRAST_UNIFORM_Color_Shirt >= 0)
                {
                        if (rsurface.texture->shirttexture)
-                               cgGLSetParameter3f(r_cg_permutation->fp_Color_Shirt, rsurface.colormap_shirtcolor[0], rsurface.colormap_shirtcolor[1], rsurface.colormap_shirtcolor[2]);
+                               DPSOFTRAST_Uniform3f(DPSOFTRAST_UNIFORM_Color_Shirt, rsurface.colormap_shirtcolor[0], rsurface.colormap_shirtcolor[1], rsurface.colormap_shirtcolor[2]);
                        else
-                               cgGLSetParameter3f(r_cg_permutation->fp_Color_Shirt, 0, 0, 0);
-                       CHECKCGERROR
-               }
-               if (r_cg_permutation->fp_FogPlane) cgGLSetParameter4f(r_cg_permutation->fp_FogPlane, rsurface.fogplane[0], rsurface.fogplane[1], rsurface.fogplane[2], rsurface.fogplane[3]);CHECKCGERROR
-               if (r_cg_permutation->fp_FogPlaneViewDist) cgGLSetParameter1f(r_cg_permutation->fp_FogPlaneViewDist, rsurface.fogplaneviewdist);CHECKCGERROR
-               if (r_cg_permutation->fp_FogRangeRecip) cgGLSetParameter1f(r_cg_permutation->fp_FogRangeRecip, rsurface.fograngerecip);CHECKCGERROR
-               if (r_cg_permutation->fp_FogHeightFade) cgGLSetParameter1f(r_cg_permutation->fp_FogHeightFade, rsurface.fogheightfade);CHECKCGERROR
-               if (r_cg_permutation->fp_OffsetMapping_Scale) cgGLSetParameter1f(r_cg_permutation->fp_OffsetMapping_Scale, r_glsl_offsetmapping_scale.value);CHECKCGERROR
-               if (r_cg_permutation->fp_ScreenToDepth) cgGLSetParameter2f(r_cg_permutation->fp_ScreenToDepth, r_refdef.view.viewport.screentodepth[0], r_refdef.view.viewport.screentodepth[1]);CHECKCGERROR
-               if (r_cg_permutation->fp_PixelToScreenTexCoord) cgGLSetParameter2f(r_cg_permutation->fp_PixelToScreenTexCoord, 1.0f/vid.width, 1.0/vid.height);CHECKCGERROR
-
-       //      if (r_cg_permutation->fp_Texture_First          ) CG_BindTexture(r_cg_permutation->fp_Texture_First          , r_texture_white                                     );CHECKCGERROR
-       //      if (r_cg_permutation->fp_Texture_Second         ) CG_BindTexture(r_cg_permutation->fp_Texture_Second         , r_texture_white                                     );CHECKCGERROR
-       //      if (r_cg_permutation->fp_Texture_GammaRamps     ) CG_BindTexture(r_cg_permutation->fp_Texture_GammaRamps     , r_texture_gammaramps                                );CHECKCGERROR
-               if (r_cg_permutation->fp_Texture_Normal         ) CG_BindTexture(r_cg_permutation->fp_Texture_Normal         , rsurface.texture->nmaptexture                       );CHECKCGERROR
-               if (r_cg_permutation->fp_Texture_Color          ) CG_BindTexture(r_cg_permutation->fp_Texture_Color          , rsurface.texture->basetexture                       );CHECKCGERROR
-               if (r_cg_permutation->fp_Texture_Gloss          ) CG_BindTexture(r_cg_permutation->fp_Texture_Gloss          , rsurface.texture->glosstexture                      );CHECKCGERROR
-               if (r_cg_permutation->fp_Texture_Glow           ) CG_BindTexture(r_cg_permutation->fp_Texture_Glow           , rsurface.texture->glowtexture                       );CHECKCGERROR
-               if (r_cg_permutation->fp_Texture_SecondaryNormal) CG_BindTexture(r_cg_permutation->fp_Texture_SecondaryNormal, rsurface.texture->backgroundnmaptexture             );CHECKCGERROR
-               if (r_cg_permutation->fp_Texture_SecondaryColor ) CG_BindTexture(r_cg_permutation->fp_Texture_SecondaryColor , rsurface.texture->backgroundbasetexture             );CHECKCGERROR
-               if (r_cg_permutation->fp_Texture_SecondaryGloss ) CG_BindTexture(r_cg_permutation->fp_Texture_SecondaryGloss , rsurface.texture->backgroundglosstexture            );CHECKCGERROR
-               if (r_cg_permutation->fp_Texture_SecondaryGlow  ) CG_BindTexture(r_cg_permutation->fp_Texture_SecondaryGlow  , rsurface.texture->backgroundglowtexture             );CHECKCGERROR
-               if (r_cg_permutation->fp_Texture_Pants          ) CG_BindTexture(r_cg_permutation->fp_Texture_Pants          , rsurface.texture->pantstexture                      );CHECKCGERROR
-               if (r_cg_permutation->fp_Texture_Shirt          ) CG_BindTexture(r_cg_permutation->fp_Texture_Shirt          , rsurface.texture->shirttexture                      );CHECKCGERROR
-               if (r_cg_permutation->fp_Texture_ReflectMask    ) CG_BindTexture(r_cg_permutation->fp_Texture_ReflectMask    , rsurface.texture->reflectmasktexture                );CHECKCGERROR
-               if (r_cg_permutation->fp_Texture_ReflectCube    ) CG_BindTexture(r_cg_permutation->fp_Texture_ReflectCube    , rsurface.texture->reflectcubetexture ? rsurface.texture->reflectcubetexture : r_texture_whitecube);CHECKCGERROR
-               if (r_cg_permutation->fp_Texture_FogMask        ) CG_BindTexture(r_cg_permutation->fp_Texture_FogMask        , r_texture_fogattenuation                            );CHECKCGERROR
-               if (r_cg_permutation->fp_Texture_Lightmap       ) CG_BindTexture(r_cg_permutation->fp_Texture_Lightmap       , r_texture_white                                     );CHECKCGERROR
-               if (r_cg_permutation->fp_Texture_Deluxemap      ) CG_BindTexture(r_cg_permutation->fp_Texture_Deluxemap      , r_texture_blanknormalmap                            );CHECKCGERROR
-               if (r_cg_permutation->fp_Texture_Attenuation    ) CG_BindTexture(r_cg_permutation->fp_Texture_Attenuation    , r_shadow_attenuationgradienttexture                 );CHECKCGERROR
-               if (r_cg_permutation->fp_Texture_Refraction     ) CG_BindTexture(r_cg_permutation->fp_Texture_Refraction     , r_texture_white                                     );CHECKCGERROR
-               if (r_cg_permutation->fp_Texture_Reflection     ) CG_BindTexture(r_cg_permutation->fp_Texture_Reflection     , r_texture_white                                     );CHECKCGERROR
-               if (r_cg_permutation->fp_Texture_ScreenDepth    ) CG_BindTexture(r_cg_permutation->fp_Texture_ScreenDepth    , r_shadow_prepassgeometrydepthtexture                );CHECKCGERROR
-               if (r_cg_permutation->fp_Texture_ScreenNormalMap) CG_BindTexture(r_cg_permutation->fp_Texture_ScreenNormalMap, r_shadow_prepassgeometrynormalmaptexture            );CHECKCGERROR
-               if (r_cg_permutation->fp_Texture_ScreenDiffuse  ) CG_BindTexture(r_cg_permutation->fp_Texture_ScreenDiffuse  , r_shadow_prepasslightingdiffusetexture              );CHECKCGERROR
-               if (r_cg_permutation->fp_Texture_ScreenSpecular ) CG_BindTexture(r_cg_permutation->fp_Texture_ScreenSpecular , r_shadow_prepasslightingspeculartexture             );CHECKCGERROR
-               if (rsurface.rtlight)
-               {
-                       if (r_cg_permutation->fp_Texture_Cube           ) CG_BindTexture(r_cg_permutation->fp_Texture_Cube           , rsurface.rtlight->currentcubemap                    );CHECKCGERROR
-                       if (r_cg_permutation->fp_Texture_ShadowMapRect  ) CG_BindTexture(r_cg_permutation->fp_Texture_ShadowMapRect  , r_shadow_shadowmaprectangletexture                  );CHECKCGERROR
-                       if (r_shadow_usingshadowmapcube)
-                               if (r_cg_permutation->fp_Texture_ShadowMapCube  ) CG_BindTexture(r_cg_permutation->fp_Texture_ShadowMapCube  , r_shadow_shadowmapcubetexture[r_shadow_shadowmaplod]);CHECKCGERROR
-                       if (r_cg_permutation->fp_Texture_ShadowMap2D    ) CG_BindTexture(r_cg_permutation->fp_Texture_ShadowMap2D    , r_shadow_shadowmap2dtexture                         );CHECKCGERROR
-                       if (r_cg_permutation->fp_Texture_CubeProjection ) CG_BindTexture(r_cg_permutation->fp_Texture_CubeProjection , r_shadow_shadowmapvsdcttexture                      );CHECKCGERROR
+                               DPSOFTRAST_Uniform3f(DPSOFTRAST_UNIFORM_Color_Shirt, 0, 0, 0);
+               }
+               DPSOFTRAST_Uniform4f(DPSOFTRAST_UNIFORM_FogPlane, rsurface.fogplane[0], rsurface.fogplane[1], rsurface.fogplane[2], rsurface.fogplane[3]);
+               DPSOFTRAST_Uniform1f(DPSOFTRAST_UNIFORM_FogPlaneViewDist, rsurface.fogplaneviewdist);
+               DPSOFTRAST_Uniform1f(DPSOFTRAST_UNIFORM_FogRangeRecip, rsurface.fograngerecip);
+               DPSOFTRAST_Uniform1f(DPSOFTRAST_UNIFORM_FogHeightFade, rsurface.fogheightfade);
+               DPSOFTRAST_Uniform1f(DPSOFTRAST_UNIFORM_OffsetMapping_Scale, r_glsl_offsetmapping_scale.value*rsurface.texture->offsetscale);
+               DPSOFTRAST_Uniform2f(DPSOFTRAST_UNIFORM_ScreenToDepth, r_refdef.view.viewport.screentodepth[0], r_refdef.view.viewport.screentodepth[1]);
+               DPSOFTRAST_Uniform2f(DPSOFTRAST_UNIFORM_PixelToScreenTexCoord, 1.0f/vid.width, 1.0f/vid.height);
+
+               R_Mesh_TexBind(GL20TU_NORMAL            , rsurface.texture->nmaptexture                       );
+               R_Mesh_TexBind(GL20TU_COLOR             , rsurface.texture->basetexture                       );
+               R_Mesh_TexBind(GL20TU_GLOSS             , rsurface.texture->glosstexture                      );
+               R_Mesh_TexBind(GL20TU_GLOW              , rsurface.texture->glowtexture                       );
+               if (permutation & SHADERPERMUTATION_VERTEXTEXTUREBLEND) R_Mesh_TexBind(GL20TU_SECONDARY_NORMAL  , rsurface.texture->backgroundnmaptexture             );
+               if (permutation & SHADERPERMUTATION_VERTEXTEXTUREBLEND) R_Mesh_TexBind(GL20TU_SECONDARY_COLOR   , rsurface.texture->backgroundbasetexture             );
+               if (permutation & SHADERPERMUTATION_VERTEXTEXTUREBLEND) R_Mesh_TexBind(GL20TU_SECONDARY_GLOSS   , rsurface.texture->backgroundglosstexture            );
+               if (permutation & SHADERPERMUTATION_VERTEXTEXTUREBLEND) R_Mesh_TexBind(GL20TU_SECONDARY_GLOW    , rsurface.texture->backgroundglowtexture             );
+               if (permutation & SHADERPERMUTATION_COLORMAPPING) R_Mesh_TexBind(GL20TU_PANTS             , rsurface.texture->pantstexture                      );
+               if (permutation & SHADERPERMUTATION_COLORMAPPING) R_Mesh_TexBind(GL20TU_SHIRT             , rsurface.texture->shirttexture                      );
+               if (permutation & SHADERPERMUTATION_REFLECTCUBE) R_Mesh_TexBind(GL20TU_REFLECTMASK       , rsurface.texture->reflectmasktexture                );
+               if (permutation & SHADERPERMUTATION_REFLECTCUBE) R_Mesh_TexBind(GL20TU_REFLECTCUBE       , rsurface.texture->reflectcubetexture ? rsurface.texture->reflectcubetexture : r_texture_whitecube);
+               if (permutation & SHADERPERMUTATION_FOGHEIGHTTEXTURE) R_Mesh_TexBind(GL20TU_FOGHEIGHTTEXTURE  , r_texture_fogheighttexture                          );
+               if (permutation & (SHADERPERMUTATION_FOGINSIDE | SHADERPERMUTATION_FOGOUTSIDE)) R_Mesh_TexBind(GL20TU_FOGMASK           , r_texture_fogattenuation                            );
+               R_Mesh_TexBind(GL20TU_LIGHTMAP          , rsurface.lightmaptexture ? rsurface.lightmaptexture : r_texture_white);
+               R_Mesh_TexBind(GL20TU_DELUXEMAP         , rsurface.deluxemaptexture ? rsurface.deluxemaptexture : r_texture_blanknormalmap);
+               if (rsurface.rtlight                                  ) R_Mesh_TexBind(GL20TU_ATTENUATION       , r_shadow_attenuationgradienttexture                 );
+               if (rsurfacepass == RSURFPASS_BACKGROUND)
+               {
+                       R_Mesh_TexBind(GL20TU_REFRACTION        , waterplane->texture_refraction ? waterplane->texture_refraction : r_texture_black);
+                       if(mode == SHADERMODE_GENERIC) R_Mesh_TexBind(GL20TU_FIRST             , waterplane->texture_camera ? waterplane->texture_camera : r_texture_black);
+                       R_Mesh_TexBind(GL20TU_REFLECTION        , waterplane->texture_reflection ? waterplane->texture_reflection : r_texture_black);
+               }
+               else
+               {
+                       if (permutation & SHADERPERMUTATION_REFLECTION        ) R_Mesh_TexBind(GL20TU_REFLECTION        , waterplane->texture_reflection ? waterplane->texture_reflection : r_texture_black);
+               }
+//             if (rsurfacepass == RSURFPASS_DEFERREDLIGHT           ) R_Mesh_TexBind(GL20TU_SCREENDEPTH       , r_shadow_prepassgeometrydepthtexture                );
+//             if (rsurfacepass == RSURFPASS_DEFERREDLIGHT           ) R_Mesh_TexBind(GL20TU_SCREENNORMALMAP   , r_shadow_prepassgeometrynormalmaptexture            );
+               if (permutation & SHADERPERMUTATION_DEFERREDLIGHTMAP  ) R_Mesh_TexBind(GL20TU_SCREENDIFFUSE     , r_shadow_prepasslightingdiffusetexture              );
+               if (permutation & SHADERPERMUTATION_DEFERREDLIGHTMAP  ) R_Mesh_TexBind(GL20TU_SCREENSPECULAR    , r_shadow_prepasslightingspeculartexture             );
+               if (rsurface.rtlight || (r_shadow_usingshadowmaportho && !(rsurface.ent_flags & RENDER_NOSELFSHADOW)))
+               {
+                       R_Mesh_TexBind(GL20TU_SHADOWMAP2D, r_shadow_shadowmap2dcolortexture);
+                       if (rsurface.rtlight)
+                       {
+                               if (permutation & SHADERPERMUTATION_CUBEFILTER        ) R_Mesh_TexBind(GL20TU_CUBE              , rsurface.rtlight->currentcubemap                    );
+                               if (permutation & SHADERPERMUTATION_SHADOWMAPVSDCT    ) R_Mesh_TexBind(GL20TU_CUBEPROJECTION    , r_shadow_shadowmapvsdcttexture                      );
+                       }
                }
-
-               CHECKGLERROR
-#endif
-               break;
-       case RENDERPATH_GL13:
-       case RENDERPATH_GL11:
                break;
        }
 }
@@ -5060,20 +2865,11 @@ void R_SetupShader_DeferredLight(const rtlight_t *rtlight)
        if (diffusescale > 0)
                permutation |= SHADERPERMUTATION_DIFFUSE;
        if (specularscale > 0)
-       {
                permutation |= SHADERPERMUTATION_SPECULAR | SHADERPERMUTATION_DIFFUSE;
-               if (r_shadow_glossexact.integer)
-                       permutation |= SHADERPERMUTATION_EXACTSPECULARMATH;
-       }
-       if (r_shadow_usingshadowmaprect || r_shadow_usingshadowmap2d || r_shadow_usingshadowmapcube)
+       if (r_shadow_usingshadowmap2d)
        {
-               if (r_shadow_usingshadowmaprect)
-                       permutation |= SHADERPERMUTATION_SHADOWMAPRECT;
-               if (r_shadow_usingshadowmap2d)
-                       permutation |= SHADERPERMUTATION_SHADOWMAP2D;
-               if (r_shadow_usingshadowmapcube)
-                       permutation |= SHADERPERMUTATION_SHADOWMAPCUBE;
-               else if(r_shadow_shadowmapvsdct)
+               permutation |= SHADERPERMUTATION_SHADOWMAP2D;
+               if (r_shadow_shadowmapvsdct)
                        permutation |= SHADERPERMUTATION_SHADOWMAPVSDCT;
 
                if (r_shadow_shadowmapsampler)
@@ -5089,57 +2885,78 @@ void R_SetupShader_DeferredLight(const rtlight_t *rtlight)
        Matrix4x4_ToArrayFloatGL(&viewtolight, viewtolight16f);
        switch(vid.renderpath)
        {
+       case RENDERPATH_D3D9:
+#ifdef SUPPORTD3D
+               R_SetupShader_SetPermutationHLSL(mode, permutation);
+               hlslPSSetParameter3f(D3DPSREGISTER_LightPosition, viewlightorigin[0], viewlightorigin[1], viewlightorigin[2]);
+               hlslPSSetParameter16f(D3DPSREGISTER_ViewToLight, viewtolight16f);
+               hlslPSSetParameter3f(D3DPSREGISTER_DeferredColor_Ambient , lightcolorbase[0] * ambientscale  * range, lightcolorbase[1] * ambientscale  * range, lightcolorbase[2] * ambientscale  * range);
+               hlslPSSetParameter3f(D3DPSREGISTER_DeferredColor_Diffuse , lightcolorbase[0] * diffusescale  * range, lightcolorbase[1] * diffusescale  * range, lightcolorbase[2] * diffusescale  * range);
+               hlslPSSetParameter3f(D3DPSREGISTER_DeferredColor_Specular, lightcolorbase[0] * specularscale * range, lightcolorbase[1] * specularscale * range, lightcolorbase[2] * specularscale * range);
+               hlslPSSetParameter2f(D3DPSREGISTER_ShadowMap_TextureScale, r_shadow_shadowmap_texturescale[0], r_shadow_shadowmap_texturescale[1]);
+               hlslPSSetParameter4f(D3DPSREGISTER_ShadowMap_Parameters, r_shadow_shadowmap_parameters[0], r_shadow_shadowmap_parameters[1], r_shadow_shadowmap_parameters[2], r_shadow_shadowmap_parameters[3]);
+               hlslPSSetParameter1f(D3DPSREGISTER_SpecularPower, (r_shadow_gloss.integer == 2 ? r_shadow_gloss2exponent.value : r_shadow_glossexponent.value) * (r_shadow_glossexact.integer ? 0.25f : 1.0f));
+               hlslPSSetParameter2f(D3DPSREGISTER_ScreenToDepth, r_refdef.view.viewport.screentodepth[0], r_refdef.view.viewport.screentodepth[1]);
+               hlslPSSetParameter2f(D3DPSREGISTER_PixelToScreenTexCoord, 1.0f/vid.width, 1.0/vid.height);
+
+               R_Mesh_TexBind(GL20TU_ATTENUATION        , r_shadow_attenuationgradienttexture                 );
+               R_Mesh_TexBind(GL20TU_SCREENDEPTH        , r_shadow_prepassgeometrydepthcolortexture           );
+               R_Mesh_TexBind(GL20TU_SCREENNORMALMAP    , r_shadow_prepassgeometrynormalmaptexture            );
+               R_Mesh_TexBind(GL20TU_CUBE               , rsurface.rtlight->currentcubemap                    );
+               R_Mesh_TexBind(GL20TU_SHADOWMAP2D        , r_shadow_shadowmap2dcolortexture                    );
+               R_Mesh_TexBind(GL20TU_CUBEPROJECTION     , r_shadow_shadowmapvsdcttexture                      );
+#endif
+               break;
+       case RENDERPATH_D3D10:
+               Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               break;
+       case RENDERPATH_D3D11:
+               Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               break;
        case RENDERPATH_GL20:
+       case RENDERPATH_GLES2:
                R_SetupShader_SetPermutationGLSL(mode, permutation);
-               if (r_glsl_permutation->loc_LightPosition             >= 0) qglUniform3fARB(       r_glsl_permutation->loc_LightPosition            , viewlightorigin[0], viewlightorigin[1], viewlightorigin[2]);
-               if (r_glsl_permutation->loc_ViewToLight               >= 0) qglUniformMatrix4fvARB(r_glsl_permutation->loc_ViewToLight              , 1, false, viewtolight16f);
-               if (r_glsl_permutation->loc_DeferredColor_Ambient     >= 0) qglUniform3fARB(       r_glsl_permutation->loc_DeferredColor_Ambient    , lightcolorbase[0] * ambientscale  * range, lightcolorbase[1] * ambientscale  * range, lightcolorbase[2] * ambientscale  * range);
-               if (r_glsl_permutation->loc_DeferredColor_Diffuse     >= 0) qglUniform3fARB(       r_glsl_permutation->loc_DeferredColor_Diffuse    , lightcolorbase[0] * diffusescale  * range, lightcolorbase[1] * diffusescale  * range, lightcolorbase[2] * diffusescale  * range);
-               if (r_glsl_permutation->loc_DeferredColor_Specular    >= 0) qglUniform3fARB(       r_glsl_permutation->loc_DeferredColor_Specular   , lightcolorbase[0] * specularscale * range, lightcolorbase[1] * specularscale * range, lightcolorbase[2] * specularscale * range);
-               if (r_glsl_permutation->loc_ShadowMap_TextureScale    >= 0) qglUniform2fARB(       r_glsl_permutation->loc_ShadowMap_TextureScale   , r_shadow_shadowmap_texturescale[0], r_shadow_shadowmap_texturescale[1]);
-               if (r_glsl_permutation->loc_ShadowMap_Parameters      >= 0) qglUniform4fARB(       r_glsl_permutation->loc_ShadowMap_Parameters     , r_shadow_shadowmap_parameters[0], r_shadow_shadowmap_parameters[1], r_shadow_shadowmap_parameters[2], r_shadow_shadowmap_parameters[3]);
-               if (r_glsl_permutation->loc_SpecularPower             >= 0) qglUniform1fARB(       r_glsl_permutation->loc_SpecularPower            , (r_shadow_gloss.integer == 2 ? r_shadow_gloss2exponent.value : r_shadow_glossexponent.value) * ((permutation & SHADERPERMUTATION_EXACTSPECULARMATH) ? 0.25f : 1.0f));
-               if (r_glsl_permutation->loc_ScreenToDepth             >= 0) qglUniform2fARB(       r_glsl_permutation->loc_ScreenToDepth            , r_refdef.view.viewport.screentodepth[0], r_refdef.view.viewport.screentodepth[1]);
-               if (r_glsl_permutation->loc_PixelToScreenTexCoord >= 0) qglUniform2fARB(r_glsl_permutation->loc_PixelToScreenTexCoord, 1.0f/vid.width, 1.0f/vid.height);
-
-               if (r_glsl_permutation->loc_Texture_Attenuation       >= 0) R_Mesh_TexBind(GL20TU_ATTENUATION        , r_shadow_attenuationgradienttexture                 );
-               if (r_glsl_permutation->loc_Texture_ScreenDepth       >= 0) R_Mesh_TexBind(GL20TU_SCREENDEPTH        , r_shadow_prepassgeometrydepthtexture                );
-               if (r_glsl_permutation->loc_Texture_ScreenNormalMap   >= 0) R_Mesh_TexBind(GL20TU_SCREENNORMALMAP    , r_shadow_prepassgeometrynormalmaptexture            );
-               if (r_glsl_permutation->loc_Texture_Cube              >= 0) R_Mesh_TexBind(GL20TU_CUBE               , rsurface.rtlight->currentcubemap                    );
-               if (r_glsl_permutation->loc_Texture_ShadowMapRect     >= 0) R_Mesh_TexBind(GL20TU_SHADOWMAPRECT      , r_shadow_shadowmaprectangletexture                  );
-               if (r_shadow_usingshadowmapcube)
-                       if (r_glsl_permutation->loc_Texture_ShadowMapCube     >= 0) R_Mesh_TexBind(GL20TU_SHADOWMAPCUBE      , r_shadow_shadowmapcubetexture[r_shadow_shadowmaplod]);
-               if (r_glsl_permutation->loc_Texture_ShadowMap2D       >= 0) R_Mesh_TexBind(GL20TU_SHADOWMAP2D        , r_shadow_shadowmap2dtexture                         );
-               if (r_glsl_permutation->loc_Texture_CubeProjection    >= 0) R_Mesh_TexBind(GL20TU_CUBEPROJECTION     , r_shadow_shadowmapvsdcttexture                      );
-               break;
-       case RENDERPATH_CGGL:
-#ifdef SUPPORTCG
-               R_SetupShader_SetPermutationCG(mode, permutation);
-               if (r_cg_permutation->fp_LightPosition            ) cgGLSetParameter3f(r_cg_permutation->fp_LightPosition, viewlightorigin[0], viewlightorigin[1], viewlightorigin[2]);CHECKCGERROR
-               if (r_cg_permutation->fp_ViewToLight              ) cgGLSetMatrixParameterfc(r_cg_permutation->fp_ViewToLight, viewtolight16f);CHECKCGERROR
-               if (r_cg_permutation->fp_DeferredColor_Ambient    ) cgGLSetParameter3f(r_cg_permutation->fp_DeferredColor_Ambient , lightcolorbase[0] * ambientscale  * range, lightcolorbase[1] * ambientscale  * range, lightcolorbase[2] * ambientscale  * range);CHECKCGERROR
-               if (r_cg_permutation->fp_DeferredColor_Diffuse    ) cgGLSetParameter3f(r_cg_permutation->fp_DeferredColor_Diffuse , lightcolorbase[0] * diffusescale  * range, lightcolorbase[1] * diffusescale  * range, lightcolorbase[2] * diffusescale  * range);CHECKCGERROR
-               if (r_cg_permutation->fp_DeferredColor_Specular   ) cgGLSetParameter3f(r_cg_permutation->fp_DeferredColor_Specular, lightcolorbase[0] * specularscale * range, lightcolorbase[1] * specularscale * range, lightcolorbase[2] * specularscale * range);CHECKCGERROR
-               if (r_cg_permutation->fp_ShadowMap_TextureScale   ) cgGLSetParameter2f(r_cg_permutation->fp_ShadowMap_TextureScale, r_shadow_shadowmap_texturescale[0], r_shadow_shadowmap_texturescale[1]);CHECKCGERROR
-               if (r_cg_permutation->fp_ShadowMap_Parameters     ) cgGLSetParameter4f(r_cg_permutation->fp_ShadowMap_Parameters, r_shadow_shadowmap_parameters[0], r_shadow_shadowmap_parameters[1], r_shadow_shadowmap_parameters[2], r_shadow_shadowmap_parameters[3]);CHECKCGERROR
-               if (r_cg_permutation->fp_SpecularPower            ) cgGLSetParameter1f(r_cg_permutation->fp_SpecularPower, (r_shadow_gloss.integer == 2 ? r_shadow_gloss2exponent.value : r_shadow_glossexponent.value) * ((permutation & SHADERPERMUTATION_EXACTSPECULARMATH) ? 0.25f : 1.0f));CHECKCGERROR
-               if (r_cg_permutation->fp_ScreenToDepth            ) cgGLSetParameter2f(r_cg_permutation->fp_ScreenToDepth, r_refdef.view.viewport.screentodepth[0], r_refdef.view.viewport.screentodepth[1]);CHECKCGERROR
-               if (r_cg_permutation->fp_PixelToScreenTexCoord    ) cgGLSetParameter2f(r_cg_permutation->fp_PixelToScreenTexCoord, 1.0f/vid.width, 1.0/vid.height);CHECKCGERROR
-
-               if (r_cg_permutation->fp_Texture_Attenuation      ) CG_BindTexture(r_cg_permutation->fp_Texture_Attenuation    , r_shadow_attenuationgradienttexture                 );CHECKCGERROR
-               if (r_cg_permutation->fp_Texture_ScreenDepth      ) CG_BindTexture(r_cg_permutation->fp_Texture_ScreenDepth    , r_shadow_prepassgeometrydepthtexture                );CHECKCGERROR
-               if (r_cg_permutation->fp_Texture_ScreenNormalMap  ) CG_BindTexture(r_cg_permutation->fp_Texture_ScreenNormalMap, r_shadow_prepassgeometrynormalmaptexture            );CHECKCGERROR
-               if (r_cg_permutation->fp_Texture_Cube             ) CG_BindTexture(r_cg_permutation->fp_Texture_Cube           , rsurface.rtlight->currentcubemap                    );CHECKCGERROR
-               if (r_cg_permutation->fp_Texture_ShadowMapRect    ) CG_BindTexture(r_cg_permutation->fp_Texture_ShadowMapRect  , r_shadow_shadowmaprectangletexture                  );CHECKCGERROR
-               if (r_shadow_usingshadowmapcube)
-                       if (r_cg_permutation->fp_Texture_ShadowMapCube    ) CG_BindTexture(r_cg_permutation->fp_Texture_ShadowMapCube  , r_shadow_shadowmapcubetexture[r_shadow_shadowmaplod]);CHECKCGERROR
-               if (r_cg_permutation->fp_Texture_ShadowMap2D      ) CG_BindTexture(r_cg_permutation->fp_Texture_ShadowMap2D    , r_shadow_shadowmap2dtexture                         );CHECKCGERROR
-               if (r_cg_permutation->fp_Texture_CubeProjection   ) CG_BindTexture(r_cg_permutation->fp_Texture_CubeProjection , r_shadow_shadowmapvsdcttexture                      );CHECKCGERROR
-#endif
+               if (r_glsl_permutation->loc_LightPosition             >= 0) qglUniform3f(       r_glsl_permutation->loc_LightPosition            , viewlightorigin[0], viewlightorigin[1], viewlightorigin[2]);
+               if (r_glsl_permutation->loc_ViewToLight               >= 0) qglUniformMatrix4fv(r_glsl_permutation->loc_ViewToLight              , 1, false, viewtolight16f);
+               if (r_glsl_permutation->loc_DeferredColor_Ambient     >= 0) qglUniform3f(       r_glsl_permutation->loc_DeferredColor_Ambient    , lightcolorbase[0] * ambientscale  * range, lightcolorbase[1] * ambientscale  * range, lightcolorbase[2] * ambientscale  * range);
+               if (r_glsl_permutation->loc_DeferredColor_Diffuse     >= 0) qglUniform3f(       r_glsl_permutation->loc_DeferredColor_Diffuse    , lightcolorbase[0] * diffusescale  * range, lightcolorbase[1] * diffusescale  * range, lightcolorbase[2] * diffusescale  * range);
+               if (r_glsl_permutation->loc_DeferredColor_Specular    >= 0) qglUniform3f(       r_glsl_permutation->loc_DeferredColor_Specular   , lightcolorbase[0] * specularscale * range, lightcolorbase[1] * specularscale * range, lightcolorbase[2] * specularscale * range);
+               if (r_glsl_permutation->loc_ShadowMap_TextureScale    >= 0) qglUniform2f(       r_glsl_permutation->loc_ShadowMap_TextureScale   , r_shadow_shadowmap_texturescale[0], r_shadow_shadowmap_texturescale[1]);
+               if (r_glsl_permutation->loc_ShadowMap_Parameters      >= 0) qglUniform4f(       r_glsl_permutation->loc_ShadowMap_Parameters     , r_shadow_shadowmap_parameters[0], r_shadow_shadowmap_parameters[1], r_shadow_shadowmap_parameters[2], r_shadow_shadowmap_parameters[3]);
+               if (r_glsl_permutation->loc_SpecularPower             >= 0) qglUniform1f(       r_glsl_permutation->loc_SpecularPower            , (r_shadow_gloss.integer == 2 ? r_shadow_gloss2exponent.value : r_shadow_glossexponent.value) * (r_shadow_glossexact.integer ? 0.25f : 1.0f));
+               if (r_glsl_permutation->loc_ScreenToDepth             >= 0) qglUniform2f(       r_glsl_permutation->loc_ScreenToDepth            , r_refdef.view.viewport.screentodepth[0], r_refdef.view.viewport.screentodepth[1]);
+               if (r_glsl_permutation->loc_PixelToScreenTexCoord     >= 0) qglUniform2f(       r_glsl_permutation->loc_PixelToScreenTexCoord    , 1.0f/vid.width, 1.0f/vid.height);
+
+               if (r_glsl_permutation->tex_Texture_Attenuation       >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_Attenuation        , r_shadow_attenuationgradienttexture                 );
+               if (r_glsl_permutation->tex_Texture_ScreenDepth       >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_ScreenDepth        , r_shadow_prepassgeometrydepthtexture                );
+               if (r_glsl_permutation->tex_Texture_ScreenNormalMap   >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_ScreenNormalMap    , r_shadow_prepassgeometrynormalmaptexture            );
+               if (r_glsl_permutation->tex_Texture_Cube              >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_Cube               , rsurface.rtlight->currentcubemap                    );
+               if (r_glsl_permutation->tex_Texture_ShadowMap2D       >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_ShadowMap2D        , r_shadow_shadowmap2dtexture                         );
+               if (r_glsl_permutation->tex_Texture_CubeProjection    >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_CubeProjection     , r_shadow_shadowmapvsdcttexture                      );
                break;
        case RENDERPATH_GL13:
        case RENDERPATH_GL11:
                break;
+       case RENDERPATH_SOFT:
+               R_SetupShader_SetPermutationGLSL(mode, permutation);
+               DPSOFTRAST_Uniform3f(       DPSOFTRAST_UNIFORM_LightPosition            , viewlightorigin[0], viewlightorigin[1], viewlightorigin[2]);
+               DPSOFTRAST_UniformMatrix4fv(DPSOFTRAST_UNIFORM_ViewToLightM1            , 1, false, viewtolight16f);
+               DPSOFTRAST_Uniform3f(       DPSOFTRAST_UNIFORM_DeferredColor_Ambient    , lightcolorbase[0] * ambientscale  * range, lightcolorbase[1] * ambientscale  * range, lightcolorbase[2] * ambientscale  * range);
+               DPSOFTRAST_Uniform3f(       DPSOFTRAST_UNIFORM_DeferredColor_Diffuse    , lightcolorbase[0] * diffusescale  * range, lightcolorbase[1] * diffusescale  * range, lightcolorbase[2] * diffusescale  * range);
+               DPSOFTRAST_Uniform3f(       DPSOFTRAST_UNIFORM_DeferredColor_Specular   , lightcolorbase[0] * specularscale * range, lightcolorbase[1] * specularscale * range, lightcolorbase[2] * specularscale * range);
+               DPSOFTRAST_Uniform2f(       DPSOFTRAST_UNIFORM_ShadowMap_TextureScale   , r_shadow_shadowmap_texturescale[0], r_shadow_shadowmap_texturescale[1]);
+               DPSOFTRAST_Uniform4f(       DPSOFTRAST_UNIFORM_ShadowMap_Parameters     , r_shadow_shadowmap_parameters[0], r_shadow_shadowmap_parameters[1], r_shadow_shadowmap_parameters[2], r_shadow_shadowmap_parameters[3]);
+               DPSOFTRAST_Uniform1f(       DPSOFTRAST_UNIFORM_SpecularPower            , (r_shadow_gloss.integer == 2 ? r_shadow_gloss2exponent.value : r_shadow_glossexponent.value) * (r_shadow_glossexact.integer ? 0.25f : 1.0f));
+               DPSOFTRAST_Uniform2f(       DPSOFTRAST_UNIFORM_ScreenToDepth            , r_refdef.view.viewport.screentodepth[0], r_refdef.view.viewport.screentodepth[1]);
+               DPSOFTRAST_Uniform2f(DPSOFTRAST_UNIFORM_PixelToScreenTexCoord, 1.0f/vid.width, 1.0f/vid.height);
+
+               R_Mesh_TexBind(GL20TU_ATTENUATION        , r_shadow_attenuationgradienttexture                 );
+               R_Mesh_TexBind(GL20TU_SCREENDEPTH        , r_shadow_prepassgeometrydepthtexture                );
+               R_Mesh_TexBind(GL20TU_SCREENNORMALMAP    , r_shadow_prepassgeometrynormalmaptexture            );
+               R_Mesh_TexBind(GL20TU_CUBE               , rsurface.rtlight->currentcubemap                    );
+               R_Mesh_TexBind(GL20TU_SHADOWMAP2D        , r_shadow_shadowmap2dtexture                         );
+               R_Mesh_TexBind(GL20TU_CUBEPROJECTION     , r_shadow_shadowmapvsdcttexture                      );
+               break;
        }
 }
 
@@ -5300,6 +3117,7 @@ skinframe_t *R_SkinFrame_Find(const char *name, int textureflags, int comparewid
                skinframe->avgcolor[3] = avgcolor[4] / (255.0 * cnt); \
        }
 
+extern cvar_t gl_picmip;
 skinframe_t *R_SkinFrame_LoadExternal(const char *name, int textureflags, qboolean complain)
 {
        int j;
@@ -5313,6 +3131,9 @@ skinframe_t *R_SkinFrame_LoadExternal(const char *name, int textureflags, qboole
        qboolean ddshasalpha = false;
        float ddsavgcolor[4];
        char basename[MAX_QPATH];
+       int miplevel = R_PicmipForFlags(textureflags);
+       int savemiplevel = miplevel;
+       int mymiplevel;
 
        if (cls.state == ca_dedicated)
                return NULL;
@@ -5327,13 +3148,15 @@ skinframe_t *R_SkinFrame_LoadExternal(const char *name, int textureflags, qboole
        Image_StripImageExtension(name, basename, sizeof(basename));
 
        // check for DDS texture file first
-       if (!r_loaddds || !(ddsbase = R_LoadTextureDDSFile(r_main_texturepool, va("dds/%s.dds", basename), textureflags, &ddshasalpha, ddsavgcolor)))
+       if (!r_loaddds || !(ddsbase = R_LoadTextureDDSFile(r_main_texturepool, va("dds/%s.dds", basename), textureflags, &ddshasalpha, ddsavgcolor, miplevel)))
        {
-               basepixels = loadimagepixelsbgra(name, complain, true);
+               basepixels = loadimagepixelsbgra(name, complain, true, r_texture_convertsRGB_skin.integer != 0, &miplevel);
                if (basepixels == NULL)
                        return NULL;
        }
 
+       // FIXME handle miplevel
+
        if (developer_loading.integer)
                Con_Printf("loading skin \"%s\"\n", name);
 
@@ -5358,14 +3181,14 @@ skinframe_t *R_SkinFrame_LoadExternal(const char *name, int textureflags, qboole
                skinframe->hasalpha = ddshasalpha;
                VectorCopy(ddsavgcolor, skinframe->avgcolor);
                if (r_loadfog && skinframe->hasalpha)
-                       skinframe->fog = R_LoadTextureDDSFile(r_main_texturepool, va("dds/%s_mask.dds", skinframe->basename), textureflags | TEXF_ALPHA, NULL, NULL);
+                       skinframe->fog = R_LoadTextureDDSFile(r_main_texturepool, va("dds/%s_mask.dds", skinframe->basename), textureflags | TEXF_ALPHA, NULL, NULL, miplevel);
                //Con_Printf("Texture %s has average colors %f %f %f alpha %f\n", name, skinframe->avgcolor[0], skinframe->avgcolor[1], skinframe->avgcolor[2], skinframe->avgcolor[3]);
        }
        else
        {
                basepixels_width = image_width;
                basepixels_height = image_height;
-               skinframe->base = R_LoadTexture2D (r_main_texturepool, skinframe->basename, basepixels_width, basepixels_height, basepixels, TEXTYPE_BGRA, skinframe->textureflags & (gl_texturecompression_color.integer ? ~0 : ~TEXF_COMPRESS), NULL);
+               skinframe->base = R_LoadTexture2D (r_main_texturepool, skinframe->basename, basepixels_width, basepixels_height, basepixels, TEXTYPE_BGRA, textureflags & (gl_texturecompression_color.integer ? ~0 : ~TEXF_COMPRESS), miplevel, NULL);
                if (textureflags & TEXF_ALPHA)
                {
                        for (j = 3;j < basepixels_width * basepixels_height * 4;j += 4)
@@ -5387,44 +3210,46 @@ skinframe_t *R_SkinFrame_LoadExternal(const char *name, int textureflags, qboole
                                        pixels[j+2] = 255;
                                        pixels[j+3] = basepixels[j+3];
                                }
-                               skinframe->fog = R_LoadTexture2D (r_main_texturepool, va("%s_mask", skinframe->basename), image_width, image_height, pixels, TEXTYPE_BGRA, skinframe->textureflags & (gl_texturecompression_color.integer ? ~0 : ~TEXF_COMPRESS), NULL);
+                               skinframe->fog = R_LoadTexture2D (r_main_texturepool, va("%s_mask", skinframe->basename), image_width, image_height, pixels, TEXTYPE_BGRA, textureflags & (gl_texturecompression_color.integer ? ~0 : ~TEXF_COMPRESS), miplevel, NULL);
                                Mem_Free(pixels);
                        }
                }
                R_SKINFRAME_LOAD_AVERAGE_COLORS(basepixels_width * basepixels_height, basepixels[4 * pix + comp]);
                //Con_Printf("Texture %s has average colors %f %f %f alpha %f\n", name, skinframe->avgcolor[0], skinframe->avgcolor[1], skinframe->avgcolor[2], skinframe->avgcolor[3]);
                if (r_savedds && qglGetCompressedTexImageARB && skinframe->base)
-                       R_SaveTextureDDSFile(skinframe->base, va("dds/%s.dds", skinframe->basename), true);
+                       R_SaveTextureDDSFile(skinframe->base, va("dds/%s.dds", skinframe->basename), true, skinframe->hasalpha);
                if (r_savedds && qglGetCompressedTexImageARB && skinframe->fog)
-                       R_SaveTextureDDSFile(skinframe->fog, va("dds/%s_mask.dds", skinframe->basename), true);
+                       R_SaveTextureDDSFile(skinframe->fog, va("dds/%s_mask.dds", skinframe->basename), true, true);
        }
 
        if (r_loaddds)
        {
+               mymiplevel = savemiplevel;
                if (r_loadnormalmap)
-                       skinframe->nmap = R_LoadTextureDDSFile(r_main_texturepool, va("dds/%s_norm.dds", skinframe->basename), textureflags | TEXF_ALPHA, NULL, NULL);
-               skinframe->glow = R_LoadTextureDDSFile(r_main_texturepool, va("dds/%s_glow.dds", skinframe->basename), textureflags, NULL, NULL);
+                       skinframe->nmap = R_LoadTextureDDSFile(r_main_texturepool, va("dds/%s_norm.dds", skinframe->basename), (TEXF_ALPHA | textureflags) & (r_mipnormalmaps.integer ? ~0 : ~TEXF_MIPMAP), NULL, NULL, mymiplevel);
+               skinframe->glow = R_LoadTextureDDSFile(r_main_texturepool, va("dds/%s_glow.dds", skinframe->basename), textureflags, NULL, NULL, mymiplevel);
                if (r_loadgloss)
-                       skinframe->gloss = R_LoadTextureDDSFile(r_main_texturepool, va("dds/%s_gloss.dds", skinframe->basename), textureflags, NULL, NULL);
-               skinframe->pants = R_LoadTextureDDSFile(r_main_texturepool, va("dds/%s_pants.dds", skinframe->basename), textureflags, NULL, NULL);
-               skinframe->shirt = R_LoadTextureDDSFile(r_main_texturepool, va("dds/%s_shirt.dds", skinframe->basename), textureflags, NULL, NULL);
-               skinframe->reflect = R_LoadTextureDDSFile(r_main_texturepool, va("dds/%s_reflect.dds", skinframe->basename), textureflags, NULL, NULL);
+                       skinframe->gloss = R_LoadTextureDDSFile(r_main_texturepool, va("dds/%s_gloss.dds", skinframe->basename), textureflags, NULL, NULL, mymiplevel);
+               skinframe->pants = R_LoadTextureDDSFile(r_main_texturepool, va("dds/%s_pants.dds", skinframe->basename), textureflags, NULL, NULL, mymiplevel);
+               skinframe->shirt = R_LoadTextureDDSFile(r_main_texturepool, va("dds/%s_shirt.dds", skinframe->basename), textureflags, NULL, NULL, mymiplevel);
+               skinframe->reflect = R_LoadTextureDDSFile(r_main_texturepool, va("dds/%s_reflect.dds", skinframe->basename), textureflags, NULL, NULL, mymiplevel);
        }
 
        // _norm is the name used by tenebrae and has been adopted as standard
        if (r_loadnormalmap && skinframe->nmap == NULL)
        {
-               if ((pixels = loadimagepixelsbgra(va("%s_norm", skinframe->basename), false, false)) != NULL)
+               mymiplevel = savemiplevel;
+               if ((pixels = loadimagepixelsbgra(va("%s_norm", skinframe->basename), false, false, false, &mymiplevel)) != NULL)
                {
-                       skinframe->nmap = R_LoadTexture2D (r_main_texturepool, va("%s_nmap", skinframe->basename), image_width, image_height, pixels, TEXTYPE_BGRA, (TEXF_ALPHA | skinframe->textureflags) & (gl_texturecompression_normal.integer ? ~0 : ~TEXF_COMPRESS), NULL);
+                       skinframe->nmap = R_LoadTexture2D (r_main_texturepool, va("%s_nmap", skinframe->basename), image_width, image_height, pixels, TEXTYPE_BGRA, (TEXF_ALPHA | textureflags) & (r_mipnormalmaps.integer ? ~0 : ~TEXF_MIPMAP) & (gl_texturecompression_normal.integer ? ~0 : ~TEXF_COMPRESS), mymiplevel, NULL);
                        Mem_Free(pixels);
                        pixels = NULL;
                }
-               else if (r_shadow_bumpscale_bumpmap.value > 0 && (bumppixels = loadimagepixelsbgra(va("%s_bump", skinframe->basename), false, false)) != NULL)
+               else if (r_shadow_bumpscale_bumpmap.value > 0 && (bumppixels = loadimagepixelsbgra(va("%s_bump", skinframe->basename), false, false, false, &mymiplevel)) != NULL)
                {
                        pixels = (unsigned char *)Mem_Alloc(tempmempool, image_width * image_height * 4);
                        Image_HeightmapToNormalmap_BGRA(bumppixels, pixels, image_width, image_height, false, r_shadow_bumpscale_bumpmap.value);
-                       skinframe->nmap = R_LoadTexture2D (r_main_texturepool, va("%s_nmap", skinframe->basename), image_width, image_height, pixels, TEXTYPE_BGRA, (TEXF_ALPHA | skinframe->textureflags) & (gl_texturecompression_normal.integer ? ~0 : ~TEXF_COMPRESS), NULL);
+                       skinframe->nmap = R_LoadTexture2D (r_main_texturepool, va("%s_nmap", skinframe->basename), image_width, image_height, pixels, TEXTYPE_BGRA, (TEXF_ALPHA | textureflags) & (r_mipnormalmaps.integer ? ~0 : ~TEXF_MIPMAP) & (gl_texturecompression_normal.integer ? ~0 : ~TEXF_COMPRESS), mymiplevel, NULL);
                        Mem_Free(pixels);
                        Mem_Free(bumppixels);
                }
@@ -5432,55 +3257,60 @@ skinframe_t *R_SkinFrame_LoadExternal(const char *name, int textureflags, qboole
                {
                        pixels = (unsigned char *)Mem_Alloc(tempmempool, basepixels_width * basepixels_height * 4);
                        Image_HeightmapToNormalmap_BGRA(basepixels, pixels, basepixels_width, basepixels_height, false, r_shadow_bumpscale_basetexture.value);
-                       skinframe->nmap = R_LoadTexture2D (r_main_texturepool, va("%s_nmap", skinframe->basename), basepixels_width, basepixels_height, pixels, TEXTYPE_BGRA, (TEXF_ALPHA | skinframe->textureflags) & (gl_texturecompression_normal.integer ? ~0 : ~TEXF_COMPRESS), NULL);
+                       skinframe->nmap = R_LoadTexture2D (r_main_texturepool, va("%s_nmap", skinframe->basename), basepixels_width, basepixels_height, pixels, TEXTYPE_BGRA, (TEXF_ALPHA | textureflags) & (r_mipnormalmaps.integer ? ~0 : ~TEXF_MIPMAP) & (gl_texturecompression_normal.integer ? ~0 : ~TEXF_COMPRESS), mymiplevel, NULL);
                        Mem_Free(pixels);
                }
                if (r_savedds && qglGetCompressedTexImageARB && skinframe->nmap)
-                       R_SaveTextureDDSFile(skinframe->nmap, va("dds/%s_norm.dds", skinframe->basename), true);
+                       R_SaveTextureDDSFile(skinframe->nmap, va("dds/%s_norm.dds", skinframe->basename), true, true);
        }
 
        // _luma is supported only for tenebrae compatibility
        // _glow is the preferred name
-       if (skinframe->glow == NULL && ((pixels = loadimagepixelsbgra(va("%s_glow",  skinframe->basename), false, false)) || (pixels = loadimagepixelsbgra(va("%s_luma", skinframe->basename), false, false))))
+       mymiplevel = savemiplevel;
+       if (skinframe->glow == NULL && ((pixels = loadimagepixelsbgra(va("%s_glow",  skinframe->basename), false, false, r_texture_convertsRGB_skin.integer != 0, &mymiplevel)) || (pixels = loadimagepixelsbgra(va("%s_luma", skinframe->basename), false, false, r_texture_convertsRGB_skin.integer != 0, &mymiplevel))))
        {
-               skinframe->glow = R_LoadTexture2D (r_main_texturepool, va("%s_glow", skinframe->basename), image_width, image_height, pixels, TEXTYPE_BGRA, skinframe->textureflags & (gl_texturecompression_glow.integer ? ~0 : ~TEXF_COMPRESS), NULL);
+               skinframe->glow = R_LoadTexture2D (r_main_texturepool, va("%s_glow", skinframe->basename), image_width, image_height, pixels, TEXTYPE_BGRA, textureflags & (gl_texturecompression_glow.integer ? ~0 : ~TEXF_COMPRESS), mymiplevel, NULL);
                if (r_savedds && qglGetCompressedTexImageARB && skinframe->glow)
-                       R_SaveTextureDDSFile(skinframe->glow, va("dds/%s_glow.dds", skinframe->basename), true);
+                       R_SaveTextureDDSFile(skinframe->glow, va("dds/%s_glow.dds", skinframe->basename), true, true);
                Mem_Free(pixels);pixels = NULL;
        }
 
-       if (skinframe->gloss == NULL && r_loadgloss && (pixels = loadimagepixelsbgra(va("%s_gloss", skinframe->basename), false, false)))
+       mymiplevel = savemiplevel;
+       if (skinframe->gloss == NULL && r_loadgloss && (pixels = loadimagepixelsbgra(va("%s_gloss", skinframe->basename), false, false, r_texture_convertsRGB_skin.integer != 0, &mymiplevel)))
        {
-               skinframe->gloss = R_LoadTexture2D (r_main_texturepool, va("%s_gloss", skinframe->basename), image_width, image_height, pixels, TEXTYPE_BGRA, skinframe->textureflags & (gl_texturecompression_gloss.integer ? ~0 : ~TEXF_COMPRESS), NULL);
+               skinframe->gloss = R_LoadTexture2D (r_main_texturepool, va("%s_gloss", skinframe->basename), image_width, image_height, pixels, TEXTYPE_BGRA, textureflags & (gl_texturecompression_gloss.integer ? ~0 : ~TEXF_COMPRESS), mymiplevel, NULL);
                if (r_savedds && qglGetCompressedTexImageARB && skinframe->gloss)
-                       R_SaveTextureDDSFile(skinframe->gloss, va("dds/%s_gloss.dds", skinframe->basename), true);
+                       R_SaveTextureDDSFile(skinframe->gloss, va("dds/%s_gloss.dds", skinframe->basename), true, true);
                Mem_Free(pixels);
                pixels = NULL;
        }
 
-       if (skinframe->pants == NULL && (pixels = loadimagepixelsbgra(va("%s_pants", skinframe->basename), false, false)))
+       mymiplevel = savemiplevel;
+       if (skinframe->pants == NULL && (pixels = loadimagepixelsbgra(va("%s_pants", skinframe->basename), false, false, r_texture_convertsRGB_skin.integer != 0, &mymiplevel)))
        {
-               skinframe->pants = R_LoadTexture2D (r_main_texturepool, va("%s_pants", skinframe->basename), image_width, image_height, pixels, TEXTYPE_BGRA, skinframe->textureflags & (gl_texturecompression_color.integer ? ~0 : ~TEXF_COMPRESS), NULL);
+               skinframe->pants = R_LoadTexture2D (r_main_texturepool, va("%s_pants", skinframe->basename), image_width, image_height, pixels, TEXTYPE_BGRA, textureflags & (gl_texturecompression_color.integer ? ~0 : ~TEXF_COMPRESS), mymiplevel, NULL);
                if (r_savedds && qglGetCompressedTexImageARB && skinframe->pants)
-                       R_SaveTextureDDSFile(skinframe->pants, va("dds/%s_pants.dds", skinframe->basename), true);
+                       R_SaveTextureDDSFile(skinframe->pants, va("dds/%s_pants.dds", skinframe->basename), true, false);
                Mem_Free(pixels);
                pixels = NULL;
        }
 
-       if (skinframe->shirt == NULL && (pixels = loadimagepixelsbgra(va("%s_shirt", skinframe->basename), false, false)))
+       mymiplevel = savemiplevel;
+       if (skinframe->shirt == NULL && (pixels = loadimagepixelsbgra(va("%s_shirt", skinframe->basename), false, false, r_texture_convertsRGB_skin.integer != 0, &mymiplevel)))
        {
-               skinframe->shirt = R_LoadTexture2D (r_main_texturepool, va("%s_shirt", skinframe->basename), image_width, image_height, pixels, TEXTYPE_BGRA, skinframe->textureflags & (gl_texturecompression_color.integer ? ~0 : ~TEXF_COMPRESS), NULL);
+               skinframe->shirt = R_LoadTexture2D (r_main_texturepool, va("%s_shirt", skinframe->basename), image_width, image_height, pixels, TEXTYPE_BGRA, textureflags & (gl_texturecompression_color.integer ? ~0 : ~TEXF_COMPRESS), mymiplevel, NULL);
                if (r_savedds && qglGetCompressedTexImageARB && skinframe->shirt)
-                       R_SaveTextureDDSFile(skinframe->shirt, va("dds/%s_shirt.dds", skinframe->basename), true);
+                       R_SaveTextureDDSFile(skinframe->shirt, va("dds/%s_shirt.dds", skinframe->basename), true, false);
                Mem_Free(pixels);
                pixels = NULL;
        }
 
-       if (skinframe->reflect == NULL && (pixels = loadimagepixelsbgra(va("%s_reflect", skinframe->basename), false, false)))
+       mymiplevel = savemiplevel;
+       if (skinframe->reflect == NULL && (pixels = loadimagepixelsbgra(va("%s_reflect", skinframe->basename), false, false, r_texture_convertsRGB_skin.integer != 0, &mymiplevel)))
        {
-               skinframe->reflect = R_LoadTexture2D (r_main_texturepool, va("%s_reflect", skinframe->basename), image_width, image_height, pixels, TEXTYPE_BGRA, skinframe->textureflags & (gl_texturecompression_reflectmask.integer ? ~0 : ~TEXF_COMPRESS), NULL);
+               skinframe->reflect = R_LoadTexture2D (r_main_texturepool, va("%s_reflect", skinframe->basename), image_width, image_height, pixels, TEXTYPE_BGRA, textureflags & (gl_texturecompression_reflectmask.integer ? ~0 : ~TEXF_COMPRESS), mymiplevel, NULL);
                if (r_savedds && qglGetCompressedTexImageARB && skinframe->reflect)
-                       R_SaveTextureDDSFile(skinframe->reflect, va("dds/%s_reflect.dds", skinframe->basename), true);
+                       R_SaveTextureDDSFile(skinframe->reflect, va("dds/%s_reflect.dds", skinframe->basename), true, true);
                Mem_Free(pixels);
                pixels = NULL;
        }
@@ -5530,10 +3360,10 @@ skinframe_t *R_SkinFrame_LoadInternalBGRA(const char *name, int textureflags, co
                temp1 = (unsigned char *)Mem_Alloc(tempmempool, width * height * 8);
                temp2 = temp1 + width * height * 4;
                Image_HeightmapToNormalmap_BGRA(skindata, temp2, width, height, false, r_shadow_bumpscale_basetexture.value);
-               skinframe->nmap = R_LoadTexture2D(r_main_texturepool, va("%s_nmap", skinframe->basename), width, height, temp2, TEXTYPE_BGRA, skinframe->textureflags | TEXF_ALPHA, NULL);
+               skinframe->nmap = R_LoadTexture2D(r_main_texturepool, va("%s_nmap", skinframe->basename), width, height, temp2, TEXTYPE_BGRA, (textureflags | TEXF_ALPHA) & (r_mipnormalmaps.integer ? ~0 : ~TEXF_MIPMAP), -1, NULL);
                Mem_Free(temp1);
        }
-       skinframe->base = skinframe->merged = R_LoadTexture2D(r_main_texturepool, skinframe->basename, width, height, skindata, TEXTYPE_BGRA, skinframe->textureflags, NULL);
+       skinframe->base = skinframe->merged = R_LoadTexture2D(r_main_texturepool, skinframe->basename, width, height, skindata, TEXTYPE_BGRA, textureflags, -1, NULL);
        if (textureflags & TEXF_ALPHA)
        {
                for (i = 3;i < width * height * 4;i += 4)
@@ -5550,7 +3380,7 @@ skinframe_t *R_SkinFrame_LoadInternalBGRA(const char *name, int textureflags, co
                        memcpy(fogpixels, skindata, width * height * 4);
                        for (i = 0;i < width * height * 4;i += 4)
                                fogpixels[i] = fogpixels[i+1] = fogpixels[i+2] = 255;
-                       skinframe->fog = R_LoadTexture2D(r_main_texturepool, va("%s_fog", skinframe->basename), width, height, fogpixels, TEXTYPE_BGRA, skinframe->textureflags, NULL);
+                       skinframe->fog = R_LoadTexture2D(r_main_texturepool, va("%s_fog", skinframe->basename), width, height, fogpixels, TEXTYPE_BGRA, textureflags, -1, NULL);
                        Mem_Free(fogpixels);
                }
        }
@@ -5595,7 +3425,7 @@ skinframe_t *R_SkinFrame_LoadInternalQuake(const char *name, int textureflags, i
                Con_Printf("loading quake skin \"%s\"\n", name);
 
        // we actually don't upload anything until the first use, because mdl skins frequently go unused, and are almost never used in both modes (colormapped and non-colormapped)
-       skinframe->qpixels = Mem_Alloc(r_main_mempool, width*height);
+       skinframe->qpixels = (unsigned char *)Mem_Alloc(r_main_mempool, width*height); // FIXME LEAK
        memcpy(skinframe->qpixels, skindata, width*height);
        skinframe->qwidth = width;
        skinframe->qheight = height;
@@ -5653,27 +3483,27 @@ static void R_SkinFrame_GenerateTexturesFromQPixels(skinframe_t *skinframe, qboo
                // use either a custom palette or the quake palette
                Image_Copy8bitBGRA(skindata, temp1, width * height, palette_bgra_complete);
                Image_HeightmapToNormalmap_BGRA(temp1, temp2, width, height, false, r_shadow_bumpscale_basetexture.value);
-               skinframe->nmap = R_LoadTexture2D(r_main_texturepool, va("%s_nmap", skinframe->basename), width, height, temp2, TEXTYPE_BGRA, skinframe->textureflags | TEXF_ALPHA, NULL);
+               skinframe->nmap = R_LoadTexture2D(r_main_texturepool, va("%s_nmap", skinframe->basename), width, height, temp2, TEXTYPE_BGRA, (skinframe->textureflags | TEXF_ALPHA) & (r_mipnormalmaps.integer ? ~0 : ~TEXF_MIPMAP), -1, NULL);
                Mem_Free(temp1);
        }
 
        if (skinframe->qgenerateglow)
        {
                skinframe->qgenerateglow = false;
-               skinframe->glow = R_LoadTexture2D(r_main_texturepool, va("%s_glow", skinframe->basename), width, height, skindata, TEXTYPE_PALETTE, skinframe->textureflags, palette_bgra_onlyfullbrights); // glow
+               skinframe->glow = R_LoadTexture2D(r_main_texturepool, va("%s_glow", skinframe->basename), width, height, skindata, TEXTYPE_PALETTE, skinframe->textureflags, -1, palette_bgra_onlyfullbrights); // glow
        }
 
        if (colormapped)
        {
                skinframe->qgeneratebase = false;
-               skinframe->base  = R_LoadTexture2D(r_main_texturepool, va("%s_nospecial", skinframe->basename), width, height, skindata, TEXTYPE_PALETTE, skinframe->textureflags, skinframe->glow ? palette_bgra_nocolormapnofullbrights : palette_bgra_nocolormap);
-               skinframe->pants = R_LoadTexture2D(r_main_texturepool, va("%s_pants", skinframe->basename), width, height, skindata, TEXTYPE_PALETTE, skinframe->textureflags, palette_bgra_pantsaswhite);
-               skinframe->shirt = R_LoadTexture2D(r_main_texturepool, va("%s_shirt", skinframe->basename), width, height, skindata, TEXTYPE_PALETTE, skinframe->textureflags, palette_bgra_shirtaswhite);
+               skinframe->base  = R_LoadTexture2D(r_main_texturepool, va("%s_nospecial", skinframe->basename), width, height, skindata, TEXTYPE_PALETTE, skinframe->textureflags, -1, skinframe->glow ? palette_bgra_nocolormapnofullbrights : palette_bgra_nocolormap);
+               skinframe->pants = R_LoadTexture2D(r_main_texturepool, va("%s_pants", skinframe->basename), width, height, skindata, TEXTYPE_PALETTE, skinframe->textureflags, -1, palette_bgra_pantsaswhite);
+               skinframe->shirt = R_LoadTexture2D(r_main_texturepool, va("%s_shirt", skinframe->basename), width, height, skindata, TEXTYPE_PALETTE, skinframe->textureflags, -1, palette_bgra_shirtaswhite);
        }
        else
        {
                skinframe->qgeneratemerged = false;
-               skinframe->merged = R_LoadTexture2D(r_main_texturepool, skinframe->basename, width, height, skindata, TEXTYPE_PALETTE, skinframe->textureflags, skinframe->glow ? palette_bgra_nofullbrights : palette_bgra_complete);
+               skinframe->merged = R_LoadTexture2D(r_main_texturepool, skinframe->basename, width, height, skindata, TEXTYPE_PALETTE, skinframe->textureflags, -1, skinframe->glow ? palette_bgra_nofullbrights : palette_bgra_complete);
        }
 
        if (!skinframe->qgeneratemerged && !skinframe->qgeneratebase)
@@ -5715,7 +3545,7 @@ skinframe_t *R_SkinFrame_LoadInternal8bit(const char *name, int textureflags, co
        if (developer_loading.integer)
                Con_Printf("loading embedded 8bit image \"%s\"\n", name);
 
-       skinframe->base = skinframe->merged = R_LoadTexture2D(r_main_texturepool, skinframe->basename, width, height, skindata, TEXTYPE_PALETTE, skinframe->textureflags, palette);
+       skinframe->base = skinframe->merged = R_LoadTexture2D(r_main_texturepool, skinframe->basename, width, height, skindata, TEXTYPE_PALETTE, textureflags, -1, palette);
        if (textureflags & TEXF_ALPHA)
        {
                for (i = 0;i < width * height;i++)
@@ -5727,7 +3557,7 @@ skinframe_t *R_SkinFrame_LoadInternal8bit(const char *name, int textureflags, co
                        }
                }
                if (r_loadfog && skinframe->hasalpha)
-                       skinframe->fog = R_LoadTexture2D(r_main_texturepool, va("%s_fog", skinframe->basename), width, height, skindata, TEXTYPE_PALETTE, skinframe->textureflags, alphapalette);
+                       skinframe->fog = R_LoadTexture2D(r_main_texturepool, va("%s_fog", skinframe->basename), width, height, skindata, TEXTYPE_PALETTE, textureflags, -1, alphapalette);
        }
 
        R_SKINFRAME_LOAD_AVERAGE_COLORS(width * height, ((unsigned char *)palette)[skindata[pix]*4 + comp]);
@@ -5767,7 +3597,7 @@ skinframe_t *R_SkinFrame_LoadMissing(void)
 //static char *suffix[6] = {"ft", "bk", "rt", "lf", "up", "dn"};
 typedef struct suffixinfo_s
 {
-       char *suffix;
+       const char *suffix;
        qboolean flipx, flipy, flipdiagonal;
 }
 suffixinfo_t;
@@ -5820,7 +3650,7 @@ rtexture_t *R_LoadCubemap(const char *basename)
                        // generate an image name based on the base and and suffix
                        dpsnprintf(name, sizeof(name), "%s%s", basename, suffix[j][i].suffix);
                        // load it
-                       if ((image_buffer = loadimagepixelsbgra(name, false, false)))
+                       if ((image_buffer = loadimagepixelsbgra(name, false, false, r_texture_convertsRGB_cubemap.integer != 0, NULL)))
                        {
                                // an image loaded, make sure width and height are equal
                                if (image_width == image_height && (!cubemappixels || image_width == cubemapsize))
@@ -5849,7 +3679,7 @@ rtexture_t *R_LoadCubemap(const char *basename)
                if (developer_loading.integer)
                        Con_Printf("loading cubemap \"%s\"\n", basename);
 
-               cubemaptexture = R_LoadTextureCubeMap(r_main_texturepool, basename, cubemapsize, cubemappixels, TEXTYPE_BGRA, (gl_texturecompression_lightcubemaps.integer ? TEXF_COMPRESS : 0) | TEXF_FORCELINEAR, NULL);
+               cubemaptexture = R_LoadTextureCubeMap(r_main_texturepool, basename, cubemapsize, cubemappixels, TEXTYPE_BGRA, (gl_texturecompression_lightcubemaps.integer ? TEXF_COMPRESS : 0) | TEXF_FORCELINEAR | TEXF_CLAMP, -1, NULL);
                Mem_Free(cubemappixels);
        }
        else
@@ -5919,7 +3749,7 @@ void R_Main_ResizeViewCache(void)
                r_refdef.viewcache.maxentities = numentities;
                if (r_refdef.viewcache.entityvisible)
                        Mem_Free(r_refdef.viewcache.entityvisible);
-               r_refdef.viewcache.entityvisible = Mem_Alloc(r_main_mempool, r_refdef.viewcache.maxentities);
+               r_refdef.viewcache.entityvisible = (unsigned char *)Mem_Alloc(r_main_mempool, r_refdef.viewcache.maxentities);
        }
        if (r_refdef.viewcache.world_numclusters != numclusters)
        {
@@ -5927,21 +3757,21 @@ void R_Main_ResizeViewCache(void)
                r_refdef.viewcache.world_numclusterbytes = numclusterbytes;
                if (r_refdef.viewcache.world_pvsbits)
                        Mem_Free(r_refdef.viewcache.world_pvsbits);
-               r_refdef.viewcache.world_pvsbits = Mem_Alloc(r_main_mempool, r_refdef.viewcache.world_numclusterbytes);
+               r_refdef.viewcache.world_pvsbits = (unsigned char *)Mem_Alloc(r_main_mempool, r_refdef.viewcache.world_numclusterbytes);
        }
        if (r_refdef.viewcache.world_numleafs != numleafs)
        {
                r_refdef.viewcache.world_numleafs = numleafs;
                if (r_refdef.viewcache.world_leafvisible)
                        Mem_Free(r_refdef.viewcache.world_leafvisible);
-               r_refdef.viewcache.world_leafvisible = Mem_Alloc(r_main_mempool, r_refdef.viewcache.world_numleafs);
+               r_refdef.viewcache.world_leafvisible = (unsigned char *)Mem_Alloc(r_main_mempool, r_refdef.viewcache.world_numleafs);
        }
        if (r_refdef.viewcache.world_numsurfaces != numsurfaces)
        {
                r_refdef.viewcache.world_numsurfaces = numsurfaces;
                if (r_refdef.viewcache.world_surfacevisible)
                        Mem_Free(r_refdef.viewcache.world_surfacevisible);
-               r_refdef.viewcache.world_surfacevisible = Mem_Alloc(r_main_mempool, r_refdef.viewcache.world_numsurfaces);
+               r_refdef.viewcache.world_surfacevisible = (unsigned char *)Mem_Alloc(r_main_mempool, r_refdef.viewcache.world_numsurfaces);
        }
 }
 
@@ -5956,16 +3786,20 @@ void gl_main_start(void)
        r_texture_whitecube = NULL;
        r_texture_normalizationcube = NULL;
        r_texture_fogattenuation = NULL;
+       r_texture_fogheighttexture = NULL;
        r_texture_gammaramps = NULL;
        r_texture_numcubemaps = 0;
 
-       r_loaddds = vid.support.arb_texture_compression && vid.support.ext_texture_compression_s3tc && r_texture_dds_load.integer;
+       r_loaddds = r_texture_dds_load.integer != 0;
        r_savedds = vid.support.arb_texture_compression && vid.support.ext_texture_compression_s3tc && r_texture_dds_save.integer;
 
        switch(vid.renderpath)
        {
        case RENDERPATH_GL20:
-       case RENDERPATH_CGGL:
+       case RENDERPATH_D3D9:
+       case RENDERPATH_D3D10:
+       case RENDERPATH_D3D11:
+       case RENDERPATH_SOFT:
                Cvar_SetValueQuick(&r_textureunits, vid.texunits);
                Cvar_SetValueQuick(&gl_combine, 1);
                Cvar_SetValueQuick(&r_glsl, 1);
@@ -5989,6 +3823,14 @@ void gl_main_start(void)
                r_loadgloss = false;
                r_loadfog = true;
                break;
+       case RENDERPATH_GLES2:
+               Cvar_SetValueQuick(&r_textureunits, 1);
+               Cvar_SetValueQuick(&gl_combine, 1);
+               Cvar_SetValueQuick(&r_glsl, 1);
+               r_loadnormalmap = true;
+               r_loadgloss = false;
+               r_loadfog = false;
+               break;
        }
 
        R_AnimCache_Free();
@@ -6015,18 +3857,21 @@ void gl_main_start(void)
                R_BuildNormalizationCube();
        }
        r_texture_fogattenuation = NULL;
+       r_texture_fogheighttexture = NULL;
        r_texture_gammaramps = NULL;
        //r_texture_fogintensity = NULL;
        memset(&r_bloomstate, 0, sizeof(r_bloomstate));
        memset(&r_waterstate, 0, sizeof(r_waterstate));
+       r_glsl_permutation = NULL;
        memset(r_glsl_permutationhash, 0, sizeof(r_glsl_permutationhash));
        Mem_ExpandableArray_NewArray(&r_glsl_permutationarray, r_main_mempool, sizeof(r_glsl_permutation_t), 256);
        glslshaderstring = NULL;
-#ifdef SUPPORTCG
-       memset(r_cg_permutationhash, 0, sizeof(r_cg_permutationhash));
-       Mem_ExpandableArray_NewArray(&r_cg_permutationarray, r_main_mempool, sizeof(r_cg_permutation_t), 256);
-       cgshaderstring = NULL;
+#ifdef SUPPORTD3D
+       r_hlsl_permutation = NULL;
+       memset(r_hlsl_permutationhash, 0, sizeof(r_hlsl_permutationhash));
+       Mem_ExpandableArray_NewArray(&r_hlsl_permutationarray, r_main_mempool, sizeof(r_hlsl_permutation_t), 256);
 #endif
+       hlslshaderstring = NULL;
        memset(&r_svbsp, 0, sizeof (r_svbsp));
 
        r_refdef.fogmasktable_density = 0;
@@ -6039,8 +3884,27 @@ void gl_main_shutdown(void)
 
        R_Main_FreeViewCache();
 
-       if (r_maxqueries)
-               qglDeleteQueriesARB(r_maxqueries, r_queries);
+       switch(vid.renderpath)
+       {
+       case RENDERPATH_GL11:
+       case RENDERPATH_GL13:
+       case RENDERPATH_GL20:
+       case RENDERPATH_GLES2:
+               if (r_maxqueries)
+                       qglDeleteQueriesARB(r_maxqueries, r_queries);
+               break;
+       case RENDERPATH_D3D9:
+               //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               break;
+       case RENDERPATH_D3D10:
+               Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               break;
+       case RENDERPATH_D3D11:
+               Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               break;
+       case RENDERPATH_SOFT:
+               break;
+       }
 
        r_numqueries = 0;
        r_maxqueries = 0;
@@ -6065,19 +3929,30 @@ void gl_main_shutdown(void)
        r_texture_whitecube = NULL;
        r_texture_normalizationcube = NULL;
        r_texture_fogattenuation = NULL;
+       r_texture_fogheighttexture = NULL;
        r_texture_gammaramps = NULL;
        r_texture_numcubemaps = 0;
        //r_texture_fogintensity = NULL;
        memset(&r_bloomstate, 0, sizeof(r_bloomstate));
        memset(&r_waterstate, 0, sizeof(r_waterstate));
        R_GLSL_Restart_f();
+
+       r_glsl_permutation = NULL;
+       memset(r_glsl_permutationhash, 0, sizeof(r_glsl_permutationhash));
+       Mem_ExpandableArray_FreeArray(&r_glsl_permutationarray);
+       glslshaderstring = NULL;
+#ifdef SUPPORTD3D
+       r_hlsl_permutation = NULL;
+       memset(r_hlsl_permutationhash, 0, sizeof(r_hlsl_permutationhash));
+       Mem_ExpandableArray_FreeArray(&r_hlsl_permutationarray);
+#endif
+       hlslshaderstring = NULL;
 }
 
 extern void CL_ParseEntityLump(char *entitystring);
 void gl_main_newmap(void)
 {
        // FIXME: move this code to client
-       int l;
        char *entities, entname[MAX_QPATH];
        if (r_qwskincache)
                Mem_Free(r_qwskincache);
@@ -6085,17 +3960,12 @@ void gl_main_newmap(void)
        r_qwskincache_size = 0;
        if (cl.worldmodel)
        {
-               strlcpy(entname, cl.worldmodel->name, sizeof(entname));
-               l = (int)strlen(entname) - 4;
-               if (l >= 0 && !strcmp(entname + l, ".bsp"))
+               dpsnprintf(entname, sizeof(entname), "%s.ent", cl.worldnamenoextension);
+               if ((entities = (char *)FS_LoadFile(entname, tempmempool, true, NULL)))
                {
-                       memcpy(entname + l, ".ent", 5);
-                       if ((entities = (char *)FS_LoadFile(entname, tempmempool, true, NULL)))
-                       {
-                               CL_ParseEntityLump(entities);
-                               Mem_Free(entities);
-                               return;
-                       }
+                       CL_ParseEntityLump(entities);
+                       Mem_Free(entities);
+                       return;
                }
                if (cl.worldmodel->brush.entities)
                        CL_ParseEntityLump(cl.worldmodel->brush.entities);
@@ -6152,16 +4022,21 @@ void GL_Main_Init(void)
        Cvar_RegisterVariable(&r_showdisabledepthtest);
        Cvar_RegisterVariable(&r_drawportals);
        Cvar_RegisterVariable(&r_drawentities);
+       Cvar_RegisterVariable(&r_draw2d);
+       Cvar_RegisterVariable(&r_drawworld);
        Cvar_RegisterVariable(&r_cullentities_trace);
        Cvar_RegisterVariable(&r_cullentities_trace_samples);
        Cvar_RegisterVariable(&r_cullentities_trace_tempentitysamples);
        Cvar_RegisterVariable(&r_cullentities_trace_enlarge);
        Cvar_RegisterVariable(&r_cullentities_trace_delay);
        Cvar_RegisterVariable(&r_drawviewmodel);
+       Cvar_RegisterVariable(&r_drawexteriormodel);
        Cvar_RegisterVariable(&r_speeds);
        Cvar_RegisterVariable(&r_fullbrights);
        Cvar_RegisterVariable(&r_wateralpha);
        Cvar_RegisterVariable(&r_dynamic);
+       Cvar_RegisterVariable(&r_fakelight);
+       Cvar_RegisterVariable(&r_fakelight_intensity);
        Cvar_RegisterVariable(&r_fullbright);
        Cvar_RegisterVariable(&r_shadows);
        Cvar_RegisterVariable(&r_shadows_darken);
@@ -6169,6 +4044,8 @@ void GL_Main_Init(void)
        Cvar_RegisterVariable(&r_shadows_castfrombmodels);
        Cvar_RegisterVariable(&r_shadows_throwdistance);
        Cvar_RegisterVariable(&r_shadows_throwdirection);
+       Cvar_RegisterVariable(&r_shadows_focus);
+       Cvar_RegisterVariable(&r_shadows_shadowmapscale);
        Cvar_RegisterVariable(&r_q1bsp_skymasking);
        Cvar_RegisterVariable(&r_polygonoffset_submodel_factor);
        Cvar_RegisterVariable(&r_polygonoffset_submodel_offset);
@@ -6179,6 +4056,11 @@ void GL_Main_Init(void)
        Cvar_RegisterVariable(&r_transparentdepthmasking);
        Cvar_RegisterVariable(&r_texture_dds_load);
        Cvar_RegisterVariable(&r_texture_dds_save);
+       Cvar_RegisterVariable(&r_texture_convertsRGB_2d);
+       Cvar_RegisterVariable(&r_texture_convertsRGB_skin);
+       Cvar_RegisterVariable(&r_texture_convertsRGB_cubemap);
+       Cvar_RegisterVariable(&r_texture_convertsRGB_skybox);
+       Cvar_RegisterVariable(&r_texture_convertsRGB_particles);
        Cvar_RegisterVariable(&r_textureunits);
        Cvar_RegisterVariable(&gl_combine);
        Cvar_RegisterVariable(&r_glsl);
@@ -6191,11 +4073,17 @@ void GL_Main_Init(void)
        Cvar_RegisterVariable(&r_glsl_postprocess_uservec2);
        Cvar_RegisterVariable(&r_glsl_postprocess_uservec3);
        Cvar_RegisterVariable(&r_glsl_postprocess_uservec4);
+       Cvar_RegisterVariable(&r_glsl_postprocess_uservec1_enable);
+       Cvar_RegisterVariable(&r_glsl_postprocess_uservec2_enable);
+       Cvar_RegisterVariable(&r_glsl_postprocess_uservec3_enable);
+       Cvar_RegisterVariable(&r_glsl_postprocess_uservec4_enable);
+
        Cvar_RegisterVariable(&r_water);
        Cvar_RegisterVariable(&r_water_resolutionmultiplier);
        Cvar_RegisterVariable(&r_water_clippingplanebias);
        Cvar_RegisterVariable(&r_water_refractdistort);
        Cvar_RegisterVariable(&r_water_reflectdistort);
+       Cvar_RegisterVariable(&r_water_scissormode);
        Cvar_RegisterVariable(&r_lerpsprites);
        Cvar_RegisterVariable(&r_lerpmodels);
        Cvar_RegisterVariable(&r_lerplightstyles);
@@ -6211,16 +4099,22 @@ void GL_Main_Init(void)
        Cvar_RegisterVariable(&r_hdr_scenebrightness);
        Cvar_RegisterVariable(&r_hdr_glowintensity);
        Cvar_RegisterVariable(&r_hdr_range);
+       Cvar_RegisterVariable(&r_hdr_irisadaptation);
+       Cvar_RegisterVariable(&r_hdr_irisadaptation_multiplier);
+       Cvar_RegisterVariable(&r_hdr_irisadaptation_minvalue);
+       Cvar_RegisterVariable(&r_hdr_irisadaptation_maxvalue);
+       Cvar_RegisterVariable(&r_hdr_irisadaptation_value);
+       Cvar_RegisterVariable(&r_hdr_irisadaptation_fade);
        Cvar_RegisterVariable(&r_smoothnormals_areaweighting);
        Cvar_RegisterVariable(&developer_texturelogging);
        Cvar_RegisterVariable(&gl_lightmaps);
        Cvar_RegisterVariable(&r_test);
-       Cvar_RegisterVariable(&r_batchmode);
        Cvar_RegisterVariable(&r_glsl_saturation);
+       Cvar_RegisterVariable(&r_glsl_saturation_redcompensate);
        Cvar_RegisterVariable(&r_framedatasize);
        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);
+       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);
@@ -6228,6 +4122,8 @@ void GL_Main_Init(void)
        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);
@@ -6291,9 +4187,7 @@ void GL_Init (void)
        Con_DPrintf("\nQuakeC extensions for server and client: %s\nQuakeC extensions for menu: %s\n", vm_sv_extensions, vm_m_extensions );
 
        // clear to black (loading plaque will be seen over this)
-       CHECKGLERROR
-       qglClearColor(0,0,0,1);CHECKGLERROR
-       qglClear(GL_COLOR_BUFFER_BIT);CHECKGLERROR
+       GL_Clear(GL_COLOR_BUFFER_BIT, NULL, 1.0f, 128);
 }
 
 int R_CullBox(const vec3_t mins, const vec3_t maxs)
@@ -6397,67 +4291,112 @@ int R_CullBoxCustomPlanes(const vec3_t mins, const vec3_t maxs, int numplanes, c
 
 // LordHavoc: this stores temporary data used within the same frame
 
-qboolean r_framedata_failed;
-static size_t r_framedata_size;
-static size_t r_framedata_current;
-static void *r_framedata_base;
+typedef struct r_framedata_mem_s
+{
+       struct r_framedata_mem_s *purge; // older mem block to free on next frame
+       size_t size; // how much usable space
+       size_t current; // how much space in use
+       size_t mark; // last "mark" location, temporary memory can be freed by returning to this
+       size_t wantedsize; // how much space was allocated
+       unsigned char *data; // start of real data (16byte aligned)
+}
+r_framedata_mem_t;
+
+static r_framedata_mem_t *r_framedata_mem;
 
 void R_FrameData_Reset(void)
 {
-       if (r_framedata_base)
-               Mem_Free(r_framedata_base);
-       r_framedata_base = NULL;
-       r_framedata_size = 0;
-       r_framedata_current = 0;
-       r_framedata_failed = false;
+       while (r_framedata_mem)
+       {
+               r_framedata_mem_t *next = r_framedata_mem->purge;
+               Mem_Free(r_framedata_mem);
+               r_framedata_mem = next;
+       }
 }
 
-void R_FrameData_NewFrame(void)
+void R_FrameData_Resize(void)
 {
        size_t wantedsize;
-       if (r_framedata_failed)
-               Cvar_SetValueQuick(&r_framedatasize, r_framedatasize.value + 1.0f);
        wantedsize = (size_t)(r_framedatasize.value * 1024*1024);
-       wantedsize = bound(65536, wantedsize, 128*1024*1024);
-       if (r_framedata_size != wantedsize)
+       wantedsize = bound(65536, wantedsize, 1000*1024*1024);
+       if (!r_framedata_mem || r_framedata_mem->wantedsize != wantedsize)
+       {
+               r_framedata_mem_t *newmem = (r_framedata_mem_t *)Mem_Alloc(r_main_mempool, wantedsize);
+               newmem->wantedsize = wantedsize;
+               newmem->data = (unsigned char *)(((size_t)(newmem+1) + 15) & ~15);
+               newmem->size = (unsigned char *)newmem + wantedsize - newmem->data;
+               newmem->current = 0;
+               newmem->mark = 0;
+               newmem->purge = r_framedata_mem;
+               r_framedata_mem = newmem;
+       }
+}
+
+void R_FrameData_NewFrame(void)
+{
+       R_FrameData_Resize();
+       if (!r_framedata_mem)
+               return;
+       // if we ran out of space on the last frame, free the old memory now
+       while (r_framedata_mem->purge)
        {
-               r_framedata_size = wantedsize;
-               if (r_framedata_base)
-                       Mem_Free(r_framedata_base);
-               r_framedata_base = Mem_Alloc(r_main_mempool, r_framedata_size);
+               // repeatedly remove the second item in the list, leaving only head
+               r_framedata_mem_t *next = r_framedata_mem->purge->purge;
+               Mem_Free(r_framedata_mem->purge);
+               r_framedata_mem->purge = next;
        }
-       r_framedata_current = 0;
-       r_framedata_failed = false;
+       // reset the current mem pointer
+       r_framedata_mem->current = 0;
+       r_framedata_mem->mark = 0;
 }
 
 void *R_FrameData_Alloc(size_t size)
 {
        void *data;
 
-       // align to 16 byte boundary
+       // align to 16 byte boundary - the data pointer is already aligned, so we
+       // only need to ensure the size of every allocation is also aligned
        size = (size + 15) & ~15;
-       data = (void *)((unsigned char*)r_framedata_base + r_framedata_current);
-       r_framedata_current += size;
 
-       // check overflow
-       if (r_framedata_current > r_framedata_size)
-               r_framedata_failed = true;
+       while (!r_framedata_mem || r_framedata_mem->current + size > r_framedata_mem->size)
+       {
+               // emergency - we ran out of space, allocate more memory
+               Cvar_SetValueQuick(&r_framedatasize, bound(0.25f, r_framedatasize.value * 2.0f, 128.0f));
+               R_FrameData_Resize();
+       }
+
+       data = r_framedata_mem->data + r_framedata_mem->current;
+       r_framedata_mem->current += size;
 
-       // return NULL on everything after a failure
-       if (r_framedata_failed)
-               return NULL;
+       // count the usage for stats
+       r_refdef.stats.framedatacurrent = max(r_refdef.stats.framedatacurrent, (int)r_framedata_mem->current);
+       r_refdef.stats.framedatasize = max(r_refdef.stats.framedatasize, (int)r_framedata_mem->size);
 
-       return data;
+       return (void *)data;
 }
 
 void *R_FrameData_Store(size_t size, void *data)
 {
        void *d = R_FrameData_Alloc(size);
-       if (d)
+       if (d && data)
                memcpy(d, data, size);
        return d;
 }
 
+void R_FrameData_SetMark(void)
+{
+       if (!r_framedata_mem)
+               return;
+       r_framedata_mem->mark = r_framedata_mem->current;
+}
+
+void R_FrameData_ReturnToMark(void)
+{
+       if (!r_framedata_mem)
+               return;
+       r_framedata_mem->current = r_framedata_mem->mark;
+}
+
 //==================================================================================
 
 // LordHavoc: animcache originally written by Echon, rewritten since then
@@ -6483,6 +4422,38 @@ void R_AnimCache_ClearCache(void)
                ent->animcache_normal3f = NULL;
                ent->animcache_svector3f = NULL;
                ent->animcache_tvector3f = NULL;
+               ent->animcache_vertexmesh = NULL;
+               ent->animcache_vertex3fbuffer = NULL;
+               ent->animcache_vertexmeshbuffer = NULL;
+       }
+}
+
+void R_AnimCache_UpdateEntityMeshBuffers(entity_render_t *ent, int numvertices)
+{
+       int i;
+
+       // check if we need the meshbuffers
+       if (!vid.useinterleavedarrays)
+               return;
+
+       if (!ent->animcache_vertexmesh && ent->animcache_normal3f)
+               ent->animcache_vertexmesh = (r_vertexmesh_t *)R_FrameData_Alloc(sizeof(r_vertexmesh_t)*numvertices);
+       // TODO: upload vertex3f buffer?
+       if (ent->animcache_vertexmesh)
+       {
+               memcpy(ent->animcache_vertexmesh, ent->model->surfmesh.vertexmesh, sizeof(r_vertexmesh_t)*numvertices);
+               for (i = 0;i < numvertices;i++)
+                       memcpy(ent->animcache_vertexmesh[i].vertex3f, ent->animcache_vertex3f + 3*i, sizeof(float[3]));
+               if (ent->animcache_svector3f)
+                       for (i = 0;i < numvertices;i++)
+                               memcpy(ent->animcache_vertexmesh[i].svector3f, ent->animcache_svector3f + 3*i, sizeof(float[3]));
+               if (ent->animcache_tvector3f)
+                       for (i = 0;i < numvertices;i++)
+                               memcpy(ent->animcache_vertexmesh[i].tvector3f, ent->animcache_tvector3f + 3*i, sizeof(float[3]));
+               if (ent->animcache_normal3f)
+                       for (i = 0;i < numvertices;i++)
+                               memcpy(ent->animcache_vertexmesh[i].normal3f, ent->animcache_normal3f + 3*i, sizeof(float[3]));
+               // TODO: upload vertexmeshbuffer?
        }
 }
 
@@ -6493,7 +4464,7 @@ qboolean R_AnimCache_GetEntity(entity_render_t *ent, qboolean wantnormals, qbool
        // see if it's already cached this frame
        if (ent->animcache_vertex3f)
        {
-               // add normals/tangents if needed
+               // add normals/tangents if needed (this only happens with multiple views, reflections, cameras, etc)
                if (wantnormals || wanttangents)
                {
                        if (ent->animcache_normal3f)
@@ -6504,14 +4475,14 @@ qboolean R_AnimCache_GetEntity(entity_render_t *ent, qboolean wantnormals, qbool
                        {
                                numvertices = model->surfmesh.num_vertices;
                                if (wantnormals)
-                                       ent->animcache_normal3f = R_FrameData_Alloc(sizeof(float[3])*numvertices);
+                                       ent->animcache_normal3f = (float *)R_FrameData_Alloc(sizeof(float[3])*numvertices);
                                if (wanttangents)
                                {
-                                       ent->animcache_svector3f = R_FrameData_Alloc(sizeof(float[3])*numvertices);
-                                       ent->animcache_tvector3f = R_FrameData_Alloc(sizeof(float[3])*numvertices);
+                                       ent->animcache_svector3f = (float *)R_FrameData_Alloc(sizeof(float[3])*numvertices);
+                                       ent->animcache_tvector3f = (float *)R_FrameData_Alloc(sizeof(float[3])*numvertices);
                                }
-                               if (!r_framedata_failed)
-                                       model->AnimateVertices(model, ent->frameblend, ent->skeleton, NULL, wantnormals ? ent->animcache_normal3f : NULL, wanttangents ? ent->animcache_svector3f : NULL, wanttangents ? ent->animcache_tvector3f : NULL);
+                               model->AnimateVertices(model, ent->frameblend, ent->skeleton, NULL, wantnormals ? ent->animcache_normal3f : NULL, wanttangents ? ent->animcache_svector3f : NULL, wanttangents ? ent->animcache_tvector3f : NULL);
+                               R_AnimCache_UpdateEntityMeshBuffers(ent, model->surfmesh.num_vertices);
                        }
                }
        }
@@ -6522,48 +4493,51 @@ qboolean R_AnimCache_GetEntity(entity_render_t *ent, qboolean wantnormals, qbool
                        return false;
                // get some memory for this entity and generate mesh data
                numvertices = model->surfmesh.num_vertices;
-               ent->animcache_vertex3f = R_FrameData_Alloc(sizeof(float[3])*numvertices);
+               ent->animcache_vertex3f = (float *)R_FrameData_Alloc(sizeof(float[3])*numvertices);
                if (wantnormals)
-                       ent->animcache_normal3f = R_FrameData_Alloc(sizeof(float[3])*numvertices);
+                       ent->animcache_normal3f = (float *)R_FrameData_Alloc(sizeof(float[3])*numvertices);
                if (wanttangents)
                {
-                       ent->animcache_svector3f = R_FrameData_Alloc(sizeof(float[3])*numvertices);
-                       ent->animcache_tvector3f = R_FrameData_Alloc(sizeof(float[3])*numvertices);
+                       ent->animcache_svector3f = (float *)R_FrameData_Alloc(sizeof(float[3])*numvertices);
+                       ent->animcache_tvector3f = (float *)R_FrameData_Alloc(sizeof(float[3])*numvertices);
                }
-               if (!r_framedata_failed)
-                       model->AnimateVertices(model, ent->frameblend, ent->skeleton, ent->animcache_vertex3f, ent->animcache_normal3f, ent->animcache_svector3f, ent->animcache_tvector3f);
+               model->AnimateVertices(model, ent->frameblend, ent->skeleton, ent->animcache_vertex3f, ent->animcache_normal3f, ent->animcache_svector3f, ent->animcache_tvector3f);
+               R_AnimCache_UpdateEntityMeshBuffers(ent, model->surfmesh.num_vertices);
        }
-       return !r_framedata_failed;
+       return true;
 }
 
 void R_AnimCache_CacheVisibleEntities(void)
 {
        int i;
-       qboolean wantnormals = !r_showsurfaces.integer;
+       qboolean wantnormals = true;
        qboolean wanttangents = !r_showsurfaces.integer;
 
        switch(vid.renderpath)
        {
        case RENDERPATH_GL20:
-       case RENDERPATH_CGGL:
+       case RENDERPATH_D3D9:
+       case RENDERPATH_D3D10:
+       case RENDERPATH_D3D11:
+       case RENDERPATH_GLES2:
                break;
        case RENDERPATH_GL13:
        case RENDERPATH_GL11:
                wanttangents = false;
                break;
+       case RENDERPATH_SOFT:
+               break;
        }
 
+       if (r_shownormals.integer)
+               wanttangents = wantnormals = true;
+
        // TODO: thread this
        // NOTE: R_PrepareRTLights() also caches entities
 
        for (i = 0;i < r_refdef.scene.numentities;i++)
                if (r_refdef.viewcache.entityvisible[i])
                        R_AnimCache_GetEntity(r_refdef.scene.entities[i], wantnormals, wanttangents);
-
-       if (r_shadows.integer)
-               for (i = 0;i < r_refdef.scene.numentities;i++)
-                       if (!r_refdef.viewcache.entityvisible[i])
-                               R_AnimCache_GetEntity(r_refdef.scene.entities[i], false, false);
 }
 
 //==================================================================================
@@ -6574,13 +4548,14 @@ static void R_View_UpdateEntityLighting (void)
        entity_render_t *ent;
        vec3_t tempdiffusenormal, avg;
        vec_t f, fa, fd, fdd;
+       qboolean skipunseen = r_shadows.integer != 1; //|| R_Shadow_ShadowMappingEnabled();
 
        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)
+               if (!r_refdef.viewcache.entityvisible[i] && skipunseen)
                        continue;
 
                // skip bsp models
@@ -6601,7 +4576,18 @@ static void R_View_UpdateEntityLighting (void)
                {
                        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);
+
+                       // complete lightning for lit sprites
+                       // todo: make a EF_ field so small ents could be lit purely by modellight and skipping real rtlight pass (like EF_NORTLIGHT)?
+                       if (ent->model->type == mod_sprite && !(ent->model->data_textures[0].basematerialflags & MATERIALFLAG_FULLBRIGHT))
+                       {
+                               if (ent->model->sprite.sprnum_type == SPR_OVERHEAD) // apply offset for overhead sprites
+                                       org[2] = org[2] + r_overheadsprites_pushback.value;
+                               R_CompleteLightPoint(ent->modellight_ambient, ent->modellight_diffuse, tempdiffusenormal, org, LP_LIGHTMAP | LP_RTWORLD | LP_DYNLIGHT);
+                       }
+                       else
+                               R_CompleteLightPoint(ent->modellight_ambient, ent->modellight_diffuse, tempdiffusenormal, org, LP_LIGHTMAP);
+
                        if(ent->flags & RENDER_EQUALIZE)
                        {
                                // first fix up ambient lighting...
@@ -6633,13 +4619,16 @@ static void R_View_UpdateEntityLighting (void)
 
                                if(r_equalize_entities_to.value > 0 && r_equalize_entities_by.value != 0)
                                {
-                                       VectorMA(ent->modellight_ambient, 0.25f, ent->modellight_diffuse, avg);
-                                       f = 0.299f * avg[0] + 0.587f * avg[1] + 0.114f * avg[2];
+                                       fa = 0.299f * ent->modellight_ambient[0] + 0.587f * ent->modellight_ambient[1] + 0.114f * ent->modellight_ambient[2];
+                                       fd = 0.299f * ent->modellight_diffuse[0] + 0.587f * ent->modellight_diffuse[1] + 0.114f * ent->modellight_diffuse[2];
+                                       f = fa + 0.25 * fd;
                                        if(f > 0)
                                        {
-                                               f = pow(f / r_equalize_entities_to.value, -r_equalize_entities_by.value);
-                                               VectorScale(ent->modellight_ambient, f, ent->modellight_ambient);
-                                               VectorScale(ent->modellight_diffuse, f, ent->modellight_diffuse);
+                                               // adjust brightness and saturation to target
+                                               avg[0] = avg[1] = avg[2] = fa / f;
+                                               VectorLerp(ent->modellight_ambient, r_equalize_entities_by.value, avg, ent->modellight_ambient);
+                                               avg[0] = avg[1] = avg[2] = fd / f;
+                                               VectorLerp(ent->modellight_diffuse, r_equalize_entities_by.value, avg, ent->modellight_diffuse);
                                        }
                                }
                        }
@@ -6676,6 +4665,10 @@ static qboolean R_CanSeeBox(int numsamples, vec_t enlarge, vec3_t eye, vec3_t en
        boxmins[2] = (enlarge+1) * entboxmins[2] - enlarge * entboxmaxs[2];
        boxmaxs[2] = (enlarge+1) * entboxmaxs[2] - enlarge * entboxmins[2];
 
+       // return true if eye is inside enlarged box
+       if (BoxesOverlap(boxmins, boxmaxs, eye, eye))
+               return true;
+
        // try center
        VectorCopy(eye, start);
        VectorMAM(0.5f, boxmins, 0.5f, boxmaxs, end);
@@ -6701,7 +4694,14 @@ static void R_View_UpdateEntityVisible (void)
        int samples;
        entity_render_t *ent;
 
-       renderimask = r_refdef.envmap ? (RENDER_EXTERIORMODEL | RENDER_VIEWMODEL) : ((chase_active.integer || r_waterstate.renderingscene) ? RENDER_VIEWMODEL : RENDER_EXTERIORMODEL);
+       renderimask = r_refdef.envmap                                    ? (RENDER_EXTERIORMODEL | RENDER_VIEWMODEL)
+               : r_waterstate.renderingrefraction                       ? (RENDER_EXTERIORMODEL | RENDER_VIEWMODEL)
+               : (chase_active.integer || r_waterstate.renderingscene)  ? RENDER_VIEWMODEL
+               :                                                          RENDER_EXTERIORMODEL;
+       if (!r_drawviewmodel.integer)
+               renderimask |= RENDER_VIEWMODEL;
+       if (!r_drawexteriormodel.integer)
+               renderimask |= RENDER_EXTERIORMODEL;
        if (r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.BoxTouchingVisibleLeafs)
        {
                // worldmodel can check visibility
@@ -6710,11 +4710,12 @@ static void R_View_UpdateEntityVisible (void)
                {
                        ent = r_refdef.scene.entities[i];
                        if (!(ent->flags & renderimask))
-                       if (!R_CullBox(ent->mins, ent->maxs) || (ent->model->type == mod_sprite && (ent->model->sprite.sprnum_type == SPR_LABEL || ent->model->sprite.sprnum_type == SPR_LABEL_SCALE)))
+                       if (!R_CullBox(ent->mins, ent->maxs) || (ent->model && ent->model->type == mod_sprite && (ent->model->sprite.sprnum_type == SPR_LABEL || ent->model->sprite.sprnum_type == SPR_LABEL_SCALE)))
                        if ((ent->flags & (RENDER_NODEPTHTEST | RENDER_VIEWMODEL)) || r_refdef.scene.worldmodel->brush.BoxTouchingVisibleLeafs(r_refdef.scene.worldmodel, r_refdef.viewcache.world_leafvisible, ent->mins, ent->maxs))
                                r_refdef.viewcache.entityvisible[i] = true;
                }
-               if(r_cullentities_trace.integer && r_refdef.scene.worldmodel->brush.TraceLineOfSight)
+               if(r_cullentities_trace.integer && r_refdef.scene.worldmodel->brush.TraceLineOfSight && !r_refdef.view.useclipplane)
+                       // sorry, this check doesn't work for portal/reflection/refraction renders as the view origin is not useful for culling
                {
                        for (i = 0;i < r_refdef.scene.numentities;i++)
                        {
@@ -6827,11 +4828,68 @@ static void R_DrawModelsAddWaterPlanes(void)
        }
 }
 
-static void R_View_SetFrustum(void)
+void R_HDR_UpdateIrisAdaptation(const vec3_t point)
+{
+       if (r_hdr_irisadaptation.integer)
+       {
+               vec3_t ambient;
+               vec3_t diffuse;
+               vec3_t diffusenormal;
+               vec_t brightness;
+               vec_t goal;
+               vec_t adjust;
+               vec_t current;
+               R_CompleteLightPoint(ambient, diffuse, diffusenormal, point, LP_LIGHTMAP | LP_RTWORLD | LP_DYNLIGHT);
+               brightness = (ambient[0] + ambient[1] + ambient[2] + diffuse[0] + diffuse[1] + diffuse[2]) * (1.0f / 3.0f);
+               brightness = max(0.0000001f, brightness);
+               goal = r_hdr_irisadaptation_multiplier.value / brightness;
+               goal = bound(r_hdr_irisadaptation_minvalue.value, goal, r_hdr_irisadaptation_maxvalue.value);
+               adjust = r_hdr_irisadaptation_fade.value * cl.realframetime;
+               current = r_hdr_irisadaptation_value.value;
+               if (current < goal)
+                       current = min(current + adjust, goal);
+               else if (current > goal)
+                       current = max(current - adjust, goal);
+               if (fabs(r_hdr_irisadaptation_value.value - current) > 0.0001f)
+                       Cvar_SetValueQuick(&r_hdr_irisadaptation_value, current);
+       }
+       else if (r_hdr_irisadaptation_value.value != 1.0f)
+               Cvar_SetValueQuick(&r_hdr_irisadaptation_value, 1.0f);
+}
+
+static void R_View_SetFrustum(const int *scissor)
 {
        int i;
-       double slopex, slopey;
-       vec3_t forward, left, up, origin;
+       double fpx = +1, fnx = -1, fpy = +1, fny = -1;
+       vec3_t forward, left, up, origin, v;
+
+       if(scissor)
+       {
+               // flipped x coordinates (because x points left here)
+               fpx =  1.0 - 2.0 * (scissor[0]              - r_refdef.view.viewport.x) / (double) (r_refdef.view.viewport.width);
+               fnx =  1.0 - 2.0 * (scissor[0] + scissor[2] - r_refdef.view.viewport.x) / (double) (r_refdef.view.viewport.width);
+
+               // D3D Y coordinate is top to bottom, OpenGL is bottom to top, fix the D3D one
+               switch(vid.renderpath)
+               {
+                       case RENDERPATH_D3D9:
+                       case RENDERPATH_D3D10:
+                       case RENDERPATH_D3D11:
+                       case RENDERPATH_SOFT:
+                               // non-flipped y coordinates
+                               fny = -1.0 + 2.0 * (vid.height - scissor[1] - scissor[3] - r_refdef.view.viewport.y) / (double) (r_refdef.view.viewport.height);
+                               fpy = -1.0 + 2.0 * (vid.height - scissor[1]              - r_refdef.view.viewport.y) / (double) (r_refdef.view.viewport.height);
+                               break;
+                       case RENDERPATH_GL11:
+                       case RENDERPATH_GL13:
+                       case RENDERPATH_GL20:
+                       case RENDERPATH_GLES2:
+                               // non-flipped y coordinates
+                               fny = -1.0 + 2.0 * (scissor[1]              - r_refdef.view.viewport.y) / (double) (r_refdef.view.viewport.height);
+                               fpy = -1.0 + 2.0 * (scissor[1] + scissor[3] - r_refdef.view.viewport.y) / (double) (r_refdef.view.viewport.height);
+                               break;
+               }
+       }
 
        // we can't trust r_refdef.view.forward and friends in reflected scenes
        Matrix4x4_ToVectors(&r_refdef.view.matrix, forward, left, up, origin);
@@ -6900,13 +4958,29 @@ static void R_View_SetFrustum(void)
 
        if (r_refdef.view.useperspective)
        {
-               slopex = 1.0 / r_refdef.view.frustum_x;
-               slopey = 1.0 / r_refdef.view.frustum_y;
-               VectorMA(forward, -slopex, left, r_refdef.view.frustum[0].normal);
-               VectorMA(forward,  slopex, left, r_refdef.view.frustum[1].normal);
-               VectorMA(forward, -slopey, up  , r_refdef.view.frustum[2].normal);
-               VectorMA(forward,  slopey, up  , r_refdef.view.frustum[3].normal);
-               VectorCopy(forward, r_refdef.view.frustum[4].normal);
+               // calculate frustum corners, which are used to calculate deformed frustum planes for shadow caster culling
+               VectorMAMAM(1024, forward, fnx * 1024.0 * r_refdef.view.frustum_x, left, fny * 1024.0 * r_refdef.view.frustum_y, up, r_refdef.view.frustumcorner[0]);
+               VectorMAMAM(1024, forward, fpx * 1024.0 * r_refdef.view.frustum_x, left, fny * 1024.0 * r_refdef.view.frustum_y, up, r_refdef.view.frustumcorner[1]);
+               VectorMAMAM(1024, forward, fnx * 1024.0 * r_refdef.view.frustum_x, left, fpy * 1024.0 * r_refdef.view.frustum_y, up, r_refdef.view.frustumcorner[2]);
+               VectorMAMAM(1024, forward, fpx * 1024.0 * r_refdef.view.frustum_x, left, fpy * 1024.0 * r_refdef.view.frustum_y, up, r_refdef.view.frustumcorner[3]);
+
+               // then the normals from the corners relative to origin
+               CrossProduct(r_refdef.view.frustumcorner[2], r_refdef.view.frustumcorner[0], r_refdef.view.frustum[0].normal);
+               CrossProduct(r_refdef.view.frustumcorner[1], r_refdef.view.frustumcorner[3], r_refdef.view.frustum[1].normal);
+               CrossProduct(r_refdef.view.frustumcorner[0], r_refdef.view.frustumcorner[1], r_refdef.view.frustum[2].normal);
+               CrossProduct(r_refdef.view.frustumcorner[3], r_refdef.view.frustumcorner[2], r_refdef.view.frustum[3].normal);
+
+               // in a NORMAL view, forward cross left == up
+               // in a REFLECTED view, forward cross left == down
+               // so our cross products above need to be adjusted for a left handed coordinate system
+               CrossProduct(forward, left, v);
+               if(DotProduct(v, up) < 0)
+               {
+                       VectorNegate(r_refdef.view.frustum[0].normal, r_refdef.view.frustum[0].normal);
+                       VectorNegate(r_refdef.view.frustum[1].normal, r_refdef.view.frustum[1].normal);
+                       VectorNegate(r_refdef.view.frustum[2].normal, r_refdef.view.frustum[2].normal);
+                       VectorNegate(r_refdef.view.frustum[3].normal, r_refdef.view.frustum[3].normal);
+               }
 
                // Leaving those out was a mistake, those were in the old code, and they
                // fix a reproducable bug in this one: frustum culling got fucked up when viewmatrix was an identity matrix
@@ -6916,11 +4990,14 @@ static void R_View_SetFrustum(void)
                VectorNormalize(r_refdef.view.frustum[2].normal);
                VectorNormalize(r_refdef.view.frustum[3].normal);
 
-               // calculate frustum corners, which are used to calculate deformed frustum planes for shadow caster culling
-               VectorMAMAMAM(1, r_refdef.view.origin, 1024, forward, -1024 * r_refdef.view.frustum_x, left, -1024 * r_refdef.view.frustum_y, up, r_refdef.view.frustumcorner[0]);
-               VectorMAMAMAM(1, r_refdef.view.origin, 1024, forward,  1024 * r_refdef.view.frustum_x, left, -1024 * r_refdef.view.frustum_y, up, r_refdef.view.frustumcorner[1]);
-               VectorMAMAMAM(1, r_refdef.view.origin, 1024, forward, -1024 * r_refdef.view.frustum_x, left,  1024 * r_refdef.view.frustum_y, up, r_refdef.view.frustumcorner[2]);
-               VectorMAMAMAM(1, r_refdef.view.origin, 1024, forward,  1024 * r_refdef.view.frustum_x, left,  1024 * r_refdef.view.frustum_y, up, r_refdef.view.frustumcorner[3]);
+               // make the corners absolute
+               VectorAdd(r_refdef.view.frustumcorner[0], r_refdef.view.origin, r_refdef.view.frustumcorner[0]);
+               VectorAdd(r_refdef.view.frustumcorner[1], r_refdef.view.origin, r_refdef.view.frustumcorner[1]);
+               VectorAdd(r_refdef.view.frustumcorner[2], r_refdef.view.origin, r_refdef.view.frustumcorner[2]);
+               VectorAdd(r_refdef.view.frustumcorner[3], r_refdef.view.origin, r_refdef.view.frustumcorner[3]);
+
+               // one more normal
+               VectorCopy(forward, r_refdef.view.frustum[4].normal);
 
                r_refdef.view.frustum[0].dist = DotProduct (r_refdef.view.origin, r_refdef.view.frustum[0].normal);
                r_refdef.view.frustum[1].dist = DotProduct (r_refdef.view.origin, r_refdef.view.frustum[1].normal);
@@ -6982,10 +5059,19 @@ static void R_View_SetFrustum(void)
        //PlaneClassify(&frustum[4]);
 }
 
+void R_View_UpdateWithScissor(const int *myscissor)
+{
+       R_Main_ResizeViewCache();
+       R_View_SetFrustum(myscissor);
+       R_View_WorldVisibility(r_refdef.view.useclipplane);
+       R_View_UpdateEntityVisible();
+       R_View_UpdateEntityLighting();
+}
+
 void R_View_Update(void)
 {
        R_Main_ResizeViewCache();
-       R_View_SetFrustum();
+       R_View_SetFrustum(NULL);
        R_View_WorldVisibility(r_refdef.view.useclipplane);
        R_View_UpdateEntityVisible();
        R_View_UpdateEntityLighting();
@@ -7031,23 +5117,31 @@ void R_EntityMatrix(const matrix4x4_t *matrix)
                CHECKGLERROR
                switch(vid.renderpath)
                {
-               case RENDERPATH_GL20:
-                       if (r_glsl_permutation && r_glsl_permutation->loc_ModelViewProjectionMatrix >= 0) qglUniformMatrix4fvARB(r_glsl_permutation->loc_ModelViewProjectionMatrix, 1, false, gl_modelviewprojection16f);
-                       if (r_glsl_permutation && r_glsl_permutation->loc_ModelViewMatrix >= 0) qglUniformMatrix4fvARB(r_glsl_permutation->loc_ModelViewMatrix, 1, false, gl_modelview16f);
-                       qglLoadMatrixf(gl_modelview16f);CHECKGLERROR
-                       break;
-               case RENDERPATH_CGGL:
-#ifdef SUPPORTCG
-                       CHECKCGERROR
-                       if (r_cg_permutation && r_cg_permutation->vp_ModelViewProjectionMatrix) cgGLSetMatrixParameterfc(r_cg_permutation->vp_ModelViewProjectionMatrix, gl_modelviewprojection16f);CHECKCGERROR
-                       if (r_cg_permutation && r_cg_permutation->vp_ModelViewMatrix) cgGLSetMatrixParameterfc(r_cg_permutation->vp_ModelViewMatrix, gl_modelview16f);CHECKCGERROR
-                       qglLoadMatrixf(gl_modelview16f);CHECKGLERROR
+               case RENDERPATH_D3D9:
+#ifdef SUPPORTD3D
+                       hlslVSSetParameter16f(D3DVSREGISTER_ModelViewProjectionMatrix, gl_modelviewprojection16f);
+                       hlslVSSetParameter16f(D3DVSREGISTER_ModelViewMatrix, gl_modelview16f);
 #endif
                        break;
+               case RENDERPATH_D3D10:
+                       Con_DPrintf("FIXME D3D10 shader %s:%i\n", __FILE__, __LINE__);
+                       break;
+               case RENDERPATH_D3D11:
+                       Con_DPrintf("FIXME D3D11 shader %s:%i\n", __FILE__, __LINE__);
+                       break;
                case RENDERPATH_GL13:
                case RENDERPATH_GL11:
                        qglLoadMatrixf(gl_modelview16f);CHECKGLERROR
                        break;
+               case RENDERPATH_SOFT:
+                       DPSOFTRAST_UniformMatrix4fv(DPSOFTRAST_UNIFORM_ModelViewProjectionMatrixM1, 1, false, gl_modelviewprojection16f);
+                       DPSOFTRAST_UniformMatrix4fv(DPSOFTRAST_UNIFORM_ModelViewMatrixM1, 1, false, gl_modelview16f);
+                       break;
+               case RENDERPATH_GL20:
+               case RENDERPATH_GLES2:
+                       if (r_glsl_permutation && r_glsl_permutation->loc_ModelViewProjectionMatrix >= 0) qglUniformMatrix4fv(r_glsl_permutation->loc_ModelViewProjectionMatrix, 1, false, gl_modelviewprojection16f);
+                       if (r_glsl_permutation && r_glsl_permutation->loc_ModelViewMatrix >= 0) qglUniformMatrix4fv(r_glsl_permutation->loc_ModelViewMatrix, 1, false, gl_modelview16f);
+                       break;
                }
        }
 }
@@ -7064,21 +5158,30 @@ void R_ResetViewRendering2D(void)
        GL_Color(1, 1, 1, 1);
        GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
        GL_BlendFunc(GL_ONE, GL_ZERO);
-       GL_AlphaTest(false);
        GL_ScissorTest(false);
        GL_DepthMask(false);
        GL_DepthRange(0, 1);
        GL_DepthTest(false);
+       GL_DepthFunc(GL_LEQUAL);
        R_EntityMatrix(&identitymatrix);
        R_Mesh_ResetTextureState();
        GL_PolygonOffset(0, 0);
-       qglEnable(GL_POLYGON_OFFSET_FILL);CHECKGLERROR
-       qglDepthFunc(GL_LEQUAL);CHECKGLERROR
-       qglDisable(GL_STENCIL_TEST);CHECKGLERROR
-       qglStencilMask(~0);CHECKGLERROR
-       qglStencilFunc(GL_ALWAYS, 128, ~0);CHECKGLERROR
-       qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);CHECKGLERROR
-       GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces
+       R_SetStencil(false, 255, GL_KEEP, GL_KEEP, GL_KEEP, GL_ALWAYS, 128, 255);
+       switch(vid.renderpath)
+       {
+       case RENDERPATH_GL11:
+       case RENDERPATH_GL13:
+       case RENDERPATH_GL20:
+       case RENDERPATH_GLES2:
+               qglEnable(GL_POLYGON_OFFSET_FILL);CHECKGLERROR
+               break;
+       case RENDERPATH_D3D9:
+       case RENDERPATH_D3D10:
+       case RENDERPATH_D3D11:
+       case RENDERPATH_SOFT:
+               break;
+       }
+       GL_CullFace(GL_NONE);
 }
 
 void R_ResetViewRendering3D(void)
@@ -7090,21 +5193,46 @@ void R_ResetViewRendering3D(void)
        GL_Color(1, 1, 1, 1);
        GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
        GL_BlendFunc(GL_ONE, GL_ZERO);
-       GL_AlphaTest(false);
        GL_ScissorTest(true);
        GL_DepthMask(true);
        GL_DepthRange(0, 1);
        GL_DepthTest(true);
+       GL_DepthFunc(GL_LEQUAL);
        R_EntityMatrix(&identitymatrix);
        R_Mesh_ResetTextureState();
        GL_PolygonOffset(r_refdef.polygonfactor, r_refdef.polygonoffset);
-       qglEnable(GL_POLYGON_OFFSET_FILL);CHECKGLERROR
-       qglDepthFunc(GL_LEQUAL);CHECKGLERROR
-       qglDisable(GL_STENCIL_TEST);CHECKGLERROR
-       qglStencilMask(~0);CHECKGLERROR
-       qglStencilFunc(GL_ALWAYS, 128, ~0);CHECKGLERROR
-       qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);CHECKGLERROR
-       GL_CullFace(r_refdef.view.cullface_back);
+       R_SetStencil(false, 255, GL_KEEP, GL_KEEP, GL_KEEP, GL_ALWAYS, 128, 255);
+       switch(vid.renderpath)
+       {
+       case RENDERPATH_GL11:
+       case RENDERPATH_GL13:
+       case RENDERPATH_GL20:
+       case RENDERPATH_GLES2:
+               qglEnable(GL_POLYGON_OFFSET_FILL);CHECKGLERROR
+               break;
+       case RENDERPATH_D3D9:
+       case RENDERPATH_D3D10:
+       case RENDERPATH_D3D11:
+       case RENDERPATH_SOFT:
+               break;
+       }
+       GL_CullFace(r_refdef.view.cullface_back);
+}
+
+/*
+================
+R_RenderView_UpdateViewVectors
+================
+*/
+static void R_RenderView_UpdateViewVectors(void)
+{
+       // break apart the view matrix into vectors for various purposes
+       // it is important that this occurs outside the RenderScene function because that can be called from reflection renders, where the vectors come out wrong
+       // however the r_refdef.view.origin IS updated in RenderScene intentionally - otherwise the sky renders at the wrong origin, etc
+       Matrix4x4_ToVectors(&r_refdef.view.matrix, r_refdef.view.forward, r_refdef.view.left, r_refdef.view.up, r_refdef.view.origin);
+       VectorNegate(r_refdef.view.left, r_refdef.view.right);
+       // make an inverted copy of the view matrix for tracking sprites
+       Matrix4x4_Invert_Simple(&r_refdef.view.inverse_matrix, &r_refdef.view.matrix);
 }
 
 void R_RenderScene(void);
@@ -7113,7 +5241,7 @@ void R_RenderWaterPlanes(void);
 static void R_Water_StartFrame(void)
 {
        int i;
-       int waterwidth, waterheight, texturewidth, textureheight;
+       int waterwidth, waterheight, texturewidth, textureheight, camerawidth, cameraheight;
        r_waterstate_waterplane_t *p;
 
        if (vid.width > (int)vid.maxtexturesize_2d || vid.height > (int)vid.maxtexturesize_2d)
@@ -7122,7 +5250,11 @@ static void R_Water_StartFrame(void)
        switch(vid.renderpath)
        {
        case RENDERPATH_GL20:
-       case RENDERPATH_CGGL:
+       case RENDERPATH_D3D9:
+       case RENDERPATH_D3D10:
+       case RENDERPATH_D3D11:
+       case RENDERPATH_SOFT:
+       case RENDERPATH_GLES2:
                break;
        case RENDERPATH_GL13:
        case RENDERPATH_GL11:
@@ -7137,20 +5269,24 @@ static void R_Water_StartFrame(void)
        // calculate desired texture sizes
        // can't use water if the card does not support the texture size
        if (!r_water.integer || r_showsurfaces.integer)
-               texturewidth = textureheight = waterwidth = waterheight = 0;
+               texturewidth = textureheight = waterwidth = waterheight = camerawidth = cameraheight = 0;
        else if (vid.support.arb_texture_non_power_of_two)
        {
                texturewidth = waterwidth;
                textureheight = waterheight;
+               camerawidth = waterwidth;
+               cameraheight = waterheight;
        }
        else
        {
                for (texturewidth   = 1;texturewidth   < waterwidth ;texturewidth   *= 2);
                for (textureheight  = 1;textureheight  < waterheight;textureheight  *= 2);
+               for (camerawidth    = 1;camerawidth   <= waterwidth; camerawidth    *= 2); camerawidth  /= 2;
+               for (cameraheight   = 1;cameraheight  <= waterheight;cameraheight   *= 2); cameraheight /= 2;
        }
 
        // allocate textures as needed
-       if (r_waterstate.texturewidth != texturewidth || r_waterstate.textureheight != textureheight)
+       if (r_waterstate.texturewidth != texturewidth || r_waterstate.textureheight != textureheight || r_waterstate.camerawidth != camerawidth || r_waterstate.cameraheight != cameraheight)
        {
                r_waterstate.maxwaterplanes = MAX_WATERPLANES;
                for (i = 0, p = r_waterstate.waterplanes;i < r_waterstate.maxwaterplanes;i++, p++)
@@ -7161,10 +5297,15 @@ static void R_Water_StartFrame(void)
                        if (p->texture_reflection)
                                R_FreeTexture(p->texture_reflection);
                        p->texture_reflection = NULL;
+                       if (p->texture_camera)
+                               R_FreeTexture(p->texture_camera);
+                       p->texture_camera = NULL;
                }
                memset(&r_waterstate, 0, sizeof(r_waterstate));
                r_waterstate.texturewidth = texturewidth;
                r_waterstate.textureheight = textureheight;
+               r_waterstate.camerawidth = camerawidth;
+               r_waterstate.cameraheight = cameraheight;
        }
 
        if (r_waterstate.texturewidth)
@@ -7186,7 +5327,7 @@ static void R_Water_StartFrame(void)
        r_waterstate.numwaterplanes = 0;
 }
 
-void R_Water_AddWaterPlane(msurface_t *surface)
+void R_Water_AddWaterPlane(msurface_t *surface, int entno)
 {
        int triangleindex, planeindex;
        const int *e;
@@ -7196,6 +5337,7 @@ void R_Water_AddWaterPlane(msurface_t *surface)
        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)
@@ -7225,8 +5367,9 @@ void R_Water_AddWaterPlane(msurface_t *surface)
 
        // find a matching plane if there is one
        for (planeindex = 0, p = r_waterstate.waterplanes;planeindex < r_waterstate.numwaterplanes;planeindex++, p++)
-               if (fabs(PlaneDiff(vert[0], &p->plane)) < 1 && fabs(PlaneDiff(vert[1], &p->plane)) < 1 && fabs(PlaneDiff(vert[2], &p->plane)) < 1)
-                       break;
+               if(p->camera_entity == t->camera_entity)
+                       if (fabs(PlaneDiff(vert[0], &p->plane)) < 1 && fabs(PlaneDiff(vert[1], &p->plane)) < 1 && fabs(PlaneDiff(vert[2], &p->plane)) < 1)
+                               break;
        if (planeindex >= r_waterstate.maxwaterplanes)
                return; // nothing we can do, out of planes
 
@@ -7239,25 +5382,43 @@ void R_Water_AddWaterPlane(msurface_t *surface)
                // clear materialflags and pvs
                p->materialflags = 0;
                p->pvsvalid = false;
+               p->camera_entity = t->camera_entity;
+               VectorCopy(surface->mins, p->mins);
+               VectorCopy(surface->maxs, p->maxs);
+       }
+       else
+       {
+               // merge mins/maxs
+               p->mins[0] = min(p->mins[0], surface->mins[0]);
+               p->mins[1] = min(p->mins[1], surface->mins[1]);
+               p->mins[2] = min(p->mins[2], surface->mins[2]);
+               p->maxs[0] = max(p->maxs[0], surface->maxs[0]);
+               p->maxs[1] = max(p->maxs[1], surface->maxs[1]);
+               p->maxs[2] = max(p->maxs[2], surface->maxs[2]);
        }
        // merge this surface's materialflags into the waterplane
        p->materialflags |= t->currentmaterialflags;
-       // merge this surface's PVS into the waterplane
-       VectorMAM(0.5f, surface->mins, 0.5f, surface->maxs, center);
-       if (p->materialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION) && r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.FatPVS
-        && r_refdef.scene.worldmodel->brush.PointInLeaf && r_refdef.scene.worldmodel->brush.PointInLeaf(r_refdef.scene.worldmodel, center)->clusterindex >= 0)
+       if(!(p->materialflags & MATERIALFLAG_CAMERA))
        {
-               r_refdef.scene.worldmodel->brush.FatPVS(r_refdef.scene.worldmodel, center, 2, p->pvsbits, sizeof(p->pvsbits), p->pvsvalid);
-               p->pvsvalid = true;
+               // merge this surface's PVS into the waterplane
+               VectorMAM(0.5f, surface->mins, 0.5f, surface->maxs, center);
+               if (p->materialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA) && r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.FatPVS
+                && r_refdef.scene.worldmodel->brush.PointInLeaf && r_refdef.scene.worldmodel->brush.PointInLeaf(r_refdef.scene.worldmodel, center)->clusterindex >= 0)
+               {
+                       r_refdef.scene.worldmodel->brush.FatPVS(r_refdef.scene.worldmodel, center, 2, p->pvsbits, sizeof(p->pvsbits), p->pvsvalid);
+                       p->pvsvalid = true;
+               }
        }
 }
 
 static void R_Water_ProcessPlanes(void)
 {
+       int myscissor[4];
        r_refdef_view_t originalview;
        r_refdef_view_t myview;
        int planeindex;
        r_waterstate_waterplane_t *p;
+       vec3_t visorigin;
 
        originalview = r_refdef.view;
 
@@ -7267,15 +5428,22 @@ static void R_Water_ProcessPlanes(void)
                if (p->materialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION))
                {
                        if (!p->texture_refraction)
-                               p->texture_refraction = R_LoadTexture2D(r_main_texturepool, va("waterplane%i_refraction", planeindex), r_waterstate.texturewidth, r_waterstate.textureheight, NULL, TEXTYPE_COLORBUFFER, TEXF_FORCELINEAR | TEXF_CLAMP, NULL);
+                               p->texture_refraction = R_LoadTexture2D(r_main_texturepool, va("waterplane%i_refraction", planeindex), r_waterstate.texturewidth, r_waterstate.textureheight, NULL, TEXTYPE_COLORBUFFER, TEXF_RENDERTARGET | TEXF_FORCELINEAR | TEXF_CLAMP, -1, NULL);
                        if (!p->texture_refraction)
                                goto error;
                }
+               else if (p->materialflags & MATERIALFLAG_CAMERA)
+               {
+                       if (!p->texture_camera)
+                               p->texture_camera = R_LoadTexture2D(r_main_texturepool, va("waterplane%i_camera", planeindex), r_waterstate.camerawidth, r_waterstate.cameraheight, NULL, TEXTYPE_COLORBUFFER, TEXF_RENDERTARGET | TEXF_FORCELINEAR, -1, NULL);
+                       if (!p->texture_camera)
+                               goto error;
+               }
 
                if (p->materialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFLECTION))
                {
                        if (!p->texture_reflection)
-                               p->texture_reflection = R_LoadTexture2D(r_main_texturepool, va("waterplane%i_reflection", planeindex), r_waterstate.texturewidth, r_waterstate.textureheight, NULL, TEXTYPE_COLORBUFFER, TEXF_FORCELINEAR | TEXF_CLAMP, NULL);
+                               p->texture_reflection = R_LoadTexture2D(r_main_texturepool, va("waterplane%i_reflection", planeindex), r_waterstate.texturewidth, r_waterstate.textureheight, NULL, TEXTYPE_COLORBUFFER, TEXF_RENDERTARGET | TEXF_FORCELINEAR | TEXF_CLAMP, -1, NULL);
                        if (!p->texture_reflection)
                                goto error;
                }
@@ -7294,11 +5462,19 @@ static void R_Water_ProcessPlanes(void)
                if (p->materialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFLECTION))
                {
                        r_refdef.view = myview;
+                       if(r_water_scissormode.integer)
+                       {
+                               R_SetupView(true);
+                               if(R_ScissorForBBox(p->mins, p->maxs, myscissor))
+                                       continue; // FIXME the plane then still may get rendered but with broken texture, but it sure won't be visible
+                       }
+
                        // 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)
                        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;
@@ -7313,7 +5489,12 @@ static void R_Water_ProcessPlanes(void)
 
                        R_ResetViewRendering3D();
                        R_ClearScreen(r_refdef.fogenabled);
-                       R_View_Update();
+                       if(r_water_scissormode.integer & 2)
+                               R_View_UpdateWithScissor(myscissor);
+                       else
+                               R_View_Update();
+                       if(r_water_scissormode.integer & 1)
+                               GL_Scissor(myscissor[0], myscissor[1], myscissor[2], myscissor[3]);
                        R_RenderScene();
 
                        R_Mesh_CopyToTexture(p->texture_reflection, 0, 0, r_refdef.view.viewport.x, r_refdef.view.viewport.y, r_refdef.view.viewport.width, r_refdef.view.viewport.height);
@@ -7324,17 +5505,93 @@ static void R_Water_ProcessPlanes(void)
                if (p->materialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION))
                {
                        r_refdef.view = myview;
+                       if(r_water_scissormode.integer)
+                       {
+                               R_SetupView(true);
+                               if(R_ScissorForBBox(p->mins, p->maxs, myscissor))
+                                       continue; // FIXME the plane then still may get rendered but with broken texture, but it sure won't be visible
+                       }
+
+                       r_waterstate.renderingrefraction = true;
+
                        r_refdef.view.clipplane = p->plane;
                        VectorNegate(r_refdef.view.clipplane.normal, r_refdef.view.clipplane.normal);
                        r_refdef.view.clipplane.dist = -r_refdef.view.clipplane.dist;
+
+                       if((p->materialflags & MATERIALFLAG_CAMERA) && p->camera_entity)
+                       {
+                               // we need to perform a matrix transform to render the view... so let's get the transformation matrix
+                               r_waterstate.renderingrefraction = false; // we don't want to hide the player model from these ones
+                               CL_VM_TransformView(p->camera_entity - MAX_EDICTS, &r_refdef.view.matrix, &r_refdef.view.clipplane, visorigin);
+                               R_RenderView_UpdateViewVectors();
+                               if(r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.FatPVS)
+                               {
+                                       r_refdef.view.usecustompvs = true;
+                                       r_refdef.scene.worldmodel->brush.FatPVS(r_refdef.scene.worldmodel, visorigin, 2, r_refdef.viewcache.world_pvsbits, (r_refdef.viewcache.world_numclusters+7)>>3, false);
+                               }
+                       }
+
                        PlaneClassify(&r_refdef.view.clipplane);
 
                        R_ResetViewRendering3D();
                        R_ClearScreen(r_refdef.fogenabled);
-                       R_View_Update();
+                       if(r_water_scissormode.integer & 2)
+                               R_View_UpdateWithScissor(myscissor);
+                       else
+                               R_View_Update();
+                       if(r_water_scissormode.integer & 1)
+                               GL_Scissor(myscissor[0], myscissor[1], myscissor[2], myscissor[3]);
                        R_RenderScene();
 
                        R_Mesh_CopyToTexture(p->texture_refraction, 0, 0, r_refdef.view.viewport.x, r_refdef.view.viewport.y, r_refdef.view.viewport.width, r_refdef.view.viewport.height);
+                       r_waterstate.renderingrefraction = false;
+               }
+               else if (p->materialflags & MATERIALFLAG_CAMERA)
+               {
+                       r_refdef.view = myview;
+
+                       r_refdef.view.clipplane = p->plane;
+                       VectorNegate(r_refdef.view.clipplane.normal, r_refdef.view.clipplane.normal);
+                       r_refdef.view.clipplane.dist = -r_refdef.view.clipplane.dist;
+
+                       r_refdef.view.width = r_waterstate.camerawidth;
+                       r_refdef.view.height = r_waterstate.cameraheight;
+                       r_refdef.view.frustum_x = 1; // tan(45 * M_PI / 180.0);
+                       r_refdef.view.frustum_y = 1; // tan(45 * M_PI / 180.0);
+
+                       if(p->camera_entity)
+                       {
+                               // we need to perform a matrix transform to render the view... so let's get the transformation matrix
+                               CL_VM_TransformView(p->camera_entity - MAX_EDICTS, &r_refdef.view.matrix, &r_refdef.view.clipplane, visorigin);
+                       }
+
+                       // note: all of the view is used for displaying... so
+                       // there is no use in scissoring
+
+                       // reverse the cullface settings for this render
+                       r_refdef.view.cullface_front = GL_FRONT;
+                       r_refdef.view.cullface_back = GL_BACK;
+                       // also reverse the view matrix
+                       Matrix4x4_ConcatScale3(&r_refdef.view.matrix, 1, 1, -1); // this serves to invert texcoords in the result, as the copied texture is mapped the wrong way round
+                       R_RenderView_UpdateViewVectors();
+                       if(p->camera_entity && r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.FatPVS)
+                       {
+                               r_refdef.view.usecustompvs = true;
+                               r_refdef.scene.worldmodel->brush.FatPVS(r_refdef.scene.worldmodel, visorigin, 2, r_refdef.viewcache.world_pvsbits, (r_refdef.viewcache.world_numclusters+7)>>3, false);
+                       }
+                       
+                       // camera needs no clipplane
+                       r_refdef.view.useclipplane = false;
+
+                       PlaneClassify(&r_refdef.view.clipplane);
+
+                       R_ResetViewRendering3D();
+                       R_ClearScreen(r_refdef.fogenabled);
+                       R_View_Update();
+                       R_RenderScene();
+
+                       R_Mesh_CopyToTexture(p->texture_camera, 0, 0, r_refdef.view.viewport.x, r_refdef.view.viewport.y, r_refdef.view.viewport.width, r_refdef.view.viewport.height);
+                       r_waterstate.renderingrefraction = false;
                }
 
        }
@@ -7359,7 +5616,11 @@ void R_Bloom_StartFrame(void)
        switch(vid.renderpath)
        {
        case RENDERPATH_GL20:
-       case RENDERPATH_CGGL:
+       case RENDERPATH_D3D9:
+       case RENDERPATH_D3D10:
+       case RENDERPATH_D3D11:
+       case RENDERPATH_SOFT:
+       case RENDERPATH_GLES2:
                break;
        case RENDERPATH_GL13:
        case RENDERPATH_GL11:
@@ -7412,7 +5673,7 @@ void R_Bloom_StartFrame(void)
                r_bloomstate.screentexturewidth = screentexturewidth;
                r_bloomstate.screentextureheight = screentextureheight;
                if (r_bloomstate.screentexturewidth && r_bloomstate.screentextureheight)
-                       r_bloomstate.texture_screen = R_LoadTexture2D(r_main_texturepool, "screen", r_bloomstate.screentexturewidth, r_bloomstate.screentextureheight, NULL, TEXTYPE_COLORBUFFER, TEXF_FORCENEAREST | TEXF_CLAMP, NULL);
+                       r_bloomstate.texture_screen = R_LoadTexture2D(r_main_texturepool, "screen", r_bloomstate.screentexturewidth, r_bloomstate.screentextureheight, NULL, TEXTYPE_COLORBUFFER, TEXF_RENDERTARGET | TEXF_FORCENEAREST | TEXF_CLAMP, -1, NULL);
        }
        if (r_bloomstate.bloomtexturewidth != bloomtexturewidth || r_bloomstate.bloomtextureheight != bloomtextureheight)
        {
@@ -7422,7 +5683,7 @@ void R_Bloom_StartFrame(void)
                r_bloomstate.bloomtexturewidth = bloomtexturewidth;
                r_bloomstate.bloomtextureheight = bloomtextureheight;
                if (r_bloomstate.bloomtexturewidth && r_bloomstate.bloomtextureheight)
-                       r_bloomstate.texture_bloom = R_LoadTexture2D(r_main_texturepool, "bloom", r_bloomstate.bloomtexturewidth, r_bloomstate.bloomtextureheight, NULL, TEXTYPE_COLORBUFFER, TEXF_FORCELINEAR | TEXF_CLAMP, NULL);
+                       r_bloomstate.texture_bloom = R_LoadTexture2D(r_main_texturepool, "bloom", r_bloomstate.bloomtexturewidth, r_bloomstate.bloomtextureheight, NULL, TEXTYPE_COLORBUFFER, TEXF_RENDERTARGET | TEXF_FORCELINEAR | TEXF_CLAMP, -1, NULL);
        }
 
        // when doing a reduced render (HDR) we want to use a smaller area
@@ -7454,6 +5715,30 @@ void R_Bloom_StartFrame(void)
        r_bloomstate.bloomtexcoord2f[6] = 0;
        r_bloomstate.bloomtexcoord2f[7] = 0;
 
+       switch(vid.renderpath)
+       {
+       case RENDERPATH_GL11:
+       case RENDERPATH_GL13:
+       case RENDERPATH_GL20:
+       case RENDERPATH_SOFT:
+       case RENDERPATH_GLES2:
+               break;
+       case RENDERPATH_D3D9:
+       case RENDERPATH_D3D10:
+       case RENDERPATH_D3D11:
+               {
+                       int i;
+                       for (i = 0;i < 4;i++)
+                       {
+                               r_bloomstate.screentexcoord2f[i*2+0] += 0.5f / (float)r_bloomstate.screentexturewidth;
+                               r_bloomstate.screentexcoord2f[i*2+1] += 0.5f / (float)r_bloomstate.screentextureheight;
+                               r_bloomstate.bloomtexcoord2f[i*2+0] += 0.5f / (float)r_bloomstate.bloomtexturewidth;
+                               r_bloomstate.bloomtexcoord2f[i*2+1] += 0.5f / (float)r_bloomstate.bloomtextureheight;
+                       }
+               }
+               break;
+       }
+
        if (r_hdr.integer || r_bloom.integer)
        {
                r_bloomstate.enabled = true;
@@ -7472,10 +5757,25 @@ void R_Bloom_CopyBloomTexture(float colorscale)
        R_SetViewport(&r_bloomstate.viewport);
        GL_BlendFunc(GL_ONE, GL_ZERO);
        GL_Color(colorscale, colorscale, colorscale, 1);
-       // TODO: optimize with multitexture or GLSL
-       R_Mesh_TexCoordPointer(0, 2, r_bloomstate.screentexcoord2f, 0, 0);
+       // D3D has upside down Y coords, the easiest way to flip this is to flip the screen vertices rather than the texcoords, so we just use a different array for that...
+       switch(vid.renderpath)
+       {
+       case RENDERPATH_GL11:
+       case RENDERPATH_GL13:
+       case RENDERPATH_GL20:
+       case RENDERPATH_SOFT:
+       case RENDERPATH_GLES2:
+               R_Mesh_PrepareVertices_Generic_Arrays(4, r_screenvertex3f, NULL, r_bloomstate.screentexcoord2f);
+               break;
+       case RENDERPATH_D3D9:
+       case RENDERPATH_D3D10:
+       case RENDERPATH_D3D11:
+               R_Mesh_PrepareVertices_Generic_Arrays(4, r_d3dscreenvertex3f, NULL, r_bloomstate.screentexcoord2f);
+               break;
+       }
+       // TODO: do boxfilter scale-down in shader?
        R_SetupShader_Generic(r_bloomstate.texture_screen, NULL, GL_MODULATE, 1);
-       R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
+       R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
        r_refdef.stats.bloom_drawpixels += r_bloomstate.bloomwidth * r_bloomstate.bloomheight;
 
        // we now have a bloom image in the framebuffer
@@ -7498,8 +5798,6 @@ void R_Bloom_MakeTexture(void)
        r_refdef.stats.bloom++;
 
        R_ResetViewRendering2D();
-       R_Mesh_VertexPointer(r_screenvertex3f, 0, 0);
-       R_Mesh_ColorPointer(NULL, 0, 0);
 
        // we have a bloom image in the framebuffer
        CHECKGLERROR
@@ -7510,10 +5808,10 @@ void R_Bloom_MakeTexture(void)
                x *= 2;
                r = bound(0, r_bloom_colorexponent.value / x, 1);
                GL_BlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
-               GL_Color(r, r, r, 1);
+               GL_Color(r,r,r,1);
+               R_Mesh_PrepareVertices_Generic_Arrays(4, r_screenvertex3f, NULL, r_bloomstate.bloomtexcoord2f);
                R_SetupShader_Generic(r_bloomstate.texture_bloom, NULL, GL_MODULATE, 1);
-               R_Mesh_TexCoordPointer(0, 2, r_bloomstate.bloomtexcoord2f, 0, 0);
-               R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
+               R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
                r_refdef.stats.bloom_drawpixels += r_bloomstate.bloomwidth * r_bloomstate.bloomheight;
 
                // copy the vertically blurred bloom view to a texture
@@ -7529,7 +5827,6 @@ void R_Bloom_MakeTexture(void)
        if(range >= 1)
                brighten *= (3 * range) / (2 * range - 1); // compensate for the "dot particle"
        R_SetupShader_Generic(r_bloomstate.texture_bloom, NULL, GL_MODULATE, 1);
-       R_Mesh_TexCoordPointer(0, 2, r_bloomstate.offsettexcoord2f, 0, 0);
 
        for (dir = 0;dir < 2;dir++)
        {
@@ -7561,7 +5858,8 @@ void R_Bloom_MakeTexture(void)
                        if(range >= 1)
                                r *= (1 - x*x/(float)(range*range));
                        GL_Color(r, r, r, 1);
-                       R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
+                       R_Mesh_PrepareVertices_Generic_Arrays(4, r_screenvertex3f, NULL, r_bloomstate.offsettexcoord2f);
+                       R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
                        r_refdef.stats.bloom_drawpixels += r_bloomstate.bloomwidth * r_bloomstate.bloomheight;
                        GL_BlendFunc(GL_ONE, GL_ONE);
                }
@@ -7570,44 +5868,24 @@ void R_Bloom_MakeTexture(void)
                R_Mesh_CopyToTexture(r_bloomstate.texture_bloom, 0, 0, r_bloomstate.viewport.x, r_bloomstate.viewport.y, r_bloomstate.viewport.width, r_bloomstate.viewport.height);
                r_refdef.stats.bloom_copypixels += r_bloomstate.viewport.width * r_bloomstate.viewport.height;
        }
-
-       // apply subtract last
-       // (just like it would be in a GLSL shader)
-       if (r_bloom_colorsubtract.value > 0 && vid.support.ext_blend_subtract)
-       {
-               GL_BlendFunc(GL_ONE, GL_ZERO);
-               R_SetupShader_Generic(r_bloomstate.texture_bloom, NULL, GL_MODULATE, 1);
-               R_Mesh_TexCoordPointer(0, 2, r_bloomstate.bloomtexcoord2f, 0, 0);
-               GL_Color(1, 1, 1, 1);
-               R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
-               r_refdef.stats.bloom_drawpixels += r_bloomstate.bloomwidth * r_bloomstate.bloomheight;
-
-               GL_BlendFunc(GL_ONE, GL_ONE);
-               qglBlendEquationEXT(GL_FUNC_REVERSE_SUBTRACT_EXT);
-               R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
-               R_Mesh_TexCoordPointer(0, 2, r_bloomstate.bloomtexcoord2f, 0, 0);
-               GL_Color(r_bloom_colorsubtract.value, r_bloom_colorsubtract.value, r_bloom_colorsubtract.value, 1);
-               R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
-               r_refdef.stats.bloom_drawpixels += r_bloomstate.bloomwidth * r_bloomstate.bloomheight;
-               qglBlendEquationEXT(GL_FUNC_ADD_EXT);
-
-               // copy the darkened bloom view to a texture
-               R_Mesh_CopyToTexture(r_bloomstate.texture_bloom, 0, 0, r_bloomstate.viewport.x, r_bloomstate.viewport.y, r_bloomstate.viewport.width, r_bloomstate.viewport.height);
-               r_refdef.stats.bloom_copypixels += r_bloomstate.viewport.width * r_bloomstate.viewport.height;
-       }
 }
 
 void R_HDR_RenderBloomTexture(void)
 {
        int oldwidth, oldheight;
        float oldcolorscale;
+       qboolean oldwaterstate;
 
+       oldwaterstate = r_waterstate.enabled;
        oldcolorscale = r_refdef.view.colorscale;
        oldwidth = r_refdef.view.width;
        oldheight = r_refdef.view.height;
        r_refdef.view.width = r_bloomstate.bloomwidth;
        r_refdef.view.height = r_bloomstate.bloomheight;
 
+       if(r_hdr.integer < 2)
+               r_waterstate.enabled = false;
+
        // 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 (using GL_EXT_framebuffer_object)
@@ -7627,7 +5905,7 @@ void R_HDR_RenderBloomTexture(void)
 
        // only do secondary renders with HDR if r_hdr is 2 or higher
        r_waterstate.numwaterplanes = 0;
-       if (r_waterstate.enabled && r_hdr.integer >= 2)
+       if (r_waterstate.enabled)
                R_RenderWaterPlanes();
 
        r_refdef.view.showdebug = true;
@@ -7640,6 +5918,7 @@ void R_HDR_RenderBloomTexture(void)
        R_Bloom_MakeTexture();
 
        // restore the view settings
+       r_waterstate.enabled = oldwaterstate;
        r_refdef.view.width = oldwidth;
        r_refdef.view.height = oldheight;
        r_refdef.view.colorscale = oldcolorscale;
@@ -7659,7 +5938,11 @@ static void R_BlendView(void)
        switch (vid.renderpath)
        {
        case RENDERPATH_GL20:
-       case RENDERPATH_CGGL:
+       case RENDERPATH_D3D9:
+       case RENDERPATH_D3D10:
+       case RENDERPATH_D3D11:
+       case RENDERPATH_SOFT:
+       case RENDERPATH_GLES2:
                permutation =
                          (r_bloomstate.texture_bloom ? SHADERPERMUTATION_BLOOM : 0)
                        | (r_refdef.viewblend[3] > 0 ? SHADERPERMUTATION_VIEWTINT : 0)
@@ -7673,8 +5956,6 @@ static void R_BlendView(void)
                        if (r_bloom_blur.value < 1) { Cvar_SetValueQuick(&r_bloom_blur, 1); }
 
                        R_ResetViewRendering2D();
-                       R_Mesh_VertexPointer(r_screenvertex3f, 0, 0);
-                       R_Mesh_ColorPointer(NULL, 0, 0);
 
                        if(!R_Stereo_Active() && (r_motionblur.value > 0 || r_damageblur.value > 0))
                        {
@@ -7705,13 +5986,27 @@ static void R_BlendView(void)
                                cl.motionbluralpha *= lhrandom(1 - r_motionblur_randomize.value, 1 + r_motionblur_randomize.value);
                                cl.motionbluralpha = bound(0, cl.motionbluralpha, r_motionblur_maxblur.value);
                                // apply the blur
-                               if (cl.motionbluralpha > 0)
+                               if (cl.motionbluralpha > 0 && !r_refdef.envmap)
                                {
                                        GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
                                        GL_Color(1, 1, 1, cl.motionbluralpha);
+                                       switch(vid.renderpath)
+                                       {
+                                       case RENDERPATH_GL11:
+                                       case RENDERPATH_GL13:
+                                       case RENDERPATH_GL20:
+                                       case RENDERPATH_SOFT:
+                                       case RENDERPATH_GLES2:
+                                               R_Mesh_PrepareVertices_Generic_Arrays(4, r_screenvertex3f, NULL, r_bloomstate.screentexcoord2f);
+                                               break;
+                                       case RENDERPATH_D3D9:
+                                       case RENDERPATH_D3D10:
+                                       case RENDERPATH_D3D11:
+                                               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_Mesh_TexCoordPointer(0, 2, r_bloomstate.screentexcoord2f, 0, 0);
-                                       R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
+                                       R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
                                        r_refdef.stats.bloom_drawpixels += r_refdef.view.viewport.width * r_refdef.view.viewport.height;
                                }
                        }
@@ -7727,12 +6022,11 @@ static void R_BlendView(void)
                        {
                                // apply a color tint to the whole view
                                R_ResetViewRendering2D();
-                               R_Mesh_VertexPointer(r_screenvertex3f, 0, 0);
-                               R_Mesh_ColorPointer(NULL, 0, 0);
+                               GL_Color(r_refdef.viewblend[0], r_refdef.viewblend[1], r_refdef.viewblend[2], r_refdef.viewblend[3]);
+                               R_Mesh_PrepareVertices_Generic_Arrays(4, r_screenvertex3f, NULL, NULL);
                                R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
                                GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-                               GL_Color(r_refdef.viewblend[0], r_refdef.viewblend[1], r_refdef.viewblend[2], r_refdef.viewblend[3]);
-                               R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
+                               R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
                        }
                        break; // no screen processing, no bloom, skip it
                }
@@ -7750,57 +6044,83 @@ static void R_BlendView(void)
 #define sscanf sscanf_s
 #endif
                memset(uservecs, 0, sizeof(uservecs));
-               sscanf(r_glsl_postprocess_uservec1.string, "%f %f %f %f", &uservecs[0][0], &uservecs[0][1], &uservecs[0][2], &uservecs[0][3]);
-               sscanf(r_glsl_postprocess_uservec2.string, "%f %f %f %f", &uservecs[1][0], &uservecs[1][1], &uservecs[1][2], &uservecs[1][3]);
-               sscanf(r_glsl_postprocess_uservec3.string, "%f %f %f %f", &uservecs[2][0], &uservecs[2][1], &uservecs[2][2], &uservecs[2][3]);
-               sscanf(r_glsl_postprocess_uservec4.string, "%f %f %f %f", &uservecs[3][0], &uservecs[3][1], &uservecs[3][2], &uservecs[3][3]);
+               if (r_glsl_postprocess_uservec1_enable.integer)
+                       sscanf(r_glsl_postprocess_uservec1.string, "%f %f %f %f", &uservecs[0][0], &uservecs[0][1], &uservecs[0][2], &uservecs[0][3]);
+               if (r_glsl_postprocess_uservec2_enable.integer)
+                       sscanf(r_glsl_postprocess_uservec2.string, "%f %f %f %f", &uservecs[1][0], &uservecs[1][1], &uservecs[1][2], &uservecs[1][3]);
+               if (r_glsl_postprocess_uservec3_enable.integer)
+                       sscanf(r_glsl_postprocess_uservec3.string, "%f %f %f %f", &uservecs[2][0], &uservecs[2][1], &uservecs[2][2], &uservecs[2][3]);
+               if (r_glsl_postprocess_uservec4_enable.integer)
+                       sscanf(r_glsl_postprocess_uservec4.string, "%f %f %f %f", &uservecs[3][0], &uservecs[3][1], &uservecs[3][2], &uservecs[3][3]);
 
                R_ResetViewRendering2D();
-               R_Mesh_VertexPointer(r_screenvertex3f, 0, 0);
-               R_Mesh_ColorPointer(NULL, 0, 0);
                GL_Color(1, 1, 1, 1);
                GL_BlendFunc(GL_ONE, GL_ZERO);
-               R_Mesh_TexCoordPointer(0, 2, r_bloomstate.screentexcoord2f, 0, 0);
-               R_Mesh_TexCoordPointer(1, 2, r_bloomstate.bloomtexcoord2f, 0, 0);
 
                switch(vid.renderpath)
                {
                case RENDERPATH_GL20:
+               case RENDERPATH_GLES2:
+                       R_Mesh_PrepareVertices_Mesh_Arrays(4, r_screenvertex3f, NULL, NULL, NULL, NULL, r_bloomstate.screentexcoord2f, r_bloomstate.bloomtexcoord2f);
                        R_SetupShader_SetPermutationGLSL(SHADERMODE_POSTPROCESS, permutation);
-                       if (r_glsl_permutation->loc_Texture_First      >= 0) R_Mesh_TexBind(GL20TU_FIRST     , r_bloomstate.texture_screen);
-                       if (r_glsl_permutation->loc_Texture_Second     >= 0) R_Mesh_TexBind(GL20TU_SECOND    , r_bloomstate.texture_bloom );
-                       if (r_glsl_permutation->loc_Texture_GammaRamps >= 0) R_Mesh_TexBind(GL20TU_GAMMARAMPS, r_texture_gammaramps       );
-                       if (r_glsl_permutation->loc_ViewTintColor      >= 0) qglUniform4fARB(r_glsl_permutation->loc_ViewTintColor     , r_refdef.viewblend[0], r_refdef.viewblend[1], r_refdef.viewblend[2], r_refdef.viewblend[3]);
-                       if (r_glsl_permutation->loc_ClientTime         >= 0) qglUniform1fARB(r_glsl_permutation->loc_ClientTime        , cl.time);
-                       if (r_glsl_permutation->loc_PixelSize          >= 0) qglUniform2fARB(r_glsl_permutation->loc_PixelSize         , 1.0/r_bloomstate.screentexturewidth, 1.0/r_bloomstate.screentextureheight);
-                       if (r_glsl_permutation->loc_UserVec1           >= 0) qglUniform4fARB(r_glsl_permutation->loc_UserVec1          , uservecs[0][0], uservecs[0][1], uservecs[0][2], uservecs[0][3]);
-                       if (r_glsl_permutation->loc_UserVec2           >= 0) qglUniform4fARB(r_glsl_permutation->loc_UserVec2          , uservecs[1][0], uservecs[1][1], uservecs[1][2], uservecs[1][3]);
-                       if (r_glsl_permutation->loc_UserVec3           >= 0) qglUniform4fARB(r_glsl_permutation->loc_UserVec3          , uservecs[2][0], uservecs[2][1], uservecs[2][2], uservecs[2][3]);
-                       if (r_glsl_permutation->loc_UserVec4           >= 0) qglUniform4fARB(r_glsl_permutation->loc_UserVec4          , uservecs[3][0], uservecs[3][1], uservecs[3][2], uservecs[3][3]);
-                       if (r_glsl_permutation->loc_Saturation         >= 0) qglUniform1fARB(r_glsl_permutation->loc_Saturation        , r_glsl_saturation.value);
-                       if (r_glsl_permutation->loc_PixelToScreenTexCoord >= 0) qglUniform2fARB(r_glsl_permutation->loc_PixelToScreenTexCoord, 1.0f/vid.width, 1.0f/vid.height);
+                       if (r_glsl_permutation->tex_Texture_First           >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_First     , r_bloomstate.texture_screen);
+                       if (r_glsl_permutation->tex_Texture_Second          >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_Second    , r_bloomstate.texture_bloom );
+                       if (r_glsl_permutation->tex_Texture_GammaRamps      >= 0) R_Mesh_TexBind(r_glsl_permutation->tex_Texture_GammaRamps, r_texture_gammaramps       );
+                       if (r_glsl_permutation->loc_ViewTintColor           >= 0) qglUniform4f(r_glsl_permutation->loc_ViewTintColor     , r_refdef.viewblend[0], r_refdef.viewblend[1], r_refdef.viewblend[2], r_refdef.viewblend[3]);
+                       if (r_glsl_permutation->loc_PixelSize               >= 0) qglUniform2f(r_glsl_permutation->loc_PixelSize         , 1.0/r_bloomstate.screentexturewidth, 1.0/r_bloomstate.screentextureheight);
+                       if (r_glsl_permutation->loc_UserVec1                >= 0) qglUniform4f(r_glsl_permutation->loc_UserVec1          , uservecs[0][0], uservecs[0][1], uservecs[0][2], uservecs[0][3]);
+                       if (r_glsl_permutation->loc_UserVec2                >= 0) qglUniform4f(r_glsl_permutation->loc_UserVec2          , uservecs[1][0], uservecs[1][1], uservecs[1][2], uservecs[1][3]);
+                       if (r_glsl_permutation->loc_UserVec3                >= 0) qglUniform4f(r_glsl_permutation->loc_UserVec3          , uservecs[2][0], uservecs[2][1], uservecs[2][2], uservecs[2][3]);
+                       if (r_glsl_permutation->loc_UserVec4                >= 0) qglUniform4f(r_glsl_permutation->loc_UserVec4          , uservecs[3][0], uservecs[3][1], uservecs[3][2], uservecs[3][3]);
+                       if (r_glsl_permutation->loc_Saturation              >= 0) qglUniform1f(r_glsl_permutation->loc_Saturation        , r_glsl_saturation.value);
+                       if (r_glsl_permutation->loc_PixelToScreenTexCoord   >= 0) qglUniform2f(r_glsl_permutation->loc_PixelToScreenTexCoord, 1.0f/vid.width, 1.0f/vid.height);
+                       if (r_glsl_permutation->loc_BloomColorSubtract      >= 0) qglUniform4f(r_glsl_permutation->loc_BloomColorSubtract   , r_bloom_colorsubtract.value, r_bloom_colorsubtract.value, r_bloom_colorsubtract.value, 0.0f);
                        break;
-               case RENDERPATH_CGGL:
-#ifdef SUPPORTCG
-                       R_SetupShader_SetPermutationCG(SHADERMODE_POSTPROCESS, permutation);
-                       if (r_cg_permutation->fp_Texture_First     ) CG_BindTexture(r_cg_permutation->fp_Texture_First     , r_bloomstate.texture_screen);CHECKCGERROR
-                       if (r_cg_permutation->fp_Texture_Second    ) CG_BindTexture(r_cg_permutation->fp_Texture_Second    , r_bloomstate.texture_bloom );CHECKCGERROR
-                       if (r_cg_permutation->fp_Texture_GammaRamps) CG_BindTexture(r_cg_permutation->fp_Texture_GammaRamps, r_texture_gammaramps       );CHECKCGERROR
-                       if (r_cg_permutation->fp_ViewTintColor     ) cgGLSetParameter4f(     r_cg_permutation->fp_ViewTintColor     , r_refdef.viewblend[0], r_refdef.viewblend[1], r_refdef.viewblend[2], r_refdef.viewblend[3]);CHECKCGERROR
-                       if (r_cg_permutation->fp_ClientTime        ) cgGLSetParameter1f(     r_cg_permutation->fp_ClientTime        , cl.time);CHECKCGERROR
-                       if (r_cg_permutation->fp_PixelSize         ) cgGLSetParameter2f(     r_cg_permutation->fp_PixelSize         , 1.0/r_bloomstate.screentexturewidth, 1.0/r_bloomstate.screentextureheight);CHECKCGERROR
-                       if (r_cg_permutation->fp_UserVec1          ) cgGLSetParameter4f(     r_cg_permutation->fp_UserVec1          , uservecs[0][0], uservecs[0][1], uservecs[0][2], uservecs[0][3]);CHECKCGERROR
-                       if (r_cg_permutation->fp_UserVec2          ) cgGLSetParameter4f(     r_cg_permutation->fp_UserVec2          , uservecs[1][0], uservecs[1][1], uservecs[1][2], uservecs[1][3]);CHECKCGERROR
-                       if (r_cg_permutation->fp_UserVec3          ) cgGLSetParameter4f(     r_cg_permutation->fp_UserVec3          , uservecs[2][0], uservecs[2][1], uservecs[2][2], uservecs[2][3]);CHECKCGERROR
-                       if (r_cg_permutation->fp_UserVec4          ) cgGLSetParameter4f(     r_cg_permutation->fp_UserVec4          , uservecs[3][0], uservecs[3][1], uservecs[3][2], uservecs[3][3]);CHECKCGERROR
-                       if (r_cg_permutation->fp_Saturation        ) cgGLSetParameter1f(     r_cg_permutation->fp_Saturation        , r_glsl_saturation.value);CHECKCGERROR
-                       if (r_cg_permutation->fp_PixelToScreenTexCoord) cgGLSetParameter2f(r_cg_permutation->fp_PixelToScreenTexCoord, 1.0f/vid.width, 1.0/vid.height);CHECKCGERROR
+               case RENDERPATH_D3D9:
+#ifdef SUPPORTD3D
+                       // D3D has upside down Y coords, the easiest way to flip this is to flip the screen vertices rather than the texcoords, so we just use a different array for that...
+                       R_Mesh_PrepareVertices_Mesh_Arrays(4, r_d3dscreenvertex3f, NULL, NULL, NULL, NULL, r_bloomstate.screentexcoord2f, r_bloomstate.bloomtexcoord2f);
+                       R_SetupShader_SetPermutationHLSL(SHADERMODE_POSTPROCESS, permutation);
+                       R_Mesh_TexBind(GL20TU_FIRST     , r_bloomstate.texture_screen);
+                       R_Mesh_TexBind(GL20TU_SECOND    , r_bloomstate.texture_bloom );
+                       R_Mesh_TexBind(GL20TU_GAMMARAMPS, r_texture_gammaramps       );
+                       hlslPSSetParameter4f(D3DPSREGISTER_ViewTintColor        , r_refdef.viewblend[0], r_refdef.viewblend[1], r_refdef.viewblend[2], r_refdef.viewblend[3]);
+                       hlslPSSetParameter2f(D3DPSREGISTER_PixelSize            , 1.0/r_bloomstate.screentexturewidth, 1.0/r_bloomstate.screentextureheight);
+                       hlslPSSetParameter4f(D3DPSREGISTER_UserVec1             , uservecs[0][0], uservecs[0][1], uservecs[0][2], uservecs[0][3]);
+                       hlslPSSetParameter4f(D3DPSREGISTER_UserVec2             , uservecs[1][0], uservecs[1][1], uservecs[1][2], uservecs[1][3]);
+                       hlslPSSetParameter4f(D3DPSREGISTER_UserVec3             , uservecs[2][0], uservecs[2][1], uservecs[2][2], uservecs[2][3]);
+                       hlslPSSetParameter4f(D3DPSREGISTER_UserVec4             , uservecs[3][0], uservecs[3][1], uservecs[3][2], uservecs[3][3]);
+                       hlslPSSetParameter1f(D3DPSREGISTER_Saturation           , r_glsl_saturation.value);
+                       hlslPSSetParameter2f(D3DPSREGISTER_PixelToScreenTexCoord, 1.0f/vid.width, 1.0/vid.height);
+                       hlslPSSetParameter4f(D3DPSREGISTER_BloomColorSubtract   , r_bloom_colorsubtract.value, r_bloom_colorsubtract.value, r_bloom_colorsubtract.value, 0.0f);
 #endif
                        break;
+               case RENDERPATH_D3D10:
+                       Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+                       break;
+               case RENDERPATH_D3D11:
+                       Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+                       break;
+               case RENDERPATH_SOFT:
+                       R_Mesh_PrepareVertices_Mesh_Arrays(4, r_screenvertex3f, NULL, NULL, NULL, NULL, r_bloomstate.screentexcoord2f, r_bloomstate.bloomtexcoord2f);
+                       R_SetupShader_SetPermutationSoft(SHADERMODE_POSTPROCESS, permutation);
+                       R_Mesh_TexBind(GL20TU_FIRST     , r_bloomstate.texture_screen);
+                       R_Mesh_TexBind(GL20TU_SECOND    , r_bloomstate.texture_bloom );
+                       R_Mesh_TexBind(GL20TU_GAMMARAMPS, r_texture_gammaramps       );
+                       DPSOFTRAST_Uniform4f(DPSOFTRAST_UNIFORM_ViewTintColor     , r_refdef.viewblend[0], r_refdef.viewblend[1], r_refdef.viewblend[2], r_refdef.viewblend[3]);
+                       DPSOFTRAST_Uniform2f(DPSOFTRAST_UNIFORM_PixelSize         , 1.0/r_bloomstate.screentexturewidth, 1.0/r_bloomstate.screentextureheight);
+                       DPSOFTRAST_Uniform4f(DPSOFTRAST_UNIFORM_UserVec1          , uservecs[0][0], uservecs[0][1], uservecs[0][2], uservecs[0][3]);
+                       DPSOFTRAST_Uniform4f(DPSOFTRAST_UNIFORM_UserVec2          , uservecs[1][0], uservecs[1][1], uservecs[1][2], uservecs[1][3]);
+                       DPSOFTRAST_Uniform4f(DPSOFTRAST_UNIFORM_UserVec3          , uservecs[2][0], uservecs[2][1], uservecs[2][2], uservecs[2][3]);
+                       DPSOFTRAST_Uniform4f(DPSOFTRAST_UNIFORM_UserVec4          , uservecs[3][0], uservecs[3][1], uservecs[3][2], uservecs[3][3]);
+                       DPSOFTRAST_Uniform1f(DPSOFTRAST_UNIFORM_Saturation        , r_glsl_saturation.value);
+                       DPSOFTRAST_Uniform2f(DPSOFTRAST_UNIFORM_PixelToScreenTexCoord, 1.0f/vid.width, 1.0f/vid.height);
+                       DPSOFTRAST_Uniform4f(DPSOFTRAST_UNIFORM_BloomColorSubtract   , r_bloom_colorsubtract.value, r_bloom_colorsubtract.value, r_bloom_colorsubtract.value, 0.0f);
+                       break;
                default:
                        break;
                }
-               R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
+               R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
                r_refdef.stats.bloom_drawpixels += r_refdef.view.viewport.width * r_refdef.view.viewport.height;
                break;
        case RENDERPATH_GL13:
@@ -7809,12 +6129,11 @@ static void R_BlendView(void)
                {
                        // apply a color tint to the whole view
                        R_ResetViewRendering2D();
-                       R_Mesh_VertexPointer(r_screenvertex3f, 0, 0);
-                       R_Mesh_ColorPointer(NULL, 0, 0);
+                       GL_Color(r_refdef.viewblend[0], r_refdef.viewblend[1], r_refdef.viewblend[2], r_refdef.viewblend[3]);
+                       R_Mesh_PrepareVertices_Generic_Arrays(4, r_screenvertex3f, NULL, NULL);
                        R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
                        GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-                       GL_Color(r_refdef.viewblend[0], r_refdef.viewblend[1], r_refdef.viewblend[2], r_refdef.viewblend[3]);
-                       R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, polygonelement3s, 0, 0);
+                       R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
                }
                break;
        }
@@ -7867,9 +6186,13 @@ void R_UpdateVariables(void)
 
        r_refdef.scene.rtworld = r_shadow_realtime_world.integer != 0;
        r_refdef.scene.rtworldshadows = r_shadow_realtime_world_shadows.integer && vid.stencil;
-       r_refdef.scene.rtdlight = (r_shadow_realtime_world.integer || r_shadow_realtime_dlight.integer) && !gl_flashblend.integer && r_dynamic.integer;
+       r_refdef.scene.rtdlight = r_shadow_realtime_dlight.integer != 0 && !gl_flashblend.integer && r_dynamic.integer;
        r_refdef.scene.rtdlightshadows = r_refdef.scene.rtdlight && r_shadow_realtime_dlight_shadows.integer && vid.stencil;
        r_refdef.lightmapintensity = r_refdef.scene.rtworld ? r_shadow_realtime_world_lightmaps.value : 1;
+       if (FAKELIGHT_ENABLED)
+       {
+               r_refdef.lightmapintensity *= r_fakelight_intensity.value;
+       }
        if (r_showsurfaces.integer)
        {
                r_refdef.scene.rtworld = false;
@@ -7928,10 +6251,14 @@ void R_UpdateVariables(void)
                r_refdef.fogrange = bound(r_refdef.fog_start, r_refdef.fogrange, r_refdef.fog_end);
                r_refdef.fograngerecip = 1.0f / r_refdef.fogrange;
                r_refdef.fogmasktabledistmultiplier = FOGMASKTABLEWIDTH * r_refdef.fograngerecip;
+               if (strcmp(r_refdef.fogheighttexturename, r_refdef.fog_height_texturename))
+                       R_BuildFogHeightTexture();
                // fog color was already set
                // update the fog texture
                if (r_refdef.fogmasktable_start != r_refdef.fog_start || r_refdef.fogmasktable_alpha != r_refdef.fog_alpha || r_refdef.fogmasktable_density != r_refdef.fog_density || r_refdef.fogmasktable_range != r_refdef.fogrange)
                        R_BuildFogTexture();
+               r_refdef.fog_height_texcoordscale = 1.0f / max(0.125f, r_refdef.fog_fadedepth);
+               r_refdef.fog_height_tablescale = r_refdef.fog_height_tablesize * r_refdef.fog_height_texcoordscale;
        }
        else
                r_refdef.fogenabled = false;
@@ -7939,7 +6266,11 @@ void R_UpdateVariables(void)
        switch(vid.renderpath)
        {
        case RENDERPATH_GL20:
-       case RENDERPATH_CGGL:
+       case RENDERPATH_D3D9:
+       case RENDERPATH_D3D10:
+       case RENDERPATH_D3D11:
+       case RENDERPATH_SOFT:
+       case RENDERPATH_GLES2:
                if(v_glslgamma.integer && !vid_gammatables_trivial)
                {
                        if(!r_texture_gammaramps || vid_gammatables_serial != r_texture_gammaramps_serial)
@@ -7962,11 +6293,11 @@ void R_UpdateVariables(void)
                                }
                                if (r_texture_gammaramps)
                                {
-                                       R_UpdateTexture(r_texture_gammaramps, &rampbgr[0][0], 0, 0, RAMPWIDTH, 1);
+                                       R_UpdateTexture(r_texture_gammaramps, &rampbgr[0][0], 0, 0, 0, RAMPWIDTH, 1, 1);
                                }
                                else
                                {
-                                       r_texture_gammaramps = R_LoadTexture2D(r_main_texturepool, "gammaramps", RAMPWIDTH, 1, &rampbgr[0][0], TEXTYPE_BGRA, TEXF_FORCELINEAR | TEXF_CLAMP | TEXF_PERSISTENT | TEXF_ALLOWUPDATES, NULL);
+                                       r_texture_gammaramps = R_LoadTexture2D(r_main_texturepool, "gammaramps", RAMPWIDTH, 1, &rampbgr[0][0], TEXTYPE_BGRA, TEXF_FORCELINEAR | TEXF_CLAMP | TEXF_PERSISTENT, -1, NULL);
                                }
                        }
                }
@@ -8018,23 +6349,38 @@ r_refdef_scene_t * R_GetScenePointer( r_refdef_scene_type_t scenetype )
 R_RenderView
 ================
 */
+int dpsoftrast_test;
 void R_RenderView(void)
 {
+       matrix4x4_t originalmatrix = r_refdef.view.matrix, offsetmatrix;
+
+       dpsoftrast_test = r_test.integer;
+
        if (r_timereport_active)
                R_TimeReport("start");
        r_textureframe++; // used only by R_GetCurrentTexture
        rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveWorldEntity/RSurf_ActiveModelEntity
 
+       if(R_CompileShader_CheckStaticParms())
+               R_GLSL_Restart_f();
+
        if (!r_drawentities.integer)
                r_refdef.scene.numentities = 0;
 
        R_AnimCache_ClearCache();
        R_FrameData_NewFrame();
 
+       /* adjust for stereo display */
+       if(R_Stereo_Active())
+       {
+               Matrix4x4_CreateFromQuakeEntity(&offsetmatrix, 0, r_stereo_separation.value * (0.5f - r_stereo_side), 0, 0, r_stereo_angle.value * (0.5f - r_stereo_side), 0, 1);
+               Matrix4x4_Concat(&r_refdef.view.matrix, &originalmatrix, &offsetmatrix);
+       }
+
        if (r_refdef.view.isoverlay)
        {
                // TODO: FIXME: move this into its own backend function maybe? [2/5/2008 Andreas]
-               GL_Clear( GL_DEPTH_BUFFER_BIT );
+               GL_Clear(GL_DEPTH_BUFFER_BIT, NULL, 1.0f, 0);
                R_TimeReport("depthclear");
 
                r_refdef.view.showdebug = false;
@@ -8044,22 +6390,21 @@ void R_RenderView(void)
 
                R_RenderScene();
 
+               r_refdef.view.matrix = originalmatrix;
+
                CHECKGLERROR
                return;
        }
 
-       if (!r_refdef.scene.entities || r_refdef.view.width * r_refdef.view.height == 0 || !r_renderview.integer/* || !r_refdef.scene.worldmodel*/)
+       if (!r_refdef.scene.entities || r_refdef.view.width * r_refdef.view.height == 0 || !r_renderview.integer || cl_videoplaying/* || !r_refdef.scene.worldmodel*/)
+       {
+               r_refdef.view.matrix = originalmatrix;
                return; //Host_Error ("R_RenderView: NULL worldmodel");
+       }
 
-       r_refdef.view.colorscale = r_hdr_scenebrightness.value;
+       r_refdef.view.colorscale = r_hdr_scenebrightness.value * r_hdr_irisadaptation_value.value;
 
-       // break apart the view matrix into vectors for various purposes
-       // it is important that this occurs outside the RenderScene function because that can be called from reflection renders, where the vectors come out wrong
-       // however the r_refdef.view.origin IS updated in RenderScene intentionally - otherwise the sky renders at the wrong origin, etc
-       Matrix4x4_ToVectors(&r_refdef.view.matrix, r_refdef.view.forward, r_refdef.view.left, r_refdef.view.up, r_refdef.view.origin);
-       VectorNegate(r_refdef.view.left, r_refdef.view.right);
-       // make an inverted copy of the view matrix for tracking sprites
-       Matrix4x4_Invert_Simple(&r_refdef.view.inverse_matrix, &r_refdef.view.matrix);
+       R_RenderView_UpdateViewVectors();
 
        R_Shadow_UpdateWorldLightSelection();
 
@@ -8107,6 +6452,9 @@ void R_RenderView(void)
 
        GL_Scissor(0, 0, vid.width, vid.height);
        GL_ScissorTest(false);
+
+       r_refdef.view.matrix = originalmatrix;
+
        CHECKGLERROR
 }
 
@@ -8142,10 +6490,17 @@ extern cvar_t cl_locs_show;
 static void R_DrawLocs(void);
 static void R_DrawEntityBBoxes(void);
 static void R_DrawModelDecals(void);
+extern void R_DrawModelShadows(void);
+extern void R_DrawModelShadowMaps(void);
 extern cvar_t cl_decals_newsystem;
 extern qboolean r_shadow_usingdeferredprepass;
 void R_RenderScene(void)
 {
+       qboolean shadowmapping = false;
+
+       if (r_timereport_active)
+               R_TimeReport("beginscene");
+
        r_refdef.stats.renders++;
 
        R_UpdateFogColor();
@@ -8160,6 +6515,9 @@ void R_RenderScene(void)
 
        Matrix4x4_CreateTranslate(&r_waterscrollmatrix, sin(r_refdef.scene.time) * 0.025 * r_waterscroll.value, sin(r_refdef.scene.time * 0.8f) * 0.025 * r_waterscroll.value, 0);
 
+       if (r_timereport_active)
+               R_TimeReport("skystartframe");
+
        if (cl.csqc_vidvars.drawworld)
        {
                // don't let sound skip if going slow
@@ -8192,9 +6550,14 @@ void R_RenderScene(void)
                R_TimeReport("animation");
 
        R_Shadow_PrepareLights();
+       if (r_shadows.integer > 0 && r_refdef.lightmapintensity > 0)
+               R_Shadow_PrepareModelShadows();
        if (r_timereport_active)
                R_TimeReport("preparelights");
 
+       if (R_Shadow_ShadowMappingEnabled())
+               shadowmapping = true;
+
        if (r_shadow_usingdeferredprepass)
                R_Shadow_DrawPrepass();
 
@@ -8211,6 +6574,15 @@ void R_RenderScene(void)
                        R_TimeReport("modeldepth");
        }
 
+       if (r_shadows.integer >= 2 && shadowmapping && r_refdef.lightmapintensity > 0)
+       {
+               R_DrawModelShadowMaps();
+               R_ResetViewRendering3D();
+               // don't let sound skip if going slow
+               if (r_refdef.scene.extraupdate)
+                       S_ExtraUpdate ();
+       }
+
        if (cl.csqc_vidvars.drawworld && r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->Draw)
        {
                r_refdef.scene.worldmodel->Draw(r_refdef.scene.worldentity);
@@ -8230,7 +6602,7 @@ void R_RenderScene(void)
        if (r_refdef.scene.extraupdate)
                S_ExtraUpdate ();
 
-       if (r_shadows.integer > 0 && !r_shadows_drawafterrtlighting.integer && r_refdef.lightmapintensity > 0)
+       if ((r_shadows.integer == 1 || (r_shadows.integer > 0 && !shadowmapping)) && !r_shadows_drawafterrtlighting.integer && r_refdef.lightmapintensity > 0)
        {
                R_DrawModelShadows();
                R_ResetViewRendering3D();
@@ -8250,7 +6622,7 @@ void R_RenderScene(void)
        if (r_refdef.scene.extraupdate)
                S_ExtraUpdate ();
 
-       if (r_shadows.integer > 0 && r_shadows_drawafterrtlighting.integer && r_refdef.lightmapintensity > 0)
+       if ((r_shadows.integer == 1 || (r_shadows.integer > 0 && !shadowmapping)) && r_shadows_drawafterrtlighting.integer && r_refdef.lightmapintensity > 0)
        {
                R_DrawModelShadows();
                R_ResetViewRendering3D();
@@ -8334,6 +6706,27 @@ void R_RenderScene(void)
                        R_TimeReport("coronas");
        }
 
+#if 0
+       {
+               GL_DepthTest(false);
+               qglPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+               GL_Color(1, 1, 1, 1);
+               qglBegin(GL_POLYGON);
+               qglVertex3f(r_refdef.view.frustumcorner[0][0], r_refdef.view.frustumcorner[0][1], r_refdef.view.frustumcorner[0][2]);
+               qglVertex3f(r_refdef.view.frustumcorner[1][0], r_refdef.view.frustumcorner[1][1], r_refdef.view.frustumcorner[1][2]);
+               qglVertex3f(r_refdef.view.frustumcorner[3][0], r_refdef.view.frustumcorner[3][1], r_refdef.view.frustumcorner[3][2]);
+               qglVertex3f(r_refdef.view.frustumcorner[2][0], r_refdef.view.frustumcorner[2][1], r_refdef.view.frustumcorner[2][2]);
+               qglEnd();
+               qglBegin(GL_POLYGON);
+               qglVertex3f(r_refdef.view.frustumcorner[0][0] + 1000 * r_refdef.view.forward[0], r_refdef.view.frustumcorner[0][1] + 1000 * r_refdef.view.forward[1], r_refdef.view.frustumcorner[0][2] + 1000 * r_refdef.view.forward[2]);
+               qglVertex3f(r_refdef.view.frustumcorner[1][0] + 1000 * r_refdef.view.forward[0], r_refdef.view.frustumcorner[1][1] + 1000 * r_refdef.view.forward[1], r_refdef.view.frustumcorner[1][2] + 1000 * r_refdef.view.forward[2]);
+               qglVertex3f(r_refdef.view.frustumcorner[3][0] + 1000 * r_refdef.view.forward[0], r_refdef.view.frustumcorner[3][1] + 1000 * r_refdef.view.forward[1], r_refdef.view.frustumcorner[3][2] + 1000 * r_refdef.view.forward[2]);
+               qglVertex3f(r_refdef.view.frustumcorner[2][0] + 1000 * r_refdef.view.forward[0], r_refdef.view.frustumcorner[2][1] + 1000 * r_refdef.view.forward[1], r_refdef.view.frustumcorner[2][2] + 1000 * r_refdef.view.forward[2]);
+               qglEnd();
+               qglPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+       }
+#endif
+
        // don't let sound skip if going slow
        if (r_refdef.scene.extraupdate)
                S_ExtraUpdate ();
@@ -8362,7 +6755,7 @@ void R_DrawBBoxMesh(vec3_t mins, vec3_t maxs, float cr, float cg, float cb, floa
        GL_DepthMask(false);
        GL_DepthRange(0, 1);
        GL_PolygonOffset(r_refdef.polygonfactor, r_refdef.polygonoffset);
-       R_Mesh_ResetTextureState();
+//     R_Mesh_ResetTextureState();
 
        vertex3f[ 0] = mins[0];vertex3f[ 1] = mins[1];vertex3f[ 2] = mins[2]; //
        vertex3f[ 3] = maxs[0];vertex3f[ 4] = mins[1];vertex3f[ 5] = mins[2];
@@ -8384,11 +6777,10 @@ void R_DrawBBoxMesh(vec3_t mins, vec3_t maxs, float cr, float cg, float cb, floa
                        c[2] = c[2] * f1 + r_refdef.fogcolor[2] * f2;
                }
        }
-       R_Mesh_VertexPointer(vertex3f, 0, 0);
-       R_Mesh_ColorPointer(color4f, 0, 0);
+       R_Mesh_PrepareVertices_Generic_Arrays(8, vertex3f, color4f, NULL);
        R_Mesh_ResetTextureState();
        R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
-       R_Mesh_Draw(0, 8, 0, 12, NULL, bboxelements, 0, 0);
+       R_Mesh_Draw(0, 8, 0, 12, NULL, NULL, 0, bboxelements, NULL, 0);
 }
 
 static void R_DrawEntityBBoxes_Callback(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist)
@@ -8533,10 +6925,7 @@ void R_DrawNoModel_TransparentCallback(const entity_render_t *ent, const rtlight
        GL_PolygonOffset(rsurface.basepolygonfactor, rsurface.basepolygonoffset);
        GL_DepthTest(!(rsurface.ent_flags & RENDER_NODEPTHTEST));
        GL_CullFace((rsurface.ent_flags & RENDER_DOUBLESIDED) ? GL_NONE : r_refdef.view.cullface_back);
-       R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
-       R_Mesh_VertexPointer(rsurface.vertex3f, rsurface.vertex3f_bufferobject, rsurface.vertex3f_bufferoffset);
        memcpy(color4f, nomodelcolor4f, sizeof(float[6*4]));
-       R_Mesh_ColorPointer(color4f, 0, 0);
        for (i = 0, c = color4f;i < 6;i++, c += 4)
        {
                c[0] *= rsurface.colormod[0];
@@ -8548,15 +6937,17 @@ void R_DrawNoModel_TransparentCallback(const entity_render_t *ent, const rtlight
        {
                for (i = 0, c = color4f;i < 6;i++, c += 4)
                {
-                       f1 = RSurf_FogVertex(rsurface.vertex3f + 3*i);
+                       f1 = RSurf_FogVertex(nomodelvertex3f + 3*i);
                        f2 = 1 - f1;
                        c[0] = (c[0] * f1 + r_refdef.fogcolor[0] * f2);
                        c[1] = (c[1] * f1 + r_refdef.fogcolor[1] * f2);
                        c[2] = (c[2] * f1 + r_refdef.fogcolor[2] * f2);
                }
        }
-       R_Mesh_ResetTextureState();
-       R_Mesh_Draw(0, 6, 0, 8, nomodelelement3i, nomodelelement3s, 0, 0);
+//     R_Mesh_ResetTextureState();
+       R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
+       R_Mesh_PrepareVertices_Generic_Arrays(6, nomodelvertex3f, color4f, NULL);
+       R_Mesh_Draw(0, 6, 0, 8, nomodelelement3i, NULL, 0, nomodelelement3s, NULL, 0);
 }
 
 void R_DrawNoModel(entity_render_t *ent)
@@ -8729,12 +7120,22 @@ static void R_Texture_AddLayer(texture_t *t, qboolean depthmask, int blendfunc1,
        layer->color[3] = a;
 }
 
+static qboolean R_TestQ3WaveFunc(q3wavefunc_t func, const float *parms)
+{
+       if(parms[0] == 0 && parms[1] == 0)
+               return false;
+       if(func >> Q3WAVEFUNC_USER_SHIFT) // assumes rsurface to be set!
+               if(rsurface.userwavefunc_param[bound(0, (func >> Q3WAVEFUNC_USER_SHIFT) - 1, Q3WAVEFUNC_USER_COUNT)] == 0)
+                       return false;
+       return true;
+}
+
 static float R_EvaluateQ3WaveFunc(q3wavefunc_t func, const float *parms)
 {
        double index, f;
        index = parms[2] + r_refdef.scene.time * parms[3];
        index -= floor(index);
-       switch (func)
+       switch (func & ((1 << Q3WAVEFUNC_USER_SHIFT) - 1))
        {
        default:
        case Q3WAVEFUNC_NONE:
@@ -8759,7 +7160,10 @@ static float R_EvaluateQ3WaveFunc(q3wavefunc_t func, const float *parms)
                        f = -(1 - f);
                break;
        }
-       return (float)(parms[0] + parms[1] * f);
+       f = parms[0] + parms[1] * f;
+       if(func >> Q3WAVEFUNC_USER_SHIFT) // assumes rsurface to be set!
+               f *= rsurface.userwavefunc_param[bound(0, (func >> Q3WAVEFUNC_USER_SHIFT) - 1, Q3WAVEFUNC_USER_COUNT)];
+       return (float) f;
 }
 
 void R_tcMod_ApplyToMatrix(matrix4x4_t *texmatrix, q3shaderinfo_layer_tcmod_t *tcmod, int currentmaterialflags)
@@ -8840,7 +7244,7 @@ void R_LoadQWSkin(r_qwskincache_t *cache, const char *skinname)
                f = FS_LoadFile(name, tempmempool, true, &filesize);
                if (f)
                {
-                       if (LoadPCX_QWSkin(f, filesize, pixels, 296, 194))
+                       if (LoadPCX_QWSkin(f, (int)filesize, pixels, 296, 194))
                                skinframe = R_SkinFrame_LoadInternalQuake(name, textureflags, true, r_fullbrights.integer, pixels, image_width, image_height);
                        Mem_Free(f);
                }
@@ -8860,6 +7264,11 @@ texture_t *R_GetCurrentTexture(texture_t *t)
        t->update_lastrenderframe = r_textureframe;
        t->update_lastrenderentity = (void *)ent;
 
+       if(ent && ent->entitynumber >= MAX_EDICTS && ent->entitynumber < 2 * MAX_EDICTS)
+               t->camera_entity = ent->entitynumber;
+       else
+               t->camera_entity = 0;
+
        // switch to an alternate material if this is a q1bsp animated material
        {
                texture_t *texture = t;
@@ -8896,29 +7305,33 @@ texture_t *R_GetCurrentTexture(texture_t *t)
                        r_qwskincache_size = cl.maxclients;
                        if (r_qwskincache)
                                Mem_Free(r_qwskincache);
-                       r_qwskincache = Mem_Alloc(r_main_mempool, sizeof(*r_qwskincache) * r_qwskincache_size);
+                       r_qwskincache = (r_qwskincache_t *)Mem_Alloc(r_main_mempool, sizeof(*r_qwskincache) * r_qwskincache_size);
                }
                if (strcmp(r_qwskincache[i].name, cl.scores[i].qw_skin))
                        R_LoadQWSkin(&r_qwskincache[i], cl.scores[i].qw_skin);
                t->currentskinframe = r_qwskincache[i].skinframe;
                if (t->currentskinframe == NULL)
-                       t->currentskinframe = t->skinframes[(int)(t->skinframerate * (cl.time - rsurface.ent_shadertime)) % t->numskinframes];
+                       t->currentskinframe = t->skinframes[(unsigned int)(t->skinframerate * (cl.time - rsurface.ent_shadertime)) % t->numskinframes];
        }
        else if (t->numskinframes >= 2)
-               t->currentskinframe = t->skinframes[(int)(t->skinframerate * (cl.time - rsurface.ent_shadertime)) % t->numskinframes];
+               t->currentskinframe = t->skinframes[(unsigned int)(t->skinframerate * (cl.time - rsurface.ent_shadertime)) % t->numskinframes];
        if (t->backgroundnumskinframes >= 2)
-               t->backgroundcurrentskinframe = t->backgroundskinframes[(int)(t->backgroundskinframerate * (cl.time - rsurface.ent_shadertime)) % t->backgroundnumskinframes];
+               t->backgroundcurrentskinframe = t->backgroundskinframes[(unsigned int)(t->backgroundskinframerate * (cl.time - rsurface.ent_shadertime)) % t->backgroundnumskinframes];
 
        t->currentmaterialflags = t->basematerialflags;
        t->currentalpha = rsurface.colormod[3];
        if (t->basematerialflags & MATERIALFLAG_WATERALPHA && (model->brush.supportwateralpha || r_novis.integer))
                t->currentalpha *= r_wateralpha.value;
        if(t->basematerialflags & MATERIALFLAG_WATERSHADER && r_waterstate.enabled && !r_refdef.view.isoverlay)
-               t->currentalpha *= t->r_water_wateralpha;
+               t->currentmaterialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW; // we apply wateralpha later
        if(!r_waterstate.enabled || r_refdef.view.isoverlay)
-               t->currentmaterialflags &= ~(MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION);
+               t->currentmaterialflags &= ~(MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA);
        if (!(rsurface.ent_flags & RENDER_LIGHT))
                t->currentmaterialflags |= MATERIALFLAG_FULLBRIGHT;
+       else if (FAKELIGHT_ENABLED)
+       {
+                       // no modellight if using fakelight for the map
+       }
        else if (rsurface.modeltexcoordlightmap2f == NULL && !(t->currentmaterialflags & MATERIALFLAG_FULLBRIGHT))
        {
                // pick a model lighting mode
@@ -8939,11 +7352,11 @@ texture_t *R_GetCurrentTexture(texture_t *t)
                t->currentmaterialflags |= MATERIALFLAG_VERTEXTEXTUREBLEND;
        if (t->currentmaterialflags & MATERIALFLAG_BLENDED)
        {
-               if (t->currentmaterialflags & (MATERIALFLAG_REFRACTION | MATERIALFLAG_WATERSHADER))
+               if (t->currentmaterialflags & (MATERIALFLAG_REFRACTION | MATERIALFLAG_WATERSHADER | MATERIALFLAG_CAMERA))
                        t->currentmaterialflags &= ~MATERIALFLAG_BLENDED;
        }
        else
-               t->currentmaterialflags &= ~(MATERIALFLAG_REFRACTION | MATERIALFLAG_WATERSHADER);
+               t->currentmaterialflags &= ~(MATERIALFLAG_REFRACTION | MATERIALFLAG_WATERSHADER | MATERIALFLAG_CAMERA);
        if ((t->currentmaterialflags & (MATERIALFLAG_BLENDED | MATERIALFLAG_NODEPTHTEST)) == MATERIALFLAG_BLENDED && r_transparentdepthmasking.integer && !(t->basematerialflags & MATERIALFLAG_BLENDED))
                t->currentmaterialflags |= MATERIALFLAG_TRANSDEPTH;
 
@@ -8990,7 +7403,7 @@ texture_t *R_GetCurrentTexture(texture_t *t)
        }
        else
        {
-               t->backgroundbasetexture = t->backgroundnumskinframes ? ((!t->colormapping && t->backgroundcurrentskinframe->merged) ? t->backgroundcurrentskinframe->merged : t->backgroundcurrentskinframe->base) : r_texture_white;
+               t->backgroundbasetexture = r_texture_white;
                t->backgroundnmaptexture = r_texture_blanknormalmap;
                t->backgroundglosstexture = r_texture_black;
                t->backgroundglowtexture = NULL;
@@ -9068,6 +7481,9 @@ texture_t *R_GetCurrentTexture(texture_t *t)
                        blendfunc1 = GL_ONE;
                        blendfunc2 = GL_ZERO;
                }
+               // don't colormod evilblend textures
+               if(!R_BlendFuncFlags(blendfunc1, blendfunc2) & BLENDFUNC_ALLOWS_COLORMOD)
+                       VectorSet(t->lightmapcolor, 1, 1, 1);
                depthmask = !(t->currentmaterialflags & MATERIALFLAG_BLENDED);
                if (t->currentmaterialflags & MATERIALFLAG_FULLBRIGHT)
                {
@@ -9135,28 +7551,6 @@ texture_t *R_GetCurrentTexture(texture_t *t)
 
 rsurfacestate_t rsurface;
 
-void R_Mesh_ResizeArrays(int newvertices)
-{
-       float *base;
-       if (rsurface.array_size >= newvertices)
-               return;
-       if (rsurface.array_modelvertex3f)
-               Mem_Free(rsurface.array_modelvertex3f);
-       rsurface.array_size = (newvertices + 1023) & ~1023;
-       base = (float *)Mem_Alloc(r_main_mempool, rsurface.array_size * sizeof(float[33]));
-       rsurface.array_modelvertex3f     = base + rsurface.array_size * 0;
-       rsurface.array_modelsvector3f    = base + rsurface.array_size * 3;
-       rsurface.array_modeltvector3f    = base + rsurface.array_size * 6;
-       rsurface.array_modelnormal3f     = base + rsurface.array_size * 9;
-       rsurface.array_deformedvertex3f  = base + rsurface.array_size * 12;
-       rsurface.array_deformedsvector3f = base + rsurface.array_size * 15;
-       rsurface.array_deformedtvector3f = base + rsurface.array_size * 18;
-       rsurface.array_deformednormal3f  = base + rsurface.array_size * 21;
-       rsurface.array_texcoord3f        = base + rsurface.array_size * 24;
-       rsurface.array_color4f           = base + rsurface.array_size * 27;
-       rsurface.array_generatedtexcoordtexture2f = base + rsurface.array_size * 31;
-}
-
 void RSurf_ActiveWorldEntity(void)
 {
        dp_model_t *model = r_refdef.scene.worldmodel;
@@ -9164,12 +7558,11 @@ void RSurf_ActiveWorldEntity(void)
        //      return;
        rsurface.entity = r_refdef.scene.worldentity;
        rsurface.skeleton = NULL;
+       memset(rsurface.userwavefunc_param, 0, sizeof(rsurface.userwavefunc_param));
        rsurface.ent_skinnum = 0;
        rsurface.ent_qwskin = -1;
        rsurface.ent_shadertime = 0;
        rsurface.ent_flags = r_refdef.scene.worldentity->flags;
-       if (rsurface.array_size < model->surfmesh.num_vertices)
-               R_Mesh_ResizeArrays(model->surfmesh.num_vertices);
        rsurface.matrix = identitymatrix;
        rsurface.inversematrix = identitymatrix;
        rsurface.matrixscale = 1;
@@ -9195,48 +7588,78 @@ void RSurf_ActiveWorldEntity(void)
        rsurface.basepolygonfactor = r_refdef.polygonfactor;
        rsurface.basepolygonoffset = r_refdef.polygonoffset;
        rsurface.modelvertex3f  = model->surfmesh.data_vertex3f;
-       rsurface.modelvertex3f_bufferobject = model->surfmesh.vbo;
+       rsurface.modelvertex3f_vertexbuffer = model->surfmesh.vbo_vertexbuffer;
        rsurface.modelvertex3f_bufferoffset = model->surfmesh.vbooffset_vertex3f;
        rsurface.modelsvector3f = model->surfmesh.data_svector3f;
-       rsurface.modelsvector3f_bufferobject = model->surfmesh.vbo;
+       rsurface.modelsvector3f_vertexbuffer = model->surfmesh.vbo_vertexbuffer;
        rsurface.modelsvector3f_bufferoffset = model->surfmesh.vbooffset_svector3f;
        rsurface.modeltvector3f = model->surfmesh.data_tvector3f;
-       rsurface.modeltvector3f_bufferobject = model->surfmesh.vbo;
+       rsurface.modeltvector3f_vertexbuffer = model->surfmesh.vbo_vertexbuffer;
        rsurface.modeltvector3f_bufferoffset = model->surfmesh.vbooffset_tvector3f;
        rsurface.modelnormal3f  = model->surfmesh.data_normal3f;
-       rsurface.modelnormal3f_bufferobject = model->surfmesh.vbo;
+       rsurface.modelnormal3f_vertexbuffer = model->surfmesh.vbo_vertexbuffer;
        rsurface.modelnormal3f_bufferoffset = model->surfmesh.vbooffset_normal3f;
        rsurface.modellightmapcolor4f  = model->surfmesh.data_lightmapcolor4f;
-       rsurface.modellightmapcolor4f_bufferobject = model->surfmesh.vbo;
+       rsurface.modellightmapcolor4f_vertexbuffer = model->surfmesh.vbo_vertexbuffer;
        rsurface.modellightmapcolor4f_bufferoffset = model->surfmesh.vbooffset_lightmapcolor4f;
        rsurface.modeltexcoordtexture2f  = model->surfmesh.data_texcoordtexture2f;
-       rsurface.modeltexcoordtexture2f_bufferobject = model->surfmesh.vbo;
+       rsurface.modeltexcoordtexture2f_vertexbuffer = model->surfmesh.vbo_vertexbuffer;
        rsurface.modeltexcoordtexture2f_bufferoffset = model->surfmesh.vbooffset_texcoordtexture2f;
        rsurface.modeltexcoordlightmap2f  = model->surfmesh.data_texcoordlightmap2f;
-       rsurface.modeltexcoordlightmap2f_bufferobject = model->surfmesh.vbo;
+       rsurface.modeltexcoordlightmap2f_vertexbuffer = model->surfmesh.vbo_vertexbuffer;
        rsurface.modeltexcoordlightmap2f_bufferoffset = model->surfmesh.vbooffset_texcoordlightmap2f;
        rsurface.modelelement3i = model->surfmesh.data_element3i;
+       rsurface.modelelement3i_indexbuffer = model->surfmesh.data_element3i_indexbuffer;
+       rsurface.modelelement3i_bufferoffset = model->surfmesh.data_element3i_bufferoffset;
        rsurface.modelelement3s = model->surfmesh.data_element3s;
-       rsurface.modelelement3i_bufferobject = model->surfmesh.ebo3i;
-       rsurface.modelelement3s_bufferobject = model->surfmesh.ebo3s;
+       rsurface.modelelement3s_indexbuffer = model->surfmesh.data_element3s_indexbuffer;
+       rsurface.modelelement3s_bufferoffset = model->surfmesh.data_element3s_bufferoffset;
        rsurface.modellightmapoffsets = model->surfmesh.data_lightmapoffsets;
-       rsurface.modelnum_vertices = model->surfmesh.num_vertices;
-       rsurface.modelnum_triangles = model->surfmesh.num_triangles;
+       rsurface.modelnumvertices = model->surfmesh.num_vertices;
+       rsurface.modelnumtriangles = model->surfmesh.num_triangles;
        rsurface.modelsurfaces = model->data_surfaces;
-       rsurface.generatedvertex = false;
-       rsurface.vertex3f  = rsurface.modelvertex3f;
-       rsurface.vertex3f_bufferobject = rsurface.modelvertex3f_bufferobject;
-       rsurface.vertex3f_bufferoffset = rsurface.modelvertex3f_bufferoffset;
-       rsurface.svector3f = rsurface.modelsvector3f;
-       rsurface.svector3f_bufferobject = rsurface.modelsvector3f_bufferobject;
-       rsurface.svector3f_bufferoffset = rsurface.modelsvector3f_bufferoffset;
-       rsurface.tvector3f = rsurface.modeltvector3f;
-       rsurface.tvector3f_bufferobject = rsurface.modeltvector3f_bufferobject;
-       rsurface.tvector3f_bufferoffset = rsurface.modeltvector3f_bufferoffset;
-       rsurface.normal3f  = rsurface.modelnormal3f;
-       rsurface.normal3f_bufferobject = rsurface.modelnormal3f_bufferobject;
-       rsurface.normal3f_bufferoffset = rsurface.modelnormal3f_bufferoffset;
-       rsurface.texcoordtexture2f = rsurface.modeltexcoordtexture2f;
+       rsurface.modelvertexmesh = model->surfmesh.vertexmesh;
+       rsurface.modelvertexmeshbuffer = model->surfmesh.vertexmeshbuffer;
+       rsurface.modelvertex3fbuffer = model->surfmesh.vertex3fbuffer;
+       rsurface.modelgeneratedvertex = false;
+       rsurface.batchgeneratedvertex = false;
+       rsurface.batchfirstvertex = 0;
+       rsurface.batchnumvertices = 0;
+       rsurface.batchfirsttriangle = 0;
+       rsurface.batchnumtriangles = 0;
+       rsurface.batchvertex3f  = NULL;
+       rsurface.batchvertex3f_vertexbuffer = NULL;
+       rsurface.batchvertex3f_bufferoffset = 0;
+       rsurface.batchsvector3f = NULL;
+       rsurface.batchsvector3f_vertexbuffer = NULL;
+       rsurface.batchsvector3f_bufferoffset = 0;
+       rsurface.batchtvector3f = NULL;
+       rsurface.batchtvector3f_vertexbuffer = NULL;
+       rsurface.batchtvector3f_bufferoffset = 0;
+       rsurface.batchnormal3f  = NULL;
+       rsurface.batchnormal3f_vertexbuffer = NULL;
+       rsurface.batchnormal3f_bufferoffset = 0;
+       rsurface.batchlightmapcolor4f = NULL;
+       rsurface.batchlightmapcolor4f_vertexbuffer = NULL;
+       rsurface.batchlightmapcolor4f_bufferoffset = 0;
+       rsurface.batchtexcoordtexture2f = NULL;
+       rsurface.batchtexcoordtexture2f_vertexbuffer = NULL;
+       rsurface.batchtexcoordtexture2f_bufferoffset = 0;
+       rsurface.batchtexcoordlightmap2f = NULL;
+       rsurface.batchtexcoordlightmap2f_vertexbuffer = NULL;
+       rsurface.batchtexcoordlightmap2f_bufferoffset = 0;
+       rsurface.batchvertexmesh = NULL;
+       rsurface.batchvertexmeshbuffer = NULL;
+       rsurface.batchvertex3fbuffer = NULL;
+       rsurface.batchelement3i = NULL;
+       rsurface.batchelement3i_indexbuffer = NULL;
+       rsurface.batchelement3i_bufferoffset = 0;
+       rsurface.batchelement3s = NULL;
+       rsurface.batchelement3s_indexbuffer = NULL;
+       rsurface.batchelement3s_bufferoffset = 0;
+       rsurface.passcolor4f = NULL;
+       rsurface.passcolor4f_vertexbuffer = NULL;
+       rsurface.passcolor4f_bufferoffset = 0;
 }
 
 void RSurf_ActiveModelEntity(const entity_render_t *ent, qboolean wantnormals, qboolean wanttangents, qboolean prepass)
@@ -9246,12 +7669,11 @@ void RSurf_ActiveModelEntity(const entity_render_t *ent, qboolean wantnormals, q
        //      return;
        rsurface.entity = (entity_render_t *)ent;
        rsurface.skeleton = ent->skeleton;
+       memcpy(rsurface.userwavefunc_param, ent->userwavefunc_param, sizeof(rsurface.userwavefunc_param));
        rsurface.ent_skinnum = ent->skinnum;
        rsurface.ent_qwskin = (ent->entitynumber <= cl.maxclients && ent->entitynumber >= 1 && cls.protocol == PROTOCOL_QUAKEWORLD && cl.scores[ent->entitynumber - 1].qw_skin[0] && !strcmp(ent->model->name, "progs/player.mdl")) ? (ent->entitynumber - 1) : -1;
        rsurface.ent_shadertime = ent->shadertime;
        rsurface.ent_flags = ent->flags;
-       if (rsurface.array_size < model->surfmesh.num_vertices)
-               R_Mesh_ResizeArrays(model->surfmesh.num_vertices);
        rsurface.matrix = ent->matrix;
        rsurface.inversematrix = ent->inversematrix;
        rsurface.matrixscale = Matrix4x4_ScaleFromMatrix(&rsurface.matrix);
@@ -9282,93 +7704,135 @@ void RSurf_ActiveModelEntity(const entity_render_t *ent, qboolean wantnormals, q
        }
        if (model->surfmesh.isanimated && model->AnimateVertices && (rsurface.frameblend[0].lerp != 1 || rsurface.frameblend[0].subframe != 0))
        {
-               if (ent->animcache_vertex3f && !r_framedata_failed)
+               if (ent->animcache_vertex3f)
                {
                        rsurface.modelvertex3f = ent->animcache_vertex3f;
                        rsurface.modelsvector3f = wanttangents ? ent->animcache_svector3f : NULL;
                        rsurface.modeltvector3f = wanttangents ? ent->animcache_tvector3f : NULL;
                        rsurface.modelnormal3f = wantnormals ? ent->animcache_normal3f : NULL;
+                       rsurface.modelvertexmesh = ent->animcache_vertexmesh;
+                       rsurface.modelvertexmeshbuffer = ent->animcache_vertexmeshbuffer;
+                       rsurface.modelvertex3fbuffer = ent->animcache_vertex3fbuffer;
                }
                else if (wanttangents)
                {
-                       rsurface.modelvertex3f = rsurface.array_modelvertex3f;
-                       rsurface.modelsvector3f = rsurface.array_modelsvector3f;
-                       rsurface.modeltvector3f = rsurface.array_modeltvector3f;
-                       rsurface.modelnormal3f = rsurface.array_modelnormal3f;
-                       model->AnimateVertices(model, rsurface.frameblend, rsurface.skeleton, rsurface.array_modelvertex3f, rsurface.array_modelnormal3f, rsurface.array_modelsvector3f, rsurface.array_modeltvector3f);
+                       rsurface.modelvertex3f = (float *)R_FrameData_Alloc(model->surfmesh.num_vertices * sizeof(float[3]));
+                       rsurface.modelsvector3f = (float *)R_FrameData_Alloc(model->surfmesh.num_vertices * sizeof(float[3]));
+                       rsurface.modeltvector3f = (float *)R_FrameData_Alloc(model->surfmesh.num_vertices * sizeof(float[3]));
+                       rsurface.modelnormal3f = (float *)R_FrameData_Alloc(model->surfmesh.num_vertices * sizeof(float[3]));
+                       model->AnimateVertices(model, rsurface.frameblend, rsurface.skeleton, rsurface.modelvertex3f, rsurface.modelnormal3f, rsurface.modelsvector3f, rsurface.modeltvector3f);
+                       rsurface.modelvertexmesh = NULL;
+                       rsurface.modelvertexmeshbuffer = NULL;
+                       rsurface.modelvertex3fbuffer = NULL;
                }
                else if (wantnormals)
                {
-                       rsurface.modelvertex3f = rsurface.array_modelvertex3f;
+                       rsurface.modelvertex3f = (float *)R_FrameData_Alloc(model->surfmesh.num_vertices * sizeof(float[3]));
                        rsurface.modelsvector3f = NULL;
                        rsurface.modeltvector3f = NULL;
-                       rsurface.modelnormal3f = rsurface.array_modelnormal3f;
-                       model->AnimateVertices(model, rsurface.frameblend, rsurface.skeleton, rsurface.array_modelvertex3f, rsurface.array_modelnormal3f, NULL, NULL);
+                       rsurface.modelnormal3f = (float *)R_FrameData_Alloc(model->surfmesh.num_vertices * sizeof(float[3]));
+                       model->AnimateVertices(model, rsurface.frameblend, rsurface.skeleton, rsurface.modelvertex3f, rsurface.modelnormal3f, NULL, NULL);
+                       rsurface.modelvertexmesh = NULL;
+                       rsurface.modelvertexmeshbuffer = NULL;
+                       rsurface.modelvertex3fbuffer = NULL;
                }
                else
                {
-                       rsurface.modelvertex3f = rsurface.array_modelvertex3f;
+                       rsurface.modelvertex3f = (float *)R_FrameData_Alloc(model->surfmesh.num_vertices * sizeof(float[3]));
                        rsurface.modelsvector3f = NULL;
                        rsurface.modeltvector3f = NULL;
                        rsurface.modelnormal3f = NULL;
-                       model->AnimateVertices(model, rsurface.frameblend, rsurface.skeleton, rsurface.array_modelvertex3f, NULL, NULL, NULL);
+                       model->AnimateVertices(model, rsurface.frameblend, rsurface.skeleton, rsurface.modelvertex3f, NULL, NULL, NULL);
+                       rsurface.modelvertexmesh = NULL;
+                       rsurface.modelvertexmeshbuffer = NULL;
+                       rsurface.modelvertex3fbuffer = NULL;
                }
-               rsurface.modelvertex3f_bufferobject = 0;
+               rsurface.modelvertex3f_vertexbuffer = 0;
                rsurface.modelvertex3f_bufferoffset = 0;
-               rsurface.modelsvector3f_bufferobject = 0;
+               rsurface.modelsvector3f_vertexbuffer = 0;
                rsurface.modelsvector3f_bufferoffset = 0;
-               rsurface.modeltvector3f_bufferobject = 0;
+               rsurface.modeltvector3f_vertexbuffer = 0;
                rsurface.modeltvector3f_bufferoffset = 0;
-               rsurface.modelnormal3f_bufferobject = 0;
+               rsurface.modelnormal3f_vertexbuffer = 0;
                rsurface.modelnormal3f_bufferoffset = 0;
-               rsurface.generatedvertex = true;
+               rsurface.modelgeneratedvertex = true;
        }
        else
        {
                rsurface.modelvertex3f  = model->surfmesh.data_vertex3f;
-               rsurface.modelvertex3f_bufferobject = model->surfmesh.vbo;
+               rsurface.modelvertex3f_vertexbuffer = model->surfmesh.vbo_vertexbuffer;
                rsurface.modelvertex3f_bufferoffset = model->surfmesh.vbooffset_vertex3f;
                rsurface.modelsvector3f = model->surfmesh.data_svector3f;
-               rsurface.modelsvector3f_bufferobject = model->surfmesh.vbo;
+               rsurface.modelsvector3f_vertexbuffer = model->surfmesh.vbo_vertexbuffer;
                rsurface.modelsvector3f_bufferoffset = model->surfmesh.vbooffset_svector3f;
                rsurface.modeltvector3f = model->surfmesh.data_tvector3f;
-               rsurface.modeltvector3f_bufferobject = model->surfmesh.vbo;
+               rsurface.modeltvector3f_vertexbuffer = model->surfmesh.vbo_vertexbuffer;
                rsurface.modeltvector3f_bufferoffset = model->surfmesh.vbooffset_tvector3f;
                rsurface.modelnormal3f  = model->surfmesh.data_normal3f;
-               rsurface.modelnormal3f_bufferobject = model->surfmesh.vbo;
+               rsurface.modelnormal3f_vertexbuffer = model->surfmesh.vbo_vertexbuffer;
                rsurface.modelnormal3f_bufferoffset = model->surfmesh.vbooffset_normal3f;
-               rsurface.generatedvertex = false;
+               rsurface.modelvertexmesh = model->surfmesh.vertexmesh;
+               rsurface.modelvertexmeshbuffer = model->surfmesh.vertexmeshbuffer;
+               rsurface.modelvertex3fbuffer = model->surfmesh.vertex3fbuffer;
+               rsurface.modelgeneratedvertex = false;
        }
        rsurface.modellightmapcolor4f  = model->surfmesh.data_lightmapcolor4f;
-       rsurface.modellightmapcolor4f_bufferobject = model->surfmesh.vbo;
+       rsurface.modellightmapcolor4f_vertexbuffer = model->surfmesh.vbo_vertexbuffer;
        rsurface.modellightmapcolor4f_bufferoffset = model->surfmesh.vbooffset_lightmapcolor4f;
        rsurface.modeltexcoordtexture2f  = model->surfmesh.data_texcoordtexture2f;
-       rsurface.modeltexcoordtexture2f_bufferobject = model->surfmesh.vbo;
+       rsurface.modeltexcoordtexture2f_vertexbuffer = model->surfmesh.vbo_vertexbuffer;
        rsurface.modeltexcoordtexture2f_bufferoffset = model->surfmesh.vbooffset_texcoordtexture2f;
        rsurface.modeltexcoordlightmap2f  = model->surfmesh.data_texcoordlightmap2f;
-       rsurface.modeltexcoordlightmap2f_bufferobject = model->surfmesh.vbo;
+       rsurface.modeltexcoordlightmap2f_vertexbuffer = model->surfmesh.vbo_vertexbuffer;
        rsurface.modeltexcoordlightmap2f_bufferoffset = model->surfmesh.vbooffset_texcoordlightmap2f;
        rsurface.modelelement3i = model->surfmesh.data_element3i;
+       rsurface.modelelement3i_indexbuffer = model->surfmesh.data_element3i_indexbuffer;
+       rsurface.modelelement3i_bufferoffset = model->surfmesh.data_element3i_bufferoffset;
        rsurface.modelelement3s = model->surfmesh.data_element3s;
-       rsurface.modelelement3i_bufferobject = model->surfmesh.ebo3i;
-       rsurface.modelelement3s_bufferobject = model->surfmesh.ebo3s;
+       rsurface.modelelement3s_indexbuffer = model->surfmesh.data_element3s_indexbuffer;
+       rsurface.modelelement3s_bufferoffset = model->surfmesh.data_element3s_bufferoffset;
        rsurface.modellightmapoffsets = model->surfmesh.data_lightmapoffsets;
-       rsurface.modelnum_vertices = model->surfmesh.num_vertices;
-       rsurface.modelnum_triangles = model->surfmesh.num_triangles;
+       rsurface.modelnumvertices = model->surfmesh.num_vertices;
+       rsurface.modelnumtriangles = model->surfmesh.num_triangles;
        rsurface.modelsurfaces = model->data_surfaces;
-       rsurface.vertex3f  = rsurface.modelvertex3f;
-       rsurface.vertex3f_bufferobject = rsurface.modelvertex3f_bufferobject;
-       rsurface.vertex3f_bufferoffset = rsurface.modelvertex3f_bufferoffset;
-       rsurface.svector3f = rsurface.modelsvector3f;
-       rsurface.svector3f_bufferobject = rsurface.modelsvector3f_bufferobject;
-       rsurface.svector3f_bufferoffset = rsurface.modelsvector3f_bufferoffset;
-       rsurface.tvector3f = rsurface.modeltvector3f;
-       rsurface.tvector3f_bufferobject = rsurface.modeltvector3f_bufferobject;
-       rsurface.tvector3f_bufferoffset = rsurface.modeltvector3f_bufferoffset;
-       rsurface.normal3f  = rsurface.modelnormal3f;
-       rsurface.normal3f_bufferobject = rsurface.modelnormal3f_bufferobject;
-       rsurface.normal3f_bufferoffset = rsurface.modelnormal3f_bufferoffset;
-       rsurface.texcoordtexture2f = rsurface.modeltexcoordtexture2f;
+       rsurface.batchgeneratedvertex = false;
+       rsurface.batchfirstvertex = 0;
+       rsurface.batchnumvertices = 0;
+       rsurface.batchfirsttriangle = 0;
+       rsurface.batchnumtriangles = 0;
+       rsurface.batchvertex3f  = NULL;
+       rsurface.batchvertex3f_vertexbuffer = NULL;
+       rsurface.batchvertex3f_bufferoffset = 0;
+       rsurface.batchsvector3f = NULL;
+       rsurface.batchsvector3f_vertexbuffer = NULL;
+       rsurface.batchsvector3f_bufferoffset = 0;
+       rsurface.batchtvector3f = NULL;
+       rsurface.batchtvector3f_vertexbuffer = NULL;
+       rsurface.batchtvector3f_bufferoffset = 0;
+       rsurface.batchnormal3f  = NULL;
+       rsurface.batchnormal3f_vertexbuffer = NULL;
+       rsurface.batchnormal3f_bufferoffset = 0;
+       rsurface.batchlightmapcolor4f = NULL;
+       rsurface.batchlightmapcolor4f_vertexbuffer = NULL;
+       rsurface.batchlightmapcolor4f_bufferoffset = 0;
+       rsurface.batchtexcoordtexture2f = NULL;
+       rsurface.batchtexcoordtexture2f_vertexbuffer = NULL;
+       rsurface.batchtexcoordtexture2f_bufferoffset = 0;
+       rsurface.batchtexcoordlightmap2f = NULL;
+       rsurface.batchtexcoordlightmap2f_vertexbuffer = NULL;
+       rsurface.batchtexcoordlightmap2f_bufferoffset = 0;
+       rsurface.batchvertexmesh = NULL;
+       rsurface.batchvertexmeshbuffer = NULL;
+       rsurface.batchvertex3fbuffer = NULL;
+       rsurface.batchelement3i = NULL;
+       rsurface.batchelement3i_indexbuffer = NULL;
+       rsurface.batchelement3i_bufferoffset = 0;
+       rsurface.batchelement3s = NULL;
+       rsurface.batchelement3s_indexbuffer = NULL;
+       rsurface.batchelement3s_bufferoffset = 0;
+       rsurface.passcolor4f = NULL;
+       rsurface.passcolor4f_vertexbuffer = NULL;
+       rsurface.passcolor4f_bufferoffset = 0;
 }
 
 void RSurf_ActiveCustomEntity(const matrix4x4_t *matrix, const matrix4x4_t *inversematrix, int entflags, double shadertime, float r, float g, float b, float a, int numvertices, const float *vertex3f, const float *texcoord2f, const float *normal3f, const float *svector3f, const float *tvector3f, const float *color4f, int numtriangles, const int *element3i, const unsigned short *element3s, qboolean wantnormals, qboolean wanttangents)
@@ -9379,10 +7843,8 @@ void RSurf_ActiveCustomEntity(const matrix4x4_t *matrix, const matrix4x4_t *inve
        rsurface.ent_qwskin = -1;
        rsurface.ent_shadertime = shadertime;
        rsurface.ent_flags = entflags;
-       rsurface.modelnum_vertices = numvertices;
-       rsurface.modelnum_triangles = numtriangles;
-       if (rsurface.array_size < rsurface.modelnum_vertices)
-               R_Mesh_ResizeArrays(rsurface.modelnum_vertices);
+       rsurface.modelnumvertices = numvertices;
+       rsurface.modelnumtriangles = numtriangles;
        rsurface.matrix = *matrix;
        rsurface.inversematrix = *inversematrix;
        rsurface.matrixscale = Matrix4x4_ScaleFromMatrix(&rsurface.matrix);
@@ -9408,69 +7870,106 @@ void RSurf_ActiveCustomEntity(const matrix4x4_t *matrix, const matrix4x4_t *inve
        rsurface.basepolygonoffset = r_refdef.polygonoffset;
        if (wanttangents)
        {
-               rsurface.modelvertex3f = vertex3f;
-               rsurface.modelsvector3f = svector3f ? svector3f : rsurface.array_modelsvector3f;
-               rsurface.modeltvector3f = tvector3f ? tvector3f : rsurface.array_modeltvector3f;
-               rsurface.modelnormal3f = normal3f ? normal3f : rsurface.array_modelnormal3f;
+               rsurface.modelvertex3f = (float *)vertex3f;
+               rsurface.modelsvector3f = svector3f ? (float *)svector3f : (float *)R_FrameData_Alloc(rsurface.modelnumvertices * sizeof(float[3]));
+               rsurface.modeltvector3f = tvector3f ? (float *)tvector3f : (float *)R_FrameData_Alloc(rsurface.modelnumvertices * sizeof(float[3]));
+               rsurface.modelnormal3f = normal3f ? (float *)normal3f : (float *)R_FrameData_Alloc(rsurface.modelnumvertices * sizeof(float[3]));
        }
        else if (wantnormals)
        {
-               rsurface.modelvertex3f = vertex3f;
+               rsurface.modelvertex3f = (float *)vertex3f;
                rsurface.modelsvector3f = NULL;
                rsurface.modeltvector3f = NULL;
-               rsurface.modelnormal3f = normal3f ? normal3f : rsurface.array_modelnormal3f;
+               rsurface.modelnormal3f = normal3f ? (float *)normal3f : (float *)R_FrameData_Alloc(rsurface.modelnumvertices * sizeof(float[3]));
        }
        else
        {
-               rsurface.modelvertex3f = vertex3f;
+               rsurface.modelvertex3f = (float *)vertex3f;
                rsurface.modelsvector3f = NULL;
                rsurface.modeltvector3f = NULL;
                rsurface.modelnormal3f = NULL;
        }
-       rsurface.modelvertex3f_bufferobject = 0;
+       rsurface.modelvertexmesh = NULL;
+       rsurface.modelvertexmeshbuffer = NULL;
+       rsurface.modelvertex3fbuffer = NULL;
+       rsurface.modelvertex3f_vertexbuffer = 0;
        rsurface.modelvertex3f_bufferoffset = 0;
-       rsurface.modelsvector3f_bufferobject = 0;
+       rsurface.modelsvector3f_vertexbuffer = 0;
        rsurface.modelsvector3f_bufferoffset = 0;
-       rsurface.modeltvector3f_bufferobject = 0;
+       rsurface.modeltvector3f_vertexbuffer = 0;
        rsurface.modeltvector3f_bufferoffset = 0;
-       rsurface.modelnormal3f_bufferobject = 0;
+       rsurface.modelnormal3f_vertexbuffer = 0;
        rsurface.modelnormal3f_bufferoffset = 0;
-       rsurface.generatedvertex = true;
-       rsurface.modellightmapcolor4f  = color4f;
-       rsurface.modellightmapcolor4f_bufferobject = 0;
+       rsurface.modelgeneratedvertex = true;
+       rsurface.modellightmapcolor4f  = (float *)color4f;
+       rsurface.modellightmapcolor4f_vertexbuffer = 0;
        rsurface.modellightmapcolor4f_bufferoffset = 0;
-       rsurface.modeltexcoordtexture2f  = texcoord2f;
-       rsurface.modeltexcoordtexture2f_bufferobject = 0;
+       rsurface.modeltexcoordtexture2f  = (float *)texcoord2f;
+       rsurface.modeltexcoordtexture2f_vertexbuffer = 0;
        rsurface.modeltexcoordtexture2f_bufferoffset = 0;
        rsurface.modeltexcoordlightmap2f  = NULL;
-       rsurface.modeltexcoordlightmap2f_bufferobject = 0;
+       rsurface.modeltexcoordlightmap2f_vertexbuffer = 0;
        rsurface.modeltexcoordlightmap2f_bufferoffset = 0;
-       rsurface.modelelement3i = element3i;
-       rsurface.modelelement3s = element3s;
-       rsurface.modelelement3i_bufferobject = 0;
-       rsurface.modelelement3s_bufferobject = 0;
+       rsurface.modelelement3i = (int *)element3i;
+       rsurface.modelelement3i_indexbuffer = NULL;
+       rsurface.modelelement3i_bufferoffset = 0;
+       rsurface.modelelement3s = (unsigned short *)element3s;
+       rsurface.modelelement3s_indexbuffer = NULL;
+       rsurface.modelelement3s_bufferoffset = 0;
        rsurface.modellightmapoffsets = NULL;
        rsurface.modelsurfaces = NULL;
-       rsurface.vertex3f  = rsurface.modelvertex3f;
-       rsurface.vertex3f_bufferobject = rsurface.modelvertex3f_bufferobject;
-       rsurface.vertex3f_bufferoffset = rsurface.modelvertex3f_bufferoffset;
-       rsurface.svector3f = rsurface.modelsvector3f;
-       rsurface.svector3f_bufferobject = rsurface.modelsvector3f_bufferobject;
-       rsurface.svector3f_bufferoffset = rsurface.modelsvector3f_bufferoffset;
-       rsurface.tvector3f = rsurface.modeltvector3f;
-       rsurface.tvector3f_bufferobject = rsurface.modeltvector3f_bufferobject;
-       rsurface.tvector3f_bufferoffset = rsurface.modeltvector3f_bufferoffset;
-       rsurface.normal3f  = rsurface.modelnormal3f;
-       rsurface.normal3f_bufferobject = rsurface.modelnormal3f_bufferobject;
-       rsurface.normal3f_bufferoffset = rsurface.modelnormal3f_bufferoffset;
-       rsurface.texcoordtexture2f = rsurface.modeltexcoordtexture2f;
-
-       if (rsurface.modelnum_vertices && rsurface.modelelement3i)
+       rsurface.batchgeneratedvertex = false;
+       rsurface.batchfirstvertex = 0;
+       rsurface.batchnumvertices = 0;
+       rsurface.batchfirsttriangle = 0;
+       rsurface.batchnumtriangles = 0;
+       rsurface.batchvertex3f  = NULL;
+       rsurface.batchvertex3f_vertexbuffer = NULL;
+       rsurface.batchvertex3f_bufferoffset = 0;
+       rsurface.batchsvector3f = NULL;
+       rsurface.batchsvector3f_vertexbuffer = NULL;
+       rsurface.batchsvector3f_bufferoffset = 0;
+       rsurface.batchtvector3f = NULL;
+       rsurface.batchtvector3f_vertexbuffer = NULL;
+       rsurface.batchtvector3f_bufferoffset = 0;
+       rsurface.batchnormal3f  = NULL;
+       rsurface.batchnormal3f_vertexbuffer = NULL;
+       rsurface.batchnormal3f_bufferoffset = 0;
+       rsurface.batchlightmapcolor4f = NULL;
+       rsurface.batchlightmapcolor4f_vertexbuffer = NULL;
+       rsurface.batchlightmapcolor4f_bufferoffset = 0;
+       rsurface.batchtexcoordtexture2f = NULL;
+       rsurface.batchtexcoordtexture2f_vertexbuffer = NULL;
+       rsurface.batchtexcoordtexture2f_bufferoffset = 0;
+       rsurface.batchtexcoordlightmap2f = NULL;
+       rsurface.batchtexcoordlightmap2f_vertexbuffer = NULL;
+       rsurface.batchtexcoordlightmap2f_bufferoffset = 0;
+       rsurface.batchvertexmesh = NULL;
+       rsurface.batchvertexmeshbuffer = NULL;
+       rsurface.batchvertex3fbuffer = NULL;
+       rsurface.batchelement3i = NULL;
+       rsurface.batchelement3i_indexbuffer = NULL;
+       rsurface.batchelement3i_bufferoffset = 0;
+       rsurface.batchelement3s = NULL;
+       rsurface.batchelement3s_indexbuffer = NULL;
+       rsurface.batchelement3s_bufferoffset = 0;
+       rsurface.passcolor4f = NULL;
+       rsurface.passcolor4f_vertexbuffer = NULL;
+       rsurface.passcolor4f_bufferoffset = 0;
+
+       if (rsurface.modelnumvertices && rsurface.modelelement3i)
        {
                if ((wantnormals || wanttangents) && !normal3f)
-                       Mod_BuildNormals(0, rsurface.modelnum_vertices, rsurface.modelnum_triangles, rsurface.modelvertex3f, rsurface.modelelement3i, rsurface.array_modelnormal3f, r_smoothnormals_areaweighting.integer != 0);
+               {
+                       rsurface.modelnormal3f = (float *)R_FrameData_Alloc(rsurface.modelnumvertices * sizeof(float[3]));
+                       Mod_BuildNormals(0, rsurface.modelnumvertices, rsurface.modelnumtriangles, rsurface.modelvertex3f, rsurface.modelelement3i, rsurface.modelnormal3f, r_smoothnormals_areaweighting.integer != 0);
+               }
                if (wanttangents && !svector3f)
-                       Mod_BuildTextureVectorsFromNormals(0, rsurface.modelnum_vertices, rsurface.modelnum_triangles, rsurface.modelvertex3f, rsurface.modeltexcoordtexture2f, rsurface.modelnormal3f, rsurface.modelelement3i, rsurface.array_modelsvector3f, rsurface.array_modeltvector3f, r_smoothnormals_areaweighting.integer != 0);
+               {
+                       rsurface.modelsvector3f = (float *)R_FrameData_Alloc(rsurface.modelnumvertices * sizeof(float[3]));
+                       rsurface.modeltvector3f = (float *)R_FrameData_Alloc(rsurface.modelnumvertices * sizeof(float[3]));
+                       Mod_BuildTextureVectorsFromNormals(0, rsurface.modelnumvertices, rsurface.modelnumtriangles, rsurface.modelvertex3f, rsurface.modeltexcoordtexture2f, rsurface.modelnormal3f, rsurface.modelelement3i, rsurface.modelsvector3f, rsurface.modeltvector3f, r_smoothnormals_areaweighting.integer != 0);
+               }
        }
 }
 
@@ -9506,70 +8005,89 @@ float RSurf_FogVertex(const float *v)
        return r_refdef.fogmasktable[min(fogmasktableindex, FOGMASKTABLEWIDTH - 1)];
 }
 
+void RSurf_RenumberElements(const int *inelement3i, int *outelement3i, int numelements, int adjust)
+{
+       int i;
+       for (i = 0;i < numelements;i++)
+               outelement3i[i] = inelement3i[i] + adjust;
+}
+
 static const int quadedges[6][2] = {{0, 1}, {0, 2}, {0, 3}, {1, 2}, {1, 3}, {2, 3}};
-void RSurf_PrepareVerticesForBatch(qboolean generatenormals, qboolean generatetangents, int texturenumsurfaces, const msurface_t **texturesurfacelist)
+extern cvar_t gl_vbo;
+void RSurf_PrepareVerticesForBatch(int batchneed, int texturenumsurfaces, const msurface_t **texturesurfacelist)
 {
        int deformindex;
-       int texturesurfaceindex;
+       int firsttriangle;
+       int numtriangles;
+       int firstvertex;
+       int endvertex;
+       int numvertices;
+       int surfacefirsttriangle;
+       int surfacenumtriangles;
+       int surfacefirstvertex;
+       int surfaceendvertex;
+       int surfacenumvertices;
+       int batchnumvertices;
+       int batchnumtriangles;
+       int needsupdate;
        int i, j;
+       qboolean gaps;
+       qboolean dynamicvertex;
        float amplitude;
        float animpos;
        float scale;
-       const float *v1, *in_tc;
-       float *out_tc;
        float center[3], forward[3], right[3], up[3], v[3], newforward[3], newright[3], newup[3];
        float waveparms[4];
        q3shaderinfo_deform_t *deform;
-       // if vertices are dynamic (animated models), generate them into the temporary rsurface.array_model* arrays and point rsurface.model* at them instead of the static data from the model itself
-       if (rsurface.generatedvertex)
+       const msurface_t *surface, *firstsurface;
+       r_vertexmesh_t *vertexmesh;
+       if (!texturenumsurfaces)
+               return;
+       // find vertex range of this surface batch
+       gaps = false;
+       firstsurface = texturesurfacelist[0];
+       firsttriangle = firstsurface->num_firsttriangle;
+       batchnumvertices = 0;
+       batchnumtriangles = 0;
+       firstvertex = endvertex = firstsurface->num_firstvertex;
+       for (i = 0;i < texturenumsurfaces;i++)
        {
-               if (rsurface.texture->tcgen.tcgen == Q3TCGEN_ENVIRONMENT)
-                       generatenormals = true;
-               for (i = 0;i < Q3MAXDEFORMS;i++)
-               {
-                       if (rsurface.texture->deforms[i].deform == Q3DEFORM_AUTOSPRITE)
-                       {
-                               generatetangents = true;
-                               generatenormals = true;
-                       }
-                       if (rsurface.texture->deforms[i].deform != Q3DEFORM_NONE)
-                               generatenormals = true;
-               }
-               if (generatenormals && !rsurface.modelnormal3f)
-               {
-                       rsurface.normal3f = rsurface.modelnormal3f = rsurface.array_modelnormal3f;
-                       rsurface.normal3f_bufferobject = rsurface.modelnormal3f_bufferobject = 0;
-                       rsurface.normal3f_bufferoffset = rsurface.modelnormal3f_bufferoffset = 0;
-                       Mod_BuildNormals(0, rsurface.modelnum_vertices, rsurface.modelnum_triangles, rsurface.modelvertex3f, rsurface.modelelement3i, rsurface.array_modelnormal3f, r_smoothnormals_areaweighting.integer != 0);
-               }
-               if (generatetangents && !rsurface.modelsvector3f)
-               {
-                       rsurface.svector3f = rsurface.modelsvector3f = rsurface.array_modelsvector3f;
-                       rsurface.svector3f_bufferobject = rsurface.modelsvector3f_bufferobject = 0;
-                       rsurface.svector3f_bufferoffset = rsurface.modelsvector3f_bufferoffset = 0;
-                       rsurface.tvector3f = rsurface.modeltvector3f = rsurface.array_modeltvector3f;
-                       rsurface.tvector3f_bufferobject = rsurface.modeltvector3f_bufferobject = 0;
-                       rsurface.tvector3f_bufferoffset = rsurface.modeltvector3f_bufferoffset = 0;
-                       Mod_BuildTextureVectorsFromNormals(0, rsurface.modelnum_vertices, rsurface.modelnum_triangles, rsurface.modelvertex3f, rsurface.modeltexcoordtexture2f, rsurface.modelnormal3f, rsurface.modelelement3i, rsurface.array_modelsvector3f, rsurface.array_modeltvector3f, r_smoothnormals_areaweighting.integer != 0);
-               }
-       }
-       rsurface.vertex3f  = rsurface.modelvertex3f;
-       rsurface.vertex3f_bufferobject = rsurface.modelvertex3f_bufferobject;
-       rsurface.vertex3f_bufferoffset = rsurface.modelvertex3f_bufferoffset;
-       rsurface.svector3f = rsurface.modelsvector3f;
-       rsurface.svector3f_bufferobject = rsurface.modelsvector3f_bufferobject;
-       rsurface.svector3f_bufferoffset = rsurface.modelsvector3f_bufferoffset;
-       rsurface.tvector3f = rsurface.modeltvector3f;
-       rsurface.tvector3f_bufferobject = rsurface.modeltvector3f_bufferobject;
-       rsurface.tvector3f_bufferoffset = rsurface.modeltvector3f_bufferoffset;
-       rsurface.normal3f  = rsurface.modelnormal3f;
-       rsurface.normal3f_bufferobject = rsurface.modelnormal3f_bufferobject;
-       rsurface.normal3f_bufferoffset = rsurface.modelnormal3f_bufferoffset;
-       // if vertices are deformed (sprite flares and things in maps, possibly
-       // water waves, bulges and other deformations), generate them into
-       // rsurface.deform* arrays from whatever the rsurface.* arrays point to
-       // (may be static model data or generated data for an animated model, or
-       //  the previous deform pass)
+               surface = texturesurfacelist[i];
+               if (surface != firstsurface + i)
+                       gaps = true;
+               surfacefirstvertex = surface->num_firstvertex;
+               surfaceendvertex = surfacefirstvertex + surface->num_vertices;
+               surfacenumvertices = surface->num_vertices;
+               surfacenumtriangles = surface->num_triangles;
+               if (firstvertex > surfacefirstvertex)
+                       firstvertex = surfacefirstvertex;
+               if (endvertex < surfaceendvertex)
+                       endvertex = surfaceendvertex;
+               batchnumvertices += surfacenumvertices;
+               batchnumtriangles += surfacenumtriangles;
+       }
+
+       // we now know the vertex range used, and if there are any gaps in it
+       rsurface.batchfirstvertex = firstvertex;
+       rsurface.batchnumvertices = endvertex - firstvertex;
+       rsurface.batchfirsttriangle = firsttriangle;
+       rsurface.batchnumtriangles = batchnumtriangles;
+
+       // this variable holds flags for which properties have been updated that
+       // may require regenerating vertexmesh array...
+       needsupdate = 0;
+
+       // check if any dynamic vertex processing must occur
+       dynamicvertex = false;
+
+       // if there is a chance of animated vertex colors, it's a dynamic batch
+       if ((batchneed & (BATCHNEED_VERTEXMESH_VERTEXCOLOR | BATCHNEED_ARRAY_VERTEXCOLOR)) && texturesurfacelist[0]->lightmapinfo)
+       {
+               dynamicvertex = true;
+               batchneed |= BATCHNEED_NOGAPS;
+               needsupdate |= BATCHNEED_VERTEXMESH_VERTEXCOLOR;
+       }
+
        for (deformindex = 0, deform = rsurface.texture->deforms;deformindex < Q3MAXDEFORMS && deform->deform;deformindex++, deform++)
        {
                switch (deform->deform)
@@ -9587,90 +8105,447 @@ void RSurf_PrepareVerticesForBatch(qboolean generatenormals, qboolean generateta
                case Q3DEFORM_NONE:
                        break;
                case Q3DEFORM_AUTOSPRITE:
-                       Matrix4x4_Transform3x3(&rsurface.inversematrix, r_refdef.view.forward, newforward);
-                       Matrix4x4_Transform3x3(&rsurface.inversematrix, r_refdef.view.right, newright);
-                       Matrix4x4_Transform3x3(&rsurface.inversematrix, r_refdef.view.up, newup);
-                       VectorNormalize(newforward);
-                       VectorNormalize(newright);
-                       VectorNormalize(newup);
-                       // make deformed versions of only the model vertices used by the specified surfaces
-                       for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
-                       {
-                               const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
-                               // a single autosprite surface can contain multiple sprites...
-                               for (j = 0;j < surface->num_vertices - 3;j += 4)
-                               {
-                                       VectorClear(center);
-                                       for (i = 0;i < 4;i++)
-                                               VectorAdd(center, (rsurface.vertex3f + 3 * surface->num_firstvertex) + (j+i) * 3, center);
-                                       VectorScale(center, 0.25f, center);
-                                       VectorCopy((rsurface.normal3f  + 3 * surface->num_firstvertex) + j*3, forward);
-                                       VectorCopy((rsurface.svector3f + 3 * surface->num_firstvertex) + j*3, right);
-                                       VectorCopy((rsurface.tvector3f + 3 * surface->num_firstvertex) + j*3, up);
-                                       for (i = 0;i < 4;i++)
-                                       {
-                                               VectorSubtract((rsurface.vertex3f + 3 * surface->num_firstvertex) + (j+i)*3, center, v);
-                                               VectorMAMAMAM(1, center, DotProduct(forward, v), newforward, DotProduct(right, v), newright, DotProduct(up, v), newup, rsurface.array_deformedvertex3f + (surface->num_firstvertex+i+j) * 3);
-                                       }
-                               }
-                               Mod_BuildNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, rsurface.vertex3f, rsurface.modelelement3i + surface->num_firsttriangle * 3, rsurface.array_deformednormal3f, r_smoothnormals_areaweighting.integer != 0);
-                               Mod_BuildTextureVectorsFromNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, rsurface.vertex3f, rsurface.modeltexcoordtexture2f, rsurface.array_deformednormal3f, rsurface.modelelement3i + surface->num_firsttriangle * 3, rsurface.array_deformedsvector3f, rsurface.array_deformedtvector3f, r_smoothnormals_areaweighting.integer != 0);
-                       }
-                       rsurface.vertex3f = rsurface.array_deformedvertex3f;
-                       rsurface.vertex3f_bufferobject = 0;
-                       rsurface.vertex3f_bufferoffset = 0;
-                       rsurface.svector3f = rsurface.array_deformedsvector3f;
-                       rsurface.svector3f_bufferobject = 0;
-                       rsurface.svector3f_bufferoffset = 0;
-                       rsurface.tvector3f = rsurface.array_deformedtvector3f;
-                       rsurface.tvector3f_bufferobject = 0;
-                       rsurface.tvector3f_bufferoffset = 0;
-                       rsurface.normal3f = rsurface.array_deformednormal3f;
-                       rsurface.normal3f_bufferobject = 0;
-                       rsurface.normal3f_bufferoffset = 0;
+                       dynamicvertex = true;
+                       batchneed |= BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_NORMAL | BATCHNEED_ARRAY_VECTOR | BATCHNEED_ARRAY_TEXCOORD | BATCHNEED_NOGAPS;
+                       needsupdate |= BATCHNEED_VERTEXMESH_VERTEX | BATCHNEED_VERTEXMESH_NORMAL | BATCHNEED_VERTEXMESH_VECTOR;
                        break;
                case Q3DEFORM_AUTOSPRITE2:
-                       Matrix4x4_Transform3x3(&rsurface.inversematrix, r_refdef.view.forward, newforward);
-                       Matrix4x4_Transform3x3(&rsurface.inversematrix, r_refdef.view.right, newright);
-                       Matrix4x4_Transform3x3(&rsurface.inversematrix, r_refdef.view.up, newup);
-                       VectorNormalize(newforward);
-                       VectorNormalize(newright);
-                       VectorNormalize(newup);
-                       // make deformed versions of only the model vertices used by the specified surfaces
-                       for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
-                       {
-                               const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
-                               const float *v1, *v2;
-                               vec3_t start, end;
-                               float f, l;
-                               struct
-                               {
-                                       float length2;
-                                       const float *v1;
-                                       const float *v2;
-                               }
-                               shortest[2];
-                               memset(shortest, 0, sizeof(shortest));
-                               // a single autosprite surface can contain multiple sprites...
-                               for (j = 0;j < surface->num_vertices - 3;j += 4)
-                               {
-                                       VectorClear(center);
-                                       for (i = 0;i < 4;i++)
-                                               VectorAdd(center, (rsurface.vertex3f + 3 * surface->num_firstvertex) + (j+i) * 3, center);
-                                       VectorScale(center, 0.25f, center);
-                                       // find the two shortest edges, then use them to define the
-                                       // axis vectors for rotating around the central axis
-                                       for (i = 0;i < 6;i++)
-                                       {
-                                               v1 = rsurface.vertex3f + 3 * (surface->num_firstvertex + quadedges[i][0]);
-                                               v2 = rsurface.vertex3f + 3 * (surface->num_firstvertex + quadedges[i][1]);
-#if 0
-                                               Debug_PolygonBegin(NULL, 0);
-                                               Debug_PolygonVertex(v1[0], v1[1], v1[2], 0, 0, 1, 0, 0, 1);
-                                               Debug_PolygonVertex((v1[0] + v2[0]) * 0.5f + rsurface.normal3f[3 * (surface->num_firstvertex + j)+0] * 4, (v1[1] + v2[1]) * 0.5f + rsurface.normal3f[3 * (surface->num_firstvertex + j)+1], (v1[2] + v2[2]) * 0.5f + rsurface.normal3f[3 * (surface->num_firstvertex + j)+2], 0, 0, 1, 1, 0, 1);
-                                               Debug_PolygonVertex(v2[0], v2[1], v2[2], 0, 0, 1, 0, 0, 1);
-                                               Debug_PolygonEnd();
-#endif
+                       dynamicvertex = true;
+                       batchneed |= BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_TEXCOORD | BATCHNEED_NOGAPS;
+                       needsupdate |= BATCHNEED_VERTEXMESH_VERTEX | BATCHNEED_VERTEXMESH_NORMAL | BATCHNEED_VERTEXMESH_VECTOR;
+                       break;
+               case Q3DEFORM_NORMAL:
+                       dynamicvertex = true;
+                       batchneed |= BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_NORMAL | BATCHNEED_ARRAY_TEXCOORD | BATCHNEED_NOGAPS;
+                       needsupdate |= BATCHNEED_VERTEXMESH_NORMAL | BATCHNEED_VERTEXMESH_VECTOR;
+                       break;
+               case Q3DEFORM_WAVE:
+                       if(!R_TestQ3WaveFunc(deform->wavefunc, deform->waveparms))
+                               break; // if wavefunc is a nop, ignore this transform
+                       dynamicvertex = true;
+                       batchneed |= BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_NORMAL | BATCHNEED_ARRAY_TEXCOORD | BATCHNEED_NOGAPS;
+                       needsupdate |= BATCHNEED_VERTEXMESH_VERTEX | BATCHNEED_VERTEXMESH_NORMAL | BATCHNEED_VERTEXMESH_VECTOR;
+                       break;
+               case Q3DEFORM_BULGE:
+                       dynamicvertex = true;
+                       batchneed |= BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_NORMAL | BATCHNEED_ARRAY_TEXCOORD | BATCHNEED_NOGAPS;
+                       needsupdate |= BATCHNEED_VERTEXMESH_VERTEX | BATCHNEED_VERTEXMESH_NORMAL | BATCHNEED_VERTEXMESH_VECTOR;
+                       break;
+               case Q3DEFORM_MOVE:
+                       if(!R_TestQ3WaveFunc(deform->wavefunc, deform->waveparms))
+                               break; // if wavefunc is a nop, ignore this transform
+                       dynamicvertex = true;
+                       batchneed |= BATCHNEED_ARRAY_VERTEX | BATCHNEED_NOGAPS;
+                       needsupdate |= BATCHNEED_VERTEXMESH_VERTEX;
+                       break;
+               }
+       }
+       switch(rsurface.texture->tcgen.tcgen)
+       {
+       default:
+       case Q3TCGEN_TEXTURE:
+               break;
+       case Q3TCGEN_LIGHTMAP:
+               dynamicvertex = true;
+               batchneed |= BATCHNEED_ARRAY_LIGHTMAP | BATCHNEED_NOGAPS;
+               needsupdate |= BATCHNEED_VERTEXMESH_LIGHTMAP;
+               break;
+       case Q3TCGEN_VECTOR:
+               dynamicvertex = true;
+               batchneed |= BATCHNEED_ARRAY_VERTEX | BATCHNEED_NOGAPS;
+               needsupdate |= BATCHNEED_VERTEXMESH_TEXCOORD;
+               break;
+       case Q3TCGEN_ENVIRONMENT:
+               dynamicvertex = true;
+               batchneed |= BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_NORMAL | BATCHNEED_NOGAPS;
+               needsupdate |= BATCHNEED_VERTEXMESH_TEXCOORD;
+               break;
+       }
+       if (rsurface.texture->tcmods[0].tcmod == Q3TCMOD_TURBULENT)
+       {
+               dynamicvertex = true;
+               batchneed |= BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_TEXCOORD | BATCHNEED_NOGAPS;
+               needsupdate |= BATCHNEED_VERTEXMESH_TEXCOORD;
+       }
+
+       if (!rsurface.modelvertexmesh && (batchneed & (BATCHNEED_VERTEXMESH_VERTEX | BATCHNEED_VERTEXMESH_NORMAL | BATCHNEED_VERTEXMESH_VECTOR | BATCHNEED_VERTEXMESH_VERTEXCOLOR | BATCHNEED_VERTEXMESH_TEXCOORD | BATCHNEED_VERTEXMESH_LIGHTMAP)))
+       {
+               dynamicvertex = true;
+               batchneed |= BATCHNEED_NOGAPS;
+               needsupdate |= (batchneed & (BATCHNEED_VERTEXMESH_VERTEX | BATCHNEED_VERTEXMESH_NORMAL | BATCHNEED_VERTEXMESH_VECTOR | BATCHNEED_VERTEXMESH_VERTEXCOLOR | BATCHNEED_VERTEXMESH_TEXCOORD | BATCHNEED_VERTEXMESH_LIGHTMAP));
+       }
+
+       if (dynamicvertex || gaps || rsurface.batchfirstvertex)
+       {
+               // when copying, we need to consider the regeneration of vertexmesh, any dependencies it may have must be set...
+               if (batchneed & BATCHNEED_VERTEXMESH_VERTEX)      batchneed |= BATCHNEED_ARRAY_VERTEX;
+               if (batchneed & BATCHNEED_VERTEXMESH_NORMAL)      batchneed |= BATCHNEED_ARRAY_NORMAL;
+               if (batchneed & BATCHNEED_VERTEXMESH_VECTOR)      batchneed |= BATCHNEED_ARRAY_VECTOR;
+               if (batchneed & BATCHNEED_VERTEXMESH_VERTEXCOLOR) batchneed |= BATCHNEED_ARRAY_VERTEXCOLOR;
+               if (batchneed & BATCHNEED_VERTEXMESH_TEXCOORD)    batchneed |= BATCHNEED_ARRAY_TEXCOORD;
+               if (batchneed & BATCHNEED_VERTEXMESH_LIGHTMAP)    batchneed |= BATCHNEED_ARRAY_LIGHTMAP;
+       }
+
+       // when the model data has no vertex buffer (dynamic mesh), we need to
+       // eliminate gaps
+       if (vid.useinterleavedarrays ? !rsurface.modelvertexmeshbuffer : !rsurface.modelvertex3f_vertexbuffer)
+               batchneed |= BATCHNEED_NOGAPS;
+
+       // if needsupdate, we have to do a dynamic vertex batch for sure
+       if (needsupdate & batchneed)
+               dynamicvertex = true;
+
+       // see if we need to build vertexmesh from arrays
+       if (!rsurface.modelvertexmesh && (batchneed & (BATCHNEED_VERTEXMESH_VERTEX | BATCHNEED_VERTEXMESH_NORMAL | BATCHNEED_VERTEXMESH_VECTOR | BATCHNEED_VERTEXMESH_VERTEXCOLOR | BATCHNEED_VERTEXMESH_TEXCOORD | BATCHNEED_VERTEXMESH_LIGHTMAP)))
+               dynamicvertex = true;
+
+       // if gaps are unacceptable, and there are gaps, it's a dynamic batch...
+       // also some drivers strongly dislike firstvertex
+       if ((batchneed & BATCHNEED_NOGAPS) && (gaps || firstvertex))
+               dynamicvertex = true;
+
+       rsurface.batchvertex3f = rsurface.modelvertex3f;
+       rsurface.batchvertex3f_vertexbuffer = rsurface.modelvertex3f_vertexbuffer;
+       rsurface.batchvertex3f_bufferoffset = rsurface.modelvertex3f_bufferoffset;
+       rsurface.batchsvector3f = rsurface.modelsvector3f;
+       rsurface.batchsvector3f_vertexbuffer = rsurface.modelsvector3f_vertexbuffer;
+       rsurface.batchsvector3f_bufferoffset = rsurface.modelsvector3f_bufferoffset;
+       rsurface.batchtvector3f = rsurface.modeltvector3f;
+       rsurface.batchtvector3f_vertexbuffer = rsurface.modeltvector3f_vertexbuffer;
+       rsurface.batchtvector3f_bufferoffset = rsurface.modeltvector3f_bufferoffset;
+       rsurface.batchnormal3f = rsurface.modelnormal3f;
+       rsurface.batchnormal3f_vertexbuffer = rsurface.modelnormal3f_vertexbuffer;
+       rsurface.batchnormal3f_bufferoffset = rsurface.modelnormal3f_bufferoffset;
+       rsurface.batchlightmapcolor4f = rsurface.modellightmapcolor4f;
+       rsurface.batchlightmapcolor4f_vertexbuffer  = rsurface.modellightmapcolor4f_vertexbuffer;
+       rsurface.batchlightmapcolor4f_bufferoffset  = rsurface.modellightmapcolor4f_bufferoffset;
+       rsurface.batchtexcoordtexture2f = rsurface.modeltexcoordtexture2f;
+       rsurface.batchtexcoordtexture2f_vertexbuffer  = rsurface.modeltexcoordtexture2f_vertexbuffer;
+       rsurface.batchtexcoordtexture2f_bufferoffset  = rsurface.modeltexcoordtexture2f_bufferoffset;
+       rsurface.batchtexcoordlightmap2f = rsurface.modeltexcoordlightmap2f;
+       rsurface.batchtexcoordlightmap2f_vertexbuffer = rsurface.modeltexcoordlightmap2f_vertexbuffer;
+       rsurface.batchtexcoordlightmap2f_bufferoffset = rsurface.modeltexcoordlightmap2f_bufferoffset;
+       rsurface.batchvertex3fbuffer = rsurface.modelvertex3fbuffer;
+       rsurface.batchvertexmesh = rsurface.modelvertexmesh;
+       rsurface.batchvertexmeshbuffer = rsurface.modelvertexmeshbuffer;
+       rsurface.batchelement3i = rsurface.modelelement3i;
+       rsurface.batchelement3i_indexbuffer = rsurface.modelelement3i_indexbuffer;
+       rsurface.batchelement3i_bufferoffset = rsurface.modelelement3i_bufferoffset;
+       rsurface.batchelement3s = rsurface.modelelement3s;
+       rsurface.batchelement3s_indexbuffer = rsurface.modelelement3s_indexbuffer;
+       rsurface.batchelement3s_bufferoffset = rsurface.modelelement3s_bufferoffset;
+
+       // if any dynamic vertex processing has to occur in software, we copy the
+       // entire surface list together before processing to rebase the vertices
+       // to start at 0 (otherwise we waste a lot of room in a vertex buffer).
+       //
+       // if any gaps exist and we do not have a static vertex buffer, we have to
+       // copy the surface list together to avoid wasting upload bandwidth on the
+       // vertices in the gaps.
+       //
+       // if gaps exist and we have a static vertex buffer, we still have to
+       // combine the index buffer ranges into one dynamic index buffer.
+       //
+       // in all cases we end up with data that can be drawn in one call.
+
+       if (!dynamicvertex)
+       {
+               // static vertex data, just set pointers...
+               rsurface.batchgeneratedvertex = false;
+               // if there are gaps, we want to build a combined index buffer,
+               // otherwise use the original static buffer with an appropriate offset
+               if (gaps)
+               {
+                       // build a new triangle elements array for this batch
+                       rsurface.batchelement3i = (int *)R_FrameData_Alloc(batchnumtriangles * sizeof(int[3]));
+                       rsurface.batchfirsttriangle = 0;
+                       numtriangles = 0;
+                       for (i = 0;i < texturenumsurfaces;i++)
+                       {
+                               surfacefirsttriangle = texturesurfacelist[i]->num_firsttriangle;
+                               surfacenumtriangles = texturesurfacelist[i]->num_triangles;
+                               memcpy(rsurface.batchelement3i + 3*numtriangles, rsurface.modelelement3i + 3*surfacefirsttriangle, surfacenumtriangles*sizeof(int[3]));
+                               numtriangles += surfacenumtriangles;
+                       }
+                       rsurface.batchelement3i_indexbuffer = NULL;
+                       rsurface.batchelement3i_bufferoffset = 0;
+                       rsurface.batchelement3s = NULL;
+                       rsurface.batchelement3s_indexbuffer = NULL;
+                       rsurface.batchelement3s_bufferoffset = 0;
+                       if (endvertex <= 65536)
+                       {
+                               // make a 16bit (unsigned short) index array if possible
+                               rsurface.batchelement3s = (unsigned short *)R_FrameData_Alloc(batchnumtriangles * sizeof(unsigned short[3]));
+                               for (i = 0;i < numtriangles*3;i++)
+                                       rsurface.batchelement3s[i] = rsurface.batchelement3i[i];
+                       }
+               }
+               return;
+       }
+
+       // something needs software processing, do it for real...
+       // we only directly handle separate array data in this case and then
+       // generate interleaved data if needed...
+       rsurface.batchgeneratedvertex = true;
+
+       // now copy the vertex data into a combined array and make an index array
+       // (this is what Quake3 does all the time)
+       //if (gaps || rsurface.batchfirstvertex)
+       {
+               rsurface.batchvertex3fbuffer = NULL;
+               rsurface.batchvertexmesh = NULL;
+               rsurface.batchvertexmeshbuffer = NULL;
+               rsurface.batchvertex3f = NULL;
+               rsurface.batchvertex3f_vertexbuffer = NULL;
+               rsurface.batchvertex3f_bufferoffset = 0;
+               rsurface.batchsvector3f = NULL;
+               rsurface.batchsvector3f_vertexbuffer = NULL;
+               rsurface.batchsvector3f_bufferoffset = 0;
+               rsurface.batchtvector3f = NULL;
+               rsurface.batchtvector3f_vertexbuffer = NULL;
+               rsurface.batchtvector3f_bufferoffset = 0;
+               rsurface.batchnormal3f = NULL;
+               rsurface.batchnormal3f_vertexbuffer = NULL;
+               rsurface.batchnormal3f_bufferoffset = 0;
+               rsurface.batchlightmapcolor4f = NULL;
+               rsurface.batchlightmapcolor4f_vertexbuffer = NULL;
+               rsurface.batchlightmapcolor4f_bufferoffset = 0;
+               rsurface.batchtexcoordtexture2f = NULL;
+               rsurface.batchtexcoordtexture2f_vertexbuffer = NULL;
+               rsurface.batchtexcoordtexture2f_bufferoffset = 0;
+               rsurface.batchtexcoordlightmap2f = NULL;
+               rsurface.batchtexcoordlightmap2f_vertexbuffer = NULL;
+               rsurface.batchtexcoordlightmap2f_bufferoffset = 0;
+               rsurface.batchelement3i = (int *)R_FrameData_Alloc(batchnumtriangles * sizeof(int[3]));
+               rsurface.batchelement3i_indexbuffer = NULL;
+               rsurface.batchelement3i_bufferoffset = 0;
+               rsurface.batchelement3s = NULL;
+               rsurface.batchelement3s_indexbuffer = NULL;
+               rsurface.batchelement3s_bufferoffset = 0;
+               // we'll only be setting up certain arrays as needed
+               if (batchneed & (BATCHNEED_VERTEXMESH_VERTEX | BATCHNEED_VERTEXMESH_NORMAL | BATCHNEED_VERTEXMESH_VECTOR | BATCHNEED_VERTEXMESH_VERTEXCOLOR | BATCHNEED_VERTEXMESH_TEXCOORD | BATCHNEED_VERTEXMESH_LIGHTMAP))
+                       rsurface.batchvertexmesh = (r_vertexmesh_t *)R_FrameData_Alloc(batchnumvertices * sizeof(r_vertexmesh_t));
+               if (batchneed & BATCHNEED_ARRAY_VERTEX)
+                       rsurface.batchvertex3f = (float *)R_FrameData_Alloc(batchnumvertices * sizeof(float[3]));
+               if (batchneed & BATCHNEED_ARRAY_NORMAL)
+                       rsurface.batchnormal3f = (float *)R_FrameData_Alloc(batchnumvertices * sizeof(float[3]));
+               if (batchneed & BATCHNEED_ARRAY_VECTOR)
+               {
+                       rsurface.batchsvector3f = (float *)R_FrameData_Alloc(batchnumvertices * sizeof(float[3]));
+                       rsurface.batchtvector3f = (float *)R_FrameData_Alloc(batchnumvertices * sizeof(float[3]));
+               }
+               if (batchneed & BATCHNEED_ARRAY_VERTEXCOLOR)
+                       rsurface.batchlightmapcolor4f = (float *)R_FrameData_Alloc(batchnumvertices * sizeof(float[4]));
+               if (batchneed & BATCHNEED_ARRAY_TEXCOORD)
+                       rsurface.batchtexcoordtexture2f = (float *)R_FrameData_Alloc(batchnumvertices * sizeof(float[2]));
+               if (batchneed & BATCHNEED_ARRAY_LIGHTMAP)
+                       rsurface.batchtexcoordlightmap2f = (float *)R_FrameData_Alloc(batchnumvertices * sizeof(float[2]));
+               numvertices = 0;
+               numtriangles = 0;
+               for (i = 0;i < texturenumsurfaces;i++)
+               {
+                       surfacefirstvertex = texturesurfacelist[i]->num_firstvertex;
+                       surfacenumvertices = texturesurfacelist[i]->num_vertices;
+                       surfacefirsttriangle = texturesurfacelist[i]->num_firsttriangle;
+                       surfacenumtriangles = texturesurfacelist[i]->num_triangles;
+                       // copy only the data requested
+                       if ((batchneed & (BATCHNEED_VERTEXMESH_VERTEX | BATCHNEED_VERTEXMESH_NORMAL | BATCHNEED_VERTEXMESH_VECTOR | BATCHNEED_VERTEXMESH_VERTEXCOLOR | BATCHNEED_VERTEXMESH_TEXCOORD | BATCHNEED_VERTEXMESH_LIGHTMAP)) && rsurface.modelvertexmesh)
+                               memcpy(rsurface.batchvertexmesh + numvertices, rsurface.modelvertexmesh + surfacefirstvertex, surfacenumvertices * sizeof(rsurface.batchvertexmesh[0]));
+                       if (batchneed & (BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_NORMAL | BATCHNEED_ARRAY_VECTOR | BATCHNEED_ARRAY_VERTEXCOLOR | BATCHNEED_ARRAY_TEXCOORD | BATCHNEED_ARRAY_LIGHTMAP))
+                       {
+                               if (batchneed & BATCHNEED_ARRAY_VERTEX)
+                                       memcpy(rsurface.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 ((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;
+                       numtriangles += surfacenumtriangles;
+               }
+
+               // generate a 16bit index array as well if possible
+               // (in general, dynamic batches fit)
+               if (numvertices <= 65536)
+               {
+                       rsurface.batchelement3s = (unsigned short *)R_FrameData_Alloc(batchnumtriangles * sizeof(unsigned short[3]));
+                       for (i = 0;i < numtriangles*3;i++)
+                               rsurface.batchelement3s[i] = rsurface.batchelement3i[i];
+               }
+
+               // since we've copied everything, the batch now starts at 0
+               rsurface.batchfirstvertex = 0;
+               rsurface.batchnumvertices = batchnumvertices;
+               rsurface.batchfirsttriangle = 0;
+               rsurface.batchnumtriangles = batchnumtriangles;
+       }
+
+       // q1bsp surfaces rendered in vertex color mode have to have colors
+       // calculated based on lightstyles
+       if ((batchneed & (BATCHNEED_VERTEXMESH_VERTEXCOLOR | BATCHNEED_ARRAY_VERTEXCOLOR)) && texturesurfacelist[0]->lightmapinfo)
+       {
+               // generate color arrays for the surfaces in this list
+               int c[4];
+               int scale;
+               int size3;
+               const int *offsets;
+               const unsigned char *lm;
+               rsurface.batchlightmapcolor4f = (float *)R_FrameData_Alloc(batchnumvertices * sizeof(float[4]));
+               rsurface.batchlightmapcolor4f_vertexbuffer = NULL;
+               rsurface.batchlightmapcolor4f_bufferoffset = 0;
+               numvertices = 0;
+               for (i = 0;i < texturenumsurfaces;i++)
+               {
+                       surface = texturesurfacelist[i];
+                       offsets = rsurface.modellightmapoffsets + surface->num_firstvertex;
+                       surfacenumvertices = surface->num_vertices;
+                       if (surface->lightmapinfo->samples)
+                       {
+                               for (j = 0;j < surfacenumvertices;j++)
+                               {
+                                       lm = surface->lightmapinfo->samples + offsets[j];
+                                       scale = r_refdef.scene.lightstylevalue[surface->lightmapinfo->styles[0]];
+                                       VectorScale(lm, scale, c);
+                                       if (surface->lightmapinfo->styles[1] != 255)
+                                       {
+                                               size3 = ((surface->lightmapinfo->extents[0]>>4)+1)*((surface->lightmapinfo->extents[1]>>4)+1)*3;
+                                               lm += size3;
+                                               scale = r_refdef.scene.lightstylevalue[surface->lightmapinfo->styles[1]];
+                                               VectorMA(c, scale, lm, c);
+                                               if (surface->lightmapinfo->styles[2] != 255)
+                                               {
+                                                       lm += size3;
+                                                       scale = r_refdef.scene.lightstylevalue[surface->lightmapinfo->styles[2]];
+                                                       VectorMA(c, scale, lm, c);
+                                                       if (surface->lightmapinfo->styles[3] != 255)
+                                                       {
+                                                               lm += size3;
+                                                               scale = r_refdef.scene.lightstylevalue[surface->lightmapinfo->styles[3]];
+                                                               VectorMA(c, scale, lm, c);
+                                                       }
+                                               }
+                                       }
+                                       c[0] >>= 7;
+                                       c[1] >>= 7;
+                                       c[2] >>= 7;
+                                       Vector4Set(rsurface.batchlightmapcolor4f + 4*numvertices, min(c[0], 255) * (1.0f / 255.0f), min(c[1], 255) * (1.0f / 255.0f), min(c[2], 255) * (1.0f / 255.0f), 1);
+                                       numvertices++;
+                               }
+                       }
+                       else
+                       {
+                               for (j = 0;j < surfacenumvertices;j++)
+                               {
+                                       Vector4Set(rsurface.batchlightmapcolor4f + 4*numvertices, 0, 0, 0, 1);
+                                       numvertices++;
+                               }
+                       }
+               }
+       }
+
+       // if vertices are deformed (sprite flares and things in maps, possibly
+       // water waves, bulges and other deformations), modify the copied vertices
+       // in place
+       for (deformindex = 0, deform = rsurface.texture->deforms;deformindex < Q3MAXDEFORMS && deform->deform;deformindex++, deform++)
+       {
+               switch (deform->deform)
+               {
+               default:
+               case Q3DEFORM_PROJECTIONSHADOW:
+               case Q3DEFORM_TEXT0:
+               case Q3DEFORM_TEXT1:
+               case Q3DEFORM_TEXT2:
+               case Q3DEFORM_TEXT3:
+               case Q3DEFORM_TEXT4:
+               case Q3DEFORM_TEXT5:
+               case Q3DEFORM_TEXT6:
+               case Q3DEFORM_TEXT7:
+               case Q3DEFORM_NONE:
+                       break;
+               case Q3DEFORM_AUTOSPRITE:
+                       Matrix4x4_Transform3x3(&rsurface.inversematrix, r_refdef.view.forward, newforward);
+                       Matrix4x4_Transform3x3(&rsurface.inversematrix, r_refdef.view.right, newright);
+                       Matrix4x4_Transform3x3(&rsurface.inversematrix, r_refdef.view.up, newup);
+                       VectorNormalize(newforward);
+                       VectorNormalize(newright);
+                       VectorNormalize(newup);
+//                     rsurface.batchvertex3f = R_FrameData_Store(batchnumvertices * sizeof(float[3]), rsurface.batchvertex3f);
+//                     rsurface.batchvertex3f_vertexbuffer = NULL;
+//                     rsurface.batchvertex3f_bufferoffset = 0;
+//                     rsurface.batchsvector3f = R_FrameData_Store(batchnumvertices * sizeof(float[3]), rsurface.batchsvector3f);
+//                     rsurface.batchsvector3f_vertexbuffer = NULL;
+//                     rsurface.batchsvector3f_bufferoffset = 0;
+//                     rsurface.batchtvector3f = R_FrameData_Store(batchnumvertices * sizeof(float[3]), rsurface.batchtvector3f);
+//                     rsurface.batchtvector3f_vertexbuffer = NULL;
+//                     rsurface.batchtvector3f_bufferoffset = 0;
+//                     rsurface.batchnormal3f = R_FrameData_Store(batchnumvertices * sizeof(float[3]), rsurface.batchnormal3f);
+//                     rsurface.batchnormal3f_vertexbuffer = NULL;
+//                     rsurface.batchnormal3f_bufferoffset = 0;
+                       // a single autosprite surface can contain multiple sprites...
+                       for (j = 0;j < batchnumvertices - 3;j += 4)
+                       {
+                               VectorClear(center);
+                               for (i = 0;i < 4;i++)
+                                       VectorAdd(center, rsurface.batchvertex3f + 3*(j+i), center);
+                               VectorScale(center, 0.25f, center);
+                               VectorCopy(rsurface.batchnormal3f + 3*j, forward);
+                               VectorCopy(rsurface.batchsvector3f + 3*j, right);
+                               VectorCopy(rsurface.batchtvector3f + 3*j, up);
+                               for (i = 0;i < 4;i++)
+                               {
+                                       VectorSubtract(rsurface.batchvertex3f + 3*(j+i), center, v);
+                                       VectorMAMAMAM(1, center, DotProduct(forward, v), newforward, DotProduct(right, v), newright, DotProduct(up, v), newup, rsurface.batchvertex3f + 3*(j+i));
+                               }
+                       }
+                       // if we get here, BATCHNEED_ARRAY_NORMAL and BATCHNEED_ARRAY_VECTOR are in batchneed, so no need to check
+                       Mod_BuildNormals(rsurface.batchfirstvertex, batchnumvertices, batchnumtriangles, rsurface.batchvertex3f, rsurface.batchelement3i + 3 * rsurface.batchfirsttriangle, rsurface.batchnormal3f, r_smoothnormals_areaweighting.integer != 0);
+                       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);
+                       break;
+               case Q3DEFORM_AUTOSPRITE2:
+                       Matrix4x4_Transform3x3(&rsurface.inversematrix, r_refdef.view.forward, newforward);
+                       Matrix4x4_Transform3x3(&rsurface.inversematrix, r_refdef.view.right, newright);
+                       Matrix4x4_Transform3x3(&rsurface.inversematrix, r_refdef.view.up, newup);
+                       VectorNormalize(newforward);
+                       VectorNormalize(newright);
+                       VectorNormalize(newup);
+//                     rsurface.batchvertex3f = R_FrameData_Store(batchnumvertices * sizeof(float[3]), rsurface.batchvertex3f);
+//                     rsurface.batchvertex3f_vertexbuffer = NULL;
+//                     rsurface.batchvertex3f_bufferoffset = 0;
+                       {
+                               const float *v1, *v2;
+                               vec3_t start, end;
+                               float f, l;
+                               struct
+                               {
+                                       float length2;
+                                       const float *v1;
+                                       const float *v2;
+                               }
+                               shortest[2];
+                               memset(shortest, 0, sizeof(shortest));
+                               // a single autosprite surface can contain multiple sprites...
+                               for (j = 0;j < batchnumvertices - 3;j += 4)
+                               {
+                                       VectorClear(center);
+                                       for (i = 0;i < 4;i++)
+                                               VectorAdd(center, rsurface.batchvertex3f + 3*(j+i), center);
+                                       VectorScale(center, 0.25f, center);
+                                       // find the two shortest edges, then use them to define the
+                                       // axis vectors for rotating around the central axis
+                                       for (i = 0;i < 6;i++)
+                                       {
+                                               v1 = rsurface.batchvertex3f + 3*(j+quadedges[i][0]);
+                                               v2 = rsurface.batchvertex3f + 3*(j+quadedges[i][1]);
                                                l = VectorDistance2(v1, v2);
                                                // this length bias tries to make sense of square polygons, assuming they are meant to be upright
                                                if (v1[2] != v2[2])
@@ -9691,13 +8566,6 @@ void RSurf_PrepareVerticesForBatch(qboolean generatenormals, qboolean generateta
                                        }
                                        VectorLerp(shortest[0].v1, 0.5f, shortest[0].v2, start);
                                        VectorLerp(shortest[1].v1, 0.5f, shortest[1].v2, end);
-#if 0
-                                       Debug_PolygonBegin(NULL, 0);
-                                       Debug_PolygonVertex(start[0], start[1], start[2], 0, 0, 1, 1, 0, 1);
-                                       Debug_PolygonVertex(center[0] + rsurface.normal3f[3 * (surface->num_firstvertex + j)+0] * 4, center[1] + rsurface.normal3f[3 * (surface->num_firstvertex + j)+1] * 4, center[2] + rsurface.normal3f[3 * (surface->num_firstvertex + j)+2] * 4, 0, 0, 0, 1, 0, 1);
-                                       Debug_PolygonVertex(end[0], end[1], end[2], 0, 0, 0, 1, 1, 1);
-                                       Debug_PolygonEnd();
-#endif
                                        // this calculates the right vector from the shortest edge
                                        // and the up vector from the edge midpoints
                                        VectorSubtract(shortest[0].v1, shortest[0].v2, right);
@@ -9712,20 +8580,6 @@ void RSurf_PrepareVerticesForBatch(qboolean generatenormals, qboolean generateta
                                        VectorNormalize(forward);
                                        CrossProduct(up, forward, newright);
                                        VectorNormalize(newright);
-#if 0
-                                       Debug_PolygonBegin(NULL, 0);
-                                       Debug_PolygonVertex(center[0] + rsurface.normal3f[3 * (surface->num_firstvertex + j)+0] * 8, center[1] + rsurface.normal3f[3 * (surface->num_firstvertex + j)+1] * 8, center[2] + rsurface.normal3f[3 * (surface->num_firstvertex + j)+2] * 8, 0, 0, 1, 0, 0, 1);
-                                       Debug_PolygonVertex(center[0] + right[0] * 8, center[1] + right[1] * 8, center[2] + right[2] * 8, 0, 0, 0, 1, 0, 1);
-                                       Debug_PolygonVertex(center[0] + up   [0] * 8, center[1] + up   [1] * 8, center[2] + up   [2] * 8, 0, 0, 0, 0, 1, 1);
-                                       Debug_PolygonEnd();
-#endif
-#if 0
-                                       Debug_PolygonBegin(NULL, 0);
-                                       Debug_PolygonVertex(center[0] + forward [0] * 8, center[1] + forward [1] * 8, center[2] + forward [2] * 8, 0, 0, 1, 0, 0, 1);
-                                       Debug_PolygonVertex(center[0] + newright[0] * 8, center[1] + newright[1] * 8, center[2] + newright[2] * 8, 0, 0, 0, 1, 0, 1);
-                                       Debug_PolygonVertex(center[0] + up      [0] * 8, center[1] + up      [1] * 8, center[2] + up      [2] * 8, 0, 0, 0, 0, 1, 1);
-                                       Debug_PolygonEnd();
-#endif
                                        // rotate the quad around the up axis vector, this is made
                                        // especially easy by the fact we know the quad is flat,
                                        // so we only have to subtract the center position and
@@ -9739,54 +8593,55 @@ void RSurf_PrepareVerticesForBatch(qboolean generatenormals, qboolean generateta
                                        l = DotProduct(right, center);
                                        for (i = 0;i < 4;i++)
                                        {
-                                               v1 = rsurface.vertex3f + 3 * (surface->num_firstvertex + j + i);
+                                               v1 = rsurface.batchvertex3f + 3*(j+i);
                                                f = DotProduct(right, v1) - l;
-                                               VectorMAMAM(1, v1, -f, right, f, newright, rsurface.array_deformedvertex3f + (surface->num_firstvertex+i+j) * 3);
+                                               VectorMAMAM(1, v1, -f, right, f, newright, rsurface.batchvertex3f + 3*(j+i));
                                        }
                                }
-                               Mod_BuildNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, rsurface.vertex3f, rsurface.modelelement3i + surface->num_firsttriangle * 3, rsurface.array_deformednormal3f, r_smoothnormals_areaweighting.integer != 0);
-                               Mod_BuildTextureVectorsFromNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, rsurface.vertex3f, rsurface.modeltexcoordtexture2f, rsurface.array_deformednormal3f, rsurface.modelelement3i + surface->num_firsttriangle * 3, rsurface.array_deformedsvector3f, rsurface.array_deformedtvector3f, r_smoothnormals_areaweighting.integer != 0);
                        }
-                       rsurface.vertex3f = rsurface.array_deformedvertex3f;
-                       rsurface.vertex3f_bufferobject = 0;
-                       rsurface.vertex3f_bufferoffset = 0;
-                       rsurface.svector3f = rsurface.array_deformedsvector3f;
-                       rsurface.svector3f_bufferobject = 0;
-                       rsurface.svector3f_bufferoffset = 0;
-                       rsurface.tvector3f = rsurface.array_deformedtvector3f;
-                       rsurface.tvector3f_bufferobject = 0;
-                       rsurface.tvector3f_bufferoffset = 0;
-                       rsurface.normal3f = rsurface.array_deformednormal3f;
-                       rsurface.normal3f_bufferobject = 0;
-                       rsurface.normal3f_bufferoffset = 0;
+                       if(batchneed & (BATCHNEED_ARRAY_NORMAL | BATCHNEED_ARRAY_VECTOR)) // otherwise these can stay NULL
+                       {
+//                             rsurface.batchnormal3f = R_FrameData_Alloc(batchnumvertices * sizeof(float[3]));
+//                             rsurface.batchnormal3f_vertexbuffer = NULL;
+//                             rsurface.batchnormal3f_bufferoffset = 0;
+                               Mod_BuildNormals(rsurface.batchfirstvertex, batchnumvertices, batchnumtriangles, rsurface.batchvertex3f, rsurface.batchelement3i + 3 * rsurface.batchfirsttriangle, rsurface.batchnormal3f, r_smoothnormals_areaweighting.integer != 0);
+                       }
+                       if(batchneed & BATCHNEED_ARRAY_VECTOR) // otherwise these can stay NULL
+                       {
+//                             rsurface.batchsvector3f = R_FrameData_Alloc(batchnumvertices * sizeof(float[3]));
+//                             rsurface.batchsvector3f_vertexbuffer = NULL;
+//                             rsurface.batchsvector3f_bufferoffset = 0;
+//                             rsurface.batchtvector3f = R_FrameData_Alloc(batchnumvertices * sizeof(float[3]));
+//                             rsurface.batchtvector3f_vertexbuffer = NULL;
+//                             rsurface.batchtvector3f_bufferoffset = 0;
+                               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);
+                       }
                        break;
                case Q3DEFORM_NORMAL:
                        // deform the normals to make reflections wavey
-                       for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+                       rsurface.batchnormal3f = (float *)R_FrameData_Store(batchnumvertices * sizeof(float[3]), rsurface.batchnormal3f);
+                       rsurface.batchnormal3f_vertexbuffer = NULL;
+                       rsurface.batchnormal3f_bufferoffset = 0;
+                       for (j = 0;j < batchnumvertices;j++)
                        {
-                               const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
-                               for (j = 0;j < surface->num_vertices;j++)
-                               {
-                                       float vertex[3];
-                                       float *normal = (rsurface.array_deformednormal3f  + 3 * surface->num_firstvertex) + j*3;
-                                       VectorScale((rsurface.vertex3f  + 3 * surface->num_firstvertex) + j*3, 0.98f, vertex);
-                                       VectorCopy((rsurface.normal3f  + 3 * surface->num_firstvertex) + j*3, normal);
-                                       normal[0] += deform->parms[0] * noise4f(      vertex[0], vertex[1], vertex[2], r_refdef.scene.time * deform->parms[1]);
-                                       normal[1] += deform->parms[0] * noise4f( 98 + vertex[0], vertex[1], vertex[2], r_refdef.scene.time * deform->parms[1]);
-                                       normal[2] += deform->parms[0] * noise4f(196 + vertex[0], vertex[1], vertex[2], r_refdef.scene.time * deform->parms[1]);
-                                       VectorNormalize(normal);
-                               }
-                               Mod_BuildTextureVectorsFromNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, rsurface.vertex3f, rsurface.modeltexcoordtexture2f, rsurface.array_deformednormal3f, rsurface.modelelement3i + surface->num_firsttriangle * 3, rsurface.array_deformedsvector3f, rsurface.array_deformedtvector3f, r_smoothnormals_areaweighting.integer != 0);
+                               float vertex[3];
+                               float *normal = rsurface.batchnormal3f + 3*j;
+                               VectorScale(rsurface.batchvertex3f + 3*j, 0.98f, vertex);
+                               normal[0] = rsurface.batchnormal3f[j*3+0] + deform->parms[0] * noise4f(      vertex[0], vertex[1], vertex[2], r_refdef.scene.time * deform->parms[1]);
+                               normal[1] = rsurface.batchnormal3f[j*3+1] + deform->parms[0] * noise4f( 98 + vertex[0], vertex[1], vertex[2], r_refdef.scene.time * deform->parms[1]);
+                               normal[2] = rsurface.batchnormal3f[j*3+2] + deform->parms[0] * noise4f(196 + vertex[0], vertex[1], vertex[2], r_refdef.scene.time * deform->parms[1]);
+                               VectorNormalize(normal);
+                       }
+                       if(batchneed & BATCHNEED_ARRAY_VECTOR) // otherwise these can stay NULL
+                       {
+//                             rsurface.batchsvector3f = R_FrameData_Alloc(batchnumvertices * sizeof(float[3]));
+//                             rsurface.batchsvector3f_vertexbuffer = NULL;
+//                             rsurface.batchsvector3f_bufferoffset = 0;
+//                             rsurface.batchtvector3f = R_FrameData_Alloc(batchnumvertices * sizeof(float[3]));
+//                             rsurface.batchtvector3f_vertexbuffer = NULL;
+//                             rsurface.batchtvector3f_bufferoffset = 0;
+                               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);
                        }
-                       rsurface.svector3f = rsurface.array_deformedsvector3f;
-                       rsurface.svector3f_bufferobject = 0;
-                       rsurface.svector3f_bufferoffset = 0;
-                       rsurface.tvector3f = rsurface.array_deformedtvector3f;
-                       rsurface.tvector3f_bufferobject = 0;
-                       rsurface.tvector3f_bufferoffset = 0;
-                       rsurface.normal3f = rsurface.array_deformednormal3f;
-                       rsurface.normal3f_bufferobject = 0;
-                       rsurface.normal3f_bufferoffset = 0;
                        break;
                case Q3DEFORM_WAVE:
                        // deform vertex array to make wavey water and flags and such
@@ -9794,125 +8649,134 @@ void RSurf_PrepareVerticesForBatch(qboolean generatenormals, qboolean generateta
                        waveparms[1] = deform->waveparms[1];
                        waveparms[2] = deform->waveparms[2];
                        waveparms[3] = deform->waveparms[3];
+                       if(!R_TestQ3WaveFunc(deform->wavefunc, waveparms))
+                               break; // if wavefunc is a nop, don't make a dynamic vertex array
                        // this is how a divisor of vertex influence on deformation
                        animpos = deform->parms[0] ? 1.0f / deform->parms[0] : 100.0f;
                        scale = R_EvaluateQ3WaveFunc(deform->wavefunc, waveparms);
-                       for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+//                     rsurface.batchvertex3f = R_FrameData_Store(batchnumvertices * sizeof(float[3]), rsurface.batchvertex3f);
+//                     rsurface.batchvertex3f_vertexbuffer = NULL;
+//                     rsurface.batchvertex3f_bufferoffset = 0;
+//                     rsurface.batchnormal3f = R_FrameData_Store(batchnumvertices * sizeof(float[3]), rsurface.batchnormal3f);
+//                     rsurface.batchnormal3f_vertexbuffer = NULL;
+//                     rsurface.batchnormal3f_bufferoffset = 0;
+                       for (j = 0;j < batchnumvertices;j++)
                        {
-                               const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
-                               for (j = 0;j < surface->num_vertices;j++)
+                               // if the wavefunc depends on time, evaluate it per-vertex
+                               if (waveparms[3])
                                {
-                                       float *vertex = (rsurface.array_deformedvertex3f  + 3 * surface->num_firstvertex) + j*3;
-                                       VectorCopy((rsurface.vertex3f  + 3 * surface->num_firstvertex) + j*3, vertex);
-                                       // if the wavefunc depends on time, evaluate it per-vertex
-                                       if (waveparms[3])
-                                       {
-                                               waveparms[2] = deform->waveparms[2] + (vertex[0] + vertex[1] + vertex[2]) * animpos;
-                                               scale = R_EvaluateQ3WaveFunc(deform->wavefunc, waveparms);
-                                       }
-                                       VectorMA(vertex, scale, (rsurface.normal3f  + 3 * surface->num_firstvertex) + j*3, vertex);
+                                       waveparms[2] = deform->waveparms[2] + (rsurface.batchvertex3f[j*3+0] + rsurface.batchvertex3f[j*3+1] + rsurface.batchvertex3f[j*3+2]) * animpos;
+                                       scale = R_EvaluateQ3WaveFunc(deform->wavefunc, waveparms);
                                }
+                               VectorMA(rsurface.batchvertex3f + 3*j, scale, rsurface.batchnormal3f + 3*j, rsurface.batchvertex3f + 3*j);
+                       }
+                       // if we get here, BATCHNEED_ARRAY_NORMAL is in batchneed, so no need to check
+                       Mod_BuildNormals(rsurface.batchfirstvertex, batchnumvertices, batchnumtriangles, rsurface.batchvertex3f, rsurface.batchelement3i + 3 * rsurface.batchfirsttriangle, rsurface.batchnormal3f, r_smoothnormals_areaweighting.integer != 0);
+                       if(batchneed & BATCHNEED_ARRAY_VECTOR) // otherwise these can stay NULL
+                       {
+//                             rsurface.batchsvector3f = R_FrameData_Alloc(batchnumvertices * sizeof(float[3]));
+//                             rsurface.batchsvector3f_vertexbuffer = NULL;
+//                             rsurface.batchsvector3f_bufferoffset = 0;
+//                             rsurface.batchtvector3f = R_FrameData_Alloc(batchnumvertices * sizeof(float[3]));
+//                             rsurface.batchtvector3f_vertexbuffer = NULL;
+//                             rsurface.batchtvector3f_bufferoffset = 0;
+                               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);
                        }
-                       rsurface.vertex3f = rsurface.array_deformedvertex3f;
-                       rsurface.vertex3f_bufferobject = 0;
-                       rsurface.vertex3f_bufferoffset = 0;
                        break;
                case Q3DEFORM_BULGE:
                        // deform vertex array to make the surface have moving bulges
-                       for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+//                     rsurface.batchvertex3f = R_FrameData_Store(batchnumvertices * sizeof(float[3]), rsurface.batchvertex3f);
+//                     rsurface.batchvertex3f_vertexbuffer = NULL;
+//                     rsurface.batchvertex3f_bufferoffset = 0;
+//                     rsurface.batchnormal3f = R_FrameData_Store(batchnumvertices * sizeof(float[3]), rsurface.batchnormal3f);
+//                     rsurface.batchnormal3f_vertexbuffer = NULL;
+//                     rsurface.batchnormal3f_bufferoffset = 0;
+                       for (j = 0;j < batchnumvertices;j++)
                        {
-                               const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
-                               for (j = 0;j < surface->num_vertices;j++)
-                               {
-                                       scale = sin((rsurface.modeltexcoordtexture2f[2 * (surface->num_firstvertex + j)] * deform->parms[0] + r_refdef.scene.time * deform->parms[2])) * deform->parms[1];
-                                       VectorMA(rsurface.vertex3f + 3 * (surface->num_firstvertex + j), scale, rsurface.normal3f + 3 * (surface->num_firstvertex + j), rsurface.array_deformedvertex3f + 3 * (surface->num_firstvertex + j));
-                               }
+                               scale = sin(rsurface.batchtexcoordtexture2f[j*2+0] * deform->parms[0] + r_refdef.scene.time * deform->parms[2]) * deform->parms[1];
+                               VectorMA(rsurface.batchvertex3f + 3*j, scale, rsurface.batchnormal3f + 3*j, rsurface.batchvertex3f + 3*j);
+                       }
+                       // if we get here, BATCHNEED_ARRAY_NORMAL is in batchneed, so no need to check
+                       Mod_BuildNormals(rsurface.batchfirstvertex, batchnumvertices, batchnumtriangles, rsurface.batchvertex3f, rsurface.batchelement3i + 3 * rsurface.batchfirsttriangle, rsurface.batchnormal3f, r_smoothnormals_areaweighting.integer != 0);
+                       if(batchneed & BATCHNEED_ARRAY_VECTOR) // otherwise these can stay NULL
+                       {
+//                             rsurface.batchsvector3f = R_FrameData_Alloc(batchnumvertices * sizeof(float[3]));
+//                             rsurface.batchsvector3f_vertexbuffer = NULL;
+//                             rsurface.batchsvector3f_bufferoffset = 0;
+//                             rsurface.batchtvector3f = R_FrameData_Alloc(batchnumvertices * sizeof(float[3]));
+//                             rsurface.batchtvector3f_vertexbuffer = NULL;
+//                             rsurface.batchtvector3f_bufferoffset = 0;
+                               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);
                        }
-                       rsurface.vertex3f = rsurface.array_deformedvertex3f;
-                       rsurface.vertex3f_bufferobject = 0;
-                       rsurface.vertex3f_bufferoffset = 0;
                        break;
                case Q3DEFORM_MOVE:
                        // deform vertex array
+                       if(!R_TestQ3WaveFunc(deform->wavefunc, deform->waveparms))
+                               break; // if wavefunc is a nop, don't make a dynamic vertex array
                        scale = R_EvaluateQ3WaveFunc(deform->wavefunc, deform->waveparms);
                        VectorScale(deform->parms, scale, waveparms);
-                       for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
-                       {
-                               const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
-                               for (j = 0;j < surface->num_vertices;j++)
-                                       VectorAdd(rsurface.vertex3f + 3 * (surface->num_firstvertex + j), waveparms, rsurface.array_deformedvertex3f + 3 * (surface->num_firstvertex + j));
-                       }
-                       rsurface.vertex3f = rsurface.array_deformedvertex3f;
-                       rsurface.vertex3f_bufferobject = 0;
-                       rsurface.vertex3f_bufferoffset = 0;
+//                     rsurface.batchvertex3f = R_FrameData_Store(batchnumvertices * sizeof(float[3]), rsurface.batchvertex3f);
+//                     rsurface.batchvertex3f_vertexbuffer = NULL;
+//                     rsurface.batchvertex3f_bufferoffset = 0;
+                       for (j = 0;j < batchnumvertices;j++)
+                               VectorAdd(rsurface.batchvertex3f + 3*j, waveparms, rsurface.batchvertex3f + 3*j);
                        break;
                }
        }
+
        // generate texcoords based on the chosen texcoord source
        switch(rsurface.texture->tcgen.tcgen)
        {
        default:
        case Q3TCGEN_TEXTURE:
-               rsurface.texcoordtexture2f               = rsurface.modeltexcoordtexture2f;
-               rsurface.texcoordtexture2f_bufferobject  = rsurface.modeltexcoordtexture2f_bufferobject;
-               rsurface.texcoordtexture2f_bufferoffset  = rsurface.modeltexcoordtexture2f_bufferoffset;
                break;
        case Q3TCGEN_LIGHTMAP:
-               rsurface.texcoordtexture2f               = rsurface.modeltexcoordlightmap2f;
-               rsurface.texcoordtexture2f_bufferobject  = rsurface.modeltexcoordlightmap2f_bufferobject;
-               rsurface.texcoordtexture2f_bufferoffset  = rsurface.modeltexcoordlightmap2f_bufferoffset;
+//             rsurface.batchtexcoordtexture2f = R_FrameData_Alloc(batchnumvertices * sizeof(float[2]));
+//             rsurface.batchtexcoordtexture2f_vertexbuffer = NULL;
+//             rsurface.batchtexcoordtexture2f_bufferoffset = 0;
+               if (rsurface.batchtexcoordlightmap2f)
+                       memcpy(rsurface.batchtexcoordlightmap2f, rsurface.batchtexcoordtexture2f, batchnumvertices * sizeof(float[2]));
                break;
        case Q3TCGEN_VECTOR:
-               for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+//             rsurface.batchtexcoordtexture2f = R_FrameData_Alloc(batchnumvertices * sizeof(float[2]));
+//             rsurface.batchtexcoordtexture2f_vertexbuffer = NULL;
+//             rsurface.batchtexcoordtexture2f_bufferoffset = 0;
+               for (j = 0;j < batchnumvertices;j++)
                {
-                       const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
-                       for (j = 0, v1 = rsurface.modelvertex3f + 3 * surface->num_firstvertex, out_tc = rsurface.array_generatedtexcoordtexture2f + 2 * surface->num_firstvertex;j < surface->num_vertices;j++, v1 += 3, out_tc += 2)
-                       {
-                               out_tc[0] = DotProduct(v1, rsurface.texture->tcgen.parms);
-                               out_tc[1] = DotProduct(v1, rsurface.texture->tcgen.parms + 3);
-                       }
+                       rsurface.batchtexcoordtexture2f[j*2+0] = DotProduct(rsurface.batchvertex3f + 3*j, rsurface.texture->tcgen.parms);
+                       rsurface.batchtexcoordtexture2f[j*2+1] = DotProduct(rsurface.batchvertex3f + 3*j, rsurface.texture->tcgen.parms + 3);
                }
-               rsurface.texcoordtexture2f               = rsurface.array_generatedtexcoordtexture2f;
-               rsurface.texcoordtexture2f_bufferobject  = 0;
-               rsurface.texcoordtexture2f_bufferoffset  = 0;
                break;
        case Q3TCGEN_ENVIRONMENT:
                // make environment reflections using a spheremap
-               for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+               rsurface.batchtexcoordtexture2f = (float *)R_FrameData_Alloc(batchnumvertices * sizeof(float[2]));
+               rsurface.batchtexcoordtexture2f_vertexbuffer = NULL;
+               rsurface.batchtexcoordtexture2f_bufferoffset = 0;
+               for (j = 0;j < batchnumvertices;j++)
                {
-                       const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
-                       const float *vertex = rsurface.modelvertex3f + 3 * surface->num_firstvertex;
-                       const float *normal = rsurface.modelnormal3f + 3 * surface->num_firstvertex;
-                       float *out_tc = rsurface.array_generatedtexcoordtexture2f + 2 * surface->num_firstvertex;
-                       for (j = 0;j < surface->num_vertices;j++, vertex += 3, normal += 3, out_tc += 2)
-                       {
-                               // identical to Q3A's method, but executed in worldspace so
-                               // carried models can be shiny too
+                       // identical to Q3A's method, but executed in worldspace so
+                       // carried models can be shiny too
 
-                               float viewer[3], d, reflected[3], worldreflected[3];
+                       float viewer[3], d, reflected[3], worldreflected[3];
 
-                               VectorSubtract(rsurface.localvieworigin, vertex, viewer);
-                               // VectorNormalize(viewer);
+                       VectorSubtract(rsurface.localvieworigin, rsurface.batchvertex3f + 3*j, viewer);
+                       // VectorNormalize(viewer);
 
-                               d = DotProduct(normal, viewer);
+                       d = DotProduct(rsurface.batchnormal3f + 3*j, viewer);
 
-                               reflected[0] = normal[0]*2*d - viewer[0];
-                               reflected[1] = normal[1]*2*d - viewer[1];
-                               reflected[2] = normal[2]*2*d - viewer[2];
-                               // note: this is proportinal to viewer, so we can normalize later
+                       reflected[0] = rsurface.batchnormal3f[j*3+0]*2*d - viewer[0];
+                       reflected[1] = rsurface.batchnormal3f[j*3+1]*2*d - viewer[1];
+                       reflected[2] = rsurface.batchnormal3f[j*3+2]*2*d - viewer[2];
+                       // note: this is proportinal to viewer, so we can normalize later
 
-                               Matrix4x4_Transform3x3(&rsurface.matrix, reflected, worldreflected);
-                               VectorNormalize(worldreflected);
+                       Matrix4x4_Transform3x3(&rsurface.matrix, reflected, worldreflected);
+                       VectorNormalize(worldreflected);
 
-                               // note: this sphere map only uses world x and z!
-                               // so positive and negative y will LOOK THE SAME.
-                               out_tc[0] = 0.5 + 0.5 * worldreflected[1];
-                               out_tc[1] = 0.5 - 0.5 * worldreflected[2];
-                       }
+                       // note: this sphere map only uses world x and z!
+                       // so positive and negative y will LOOK THE SAME.
+                       rsurface.batchtexcoordtexture2f[j*2+0] = 0.5 + 0.5 * worldreflected[1];
+                       rsurface.batchtexcoordtexture2f[j*2+1] = 0.5 - 0.5 * worldreflected[2];
                }
-               rsurface.texcoordtexture2f               = rsurface.array_generatedtexcoordtexture2f;
-               rsurface.texcoordtexture2f_bufferobject  = 0;
-               rsurface.texcoordtexture2f_bufferoffset  = 0;
                break;
        }
        // the only tcmod that needs software vertex processing is turbulent, so
@@ -9924,528 +8788,326 @@ void RSurf_PrepareVerticesForBatch(qboolean generatenormals, qboolean generateta
        {
                amplitude = rsurface.texture->tcmods[0].parms[1];
                animpos = rsurface.texture->tcmods[0].parms[2] + r_refdef.scene.time * rsurface.texture->tcmods[0].parms[3];
-               for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+//             rsurface.batchtexcoordtexture2f = R_FrameData_Alloc(batchnumvertices * sizeof(float[2]));
+//             rsurface.batchtexcoordtexture2f_vertexbuffer = NULL;
+//             rsurface.batchtexcoordtexture2f_bufferoffset = 0;
+               for (j = 0;j < batchnumvertices;j++)
                {
-                       const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
-                       for (j = 0, v1 = rsurface.modelvertex3f + 3 * surface->num_firstvertex, in_tc = rsurface.texcoordtexture2f + 2 * surface->num_firstvertex, out_tc = rsurface.array_generatedtexcoordtexture2f + 2 * surface->num_firstvertex;j < surface->num_vertices;j++, v1 += 3, in_tc += 2, out_tc += 2)
-                       {
-                               out_tc[0] = in_tc[0] + amplitude * sin(((v1[0] + v1[2]) * 1.0 / 1024.0f + animpos) * M_PI * 2);
-                               out_tc[1] = in_tc[1] + amplitude * sin(((v1[1]        ) * 1.0 / 1024.0f + animpos) * M_PI * 2);
-                       }
+                       rsurface.batchtexcoordtexture2f[j*2+0] += amplitude * sin(((rsurface.batchvertex3f[j*3+0] + rsurface.batchvertex3f[j*3+2]) * 1.0 / 1024.0f + animpos) * M_PI * 2);
+                       rsurface.batchtexcoordtexture2f[j*2+1] += amplitude * sin(((rsurface.batchvertex3f[j*3+1]                                ) * 1.0 / 1024.0f + animpos) * M_PI * 2);
                }
-               rsurface.texcoordtexture2f               = rsurface.array_generatedtexcoordtexture2f;
-               rsurface.texcoordtexture2f_bufferobject  = 0;
-               rsurface.texcoordtexture2f_bufferoffset  = 0;
        }
-       rsurface.texcoordlightmap2f              = rsurface.modeltexcoordlightmap2f;
-       rsurface.texcoordlightmap2f_bufferobject = rsurface.modeltexcoordlightmap2f_bufferobject;
-       rsurface.texcoordlightmap2f_bufferoffset = rsurface.modeltexcoordlightmap2f_bufferoffset;
-       R_Mesh_VertexPointer(rsurface.vertex3f, rsurface.vertex3f_bufferobject, rsurface.vertex3f_bufferoffset);
-}
 
-void RSurf_DrawBatch_Simple(int texturenumsurfaces, const msurface_t **texturesurfacelist)
-{
-       int i, j;
-       const msurface_t *surface = texturesurfacelist[0];
-       const msurface_t *surface2;
-       int firstvertex;
-       int endvertex;
-       int numvertices;
-       int numtriangles;
-       // TODO: lock all array ranges before render, rather than on each surface
-       if (texturenumsurfaces == 1)
-               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_firsttriangle, surface->num_triangles, rsurface.modelelement3i, rsurface.modelelement3s, rsurface.modelelement3i_bufferobject, rsurface.modelelement3s_bufferobject);
-       else if (r_batchmode.integer == 2)
+       if (needsupdate & batchneed & (BATCHNEED_VERTEXMESH_VERTEX | BATCHNEED_VERTEXMESH_NORMAL | BATCHNEED_VERTEXMESH_VECTOR | BATCHNEED_VERTEXMESH_VERTEXCOLOR | BATCHNEED_VERTEXMESH_TEXCOORD | BATCHNEED_VERTEXMESH_LIGHTMAP))
        {
-               #define MAXBATCHTRIANGLES 4096
-               int batchtriangles = 0;
-               static int batchelements[MAXBATCHTRIANGLES*3];
-               for (i = 0;i < texturenumsurfaces;i = j)
+               // convert the modified arrays to vertex structs
+//             rsurface.batchvertexmesh = R_FrameData_Alloc(batchnumvertices * sizeof(r_vertexmesh_t));
+//             rsurface.batchvertexmeshbuffer = NULL;
+               if (batchneed & BATCHNEED_VERTEXMESH_VERTEX)
+                       for (j = 0, vertexmesh = rsurface.batchvertexmesh;j < batchnumvertices;j++, vertexmesh++)
+                               VectorCopy(rsurface.batchvertex3f + 3*j, vertexmesh->vertex3f);
+               if (batchneed & BATCHNEED_VERTEXMESH_NORMAL)
+                       for (j = 0, vertexmesh = rsurface.batchvertexmesh;j < batchnumvertices;j++, vertexmesh++)
+                               VectorCopy(rsurface.batchnormal3f + 3*j, vertexmesh->normal3f);
+               if (batchneed & BATCHNEED_VERTEXMESH_VECTOR)
                {
-                       surface = texturesurfacelist[i];
-                       j = i + 1;
-                       if (surface->num_triangles > MAXBATCHTRIANGLES)
+                       for (j = 0, vertexmesh = rsurface.batchvertexmesh;j < batchnumvertices;j++, vertexmesh++)
                        {
-                               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_firsttriangle, surface->num_triangles, rsurface.modelelement3i, rsurface.modelelement3s, rsurface.modelelement3i_bufferobject, rsurface.modelelement3s_bufferobject);
-                               continue;
+                               VectorCopy(rsurface.batchsvector3f + 3*j, vertexmesh->svector3f);
+                               VectorCopy(rsurface.batchtvector3f + 3*j, vertexmesh->tvector3f);
                        }
-                       memcpy(batchelements, rsurface.modelelement3i + 3 * surface->num_firsttriangle, surface->num_triangles * sizeof(int[3]));
-                       batchtriangles = surface->num_triangles;
-                       firstvertex = surface->num_firstvertex;
-                       endvertex = surface->num_firstvertex + surface->num_vertices;
-                       for (;j < texturenumsurfaces;j++)
-                       {
-                               surface2 = texturesurfacelist[j];
-                               if (batchtriangles + surface2->num_triangles > MAXBATCHTRIANGLES)
-                                       break;
-                               memcpy(batchelements + batchtriangles * 3, rsurface.modelelement3i + 3 * surface2->num_firsttriangle, surface2->num_triangles * sizeof(int[3]));
-                               batchtriangles += surface2->num_triangles;
-                               firstvertex = min(firstvertex, surface2->num_firstvertex);
-                               endvertex = max(endvertex, surface2->num_firstvertex + surface2->num_vertices);
-                       }
-                       surface2 = texturesurfacelist[j-1];
-                       numvertices = endvertex - firstvertex;
-                       R_Mesh_Draw(firstvertex, numvertices, 0, batchtriangles, batchelements, NULL, 0, 0);
-               }
-       }
-       else if (r_batchmode.integer == 1)
-       {
-               for (i = 0;i < texturenumsurfaces;i = j)
-               {
-                       surface = texturesurfacelist[i];
-                       for (j = i + 1, surface2 = surface + 1;j < texturenumsurfaces;j++, surface2++)
-                               if (texturesurfacelist[j] != surface2)
-                                       break;
-                       surface2 = texturesurfacelist[j-1];
-                       numvertices = surface2->num_firstvertex + surface2->num_vertices - surface->num_firstvertex;
-                       numtriangles = surface2->num_firsttriangle + surface2->num_triangles - surface->num_firsttriangle;
-                       R_Mesh_Draw(surface->num_firstvertex, numvertices, surface->num_firsttriangle, numtriangles, rsurface.modelelement3i, rsurface.modelelement3s, rsurface.modelelement3i_bufferobject, rsurface.modelelement3s_bufferobject);
-               }
-       }
-       else
-       {
-               for (i = 0;i < texturenumsurfaces;i++)
-               {
-                       surface = texturesurfacelist[i];
-                       R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_firsttriangle, surface->num_triangles, rsurface.modelelement3i, rsurface.modelelement3s, rsurface.modelelement3i_bufferobject, rsurface.modelelement3s_bufferobject);
                }
+               if ((batchneed & BATCHNEED_VERTEXMESH_VERTEXCOLOR) && rsurface.batchlightmapcolor4f)
+                       for (j = 0, vertexmesh = rsurface.batchvertexmesh;j < batchnumvertices;j++, vertexmesh++)
+                               Vector4Copy(rsurface.batchlightmapcolor4f + 4*j, vertexmesh->color4f);
+               if (batchneed & BATCHNEED_VERTEXMESH_TEXCOORD)
+                       for (j = 0, vertexmesh = rsurface.batchvertexmesh;j < batchnumvertices;j++, vertexmesh++)
+                               Vector2Copy(rsurface.batchtexcoordtexture2f + 2*j, vertexmesh->texcoordtexture2f);
+               if ((batchneed & BATCHNEED_VERTEXMESH_LIGHTMAP) && rsurface.batchtexcoordlightmap2f)
+                       for (j = 0, vertexmesh = rsurface.batchvertexmesh;j < batchnumvertices;j++, vertexmesh++)
+                               Vector2Copy(rsurface.batchtexcoordlightmap2f + 2*j, vertexmesh->texcoordlightmap2f);
        }
 }
 
-static void RSurf_BindLightmapForSurface(const msurface_t *surface)
+void RSurf_DrawBatch(void)
 {
-       switch(vid.renderpath)
-       {
-       case RENDERPATH_CGGL:
-#ifdef SUPPORTCG
-               if (r_cg_permutation->fp_Texture_Lightmap ) CG_BindTexture(r_cg_permutation->fp_Texture_Lightmap , surface->lightmaptexture );CHECKCGERROR
-               if (r_cg_permutation->fp_Texture_Deluxemap) CG_BindTexture(r_cg_permutation->fp_Texture_Deluxemap, surface->deluxemaptexture);CHECKCGERROR
-#endif
-               break;
-       case RENDERPATH_GL20:
-               if (r_glsl_permutation->loc_Texture_Lightmap  >= 0) R_Mesh_TexBind(GL20TU_LIGHTMAP , surface->lightmaptexture );
-               if (r_glsl_permutation->loc_Texture_Deluxemap >= 0) R_Mesh_TexBind(GL20TU_DELUXEMAP, surface->deluxemaptexture);
-               break;
-       case RENDERPATH_GL13:
-       case RENDERPATH_GL11:
-               R_Mesh_TexBind(0, surface->lightmaptexture);
-               break;
+       // sometimes a zero triangle surface (usually a degenerate patch) makes it
+       // through the pipeline, killing it earlier in the pipeline would have
+       // per-surface overhead rather than per-batch overhead, so it's best to
+       // reject it here, before it hits glDraw.
+       if (rsurface.batchnumtriangles == 0)
+               return;
+#if 0
+       // batch debugging code
+       if (r_test.integer && rsurface.entity == r_refdef.scene.worldentity && rsurface.batchvertex3f == r_refdef.scene.worldentity->model->surfmesh.data_vertex3f)
+       {
+               int i;
+               int j;
+               int c;
+               const int *e;
+               e = rsurface.batchelement3i + rsurface.batchfirsttriangle*3;
+               for (i = 0;i < rsurface.batchnumtriangles*3;i++)
+               {
+                       c = e[i];
+                       for (j = 0;j < rsurface.entity->model->num_surfaces;j++)
+                       {
+                               if (c >= rsurface.modelsurfaces[j].num_firstvertex && c < (rsurface.modelsurfaces[j].num_firstvertex + rsurface.modelsurfaces[j].num_vertices))
+                               {
+                                       if (rsurface.modelsurfaces[j].texture != rsurface.texture)
+                                               Sys_Error("RSurf_DrawBatch: index %i uses different texture (%s) than surface %i which it belongs to (which uses %s)\n", c, rsurface.texture->name, j, rsurface.modelsurfaces[j].texture->name);
+                                       break;
+                               }
+                       }
+               }
        }
+#endif
+       R_Mesh_Draw(rsurface.batchfirstvertex, rsurface.batchnumvertices, rsurface.batchfirsttriangle, rsurface.batchnumtriangles, rsurface.batchelement3i, rsurface.batchelement3i_indexbuffer, rsurface.batchelement3i_bufferoffset, rsurface.batchelement3s, rsurface.batchelement3s_indexbuffer, rsurface.batchelement3s_bufferoffset);
 }
 
-static void RSurf_BindReflectionForSurface(const msurface_t *surface)
+static int RSurf_FindWaterPlaneForSurface(const msurface_t *surface)
 {
-       // pick the closest matching water plane and bind textures
-       int planeindex, vertexindex;
+       // pick the closest matching water plane
+       int planeindex, vertexindex, bestplaneindex = -1;
        float d, bestd;
        vec3_t vert;
        const float *v;
-       r_waterstate_waterplane_t *p, *bestp;
+       r_waterstate_waterplane_t *p;
+       qboolean prepared = false;
        bestd = 0;
-       bestp = NULL;
        for (planeindex = 0, p = r_waterstate.waterplanes;planeindex < r_waterstate.numwaterplanes;planeindex++, p++)
        {
+               if(p->camera_entity != rsurface.texture->camera_entity)
+                       continue;
                d = 0;
-               for (vertexindex = 0, v = rsurface.modelvertex3f + surface->num_firstvertex * 3;vertexindex < surface->num_vertices;vertexindex++, v += 3)
+               if(!prepared)
+               {
+                       RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_NOGAPS, 1, &surface);
+                       prepared = true;
+                       if(rsurface.batchnumvertices == 0)
+                               break;
+               }
+               for (vertexindex = 0, v = rsurface.batchvertex3f + rsurface.batchfirstvertex * 3;vertexindex < rsurface.batchnumvertices;vertexindex++, v += 3)
                {
                        Matrix4x4_Transform(&rsurface.matrix, v, vert);
                        d += fabs(PlaneDiff(vert, &p->plane));
                }
-               if (bestd > d || !bestp)
+               if (bestd > d || bestplaneindex < 0)
                {
                        bestd = d;
-                       bestp = p;
-               }
-       }
-       switch(vid.renderpath)
-       {
-       case RENDERPATH_CGGL:
-#ifdef SUPPORTCG
-               if (r_cg_permutation->fp_Texture_Refraction) CG_BindTexture(r_cg_permutation->fp_Texture_Refraction, bestp ? bestp->texture_refraction : r_texture_black);CHECKCGERROR
-               if (r_cg_permutation->fp_Texture_Reflection) CG_BindTexture(r_cg_permutation->fp_Texture_Reflection, bestp ? bestp->texture_reflection : r_texture_black);CHECKCGERROR
-#endif
-               break;
-       case RENDERPATH_GL20:
-               if (r_glsl_permutation->loc_Texture_Refraction >= 0) R_Mesh_TexBind(GL20TU_REFRACTION, bestp ? bestp->texture_refraction : r_texture_black);
-               if (r_glsl_permutation->loc_Texture_Reflection >= 0) R_Mesh_TexBind(GL20TU_REFLECTION, bestp ? bestp->texture_reflection : r_texture_black);
-               break;
-       case RENDERPATH_GL13:
-       case RENDERPATH_GL11:
-               break;
-       }
-}
-
-static void RSurf_DrawBatch_WithLightmapSwitching_WithWaterTextureSwitching(int texturenumsurfaces, const msurface_t **texturesurfacelist)
-{
-       int i;
-       const msurface_t *surface;
-       if (r_waterstate.renderingscene)
-               return;
-       for (i = 0;i < texturenumsurfaces;i++)
-       {
-               surface = texturesurfacelist[i];
-               RSurf_BindLightmapForSurface(surface);
-               RSurf_BindReflectionForSurface(surface);
-               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_firsttriangle, surface->num_triangles, rsurface.modelelement3i, rsurface.modelelement3s, rsurface.modelelement3i_bufferobject, rsurface.modelelement3s_bufferobject);
-       }
-}
-
-static void RSurf_DrawBatch_WithLightmapSwitching(int texturenumsurfaces, const msurface_t **texturesurfacelist)
-{
-       int i;
-       int j;
-       const msurface_t *surface = texturesurfacelist[0];
-       const msurface_t *surface2;
-       int firstvertex;
-       int endvertex;
-       int numvertices;
-       int numtriangles;
-       if (texturenumsurfaces == 1)
-       {
-               RSurf_BindLightmapForSurface(surface);
-               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_firsttriangle, surface->num_triangles, rsurface.modelelement3i, rsurface.modelelement3s, rsurface.modelelement3i_bufferobject, rsurface.modelelement3s_bufferobject);
-       }
-       else if (r_batchmode.integer == 2)
-       {
-#define MAXBATCHTRIANGLES 4096
-               int batchtriangles = 0;
-               static int batchelements[MAXBATCHTRIANGLES*3];
-               for (i = 0;i < texturenumsurfaces;i = j)
-               {
-                       surface = texturesurfacelist[i];
-                       RSurf_BindLightmapForSurface(surface);
-                       j = i + 1;
-                       if (surface->num_triangles > MAXBATCHTRIANGLES)
-                       {
-                               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_firsttriangle, surface->num_triangles, rsurface.modelelement3i, rsurface.modelelement3s, rsurface.modelelement3i_bufferobject, rsurface.modelelement3s_bufferobject);
-                               continue;
-                       }
-                       memcpy(batchelements, rsurface.modelelement3i + 3 * surface->num_firsttriangle, surface->num_triangles * sizeof(int[3]));
-                       batchtriangles = surface->num_triangles;
-                       firstvertex = surface->num_firstvertex;
-                       endvertex = surface->num_firstvertex + surface->num_vertices;
-                       for (;j < texturenumsurfaces;j++)
-                       {
-                               surface2 = texturesurfacelist[j];
-                               if (surface2->lightmaptexture != surface->lightmaptexture || batchtriangles + surface2->num_triangles > MAXBATCHTRIANGLES)
-                                       break;
-                               memcpy(batchelements + batchtriangles * 3, rsurface.modelelement3i + 3 * surface2->num_firsttriangle, surface2->num_triangles * sizeof(int[3]));
-                               batchtriangles += surface2->num_triangles;
-                               firstvertex = min(firstvertex, surface2->num_firstvertex);
-                               endvertex = max(endvertex, surface2->num_firstvertex + surface2->num_vertices);
-                       }
-                       surface2 = texturesurfacelist[j-1];
-                       numvertices = endvertex - firstvertex;
-                       R_Mesh_Draw(firstvertex, numvertices, 0, batchtriangles, batchelements, NULL, 0, 0);
-               }
-       }
-       else if (r_batchmode.integer == 1)
-       {
-#if 0
-               Con_Printf("%s batch sizes ignoring lightmap:", rsurface.texture->name);
-               for (i = 0;i < texturenumsurfaces;i = j)
-               {
-                       surface = texturesurfacelist[i];
-                       for (j = i + 1, surface2 = surface + 1;j < texturenumsurfaces;j++, surface2++)
-                               if (texturesurfacelist[j] != surface2)
-                                       break;
-                       Con_Printf(" %i", j - i);
-               }
-               Con_Printf("\n");
-               Con_Printf("%s batch sizes honoring lightmap:", rsurface.texture->name);
-#endif
-               for (i = 0;i < texturenumsurfaces;i = j)
-               {
-                       surface = texturesurfacelist[i];
-                       RSurf_BindLightmapForSurface(surface);
-                       for (j = i + 1, surface2 = surface + 1;j < texturenumsurfaces;j++, surface2++)
-                               if (texturesurfacelist[j] != surface2 || texturesurfacelist[j]->lightmaptexture != surface->lightmaptexture)
-                                       break;
-#if 0
-                       Con_Printf(" %i", j - i);
-#endif
-                       surface2 = texturesurfacelist[j-1];
-                       numvertices = surface2->num_firstvertex + surface2->num_vertices - surface->num_firstvertex;
-                       numtriangles = surface2->num_firsttriangle + surface2->num_triangles - surface->num_firsttriangle;
-                       R_Mesh_Draw(surface->num_firstvertex, numvertices, surface->num_firsttriangle, numtriangles, rsurface.modelelement3i, rsurface.modelelement3s, rsurface.modelelement3i_bufferobject, rsurface.modelelement3s_bufferobject);
-               }
-#if 0
-               Con_Printf("\n");
-#endif
-       }
-       else
-       {
-               for (i = 0;i < texturenumsurfaces;i++)
-               {
-                       surface = texturesurfacelist[i];
-                       RSurf_BindLightmapForSurface(surface);
-                       R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_firsttriangle, surface->num_triangles, rsurface.modelelement3i, rsurface.modelelement3s, rsurface.modelelement3i_bufferobject, rsurface.modelelement3s_bufferobject);
-               }
-       }
-}
-
-static void RSurf_DrawBatch_ShowSurfaces(int texturenumsurfaces, const msurface_t **texturesurfacelist)
-{
-       int j;
-       int texturesurfaceindex;
-       if (r_showsurfaces.integer == 2)
-       {
-               for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
-               {
-                       const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
-                       for (j = 0;j < surface->num_triangles;j++)
-                       {
-                               float f = ((j + surface->num_firsttriangle) & 31) * (1.0f / 31.0f) * r_refdef.view.colorscale;
-                               GL_Color(f, f, f, 1);
-                               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_firsttriangle + j, 1, rsurface.modelelement3i, rsurface.modelelement3s, rsurface.modelelement3i_bufferobject, rsurface.modelelement3s_bufferobject);
-                       }
-               }
-       }
-       else
-       {
-               for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
-               {
-                       const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
-                       int k = (int)(((size_t)surface) / sizeof(msurface_t));
-                       GL_Color((k & 15) * (1.0f / 16.0f) * r_refdef.view.colorscale, ((k >> 4) & 15) * (1.0f / 16.0f) * r_refdef.view.colorscale, ((k >> 8) & 15) * (1.0f / 16.0f) * r_refdef.view.colorscale, 1);
-                       R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_firsttriangle, surface->num_triangles, rsurface.modelelement3i, rsurface.modelelement3s, rsurface.modelelement3i_bufferobject, rsurface.modelelement3s_bufferobject);
+                       bestplaneindex = planeindex;
                }
        }
+       return bestplaneindex;
+       // NOTE: this MAY return a totally unrelated water plane; we can ignore
+       // this situation though, as it might be better to render single larger
+       // batches with useless stuff (backface culled for example) than to
+       // render multiple smaller batches
 }
 
-static void RSurf_DrawBatch_GL11_MakeFullbrightLightmapColorArray(int texturenumsurfaces, const msurface_t **texturesurfacelist)
+static void RSurf_DrawBatch_GL11_MakeFullbrightLightmapColorArray(void)
 {
-       int texturesurfaceindex;
        int i;
-       const float *v;
-       float *c2;
-       for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
-       {
-               const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
-               for (i = 0, v = (rsurface.vertex3f + 3 * surface->num_firstvertex), c2 = (rsurface.array_color4f + 4 * surface->num_firstvertex);i < surface->num_vertices;i++, v += 3, c2 += 4)
-               {
-                       c2[0] = 0.5;
-                       c2[1] = 0.5;
-                       c2[2] = 0.5;
-                       c2[3] = 1;
-               }
-       }
-       rsurface.lightmapcolor4f = rsurface.array_color4f;
-       rsurface.lightmapcolor4f_bufferobject = 0;
-       rsurface.lightmapcolor4f_bufferoffset = 0;
+       rsurface.passcolor4f = (float *)R_FrameData_Alloc(rsurface.batchnumvertices * sizeof(float[4]));
+       rsurface.passcolor4f_vertexbuffer = 0;
+       rsurface.passcolor4f_bufferoffset = 0;
+       for (i = 0;i < rsurface.batchnumvertices;i++)
+               Vector4Set(rsurface.passcolor4f + 4*i, 0.5f, 0.5f, 0.5f, 1.0f);
 }
 
-static void RSurf_DrawBatch_GL11_ApplyFog(int texturenumsurfaces, const msurface_t **texturesurfacelist)
+static void RSurf_DrawBatch_GL11_ApplyFog(void)
 {
-       int texturesurfaceindex;
        int i;
        float f;
        const float *v;
        const float *c;
        float *c2;
-       if (rsurface.lightmapcolor4f)
+       if (rsurface.passcolor4f)
        {
-               // generate color arrays for the surfaces in this list
-               for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+               // generate color arrays
+               c = rsurface.passcolor4f + rsurface.batchfirstvertex * 4;
+               rsurface.passcolor4f = (float *)R_FrameData_Alloc(rsurface.batchnumvertices * sizeof(float[4]));
+               rsurface.passcolor4f_vertexbuffer = 0;
+               rsurface.passcolor4f_bufferoffset = 0;
+               for (i = 0, v = rsurface.batchvertex3f + rsurface.batchfirstvertex * 3, c = rsurface.passcolor4f + rsurface.batchfirstvertex * 4, c2 = rsurface.passcolor4f + rsurface.batchfirstvertex * 4;i < rsurface.batchnumvertices;i++, v += 3, c += 4, c2 += 4)
                {
-                       const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
-                       for (i = 0, v = (rsurface.vertex3f + 3 * surface->num_firstvertex), c = (rsurface.lightmapcolor4f + 4 * surface->num_firstvertex), c2 = (rsurface.array_color4f + 4 * surface->num_firstvertex);i < surface->num_vertices;i++, v += 3, c += 4, c2 += 4)
-                       {
-                               f = RSurf_FogVertex(v);
-                               c2[0] = c[0] * f;
-                               c2[1] = c[1] * f;
-                               c2[2] = c[2] * f;
-                               c2[3] = c[3];
-                       }
+                       f = RSurf_FogVertex(v);
+                       c2[0] = c[0] * f;
+                       c2[1] = c[1] * f;
+                       c2[2] = c[2] * f;
+                       c2[3] = c[3];
                }
        }
        else
        {
-               for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+               rsurface.passcolor4f = (float *)R_FrameData_Alloc(rsurface.batchnumvertices * sizeof(float[4]));
+               rsurface.passcolor4f_vertexbuffer = 0;
+               rsurface.passcolor4f_bufferoffset = 0;
+               for (i = 0, v = rsurface.batchvertex3f + rsurface.batchfirstvertex * 3, c2 = rsurface.passcolor4f + rsurface.batchfirstvertex * 4;i < rsurface.batchnumvertices;i++, v += 3, c2 += 4)
                {
-                       const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
-                       for (i = 0, v = (rsurface.vertex3f + 3 * surface->num_firstvertex), c2 = (rsurface.array_color4f + 4 * surface->num_firstvertex);i < surface->num_vertices;i++, v += 3, c2 += 4)
-                       {
-                               f = RSurf_FogVertex(v);
-                               c2[0] = f;
-                               c2[1] = f;
-                               c2[2] = f;
-                               c2[3] = 1;
-                       }
+                       f = RSurf_FogVertex(v);
+                       c2[0] = f;
+                       c2[1] = f;
+                       c2[2] = f;
+                       c2[3] = 1;
                }
        }
-       rsurface.lightmapcolor4f = rsurface.array_color4f;
-       rsurface.lightmapcolor4f_bufferobject = 0;
-       rsurface.lightmapcolor4f_bufferoffset = 0;
 }
 
-static void RSurf_DrawBatch_GL11_ApplyFogToFinishedVertexColors(int texturenumsurfaces, const msurface_t **texturesurfacelist)
+static void RSurf_DrawBatch_GL11_ApplyFogToFinishedVertexColors(void)
 {
-       int texturesurfaceindex;
        int i;
        float f;
        const float *v;
        const float *c;
        float *c2;
-       if (!rsurface.lightmapcolor4f)
+       if (!rsurface.passcolor4f)
                return;
-       // generate color arrays for the surfaces in this list
-       for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+       c = rsurface.passcolor4f + rsurface.batchfirstvertex * 4;
+       rsurface.passcolor4f = (float *)R_FrameData_Alloc(rsurface.batchnumvertices * sizeof(float[4]));
+       rsurface.passcolor4f_vertexbuffer = 0;
+       rsurface.passcolor4f_bufferoffset = 0;
+       for (i = 0, v = rsurface.batchvertex3f + rsurface.batchfirstvertex * 3, c2 = rsurface.passcolor4f + rsurface.batchfirstvertex * 4;i < rsurface.batchnumvertices;i++, v += 3, c += 4, c2 += 4)
        {
-               const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
-               for (i = 0, v = (rsurface.vertex3f + 3 * surface->num_firstvertex), c = (rsurface.lightmapcolor4f + 4 * surface->num_firstvertex), c2 = (rsurface.array_color4f + 4 * surface->num_firstvertex);i < surface->num_vertices;i++, v += 3, c += 4, c2 += 4)
-               {
-                       f = RSurf_FogVertex(v);
-                       c2[0] = c[0] * f + r_refdef.fogcolor[0] * (1 - f);
-                       c2[1] = c[1] * f + r_refdef.fogcolor[1] * (1 - f);
-                       c2[2] = c[2] * f + r_refdef.fogcolor[2] * (1 - f);
-                       c2[3] = c[3];
-               }
+               f = RSurf_FogVertex(v);
+               c2[0] = c[0] * f + r_refdef.fogcolor[0] * (1 - f);
+               c2[1] = c[1] * f + r_refdef.fogcolor[1] * (1 - f);
+               c2[2] = c[2] * f + r_refdef.fogcolor[2] * (1 - f);
+               c2[3] = c[3];
        }
-       rsurface.lightmapcolor4f = rsurface.array_color4f;
-       rsurface.lightmapcolor4f_bufferobject = 0;
-       rsurface.lightmapcolor4f_bufferoffset = 0;
 }
 
-static void RSurf_DrawBatch_GL11_ApplyColor(int texturenumsurfaces, const msurface_t **texturesurfacelist, float r, float g, float b, float a)
+static void RSurf_DrawBatch_GL11_ApplyColor(float r, float g, float b, float a)
 {
-       int texturesurfaceindex;
        int i;
        const float *c;
        float *c2;
-       if (!rsurface.lightmapcolor4f)
+       if (!rsurface.passcolor4f)
                return;
-       for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+       c = rsurface.passcolor4f + rsurface.batchfirstvertex * 4;
+       rsurface.passcolor4f = (float *)R_FrameData_Alloc(rsurface.batchnumvertices * sizeof(float[4]));
+       rsurface.passcolor4f_vertexbuffer = 0;
+       rsurface.passcolor4f_bufferoffset = 0;
+       for (i = 0, c2 = rsurface.passcolor4f + rsurface.batchfirstvertex * 4;i < rsurface.batchnumvertices;i++, c += 4, c2 += 4)
        {
-               const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
-               for (i = 0, c = (rsurface.lightmapcolor4f + 4 * surface->num_firstvertex), c2 = (rsurface.array_color4f + 4 * surface->num_firstvertex);i < surface->num_vertices;i++, c += 4, c2 += 4)
-               {
-                       c2[0] = c[0] * r;
-                       c2[1] = c[1] * g;
-                       c2[2] = c[2] * b;
-                       c2[3] = c[3] * a;
-               }
+               c2[0] = c[0] * r;
+               c2[1] = c[1] * g;
+               c2[2] = c[2] * b;
+               c2[3] = c[3] * a;
        }
-       rsurface.lightmapcolor4f = rsurface.array_color4f;
-       rsurface.lightmapcolor4f_bufferobject = 0;
-       rsurface.lightmapcolor4f_bufferoffset = 0;
 }
 
-static void RSurf_DrawBatch_GL11_ApplyAmbient(int texturenumsurfaces, const msurface_t **texturesurfacelist)
+static void RSurf_DrawBatch_GL11_ApplyAmbient(void)
 {
-       int texturesurfaceindex;
        int i;
        const float *c;
        float *c2;
-       if (!rsurface.lightmapcolor4f)
+       if (!rsurface.passcolor4f)
                return;
-       for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+       c = rsurface.passcolor4f + rsurface.batchfirstvertex * 4;
+       rsurface.passcolor4f = (float *)R_FrameData_Alloc(rsurface.batchnumvertices * sizeof(float[4]));
+       rsurface.passcolor4f_vertexbuffer = 0;
+       rsurface.passcolor4f_bufferoffset = 0;
+       for (i = 0, c2 = rsurface.passcolor4f + rsurface.batchfirstvertex * 4;i < rsurface.batchnumvertices;i++, c += 4, c2 += 4)
        {
-               const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
-               for (i = 0, c = (rsurface.lightmapcolor4f + 4 * surface->num_firstvertex), c2 = (rsurface.array_color4f + 4 * surface->num_firstvertex);i < surface->num_vertices;i++, c += 4, c2 += 4)
-               {
-                       c2[0] = c[0] + r_refdef.scene.ambient;
-                       c2[1] = c[1] + r_refdef.scene.ambient;
-                       c2[2] = c[2] + r_refdef.scene.ambient;
-                       c2[3] = c[3];
-               }
+               c2[0] = c[0] + r_refdef.scene.ambient;
+               c2[1] = c[1] + r_refdef.scene.ambient;
+               c2[2] = c[2] + r_refdef.scene.ambient;
+               c2[3] = c[3];
        }
-       rsurface.lightmapcolor4f = rsurface.array_color4f;
-       rsurface.lightmapcolor4f_bufferobject = 0;
-       rsurface.lightmapcolor4f_bufferoffset = 0;
 }
 
-static void RSurf_DrawBatch_GL11_Lightmap(int texturenumsurfaces, const msurface_t **texturesurfacelist, float r, float g, float b, float a, qboolean applycolor, qboolean applyfog)
+static void RSurf_DrawBatch_GL11_Lightmap(float r, float g, float b, float a, qboolean applycolor, qboolean applyfog)
 {
        // TODO: optimize
-       rsurface.lightmapcolor4f = NULL;
-       rsurface.lightmapcolor4f_bufferobject = 0;
-       rsurface.lightmapcolor4f_bufferoffset = 0;
-       if (applyfog)   RSurf_DrawBatch_GL11_ApplyFog(texturenumsurfaces, texturesurfacelist);
-       if (applycolor) RSurf_DrawBatch_GL11_ApplyColor(texturenumsurfaces, texturesurfacelist, r, g, b, a);
-       R_Mesh_ColorPointer(rsurface.lightmapcolor4f, rsurface.lightmapcolor4f_bufferobject, rsurface.lightmapcolor4f_bufferoffset);
+       rsurface.passcolor4f = NULL;
+       rsurface.passcolor4f_vertexbuffer = 0;
+       rsurface.passcolor4f_bufferoffset = 0;
+       if (applyfog)   RSurf_DrawBatch_GL11_ApplyFog();
+       if (applycolor) RSurf_DrawBatch_GL11_ApplyColor(r, g, b, a);
+       R_Mesh_ColorPointer(4, GL_FLOAT, sizeof(float[4]), rsurface.passcolor4f, rsurface.passcolor4f_vertexbuffer, rsurface.passcolor4f_bufferoffset);
        GL_Color(r, g, b, a);
-       RSurf_DrawBatch_WithLightmapSwitching(texturenumsurfaces, texturesurfacelist);
+       R_Mesh_TexBind(0, rsurface.lightmaptexture);
+       RSurf_DrawBatch();
 }
 
-static void RSurf_DrawBatch_GL11_Unlit(int texturenumsurfaces, const msurface_t **texturesurfacelist, float r, float g, float b, float a, qboolean applycolor, qboolean applyfog)
+static void RSurf_DrawBatch_GL11_Unlit(float r, float g, float b, float a, qboolean applycolor, qboolean applyfog)
 {
        // TODO: optimize applyfog && applycolor case
        // just apply fog if necessary, and tint the fog color array if necessary
-       rsurface.lightmapcolor4f = NULL;
-       rsurface.lightmapcolor4f_bufferobject = 0;
-       rsurface.lightmapcolor4f_bufferoffset = 0;
-       if (applyfog)   RSurf_DrawBatch_GL11_ApplyFog(texturenumsurfaces, texturesurfacelist);
-       if (applycolor) RSurf_DrawBatch_GL11_ApplyColor(texturenumsurfaces, texturesurfacelist, r, g, b, a);
-       R_Mesh_ColorPointer(rsurface.lightmapcolor4f, rsurface.lightmapcolor4f_bufferobject, rsurface.lightmapcolor4f_bufferoffset);
+       rsurface.passcolor4f = NULL;
+       rsurface.passcolor4f_vertexbuffer = 0;
+       rsurface.passcolor4f_bufferoffset = 0;
+       if (applyfog)   RSurf_DrawBatch_GL11_ApplyFog();
+       if (applycolor) RSurf_DrawBatch_GL11_ApplyColor(r, g, b, a);
+       R_Mesh_ColorPointer(4, GL_FLOAT, sizeof(float[4]), rsurface.passcolor4f, rsurface.passcolor4f_vertexbuffer, rsurface.passcolor4f_bufferoffset);
        GL_Color(r, g, b, a);
-       RSurf_DrawBatch_Simple(texturenumsurfaces, texturesurfacelist);
+       RSurf_DrawBatch();
 }
 
-static void RSurf_DrawBatch_GL11_VertexColor(int texturenumsurfaces, const msurface_t **texturesurfacelist, float r, float g, float b, float a, qboolean applycolor, qboolean applyfog)
+static void RSurf_DrawBatch_GL11_VertexColor(float r, float g, float b, float a, qboolean applycolor, qboolean applyfog)
 {
-       int texturesurfaceindex;
-       int i;
-       float *c;
        // TODO: optimize
-       if (texturesurfacelist[0]->lightmapinfo)
+       rsurface.passcolor4f = rsurface.batchlightmapcolor4f;
+       rsurface.passcolor4f_vertexbuffer = rsurface.batchlightmapcolor4f_vertexbuffer;
+       rsurface.passcolor4f_bufferoffset = rsurface.batchlightmapcolor4f_bufferoffset;
+       if (applyfog)   RSurf_DrawBatch_GL11_ApplyFog();
+       if (applycolor) RSurf_DrawBatch_GL11_ApplyColor(r, g, b, a);
+       R_Mesh_ColorPointer(4, GL_FLOAT, sizeof(float[4]), rsurface.passcolor4f, rsurface.passcolor4f_vertexbuffer, rsurface.passcolor4f_bufferoffset);
+       GL_Color(r, g, b, a);
+       RSurf_DrawBatch();
+}
+
+static void RSurf_DrawBatch_GL11_ClampColor(void)
+{
+       int i;
+       const float *c1;
+       float *c2;
+       if (!rsurface.passcolor4f)
+               return;
+       for (i = 0, c1 = rsurface.passcolor4f + 4*rsurface.batchfirstvertex, c2 = rsurface.passcolor4f + 4*rsurface.batchfirstvertex;i < rsurface.batchnumvertices;i++, c1 += 4, c2 += 4)
        {
-               // generate color arrays for the surfaces in this list
-               for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
-               {
-                       const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
-                       for (i = 0, c = rsurface.array_color4f + 4 * surface->num_firstvertex;i < surface->num_vertices;i++, c += 4)
-                       {
-                               if (surface->lightmapinfo->samples)
-                               {
-                                       const unsigned char *lm = surface->lightmapinfo->samples + (rsurface.modellightmapoffsets + surface->num_firstvertex)[i];
-                                       float scale = r_refdef.scene.lightstylevalue[surface->lightmapinfo->styles[0]] * (1.0f / 32768.0f);
-                                       VectorScale(lm, scale, c);
-                                       if (surface->lightmapinfo->styles[1] != 255)
-                                       {
-                                               int size3 = ((surface->lightmapinfo->extents[0]>>4)+1)*((surface->lightmapinfo->extents[1]>>4)+1)*3;
-                                               lm += size3;
-                                               scale = r_refdef.scene.lightstylevalue[surface->lightmapinfo->styles[1]] * (1.0f / 32768.0f);
-                                               VectorMA(c, scale, lm, c);
-                                               if (surface->lightmapinfo->styles[2] != 255)
-                                               {
-                                                       lm += size3;
-                                                       scale = r_refdef.scene.lightstylevalue[surface->lightmapinfo->styles[2]] * (1.0f / 32768.0f);
-                                                       VectorMA(c, scale, lm, c);
-                                                       if (surface->lightmapinfo->styles[3] != 255)
-                                                       {
-                                                               lm += size3;
-                                                               scale = r_refdef.scene.lightstylevalue[surface->lightmapinfo->styles[3]] * (1.0f / 32768.0f);
-                                                               VectorMA(c, scale, lm, c);
-                                                       }
-                                               }
-                                       }
-                               }
-                               else
-                                       VectorClear(c);
-                               c[3] = 1;
-                       }
-               }
-               rsurface.lightmapcolor4f = rsurface.array_color4f;
-               rsurface.lightmapcolor4f_bufferobject = 0;
-               rsurface.lightmapcolor4f_bufferoffset = 0;
+               c2[0] = bound(0.0f, c1[0], 1.0f);
+               c2[1] = bound(0.0f, c1[1], 1.0f);
+               c2[2] = bound(0.0f, c1[2], 1.0f);
+               c2[3] = bound(0.0f, c1[3], 1.0f);
        }
-       else
+}
+
+static void RSurf_DrawBatch_GL11_ApplyFakeLight(void)
+{
+       int i;
+       float f;
+       const float *v;
+       const float *n;
+       float *c;
+       //vec3_t eyedir;
+
+       // fake shading
+       rsurface.passcolor4f = (float *)R_FrameData_Alloc(rsurface.batchnumvertices * sizeof(float[4]));
+       rsurface.passcolor4f_vertexbuffer = 0;
+       rsurface.passcolor4f_bufferoffset = 0;
+       for (i = 0, v = rsurface.batchvertex3f + rsurface.batchfirstvertex * 3, n = rsurface.batchnormal3f + rsurface.batchfirstvertex * 3, c = rsurface.passcolor4f + rsurface.batchfirstvertex * 4;i < rsurface.batchnumvertices;i++, v += 3, n += 3, c += 4)
        {
-               rsurface.lightmapcolor4f = rsurface.modellightmapcolor4f;
-               rsurface.lightmapcolor4f_bufferobject = rsurface.modellightmapcolor4f_bufferobject;
-               rsurface.lightmapcolor4f_bufferoffset = rsurface.modellightmapcolor4f_bufferoffset;
+               f = -DotProduct(r_refdef.view.forward, n);
+               f = max(0, f);
+               f = f * 0.85 + 0.15; // work around so stuff won't get black
+               f *= r_refdef.lightmapintensity;
+               Vector4Set(c, f, f, f, 1);
        }
-       if (applyfog)   RSurf_DrawBatch_GL11_ApplyFog(texturenumsurfaces, texturesurfacelist);
-       if (applycolor) RSurf_DrawBatch_GL11_ApplyColor(texturenumsurfaces, texturesurfacelist, r, g, b, a);
-       R_Mesh_ColorPointer(rsurface.lightmapcolor4f, rsurface.lightmapcolor4f_bufferobject, rsurface.lightmapcolor4f_bufferoffset);
+}
+
+static void RSurf_DrawBatch_GL11_FakeLight(float r, float g, float b, float a, qboolean applycolor, qboolean applyfog)
+{
+       RSurf_DrawBatch_GL11_ApplyFakeLight();
+       if (applyfog)   RSurf_DrawBatch_GL11_ApplyFog();
+       if (applycolor) RSurf_DrawBatch_GL11_ApplyColor(r, g, b, a);
+       R_Mesh_ColorPointer(4, GL_FLOAT, sizeof(float[4]), rsurface.passcolor4f, rsurface.passcolor4f_vertexbuffer, rsurface.passcolor4f_bufferoffset);
        GL_Color(r, g, b, a);
-       RSurf_DrawBatch_Simple(texturenumsurfaces, texturesurfacelist);
+       RSurf_DrawBatch();
 }
 
-static void RSurf_DrawBatch_GL11_ApplyVertexShade(int texturenumsurfaces, const msurface_t **texturesurfacelist, float *r, float *g, float *b, float *a, qboolean *applycolor)
+static void RSurf_DrawBatch_GL11_ApplyVertexShade(float *r, float *g, float *b, float *a, qboolean *applycolor)
 {
-       int texturesurfaceindex;
        int i;
        float f;
        float alpha;
@@ -10466,33 +9128,24 @@ static void RSurf_DrawBatch_GL11_ApplyVertexShade(int texturenumsurfaces, const
        diffusecolor[1] = rsurface.modellight_diffuse[1] * *g * f;
        diffusecolor[2] = rsurface.modellight_diffuse[2] * *b * f;
        alpha = *a;
-       if (VectorLength2(diffusecolor) > 0 && rsurface.normal3f)
+       if (VectorLength2(diffusecolor) > 0)
        {
-               // generate color arrays for the surfaces in this list
-               for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+               // q3-style directional shading
+               rsurface.passcolor4f = (float *)R_FrameData_Alloc(rsurface.batchnumvertices * sizeof(float[4]));
+               rsurface.passcolor4f_vertexbuffer = 0;
+               rsurface.passcolor4f_bufferoffset = 0;
+               for (i = 0, v = rsurface.batchvertex3f + rsurface.batchfirstvertex * 3, n = rsurface.batchnormal3f + rsurface.batchfirstvertex * 3, c = rsurface.passcolor4f + rsurface.batchfirstvertex * 4;i < rsurface.batchnumvertices;i++, v += 3, n += 3, c += 4)
                {
-                       const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
-                       int numverts = surface->num_vertices;
-                       v = rsurface.vertex3f + 3 * surface->num_firstvertex;
-                       n = rsurface.normal3f + 3 * surface->num_firstvertex;
-                       c = rsurface.array_color4f + 4 * surface->num_firstvertex;
-                       // q3-style directional shading
-                       for (i = 0;i < numverts;i++, v += 3, n += 3, c += 4)
-                       {
-                               if ((f = DotProduct(n, lightdir)) > 0)
-                                       VectorMA(ambientcolor, f, diffusecolor, c);
-                               else
-                                       VectorCopy(ambientcolor, c);
-                               c[3] = alpha;
-                       }
+                       if ((f = DotProduct(n, lightdir)) > 0)
+                               VectorMA(ambientcolor, f, diffusecolor, c);
+                       else
+                               VectorCopy(ambientcolor, c);
+                       c[3] = alpha;
                }
                *r = 1;
                *g = 1;
                *b = 1;
                *a = 1;
-               rsurface.lightmapcolor4f = rsurface.array_color4f;
-               rsurface.lightmapcolor4f_bufferobject = 0;
-               rsurface.lightmapcolor4f_bufferoffset = 0;
                *applycolor = false;
        }
        else
@@ -10500,20 +9153,36 @@ static void RSurf_DrawBatch_GL11_ApplyVertexShade(int texturenumsurfaces, const
                *r = ambientcolor[0];
                *g = ambientcolor[1];
                *b = ambientcolor[2];
-               rsurface.lightmapcolor4f = NULL;
-               rsurface.lightmapcolor4f_bufferobject = 0;
-               rsurface.lightmapcolor4f_bufferoffset = 0;
+               rsurface.passcolor4f = NULL;
+               rsurface.passcolor4f_vertexbuffer = 0;
+               rsurface.passcolor4f_bufferoffset = 0;
        }
 }
 
-static void RSurf_DrawBatch_GL11_VertexShade(int texturenumsurfaces, const msurface_t **texturesurfacelist, float r, float g, float b, float a, qboolean applycolor, qboolean applyfog)
+static void RSurf_DrawBatch_GL11_VertexShade(float r, float g, float b, float a, qboolean applycolor, qboolean applyfog)
 {
-       RSurf_DrawBatch_GL11_ApplyVertexShade(texturenumsurfaces, texturesurfacelist, &r, &g, &b, &a, &applycolor);
-       if (applyfog)   RSurf_DrawBatch_GL11_ApplyFog(texturenumsurfaces, texturesurfacelist);
-       if (applycolor) RSurf_DrawBatch_GL11_ApplyColor(texturenumsurfaces, texturesurfacelist, r, g, b, a);
-       R_Mesh_ColorPointer(rsurface.lightmapcolor4f, rsurface.lightmapcolor4f_bufferobject, rsurface.lightmapcolor4f_bufferoffset);
+       RSurf_DrawBatch_GL11_ApplyVertexShade(&r, &g, &b, &a, &applycolor);
+       if (applyfog)   RSurf_DrawBatch_GL11_ApplyFog();
+       if (applycolor) RSurf_DrawBatch_GL11_ApplyColor(r, g, b, a);
+       R_Mesh_ColorPointer(4, GL_FLOAT, sizeof(float[4]), rsurface.passcolor4f, rsurface.passcolor4f_vertexbuffer, rsurface.passcolor4f_bufferoffset);
        GL_Color(r, g, b, a);
-       RSurf_DrawBatch_Simple(texturenumsurfaces, texturesurfacelist);
+       RSurf_DrawBatch();
+}
+
+static void RSurf_DrawBatch_GL11_MakeFogColor(float r, float g, float b, float a)
+{
+       int i;
+       float f;
+       const float *v;
+       float *c;
+       for (i = 0, v = rsurface.batchvertex3f + rsurface.batchfirstvertex * 3, c = rsurface.passcolor4f + rsurface.batchfirstvertex * 4;i < rsurface.batchnumvertices;i++, v += 3, c += 4)
+       {
+               f = 1 - RSurf_FogVertex(v);
+               c[0] = r;
+               c[1] = g;
+               c[2] = b;
+               c[3] = f * a;
+       }
 }
 
 void RSurf_SetupDepthAndCulling(void)
@@ -10544,8 +9213,6 @@ static void R_DrawTextureSurfaceList_Sky(int texturenumsurfaces, const msurface_
        // level, so don't use it then either.
        if (r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->type == mod_brushq1 && r_q1bsp_skymasking.integer && !r_refdef.viewcache.world_novis)
        {
-               GL_Color(r_refdef.fogcolor[0], r_refdef.fogcolor[1], r_refdef.fogcolor[2], 1);
-               R_Mesh_ColorPointer(NULL, 0, 0);
                R_Mesh_ResetTextureState();
                if (skyrendermasked)
                {
@@ -10555,15 +9222,22 @@ static void R_DrawTextureSurfaceList_Sky(int texturenumsurfaces, const msurface_
                        // just to make sure that braindead drivers don't draw
                        // anything despite that colormask...
                        GL_BlendFunc(GL_ZERO, GL_ONE);
+                       RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_NOGAPS, texturenumsurfaces, texturesurfacelist);
+                       if (rsurface.batchvertex3fbuffer)
+                               R_Mesh_PrepareVertices_Vertex3f(rsurface.batchnumvertices, rsurface.batchvertex3f, rsurface.batchvertex3fbuffer);
+                       else
+                               R_Mesh_PrepareVertices_Vertex3f(rsurface.batchnumvertices, rsurface.batchvertex3f, rsurface.batchvertex3f_vertexbuffer);
                }
                else
                {
                        R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
                        // fog sky
                        GL_BlendFunc(GL_ONE, GL_ZERO);
+                       RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_NOGAPS, texturenumsurfaces, texturesurfacelist);
+                       GL_Color(r_refdef.fogcolor[0], r_refdef.fogcolor[1], r_refdef.fogcolor[2], 1);
+                       R_Mesh_PrepareVertices_Generic_Arrays(rsurface.batchnumvertices, rsurface.batchvertex3f, NULL, NULL);
                }
-               RSurf_PrepareVerticesForBatch(false, false, texturenumsurfaces, texturesurfacelist);
-               RSurf_DrawBatch_Simple(texturenumsurfaces, texturesurfacelist);
+               RSurf_DrawBatch();
                if (skyrendermasked)
                        GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
        }
@@ -10575,52 +9249,73 @@ extern rtexture_t *r_shadow_prepasslightingdiffusetexture;
 extern rtexture_t *r_shadow_prepasslightingspeculartexture;
 static void R_DrawTextureSurfaceList_GL20(int texturenumsurfaces, const msurface_t **texturesurfacelist, qboolean writedepth, qboolean prepass)
 {
-       if (r_waterstate.renderingscene && (rsurface.texture->currentmaterialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION)))
+       if (r_waterstate.renderingscene && (rsurface.texture->currentmaterialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA)))
                return;
-       RSurf_PrepareVerticesForBatch(true, true, texturenumsurfaces, texturesurfacelist);
        if (prepass)
        {
                // render screenspace normalmap to texture
                GL_DepthMask(true);
-               R_SetupShader_Surface(vec3_origin, (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT) != 0, 1, 1, rsurface.texture->specularscale, RSURFPASS_DEFERREDGEOMETRY);
-               RSurf_DrawBatch_Simple(texturenumsurfaces, texturesurfacelist);
-       }
-       else if ((rsurface.texture->currentmaterialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION)) && !r_waterstate.renderingscene)
-       {
-               // render water or distortion background, then blend surface on top
-               GL_DepthMask(true);
-               R_SetupShader_Surface(vec3_origin, (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT) != 0, 1, 1, rsurface.texture->specularscale, RSURFPASS_BACKGROUND);
-               RSurf_DrawBatch_WithLightmapSwitching_WithWaterTextureSwitching(texturenumsurfaces, texturesurfacelist);
-               GL_DepthMask(false);
-               R_SetupShader_Surface(vec3_origin, (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT) != 0, 1, 1, rsurface.texture->specularscale, RSURFPASS_BASE);
-               if (rsurface.uselightmaptexture && !(rsurface.texture->currentmaterialflags & MATERIALFLAG_FULLBRIGHT))
-                       RSurf_DrawBatch_WithLightmapSwitching(texturenumsurfaces, texturesurfacelist);
-               else
-                       RSurf_DrawBatch_Simple(texturenumsurfaces, texturesurfacelist);
+               R_SetupShader_Surface(vec3_origin, (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT) != 0, 1, 1, rsurface.texture->specularscale, RSURFPASS_DEFERREDGEOMETRY, texturenumsurfaces, texturesurfacelist, NULL);
+               RSurf_DrawBatch();
+               return;
        }
-       else
+
+       // bind lightmap texture
+
+       // water/refraction/reflection/camera surfaces have to be handled specially
+       if ((rsurface.texture->currentmaterialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_CAMERA | MATERIALFLAG_REFLECTION)))
        {
-               // render surface normally
-               GL_DepthMask(writedepth && !(rsurface.texture->currentmaterialflags & MATERIALFLAG_BLENDED));
-               R_SetupShader_Surface(vec3_origin, (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT) != 0, 1, 1, rsurface.texture->specularscale, RSURFPASS_BASE);
-               if (rsurface.texture->currentmaterialflags & MATERIALFLAG_REFLECTION)
-                       RSurf_DrawBatch_WithLightmapSwitching_WithWaterTextureSwitching(texturenumsurfaces, texturesurfacelist);
-               else if (rsurface.uselightmaptexture && !(rsurface.texture->currentmaterialflags & MATERIALFLAG_FULLBRIGHT))
-                       RSurf_DrawBatch_WithLightmapSwitching(texturenumsurfaces, texturesurfacelist);
-               else
-                       RSurf_DrawBatch_Simple(texturenumsurfaces, texturesurfacelist);
+               int start, end, startplaneindex;
+               for (start = 0;start < texturenumsurfaces;start = end)
+               {
+                       startplaneindex = RSurf_FindWaterPlaneForSurface(texturesurfacelist[start]);
+                       if(startplaneindex < 0)
+                       {
+                               // this happens if the plane e.g. got backface culled and thus didn't get a water plane. We can just ignore this.
+                               // Con_Printf("No matching water plane for surface with material flags 0x%08x - PLEASE DEBUG THIS\n", rsurface.texture->currentmaterialflags);
+                               end = start + 1;
+                               continue;
+                       }
+                       for (end = start + 1;end < texturenumsurfaces && startplaneindex == RSurf_FindWaterPlaneForSurface(texturesurfacelist[end]);end++)
+                               ;
+                       // now that we have a batch using the same planeindex, render it
+                       if ((rsurface.texture->currentmaterialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_CAMERA)))
+                       {
+                               // 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));
+                               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);
+                               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));
+                               RSurf_DrawBatch();
+                       }
+               }
+               return;
        }
+
+       // render surface batch normally
+       GL_DepthMask(writedepth && !(rsurface.texture->currentmaterialflags & MATERIALFLAG_BLENDED));
+       R_SetupShader_Surface(vec3_origin, (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT) != 0, 1, 1, rsurface.texture->specularscale, RSURFPASS_BASE, texturenumsurfaces, texturesurfacelist, NULL);
+       RSurf_DrawBatch();
 }
 
 static void R_DrawTextureSurfaceList_GL13(int texturenumsurfaces, const msurface_t **texturesurfacelist, qboolean writedepth)
 {
        // OpenGL 1.3 path - anything not completely ancient
-       int texturesurfaceindex;
        qboolean applycolor;
        qboolean applyfog;
        int layerindex;
        const texturelayer_t *layer;
-       RSurf_PrepareVerticesForBatch(true, false, texturenumsurfaces, texturesurfacelist);
+       RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_NORMAL | ((!rsurface.uselightmaptexture && !(rsurface.texture->currentmaterialflags & MATERIALFLAG_FULLBRIGHT)) ? BATCHNEED_ARRAY_VERTEXCOLOR : 0) | BATCHNEED_ARRAY_TEXCOORD | (rsurface.modeltexcoordlightmap2f ? BATCHNEED_ARRAY_LIGHTMAP : 0) | BATCHNEED_NOGAPS, texturenumsurfaces, texturesurfacelist);
+       R_Mesh_VertexPointer(3, GL_FLOAT, sizeof(float[3]), rsurface.batchvertex3f, rsurface.batchvertex3f_vertexbuffer, rsurface.batchvertex3f_bufferoffset);
 
        for (layerindex = 0, layer = rsurface.texture->currentlayers;layerindex < rsurface.texture->currentnumlayers;layerindex++, layer++)
        {
@@ -10633,7 +9328,7 @@ static void R_DrawTextureSurfaceList_GL13(int texturenumsurfaces, const msurface
                        else
                        {
                                GL_AlphaTest(false);
-                               qglDepthFunc(GL_EQUAL);CHECKGLERROR
+                               GL_DepthFunc(GL_EQUAL);
                        }
                }
                GL_DepthMask(layer->depthmask && writedepth);
@@ -10655,7 +9350,7 @@ static void R_DrawTextureSurfaceList_GL13(int texturenumsurfaces, const msurface
                }
                layercolor[3] = layer->color[3];
                applycolor = layercolor[0] != 1 || layercolor[1] != 1 || layercolor[2] != 1 || layercolor[3] != 1;
-               R_Mesh_ColorPointer(NULL, 0, 0);
+               R_Mesh_ColorPointer(4, GL_FLOAT, sizeof(float[4]), NULL, 0, 0);
                applyfog = r_refdef.fogenabled && (rsurface.texture->currentmaterialflags & MATERIALFLAG_BLENDED);
                switch (layer->type)
                {
@@ -10664,27 +9359,29 @@ static void R_DrawTextureSurfaceList_GL13(int texturenumsurfaces, const msurface
                        R_Mesh_TexBind(0, r_texture_white);
                        R_Mesh_TexMatrix(0, NULL);
                        R_Mesh_TexCombine(0, GL_MODULATE, GL_MODULATE, 1, 1);
-                       R_Mesh_TexCoordPointer(0, 2, rsurface.modeltexcoordlightmap2f, rsurface.modeltexcoordlightmap2f_bufferobject, rsurface.modeltexcoordlightmap2f_bufferoffset);
+                       R_Mesh_TexCoordPointer(0, 2, GL_FLOAT, sizeof(float[2]), rsurface.batchtexcoordlightmap2f, rsurface.batchtexcoordlightmap2f_vertexbuffer, rsurface.batchtexcoordlightmap2f_bufferoffset);
                        R_Mesh_TexBind(1, layer->texture);
                        R_Mesh_TexMatrix(1, &layer->texmatrix);
                        R_Mesh_TexCombine(1, GL_MODULATE, GL_MODULATE, layertexrgbscale, 1);
-                       R_Mesh_TexCoordPointer(1, 2, rsurface.texcoordtexture2f, rsurface.texcoordtexture2f_bufferobject, rsurface.texcoordtexture2f_bufferoffset);
+                       R_Mesh_TexCoordPointer(1, 2, GL_FLOAT, sizeof(float[2]), rsurface.batchtexcoordtexture2f, rsurface.batchtexcoordtexture2f_vertexbuffer, rsurface.batchtexcoordtexture2f_bufferoffset);
                        if (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT)
-                               RSurf_DrawBatch_GL11_VertexShade(texturenumsurfaces, texturesurfacelist, layercolor[0], layercolor[1], layercolor[2], layercolor[3], applycolor, applyfog);
+                               RSurf_DrawBatch_GL11_VertexShade(layercolor[0], layercolor[1], layercolor[2], layercolor[3], applycolor, applyfog);
+                       else if (FAKELIGHT_ENABLED)
+                               RSurf_DrawBatch_GL11_FakeLight(layercolor[0], layercolor[1], layercolor[2], layercolor[3], applycolor, applyfog);
                        else if (rsurface.uselightmaptexture)
-                               RSurf_DrawBatch_GL11_Lightmap(texturenumsurfaces, texturesurfacelist, layercolor[0], layercolor[1], layercolor[2], layercolor[3], applycolor, applyfog);
+                               RSurf_DrawBatch_GL11_Lightmap(layercolor[0], layercolor[1], layercolor[2], layercolor[3], applycolor, applyfog);
                        else
-                               RSurf_DrawBatch_GL11_VertexColor(texturenumsurfaces, texturesurfacelist, layercolor[0], layercolor[1], layercolor[2], layercolor[3], applycolor, applyfog);
+                               RSurf_DrawBatch_GL11_VertexColor(layercolor[0], layercolor[1], layercolor[2], layercolor[3], applycolor, applyfog);
                        break;
                case TEXTURELAYERTYPE_TEXTURE:
                        // singletexture unlit texture with transparency support
                        R_Mesh_TexBind(0, layer->texture);
                        R_Mesh_TexMatrix(0, &layer->texmatrix);
                        R_Mesh_TexCombine(0, GL_MODULATE, GL_MODULATE, layertexrgbscale, 1);
-                       R_Mesh_TexCoordPointer(0, 2, rsurface.texcoordtexture2f, rsurface.texcoordtexture2f_bufferobject, rsurface.texcoordtexture2f_bufferoffset);
+                       R_Mesh_TexCoordPointer(0, 2, GL_FLOAT, sizeof(float[2]), rsurface.batchtexcoordtexture2f, rsurface.batchtexcoordtexture2f_vertexbuffer, rsurface.batchtexcoordtexture2f_bufferoffset);
                        R_Mesh_TexBind(1, 0);
-                       R_Mesh_TexCoordPointer(1, 2, NULL, 0, 0);
-                       RSurf_DrawBatch_GL11_Unlit(texturenumsurfaces, texturesurfacelist, layercolor[0], layercolor[1], layercolor[2], layercolor[3], applycolor, applyfog);
+                       R_Mesh_TexCoordPointer(1, 2, GL_FLOAT, sizeof(float[2]), NULL, 0, 0);
+                       RSurf_DrawBatch_GL11_Unlit(layercolor[0], layercolor[1], layercolor[2], layercolor[3], applycolor, applyfog);
                        break;
                case TEXTURELAYERTYPE_FOG:
                        // singletexture fogging
@@ -10693,43 +9390,27 @@ static void R_DrawTextureSurfaceList_GL13(int texturenumsurfaces, const msurface
                                R_Mesh_TexBind(0, layer->texture);
                                R_Mesh_TexMatrix(0, &layer->texmatrix);
                                R_Mesh_TexCombine(0, GL_MODULATE, GL_MODULATE, layertexrgbscale, 1);
-                               R_Mesh_TexCoordPointer(0, 2, rsurface.texcoordtexture2f, rsurface.texcoordtexture2f_bufferobject, rsurface.texcoordtexture2f_bufferoffset);
+                               R_Mesh_TexCoordPointer(0, 2, GL_FLOAT, sizeof(float[2]), rsurface.batchtexcoordtexture2f, rsurface.batchtexcoordtexture2f_vertexbuffer, rsurface.batchtexcoordtexture2f_bufferoffset);
                        }
                        else
                        {
                                R_Mesh_TexBind(0, 0);
-                               R_Mesh_TexCoordPointer(0, 2, NULL, 0, 0);
+                               R_Mesh_TexCoordPointer(0, 2, GL_FLOAT, sizeof(float[2]), NULL, 0, 0);
                        }
                        R_Mesh_TexBind(1, 0);
-                       R_Mesh_TexCoordPointer(1, 2, NULL, 0, 0);
+                       R_Mesh_TexCoordPointer(1, 2, GL_FLOAT, sizeof(float[2]), NULL, 0, 0);
                        // generate a color array for the fog pass
-                       R_Mesh_ColorPointer(rsurface.array_color4f, 0, 0);
-                       for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
-                       {
-                               int i;
-                               float f;
-                               const float *v;
-                               float *c;
-                               const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
-                               for (i = 0, v = (rsurface.vertex3f + 3 * surface->num_firstvertex), c = (rsurface.array_color4f + 4 * surface->num_firstvertex);i < surface->num_vertices;i++, v += 3, c += 4)
-                               {
-                                       f = 1 - RSurf_FogVertex(v);
-                                       c[0] = layercolor[0];
-                                       c[1] = layercolor[1];
-                                       c[2] = layercolor[2];
-                                       c[3] = f * layercolor[3];
-                               }
-                       }
-                       RSurf_DrawBatch_Simple(texturenumsurfaces, texturesurfacelist);
+                       R_Mesh_ColorPointer(4, GL_FLOAT, sizeof(float[4]), rsurface.passcolor4f, 0, 0);
+                       RSurf_DrawBatch_GL11_MakeFogColor(layercolor[0], layercolor[1], layercolor[2], layercolor[3]);
+                       RSurf_DrawBatch();
                        break;
                default:
                        Con_Printf("R_DrawTextureSurfaceList: unknown layer type %i\n", layer->type);
                }
        }
-       CHECKGLERROR
        if (rsurface.texture->currentmaterialflags & MATERIALFLAG_ALPHATEST)
        {
-               qglDepthFunc(GL_LEQUAL);CHECKGLERROR
+               GL_DepthFunc(GL_LEQUAL);
                GL_AlphaTest(false);
        }
 }
@@ -10737,11 +9418,11 @@ static void R_DrawTextureSurfaceList_GL13(int texturenumsurfaces, const msurface
 static void R_DrawTextureSurfaceList_GL11(int texturenumsurfaces, const msurface_t **texturesurfacelist, qboolean writedepth)
 {
        // OpenGL 1.1 - crusty old voodoo path
-       int texturesurfaceindex;
        qboolean applyfog;
        int layerindex;
        const texturelayer_t *layer;
-       RSurf_PrepareVerticesForBatch(true, false, texturenumsurfaces, texturesurfacelist);
+       RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_NORMAL | ((!rsurface.uselightmaptexture && !(rsurface.texture->currentmaterialflags & MATERIALFLAG_FULLBRIGHT)) ? BATCHNEED_ARRAY_VERTEXCOLOR : 0) | BATCHNEED_ARRAY_TEXCOORD | (rsurface.modeltexcoordlightmap2f ? BATCHNEED_ARRAY_LIGHTMAP : 0) | BATCHNEED_NOGAPS, texturenumsurfaces, texturesurfacelist);
+       R_Mesh_VertexPointer(3, GL_FLOAT, sizeof(float[3]), rsurface.batchvertex3f, rsurface.batchvertex3f_vertexbuffer, rsurface.batchvertex3f_bufferoffset);
 
        for (layerindex = 0, layer = rsurface.texture->currentlayers;layerindex < rsurface.texture->currentnumlayers;layerindex++, layer++)
        {
@@ -10752,12 +9433,12 @@ static void R_DrawTextureSurfaceList_GL11(int texturenumsurfaces, const msurface
                        else
                        {
                                GL_AlphaTest(false);
-                               qglDepthFunc(GL_EQUAL);CHECKGLERROR
+                               GL_DepthFunc(GL_EQUAL);
                        }
                }
                GL_DepthMask(layer->depthmask && writedepth);
                GL_BlendFunc(layer->blendfunc1, layer->blendfunc2);
-               R_Mesh_ColorPointer(NULL, 0, 0);
+               R_Mesh_ColorPointer(4, GL_FLOAT, sizeof(float[4]), NULL, 0, 0);
                applyfog = r_refdef.fogenabled && (rsurface.texture->currentmaterialflags & MATERIALFLAG_BLENDED);
                switch (layer->type)
                {
@@ -10769,20 +9450,22 @@ static void R_DrawTextureSurfaceList_GL11(int texturenumsurfaces, const msurface
                                R_Mesh_TexBind(0, r_texture_white);
                                R_Mesh_TexMatrix(0, NULL);
                                R_Mesh_TexCombine(0, GL_MODULATE, GL_MODULATE, 1, 1);
-                               R_Mesh_TexCoordPointer(0, 2, rsurface.modeltexcoordlightmap2f, rsurface.modeltexcoordlightmap2f_bufferobject, rsurface.modeltexcoordlightmap2f_bufferoffset);
+                               R_Mesh_TexCoordPointer(0, 2, GL_FLOAT, sizeof(float[2]), rsurface.batchtexcoordlightmap2f, rsurface.batchtexcoordlightmap2f_vertexbuffer, rsurface.batchtexcoordlightmap2f_bufferoffset);
                                if (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT)
-                                       RSurf_DrawBatch_GL11_VertexShade(texturenumsurfaces, texturesurfacelist, 1, 1, 1, 1, false, false);
+                                       RSurf_DrawBatch_GL11_VertexShade(1, 1, 1, 1, false, false);
+                               else if (FAKELIGHT_ENABLED)
+                                       RSurf_DrawBatch_GL11_FakeLight(1, 1, 1, 1, false, false);
                                else if (rsurface.uselightmaptexture)
-                                       RSurf_DrawBatch_GL11_Lightmap(texturenumsurfaces, texturesurfacelist, 1, 1, 1, 1, false, false);
+                                       RSurf_DrawBatch_GL11_Lightmap(1, 1, 1, 1, false, false);
                                else
-                                       RSurf_DrawBatch_GL11_VertexColor(texturenumsurfaces, texturesurfacelist, 1, 1, 1, 1, false, false);
+                                       RSurf_DrawBatch_GL11_VertexColor(1, 1, 1, 1, false, false);
                                // then apply the texture to it
                                GL_BlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
                                R_Mesh_TexBind(0, layer->texture);
                                R_Mesh_TexMatrix(0, &layer->texmatrix);
                                R_Mesh_TexCombine(0, GL_MODULATE, GL_MODULATE, 1, 1);
-                               R_Mesh_TexCoordPointer(0, 2, rsurface.texcoordtexture2f, rsurface.texcoordtexture2f_bufferobject, rsurface.texcoordtexture2f_bufferoffset);
-                               RSurf_DrawBatch_GL11_Unlit(texturenumsurfaces, texturesurfacelist, layer->color[0] * 0.5f, layer->color[1] * 0.5f, layer->color[2] * 0.5f, layer->color[3], layer->color[0] != 2 || layer->color[1] != 2 || layer->color[2] != 2 || layer->color[3] != 1, false);
+                               R_Mesh_TexCoordPointer(0, 2, GL_FLOAT, sizeof(float[2]), rsurface.batchtexcoordtexture2f, rsurface.batchtexcoordtexture2f_vertexbuffer, rsurface.batchtexcoordtexture2f_bufferoffset);
+                               RSurf_DrawBatch_GL11_Unlit(layer->color[0] * 0.5f, layer->color[1] * 0.5f, layer->color[2] * 0.5f, layer->color[3], layer->color[0] != 2 || layer->color[1] != 2 || layer->color[2] != 2 || layer->color[3] != 1, false);
                        }
                        else
                        {
@@ -10790,11 +9473,11 @@ static void R_DrawTextureSurfaceList_GL11(int texturenumsurfaces, const msurface
                                R_Mesh_TexBind(0, layer->texture);
                                R_Mesh_TexMatrix(0, &layer->texmatrix);
                                R_Mesh_TexCombine(0, GL_MODULATE, GL_MODULATE, 1, 1);
-                               R_Mesh_TexCoordPointer(0, 2, rsurface.texcoordtexture2f, rsurface.texcoordtexture2f_bufferobject, rsurface.texcoordtexture2f_bufferoffset);
+                               R_Mesh_TexCoordPointer(0, 2, GL_FLOAT, sizeof(float[2]), rsurface.batchtexcoordtexture2f, rsurface.batchtexcoordtexture2f_vertexbuffer, rsurface.batchtexcoordtexture2f_bufferoffset);
                                if (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT)
-                                       RSurf_DrawBatch_GL11_VertexShade(texturenumsurfaces, texturesurfacelist, layer->color[0], layer->color[1], layer->color[2], layer->color[3], layer->color[0] != 1 || layer->color[1] != 1 || layer->color[2] != 1 || layer->color[3] != 1, applyfog);
+                                       RSurf_DrawBatch_GL11_VertexShade(layer->color[0], layer->color[1], layer->color[2], layer->color[3], layer->color[0] != 1 || layer->color[1] != 1 || layer->color[2] != 1 || layer->color[3] != 1, applyfog);
                                else
-                                       RSurf_DrawBatch_GL11_VertexColor(texturenumsurfaces, texturesurfacelist, layer->color[0], layer->color[1], layer->color[2], layer->color[3], layer->color[0] != 1 || layer->color[1] != 1 || layer->color[2] != 1 || layer->color[3] != 1, applyfog);
+                                       RSurf_DrawBatch_GL11_VertexColor(layer->color[0], layer->color[1], layer->color[2], layer->color[3], layer->color[0] != 1 || layer->color[1] != 1 || layer->color[2] != 1 || layer->color[3] != 1, applyfog);
                        }
                        break;
                case TEXTURELAYERTYPE_TEXTURE:
@@ -10802,8 +9485,8 @@ static void R_DrawTextureSurfaceList_GL11(int texturenumsurfaces, const msurface
                        R_Mesh_TexBind(0, layer->texture);
                        R_Mesh_TexMatrix(0, &layer->texmatrix);
                        R_Mesh_TexCombine(0, GL_MODULATE, GL_MODULATE, 1, 1);
-                       R_Mesh_TexCoordPointer(0, 2, rsurface.texcoordtexture2f, rsurface.texcoordtexture2f_bufferobject, rsurface.texcoordtexture2f_bufferoffset);
-                       RSurf_DrawBatch_GL11_Unlit(texturenumsurfaces, texturesurfacelist, layer->color[0], layer->color[1], layer->color[2], layer->color[3], layer->color[0] != 1 || layer->color[1] != 1 || layer->color[2] != 1 || layer->color[3] != 1, applyfog);
+                       R_Mesh_TexCoordPointer(0, 2, GL_FLOAT, sizeof(float[2]), rsurface.batchtexcoordtexture2f, rsurface.batchtexcoordtexture2f_vertexbuffer, rsurface.batchtexcoordtexture2f_bufferoffset);
+                       RSurf_DrawBatch_GL11_Unlit(layer->color[0], layer->color[1], layer->color[2], layer->color[3], layer->color[0] != 1 || layer->color[1] != 1 || layer->color[2] != 1 || layer->color[3] != 1, applyfog);
                        break;
                case TEXTURELAYERTYPE_FOG:
                        // singletexture fogging
@@ -10812,52 +9495,37 @@ static void R_DrawTextureSurfaceList_GL11(int texturenumsurfaces, const msurface
                                R_Mesh_TexBind(0, layer->texture);
                                R_Mesh_TexMatrix(0, &layer->texmatrix);
                                R_Mesh_TexCombine(0, GL_MODULATE, GL_MODULATE, 1, 1);
-                               R_Mesh_TexCoordPointer(0, 2, rsurface.texcoordtexture2f, rsurface.texcoordtexture2f_bufferobject, rsurface.texcoordtexture2f_bufferoffset);
+                               R_Mesh_TexCoordPointer(0, 2, GL_FLOAT, sizeof(float[2]), rsurface.batchtexcoordtexture2f, rsurface.batchtexcoordtexture2f_vertexbuffer, rsurface.batchtexcoordtexture2f_bufferoffset);
                        }
                        else
                        {
-                               R_Mesh_TexBind(0, 0);
-                               R_Mesh_TexCoordPointer(0, 2, NULL, 0, 0);
-                       }
-                       // generate a color array for the fog pass
-                       R_Mesh_ColorPointer(rsurface.array_color4f, 0, 0);
-                       for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
-                       {
-                               int i;
-                               float f;
-                               const float *v;
-                               float *c;
-                               const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
-                               for (i = 0, v = (rsurface.vertex3f + 3 * surface->num_firstvertex), c = (rsurface.array_color4f + 4 * surface->num_firstvertex);i < surface->num_vertices;i++, v += 3, c += 4)
-                               {
-                                       f = 1 - RSurf_FogVertex(v);
-                                       c[0] = layer->color[0];
-                                       c[1] = layer->color[1];
-                                       c[2] = layer->color[2];
-                                       c[3] = f * layer->color[3];
-                               }
+                               R_Mesh_TexBind(0, 0);
+                               R_Mesh_TexCoordPointer(0, 2, GL_FLOAT, sizeof(float[2]), NULL, 0, 0);
                        }
-                       RSurf_DrawBatch_Simple(texturenumsurfaces, texturesurfacelist);
+                       // generate a color array for the fog pass
+                       R_Mesh_ColorPointer(4, GL_FLOAT, sizeof(float[4]), rsurface.passcolor4f, 0, 0);
+                       RSurf_DrawBatch_GL11_MakeFogColor(layer->color[0], layer->color[1], layer->color[2], layer->color[3]);
+                       RSurf_DrawBatch();
                        break;
                default:
                        Con_Printf("R_DrawTextureSurfaceList: unknown layer type %i\n", layer->type);
                }
        }
-       CHECKGLERROR
        if (rsurface.texture->currentmaterialflags & MATERIALFLAG_ALPHATEST)
        {
-               qglDepthFunc(GL_LEQUAL);CHECKGLERROR
+               GL_DepthFunc(GL_LEQUAL);
                GL_AlphaTest(false);
        }
 }
 
-static void R_DrawTextureSurfaceList_ShowSurfaces3(int texturenumsurfaces, const msurface_t **texturesurfacelist, qboolean writedepth)
+static void R_DrawTextureSurfaceList_ShowSurfaces(int texturenumsurfaces, const msurface_t **texturesurfacelist, qboolean writedepth)
 {
+       int vi;
+       int j;
+       r_vertexgeneric_t *batchvertex;
        float c[4];
 
-       GL_AlphaTest(false);
-       R_Mesh_ColorPointer(NULL, 0, 0);
-       R_Mesh_ResetTextureState();
+//     R_Mesh_ResetTextureState();
        R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
 
        if(rsurface.texture && rsurface.texture->currentskinframe)
@@ -10914,61 +9582,145 @@ static void R_DrawTextureSurfaceList_ShowSurfaces3(int texturenumsurfaces, const
                GL_DepthMask(writedepth);
        }
 
-       rsurface.lightmapcolor4f = NULL;
-
-       if (rsurface.texture->currentmaterialflags & MATERIALFLAG_FULLBRIGHT)
+       if (r_showsurfaces.integer == 3)
        {
-               RSurf_PrepareVerticesForBatch(false, false, texturenumsurfaces, texturesurfacelist);
+               rsurface.passcolor4f = NULL;
 
-               rsurface.lightmapcolor4f = NULL;
-               rsurface.lightmapcolor4f_bufferobject = 0;
-               rsurface.lightmapcolor4f_bufferoffset = 0;
-       }
-       else if (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT)
-       {
-               qboolean applycolor = true;
-               float one = 1.0;
+               if (rsurface.texture->currentmaterialflags & MATERIALFLAG_FULLBRIGHT)
+               {
+                       RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_NOGAPS, texturenumsurfaces, texturesurfacelist);
 
-               RSurf_PrepareVerticesForBatch(true, false, texturenumsurfaces, texturesurfacelist);
+                       rsurface.passcolor4f = NULL;
+                       rsurface.passcolor4f_vertexbuffer = 0;
+                       rsurface.passcolor4f_bufferoffset = 0;
+               }
+               else if (rsurface.texture->currentmaterialflags & MATERIALFLAG_MODELLIGHT)
+               {
+                       qboolean applycolor = true;
+                       float one = 1.0;
 
-               r_refdef.lightmapintensity = 1;
-               RSurf_DrawBatch_GL11_ApplyVertexShade(texturenumsurfaces, texturesurfacelist, &one, &one, &one, &one, &applycolor);
-               r_refdef.lightmapintensity = 0; // we're in showsurfaces, after all
-       }
-       else
-       {
-               RSurf_PrepareVerticesForBatch(false, false, texturenumsurfaces, texturesurfacelist);
+                       RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_NORMAL | BATCHNEED_NOGAPS, texturenumsurfaces, texturesurfacelist);
 
-               rsurface.lightmapcolor4f = rsurface.modellightmapcolor4f;
-               rsurface.lightmapcolor4f_bufferobject = rsurface.modellightmapcolor4f_bufferobject;
-               rsurface.lightmapcolor4f_bufferoffset = rsurface.modellightmapcolor4f_bufferoffset;
-       }
+                       r_refdef.lightmapintensity = 1;
+                       RSurf_DrawBatch_GL11_ApplyVertexShade(&one, &one, &one, &one, &applycolor);
+                       r_refdef.lightmapintensity = 0; // we're in showsurfaces, after all
+               }
+               else if (FAKELIGHT_ENABLED)
+               {
+                       RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_NORMAL | BATCHNEED_NOGAPS, texturenumsurfaces, texturesurfacelist);
+
+                       r_refdef.lightmapintensity = r_fakelight_intensity.value;
+                       RSurf_DrawBatch_GL11_ApplyFakeLight();
+                       r_refdef.lightmapintensity = 0; // we're in showsurfaces, after all
+               }
+               else
+               {
+                       RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_VERTEXCOLOR | BATCHNEED_NOGAPS, texturenumsurfaces, texturesurfacelist);
+
+                       rsurface.passcolor4f = rsurface.batchlightmapcolor4f;
+                       rsurface.passcolor4f_vertexbuffer = rsurface.batchlightmapcolor4f_vertexbuffer;
+                       rsurface.passcolor4f_bufferoffset = rsurface.batchlightmapcolor4f_bufferoffset;
+               }
 
-       if(!rsurface.lightmapcolor4f)
-               RSurf_DrawBatch_GL11_MakeFullbrightLightmapColorArray(texturenumsurfaces, texturesurfacelist);
+               if(!rsurface.passcolor4f)
+                       RSurf_DrawBatch_GL11_MakeFullbrightLightmapColorArray();
 
-       RSurf_DrawBatch_GL11_ApplyAmbient(texturenumsurfaces, texturesurfacelist);
-       RSurf_DrawBatch_GL11_ApplyColor(texturenumsurfaces, texturesurfacelist, c[0], c[1], c[2], c[3]);
-       if(r_refdef.fogenabled)
-               RSurf_DrawBatch_GL11_ApplyFogToFinishedVertexColors(texturenumsurfaces, texturesurfacelist);
+               RSurf_DrawBatch_GL11_ApplyAmbient();
+               RSurf_DrawBatch_GL11_ApplyColor(c[0], c[1], c[2], c[3]);
+               if(r_refdef.fogenabled)
+                       RSurf_DrawBatch_GL11_ApplyFogToFinishedVertexColors();
+               RSurf_DrawBatch_GL11_ClampColor();
 
-       R_Mesh_ColorPointer(rsurface.lightmapcolor4f, rsurface.lightmapcolor4f_bufferobject, rsurface.lightmapcolor4f_bufferoffset);
-       RSurf_DrawBatch_Simple(texturenumsurfaces, texturesurfacelist);
+               R_Mesh_PrepareVertices_Generic_Arrays(rsurface.batchnumvertices, rsurface.batchvertex3f, rsurface.passcolor4f, NULL);
+               R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
+               RSurf_DrawBatch();
+       }
+       else if (!r_refdef.view.showdebug)
+       {
+               RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_NOGAPS, texturenumsurfaces, texturesurfacelist);
+               batchvertex = R_Mesh_PrepareVertices_Generic_Lock(rsurface.batchnumvertices);
+               for (j = 0, vi = rsurface.batchfirstvertex;j < rsurface.batchnumvertices;j++, vi++)
+               {
+                       VectorCopy(rsurface.batchvertex3f + 3*vi, batchvertex[vi].vertex3f);
+                       Vector4Set(batchvertex[vi].color4f, 0, 0, 0, 1);
+               }
+               R_Mesh_PrepareVertices_Generic_Unlock();
+               RSurf_DrawBatch();
+       }
+       else if (r_showsurfaces.integer == 4)
+       {
+               RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_NOGAPS, texturenumsurfaces, texturesurfacelist);
+               batchvertex = R_Mesh_PrepareVertices_Generic_Lock(rsurface.batchnumvertices);
+               for (j = 0, vi = rsurface.batchfirstvertex;j < rsurface.batchnumvertices;j++, vi++)
+               {
+                       unsigned char c = (vi << 3) * (1.0f / 256.0f);
+                       VectorCopy(rsurface.batchvertex3f + 3*vi, batchvertex[vi].vertex3f);
+                       Vector4Set(batchvertex[vi].color4f, c, c, c, 1);
+               }
+               R_Mesh_PrepareVertices_Generic_Unlock();
+               RSurf_DrawBatch();
+       }
+       else if (r_showsurfaces.integer == 2)
+       {
+               const int *e;
+               RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_NOGAPS, texturenumsurfaces, texturesurfacelist);
+               batchvertex = R_Mesh_PrepareVertices_Generic_Lock(3*rsurface.batchnumtriangles);
+               for (j = 0, e = rsurface.batchelement3i + 3 * rsurface.batchfirsttriangle;j < rsurface.batchnumtriangles;j++, e += 3)
+               {
+                       unsigned char c = ((j + rsurface.batchfirsttriangle) << 3) * (1.0f / 256.0f);
+                       VectorCopy(rsurface.batchvertex3f + 3*e[0], batchvertex[j*3+0].vertex3f);
+                       VectorCopy(rsurface.batchvertex3f + 3*e[1], batchvertex[j*3+1].vertex3f);
+                       VectorCopy(rsurface.batchvertex3f + 3*e[2], batchvertex[j*3+2].vertex3f);
+                       Vector4Set(batchvertex[j*3+0].color4f, c, c, c, 1);
+                       Vector4Set(batchvertex[j*3+1].color4f, c, c, c, 1);
+                       Vector4Set(batchvertex[j*3+2].color4f, c, c, c, 1);
+               }
+               R_Mesh_PrepareVertices_Generic_Unlock();
+               R_Mesh_Draw(0, rsurface.batchnumtriangles*3, 0, rsurface.batchnumtriangles, NULL, NULL, 0, NULL, NULL, 0);
+       }
+       else
+       {
+               int texturesurfaceindex;
+               int k;
+               const msurface_t *surface;
+               float surfacecolor4f[4];
+               RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_NOGAPS, texturenumsurfaces, texturesurfacelist);
+               batchvertex = R_Mesh_PrepareVertices_Generic_Lock(rsurface.batchfirstvertex + rsurface.batchnumvertices);
+               vi = 0;
+               for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+               {
+                       surface = texturesurfacelist[texturesurfaceindex];
+                       k = (int)(((size_t)surface) / sizeof(msurface_t));
+                       Vector4Set(surfacecolor4f, (k & 0xF) * (1.0f / 16.0f), (k & 0xF0) * (1.0f / 256.0f), (k & 0xF00) * (1.0f / 4096.0f), 1);
+                       for (j = 0;j < surface->num_vertices;j++)
+                       {
+                               VectorCopy(rsurface.batchvertex3f + 3*vi, batchvertex[vi].vertex3f);
+                               Vector4Copy(surfacecolor4f, batchvertex[vi].color4f);
+                               vi++;
+                       }
+               }
+               R_Mesh_PrepareVertices_Generic_Unlock();
+               RSurf_DrawBatch();
+       }
 }
 
 static void R_DrawWorldTextureSurfaceList(int texturenumsurfaces, const msurface_t **texturesurfacelist, qboolean writedepth, qboolean prepass)
 {
        CHECKGLERROR
        RSurf_SetupDepthAndCulling();
-       if (r_showsurfaces.integer == 3 && !prepass)
+       if (r_showsurfaces.integer)
        {
-               R_DrawTextureSurfaceList_ShowSurfaces3(texturenumsurfaces, texturesurfacelist, writedepth);
+               R_DrawTextureSurfaceList_ShowSurfaces(texturenumsurfaces, texturesurfacelist, writedepth);
                return;
        }
        switch (vid.renderpath)
        {
        case RENDERPATH_GL20:
-       case RENDERPATH_CGGL:
+       case RENDERPATH_D3D9:
+       case RENDERPATH_D3D10:
+       case RENDERPATH_D3D11:
+       case RENDERPATH_SOFT:
+       case RENDERPATH_GLES2:
                R_DrawTextureSurfaceList_GL20(texturenumsurfaces, texturesurfacelist, writedepth, prepass);
                break;
        case RENDERPATH_GL13:
@@ -10985,15 +9737,19 @@ static void R_DrawModelTextureSurfaceList(int texturenumsurfaces, const msurface
 {
        CHECKGLERROR
        RSurf_SetupDepthAndCulling();
-       if (r_showsurfaces.integer == 3 && !prepass)
+       if (r_showsurfaces.integer)
        {
-               R_DrawTextureSurfaceList_ShowSurfaces3(texturenumsurfaces, texturesurfacelist, writedepth);
+               R_DrawTextureSurfaceList_ShowSurfaces(texturenumsurfaces, texturesurfacelist, writedepth);
                return;
        }
        switch (vid.renderpath)
        {
        case RENDERPATH_GL20:
-       case RENDERPATH_CGGL:
+       case RENDERPATH_D3D9:
+       case RENDERPATH_D3D10:
+       case RENDERPATH_D3D11:
+       case RENDERPATH_SOFT:
+       case RENDERPATH_GLES2:
                R_DrawTextureSurfaceList_GL20(texturenumsurfaces, texturesurfacelist, writedepth, prepass);
                break;
        case RENDERPATH_GL13:
@@ -11012,7 +9768,8 @@ static void R_DrawSurface_TransparentCallback(const entity_render_t *ent, const
        int texturenumsurfaces, endsurface;
        texture_t *texture;
        const msurface_t *surface;
-       const msurface_t *texturesurfacelist[256];
+#define MAXBATCH_TRANSPARENTSURFACES 256
+       const msurface_t *texturesurfacelist[MAXBATCH_TRANSPARENTSURFACES];
 
        // if the model is static it doesn't matter what value we give for
        // wantnormals and wanttangents, so this logic uses only rules applicable
@@ -11026,7 +9783,11 @@ static void R_DrawSurface_TransparentCallback(const entity_render_t *ent, const
                switch (vid.renderpath)
                {
                case RENDERPATH_GL20:
-               case RENDERPATH_CGGL:
+               case RENDERPATH_D3D9:
+               case RENDERPATH_D3D10:
+               case RENDERPATH_D3D11:
+               case RENDERPATH_SOFT:
+               case RENDERPATH_GLES2:
                        RSurf_ActiveModelEntity(ent, true, true, false);
                        break;
                case RENDERPATH_GL13:
@@ -11045,7 +9806,9 @@ static void R_DrawSurface_TransparentCallback(const entity_render_t *ent, const
                        surface = rsurface.modelsurfaces + surfacelist[i];
                        texture = surface->texture;
                        rsurface.texture = R_GetCurrentTexture(texture);
-                       rsurface.uselightmaptexture = surface->lightmaptexture != NULL;
+                       rsurface.lightmaptexture = NULL;
+                       rsurface.deluxemaptexture = NULL;
+                       rsurface.uselightmaptexture = false;
                        // scan ahead until we find a different texture
                        endsurface = min(i + 1024, numsurfaces);
                        texturenumsurfaces = 0;
@@ -11053,7 +9816,7 @@ static void R_DrawSurface_TransparentCallback(const entity_render_t *ent, const
                        for (;j < endsurface;j++)
                        {
                                surface = rsurface.modelsurfaces + surfacelist[j];
-                               if (texture != surface->texture || rsurface.uselightmaptexture != (surface->lightmaptexture != NULL))
+                               if (texture != surface->texture)
                                        break;
                                texturesurfacelist[texturenumsurfaces++] = surface;
                        }
@@ -11068,14 +9831,16 @@ static void R_DrawSurface_TransparentCallback(const entity_render_t *ent, const
                                GL_DepthTest(true);
                                GL_BlendFunc(GL_ONE, GL_ZERO);
                                GL_DepthMask(true);
-                               GL_AlphaTest(false);
-                               R_Mesh_ColorPointer(NULL, 0, 0);
-                               R_Mesh_ResetTextureState();
+//                             R_Mesh_ResetTextureState();
                                R_SetupShader_DepthOrShadow();
                        }
                        RSurf_SetupDepthAndCulling();
-                       RSurf_PrepareVerticesForBatch(false, false, texturenumsurfaces, texturesurfacelist);
-                       RSurf_DrawBatch_Simple(texturenumsurfaces, texturesurfacelist);
+                       RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX, texturenumsurfaces, texturesurfacelist);
+                       if (rsurface.batchvertex3fbuffer)
+                               R_Mesh_PrepareVertices_Vertex3f(rsurface.batchnumvertices, rsurface.batchvertex3f, rsurface.batchvertex3fbuffer);
+                       else
+                               R_Mesh_PrepareVertices_Vertex3f(rsurface.batchnumvertices, rsurface.batchvertex3f, rsurface.batchvertex3f_vertexbuffer);
+                       RSurf_DrawBatch();
                }
                if (setup)
                        GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
@@ -11087,17 +9852,35 @@ static void R_DrawSurface_TransparentCallback(const entity_render_t *ent, const
                surface = rsurface.modelsurfaces + surfacelist[i];
                texture = surface->texture;
                rsurface.texture = R_GetCurrentTexture(texture);
-               rsurface.uselightmaptexture = surface->lightmaptexture != NULL;
                // scan ahead until we find a different texture
-               endsurface = min(i + 1024, numsurfaces);
+               endsurface = min(i + MAXBATCH_TRANSPARENTSURFACES, numsurfaces);
                texturenumsurfaces = 0;
                texturesurfacelist[texturenumsurfaces++] = surface;
-               for (;j < endsurface;j++)
+               if(FAKELIGHT_ENABLED)
                {
-                       surface = rsurface.modelsurfaces + surfacelist[j];
-                       if (texture != surface->texture || rsurface.uselightmaptexture != (surface->lightmaptexture != NULL))
-                               break;
-                       texturesurfacelist[texturenumsurfaces++] = surface;
+                       rsurface.lightmaptexture = NULL;
+                       rsurface.deluxemaptexture = NULL;
+                       rsurface.uselightmaptexture = false;
+                       for (;j < endsurface;j++)
+                       {
+                               surface = rsurface.modelsurfaces + surfacelist[j];
+                               if (texture != surface->texture)
+                                       break;
+                               texturesurfacelist[texturenumsurfaces++] = surface;
+                       }
+               }
+               else
+               {
+                       rsurface.lightmaptexture = surface->lightmaptexture;
+                       rsurface.deluxemaptexture = surface->deluxemaptexture;
+                       rsurface.uselightmaptexture = surface->lightmaptexture != NULL;
+                       for (;j < endsurface;j++)
+                       {
+                               surface = rsurface.modelsurfaces + surfacelist[j];
+                               if (texture != surface->texture || rsurface.lightmaptexture != surface->lightmaptexture)
+                                       break;
+                               texturesurfacelist[texturenumsurfaces++] = surface;
+                       }
                }
                // render the range of surfaces
                if (ent == r_refdef.scene.worldentity)
@@ -11106,7 +9889,6 @@ static void R_DrawSurface_TransparentCallback(const entity_render_t *ent, const
                        R_DrawModelTextureSurfaceList(texturenumsurfaces, texturesurfacelist, false, false);
        }
        rsurface.entity = NULL; // used only by R_GetCurrentTexture and RSurf_ActiveWorldEntity/RSurf_ActiveModelEntity
-       GL_AlphaTest(false);
 }
 
 static void R_ProcessTransparentTextureSurfaceList(int texturenumsurfaces, const msurface_t **texturesurfacelist, const entity_render_t *queueentity)
@@ -11132,20 +9914,27 @@ static void R_ProcessTransparentTextureSurfaceList(int texturenumsurfaces, const
        }
 }
 
+static void R_DrawTextureSurfaceList_DepthOnly(int texturenumsurfaces, const msurface_t **texturesurfacelist)
+{
+       if ((rsurface.texture->currentmaterialflags & (MATERIALFLAG_NODEPTHTEST | MATERIALFLAG_BLENDED | MATERIALFLAG_ALPHATEST)))
+               return;
+       if (r_waterstate.renderingscene && (rsurface.texture->currentmaterialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFLECTION)))
+               return;
+       RSurf_SetupDepthAndCulling();
+       RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX, texturenumsurfaces, texturesurfacelist);
+       if (rsurface.batchvertex3fbuffer)
+               R_Mesh_PrepareVertices_Vertex3f(rsurface.batchnumvertices, rsurface.batchvertex3f, rsurface.batchvertex3fbuffer);
+       else
+               R_Mesh_PrepareVertices_Vertex3f(rsurface.batchnumvertices, rsurface.batchvertex3f, rsurface.batchvertex3f_vertexbuffer);
+       RSurf_DrawBatch();
+}
+
 static void R_ProcessWorldTextureSurfaceList(int texturenumsurfaces, const msurface_t **texturesurfacelist, qboolean writedepth, qboolean depthonly, qboolean prepass)
 {
        const entity_render_t *queueentity = r_refdef.scene.worldentity;
        CHECKGLERROR
        if (depthonly)
-       {
-               if ((rsurface.texture->currentmaterialflags & (MATERIALFLAG_NODEPTHTEST | MATERIALFLAG_BLENDED | MATERIALFLAG_ALPHATEST)))
-                       return;
-               if (r_waterstate.renderingscene && (rsurface.texture->currentmaterialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFLECTION)))
-                       return;
-               RSurf_SetupDepthAndCulling();
-               RSurf_PrepareVerticesForBatch(false, false, texturenumsurfaces, texturesurfacelist);
-               RSurf_DrawBatch_Simple(texturenumsurfaces, texturesurfacelist);
-       }
+               R_DrawTextureSurfaceList_DepthOnly(texturenumsurfaces, texturesurfacelist);
        else if (prepass)
        {
                if (!rsurface.texture->currentnumlayers)
@@ -11155,34 +9944,7 @@ static void R_ProcessWorldTextureSurfaceList(int texturenumsurfaces, const msurf
                else
                        R_DrawWorldTextureSurfaceList(texturenumsurfaces, texturesurfacelist, writedepth, prepass);
        }
-       else if (r_showsurfaces.integer && !r_refdef.view.showdebug && !prepass)
-       {
-               RSurf_SetupDepthAndCulling();
-               GL_AlphaTest(false);
-               R_Mesh_ColorPointer(NULL, 0, 0);
-               R_Mesh_ResetTextureState();
-               R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
-               RSurf_PrepareVerticesForBatch(false, false, texturenumsurfaces, texturesurfacelist);
-               GL_DepthMask(true);
-               GL_BlendFunc(GL_ONE, GL_ZERO);
-               GL_Color(0, 0, 0, 1);
-               GL_DepthTest(writedepth);
-               RSurf_DrawBatch_Simple(texturenumsurfaces, texturesurfacelist);
-       }
-       else if (r_showsurfaces.integer && r_showsurfaces.integer != 3 && !prepass)
-       {
-               RSurf_SetupDepthAndCulling();
-               GL_AlphaTest(false);
-               R_Mesh_ColorPointer(NULL, 0, 0);
-               R_Mesh_ResetTextureState();
-               R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
-               RSurf_PrepareVerticesForBatch(false, false, texturenumsurfaces, texturesurfacelist);
-               GL_DepthMask(true);
-               GL_BlendFunc(GL_ONE, GL_ZERO);
-               GL_DepthTest(true);
-               RSurf_DrawBatch_ShowSurfaces(texturenumsurfaces, texturesurfacelist);
-       }
-       else if (rsurface.texture->currentmaterialflags & MATERIALFLAG_SKY)
+       else if ((rsurface.texture->currentmaterialflags & MATERIALFLAG_SKY) && !r_showsurfaces.integer)
                R_DrawTextureSurfaceList_Sky(texturenumsurfaces, texturesurfacelist);
        else if (!rsurface.texture->currentnumlayers)
                return;
@@ -11204,6 +9966,7 @@ void R_QueueWorldSurfaceList(int numsurfaces, const msurface_t **surfacelist, in
 {
        int i, j;
        texture_t *texture;
+       R_FrameData_SetMark();
        // break the surface list down into batches by texture and use of lightmapping
        for (i = 0;i < numsurfaces;i = j)
        {
@@ -11214,7 +9977,6 @@ void R_QueueWorldSurfaceList(int numsurfaces, const msurface_t **surfacelist, in
                // use skin 1 instead)
                texture = surfacelist[i]->texture;
                rsurface.texture = R_GetCurrentTexture(texture);
-               rsurface.uselightmaptexture = surfacelist[i]->lightmaptexture != NULL && !depthonly && !prepass;
                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
@@ -11222,27 +9984,35 @@ void R_QueueWorldSurfaceList(int numsurfaces, const msurface_t **surfacelist, in
                                ;
                        continue;
                }
-               // simply scan ahead until we find a different texture or lightmap state
-               for (;j < numsurfaces && texture == surfacelist[j]->texture && rsurface.uselightmaptexture == (surfacelist[j]->lightmaptexture != NULL);j++)
-                       ;
+               if(FAKELIGHT_ENABLED || depthonly || prepass)
+               {
+                       rsurface.lightmaptexture = NULL;
+                       rsurface.deluxemaptexture = NULL;
+                       rsurface.uselightmaptexture = false;
+                       // simply scan ahead until we find a different texture or lightmap state
+                       for (;j < numsurfaces && texture == surfacelist[j]->texture;j++)
+                               ;
+               }
+               else
+               {
+                       rsurface.lightmaptexture = surfacelist[i]->lightmaptexture;
+                       rsurface.deluxemaptexture = surfacelist[i]->deluxemaptexture;
+                       rsurface.uselightmaptexture = surfacelist[i]->lightmaptexture != NULL;
+                       // simply scan ahead until we find a different texture or lightmap state
+                       for (;j < numsurfaces && texture == surfacelist[j]->texture && rsurface.lightmaptexture == surfacelist[j]->lightmaptexture;j++)
+                               ;
+               }
                // render the range of surfaces
                R_ProcessWorldTextureSurfaceList(j - i, surfacelist + i, writedepth, depthonly, prepass);
        }
+       R_FrameData_ReturnToMark();
 }
 
 static void R_ProcessModelTextureSurfaceList(int texturenumsurfaces, const msurface_t **texturesurfacelist, qboolean writedepth, qboolean depthonly, const entity_render_t *queueentity, qboolean prepass)
 {
        CHECKGLERROR
        if (depthonly)
-       {
-               if ((rsurface.texture->currentmaterialflags & (MATERIALFLAG_NODEPTHTEST | MATERIALFLAG_BLENDED | MATERIALFLAG_ALPHATEST)))
-                       return;
-               if (r_waterstate.renderingscene && (rsurface.texture->currentmaterialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFLECTION)))
-                       return;
-               RSurf_SetupDepthAndCulling();
-               RSurf_PrepareVerticesForBatch(false, false, texturenumsurfaces, texturesurfacelist);
-               RSurf_DrawBatch_Simple(texturenumsurfaces, texturesurfacelist);
-       }
+               R_DrawTextureSurfaceList_DepthOnly(texturenumsurfaces, texturesurfacelist);
        else if (prepass)
        {
                if (!rsurface.texture->currentnumlayers)
@@ -11252,34 +10022,7 @@ static void R_ProcessModelTextureSurfaceList(int texturenumsurfaces, const msurf
                else
                        R_DrawModelTextureSurfaceList(texturenumsurfaces, texturesurfacelist, writedepth, prepass);
        }
-       else if (r_showsurfaces.integer && !r_refdef.view.showdebug)
-       {
-               RSurf_SetupDepthAndCulling();
-               GL_AlphaTest(false);
-               R_Mesh_ColorPointer(NULL, 0, 0);
-               R_Mesh_ResetTextureState();
-               R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
-               RSurf_PrepareVerticesForBatch(false, false, texturenumsurfaces, texturesurfacelist);
-               GL_DepthMask(true);
-               GL_BlendFunc(GL_ONE, GL_ZERO);
-               GL_Color(0, 0, 0, 1);
-               GL_DepthTest(writedepth);
-               RSurf_DrawBatch_Simple(texturenumsurfaces, texturesurfacelist);
-       }
-       else if (r_showsurfaces.integer && r_showsurfaces.integer != 3)
-       {
-               RSurf_SetupDepthAndCulling();
-               GL_AlphaTest(false);
-               R_Mesh_ColorPointer(NULL, 0, 0);
-               R_Mesh_ResetTextureState();
-               R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
-               RSurf_PrepareVerticesForBatch(false, false, texturenumsurfaces, texturesurfacelist);
-               GL_DepthMask(true);
-               GL_BlendFunc(GL_ONE, GL_ZERO);
-               GL_DepthTest(true);
-               RSurf_DrawBatch_ShowSurfaces(texturenumsurfaces, texturesurfacelist);
-       }
-       else if (rsurface.texture->currentmaterialflags & MATERIALFLAG_SKY)
+       else if ((rsurface.texture->currentmaterialflags & MATERIALFLAG_SKY) && !r_showsurfaces.integer)
                R_DrawTextureSurfaceList_Sky(texturenumsurfaces, texturesurfacelist);
        else if (!rsurface.texture->currentnumlayers)
                return;
@@ -11301,6 +10044,7 @@ void R_QueueModelSurfaceList(entity_render_t *ent, int numsurfaces, const msurfa
 {
        int i, j;
        texture_t *texture;
+       R_FrameData_SetMark();
        // break the surface list down into batches by texture and use of lightmapping
        for (i = 0;i < numsurfaces;i = j)
        {
@@ -11311,7 +10055,6 @@ void R_QueueModelSurfaceList(entity_render_t *ent, int numsurfaces, const msurfa
                // use skin 1 instead)
                texture = surfacelist[i]->texture;
                rsurface.texture = R_GetCurrentTexture(texture);
-               rsurface.uselightmaptexture = surfacelist[i]->lightmaptexture != NULL && !depthonly && !prepass;
                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
@@ -11319,12 +10062,28 @@ void R_QueueModelSurfaceList(entity_render_t *ent, int numsurfaces, const msurfa
                                ;
                        continue;
                }
-               // simply scan ahead until we find a different texture or lightmap state
-               for (;j < numsurfaces && texture == surfacelist[j]->texture && rsurface.uselightmaptexture == (surfacelist[j]->lightmaptexture != NULL);j++)
-                       ;
+               if(FAKELIGHT_ENABLED || depthonly || prepass)
+               {
+                       rsurface.lightmaptexture = NULL;
+                       rsurface.deluxemaptexture = NULL;
+                       rsurface.uselightmaptexture = false;
+                       // simply scan ahead until we find a different texture or lightmap state
+                       for (;j < numsurfaces && texture == surfacelist[j]->texture;j++)
+                               ;
+               }
+               else
+               {
+                       rsurface.lightmaptexture = surfacelist[i]->lightmaptexture;
+                       rsurface.deluxemaptexture = surfacelist[i]->deluxemaptexture;
+                       rsurface.uselightmaptexture = surfacelist[i]->lightmaptexture != NULL;
+                       // simply scan ahead until we find a different texture or lightmap state
+                       for (;j < numsurfaces && texture == surfacelist[j]->texture && rsurface.lightmaptexture == surfacelist[j]->lightmaptexture;j++)
+                               ;
+               }
                // render the range of surfaces
                R_ProcessModelTextureSurfaceList(j - i, surfacelist + i, writedepth, depthonly, ent, prepass);
        }
+       R_FrameData_ReturnToMark();
 }
 
 float locboxvertex3f[6*4*3] =
@@ -11362,10 +10121,7 @@ void R_DrawLoc_Callback(const entity_render_t *ent, const rtlight_t *rtlight, in
        GL_CullFace(GL_NONE);
        R_EntityMatrix(&identitymatrix);
 
-       R_Mesh_VertexPointer(vertex3f, 0, 0);
-       R_Mesh_ColorPointer(NULL, 0, 0);
-       R_Mesh_ResetTextureState();
-       R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
+//     R_Mesh_ResetTextureState();
 
        i = surfacelist[0];
        GL_Color(((i & 0x0007) >> 0) * (1.0f / 7.0f) * r_refdef.view.colorscale,
@@ -11388,7 +10144,9 @@ void R_DrawLoc_Callback(const entity_render_t *ent, const rtlight_t *rtlight, in
                for (j = 0;j < 3;j++, i++)
                        vertex3f[i] = mins[j] + size[j] * locboxvertex3f[i];
 
-       R_Mesh_Draw(0, 6*4, 0, 6*2, NULL, locboxelements, 0, 0);
+       R_Mesh_PrepareVertices_Generic_Arrays(6*4, vertex3f, NULL, NULL);
+       R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
+       R_Mesh_Draw(0, 6*4, 0, 6*2, NULL, NULL, 0, locboxelements, NULL, 0);
 }
 
 void R_DrawLocs(void)
@@ -11416,7 +10174,6 @@ static void R_DecalSystem_SpawnTriangle(decalsystem_t *decalsystem, const float
        tridecal_t *decal;
        tridecal_t *decals;
        int i;
-       int maxdecals;
 
        // expand or initialize the system
        if (decalsystem->maxdecals <= decalsystem->numdecals)
@@ -11425,7 +10182,7 @@ static void R_DecalSystem_SpawnTriangle(decalsystem_t *decalsystem, const float
                qboolean useshortelements;
                decalsystem->maxdecals = max(16, decalsystem->maxdecals * 2);
                useshortelements = decalsystem->maxdecals * 3 <= 65536;
-               decalsystem->decals = Mem_Alloc(cls.levelmempool, decalsystem->maxdecals * (sizeof(tridecal_t) + sizeof(float[3][3]) + sizeof(float[3][2]) + sizeof(float[3][4]) + sizeof(int[3]) + (useshortelements ? sizeof(unsigned short[3]) : 0)));
+               decalsystem->decals = (tridecal_t *)Mem_Alloc(cls.levelmempool, decalsystem->maxdecals * (sizeof(tridecal_t) + sizeof(float[3][3]) + sizeof(float[3][2]) + sizeof(float[3][4]) + sizeof(int[3]) + (useshortelements ? sizeof(unsigned short[3]) : 0)));
                decalsystem->color4f = (float *)(decalsystem->decals + decalsystem->maxdecals);
                decalsystem->texcoord2f = (float *)(decalsystem->color4f + decalsystem->maxdecals*12);
                decalsystem->vertex3f = (float *)(decalsystem->texcoord2f + decalsystem->maxdecals*6);
@@ -11443,10 +10200,9 @@ static void R_DecalSystem_SpawnTriangle(decalsystem_t *decalsystem, const float
        }
 
        // grab a decal and search for another free slot for the next one
-       maxdecals = decalsystem->maxdecals;
        decals = decalsystem->decals;
        decal = decalsystem->decals + (i = decalsystem->freedecal++);
-       for (i = decalsystem->freedecal;i < decalsystem->numdecals && decals[i].color4ub[0][3];i++)
+       for (i = decalsystem->freedecal;i < decalsystem->numdecals && decals[i].color4f[0][3];i++)
                ;
        decalsystem->freedecal = i;
        if (decalsystem->numdecals <= i)
@@ -11457,18 +10213,18 @@ static void R_DecalSystem_SpawnTriangle(decalsystem_t *decalsystem, const float
        decal->triangleindex = triangleindex;
        decal->surfaceindex = surfaceindex;
        decal->decalsequence = decalsequence;
-       decal->color4ub[0][0] = (unsigned char)(c0[0]*255.0f);
-       decal->color4ub[0][1] = (unsigned char)(c0[1]*255.0f);
-       decal->color4ub[0][2] = (unsigned char)(c0[2]*255.0f);
-       decal->color4ub[0][3] = 255;
-       decal->color4ub[1][0] = (unsigned char)(c1[0]*255.0f);
-       decal->color4ub[1][1] = (unsigned char)(c1[1]*255.0f);
-       decal->color4ub[1][2] = (unsigned char)(c1[2]*255.0f);
-       decal->color4ub[1][3] = 255;
-       decal->color4ub[2][0] = (unsigned char)(c2[0]*255.0f);
-       decal->color4ub[2][1] = (unsigned char)(c2[1]*255.0f);
-       decal->color4ub[2][2] = (unsigned char)(c2[2]*255.0f);
-       decal->color4ub[2][3] = 255;
+       decal->color4f[0][0] = c0[0];
+       decal->color4f[0][1] = c0[1];
+       decal->color4f[0][2] = c0[2];
+       decal->color4f[0][3] = 1;
+       decal->color4f[1][0] = c1[0];
+       decal->color4f[1][1] = c1[1];
+       decal->color4f[1][2] = c1[2];
+       decal->color4f[1][3] = 1;
+       decal->color4f[2][0] = c2[0];
+       decal->color4f[2][1] = c2[1];
+       decal->color4f[2][2] = c2[2];
+       decal->color4f[2][3] = 1;
        decal->vertex3f[0][0] = v0[0];
        decal->vertex3f[0][1] = v0[1];
        decal->vertex3f[0][2] = v0[2];
@@ -11489,43 +10245,115 @@ static void R_DecalSystem_SpawnTriangle(decalsystem_t *decalsystem, const float
 extern cvar_t cl_decals_bias;
 extern cvar_t cl_decals_models;
 extern cvar_t cl_decals_newsystem_intensitymultiplier;
+// baseparms, parms, temps
+static void R_DecalSystem_SplatTriangle(decalsystem_t *decalsystem, float r, float g, float b, float a, float s1, float t1, float s2, float t2, int decalsequence, qboolean dynamic, float (*planes)[4], matrix4x4_t *projection, int triangleindex, int surfaceindex)
+{
+       int cornerindex;
+       int index;
+       float v[9][3];
+       const float *vertex3f;
+       const float *normal3f;
+       int numpoints;
+       float points[2][9][3];
+       float temp[3];
+       float tc[9][2];
+       float f;
+       float c[9][4];
+       const int *e;
+
+       e = rsurface.modelelement3i + 3*triangleindex;
+
+       vertex3f = rsurface.modelvertex3f;
+       normal3f = rsurface.modelnormal3f;
+
+       for (cornerindex = 0;cornerindex < 3;cornerindex++)
+       {
+               index = 3*e[cornerindex];
+               VectorMA(vertex3f + index, cl_decals_bias.value, normal3f + index, v[cornerindex]);
+       }
+       // cull backfaces
+       //TriangleNormal(v[0], v[1], v[2], normal);
+       //if (DotProduct(normal, localnormal) < 0.0f)
+       //      continue;
+       // clip by each of the box planes formed from the projection matrix
+       // if anything survives, we emit the decal
+       numpoints = PolygonF_Clip(3        , v[0]        , planes[0][0], planes[0][1], planes[0][2], planes[0][3], 1.0f/64.0f, sizeof(points[0])/sizeof(points[0][0]), points[1][0]);
+       if (numpoints < 3)
+               return;
+       numpoints = PolygonF_Clip(numpoints, points[1][0], planes[1][0], planes[1][1], planes[1][2], planes[1][3], 1.0f/64.0f, sizeof(points[0])/sizeof(points[0][0]), points[0][0]);
+       if (numpoints < 3)
+               return;
+       numpoints = PolygonF_Clip(numpoints, points[0][0], planes[2][0], planes[2][1], planes[2][2], planes[2][3], 1.0f/64.0f, sizeof(points[0])/sizeof(points[0][0]), points[1][0]);
+       if (numpoints < 3)
+               return;
+       numpoints = PolygonF_Clip(numpoints, points[1][0], planes[3][0], planes[3][1], planes[3][2], planes[3][3], 1.0f/64.0f, sizeof(points[0])/sizeof(points[0][0]), points[0][0]);
+       if (numpoints < 3)
+               return;
+       numpoints = PolygonF_Clip(numpoints, points[0][0], planes[4][0], planes[4][1], planes[4][2], planes[4][3], 1.0f/64.0f, sizeof(points[0])/sizeof(points[0][0]), points[1][0]);
+       if (numpoints < 3)
+               return;
+       numpoints = PolygonF_Clip(numpoints, points[1][0], planes[5][0], planes[5][1], planes[5][2], planes[5][3], 1.0f/64.0f, sizeof(points[0])/sizeof(points[0][0]), v[0]);
+       if (numpoints < 3)
+               return;
+       // some part of the triangle survived, so we have to accept it...
+       if (dynamic)
+       {
+               // dynamic always uses the original triangle
+               numpoints = 3;
+               for (cornerindex = 0;cornerindex < 3;cornerindex++)
+               {
+                       index = 3*e[cornerindex];
+                       VectorCopy(vertex3f + index, v[cornerindex]);
+               }
+       }
+       for (cornerindex = 0;cornerindex < numpoints;cornerindex++)
+       {
+               // convert vertex positions to texcoords
+               Matrix4x4_Transform(projection, v[cornerindex], temp);
+               tc[cornerindex][0] = (temp[1]+1.0f)*0.5f * (s2-s1) + s1;
+               tc[cornerindex][1] = (temp[2]+1.0f)*0.5f * (t2-t1) + t1;
+               // calculate distance fade from the projection origin
+               f = a * (1.0f-fabs(temp[0])) * cl_decals_newsystem_intensitymultiplier.value;
+               f = bound(0.0f, f, 1.0f);
+               c[cornerindex][0] = r * f;
+               c[cornerindex][1] = g * f;
+               c[cornerindex][2] = b * f;
+               c[cornerindex][3] = 1.0f;
+               //VectorMA(v[cornerindex], cl_decals_bias.value, localnormal, v[cornerindex]);
+       }
+       if (dynamic)
+               R_DecalSystem_SpawnTriangle(decalsystem, v[0], v[1], v[2], tc[0], tc[1], tc[2], c[0], c[1], c[2], triangleindex, surfaceindex, decalsequence);
+       else
+               for (cornerindex = 0;cornerindex < numpoints-2;cornerindex++)
+                       R_DecalSystem_SpawnTriangle(decalsystem, v[0], v[cornerindex+1], v[cornerindex+2], tc[0], tc[cornerindex+1], tc[cornerindex+2], c[0], c[cornerindex+1], c[cornerindex+2], -1, surfaceindex, decalsequence);
+}
 static void R_DecalSystem_SplatEntity(entity_render_t *ent, const vec3_t worldorigin, const vec3_t worldnormal, float r, float g, float b, float a, float s1, float t1, float s2, float t2, float worldsize, int decalsequence)
 {
        matrix4x4_t projection;
        decalsystem_t *decalsystem;
        qboolean dynamic;
        dp_model_t *model;
-       const float *vertex3f;
        const msurface_t *surface;
        const msurface_t *surfaces;
        const int *surfacelist;
        const texture_t *texture;
-       int numvertices;
        int numtriangles;
        int numsurfacelist;
        int surfacelistindex;
        int surfaceindex;
        int triangleindex;
-       int decalsurfaceindex;
-       int cornerindex;
-       int index;
-       int numpoints;
-       const int *e;
        float localorigin[3];
        float localnormal[3];
        float localmins[3];
        float localmaxs[3];
        float localsize;
-       float ilocalsize;
-       float v[9][3];
-       float tc[9][2];
-       float c[9][4];
        //float normal[3];
        float planes[6][4];
-       float f;
-       float points[2][9][3];
        float angles[3];
-       float temp[3];
+       bih_t *bih;
+       int bih_triangles_count;
+       int bih_triangles[256];
+       int bih_surfaces[256];
 
        decalsystem = &ent->decalsystem;
        model = ent->model;
@@ -11535,7 +10363,7 @@ static void R_DecalSystem_SplatEntity(entity_render_t *ent, const vec3_t worldor
                return;
        }
 
-       if (!model->brush.data_nodes && !cl_decals_models.integer)
+       if (!model->brush.data_leafs && !cl_decals_models.integer)
        {
                if (decalsystem->model)
                        R_DecalSystem_Reset(decalsystem);
@@ -11546,13 +10374,12 @@ static void R_DecalSystem_SplatEntity(entity_render_t *ent, const vec3_t worldor
                R_DecalSystem_Reset(decalsystem);
        decalsystem->model = model;
 
-       RSurf_ActiveModelEntity(ent, false, false, false);
+       RSurf_ActiveModelEntity(ent, true, false, false);
 
        Matrix4x4_Transform(&rsurface.inversematrix, worldorigin, localorigin);
        Matrix4x4_Transform3x3(&rsurface.inversematrix, worldnormal, localnormal);
        VectorNormalize(localnormal);
        localsize = worldsize*rsurface.inversematrixscale;
-       ilocalsize = 1.0f / localsize;
        localmins[0] = localorigin[0] - localsize;
        localmins[1] = localorigin[1] - localsize;
        localmins[2] = localorigin[2] - localsize;
@@ -11605,88 +10432,57 @@ static void R_DecalSystem_SplatEntity(entity_render_t *ent, const vec3_t worldor
 #endif
 
        dynamic = model->surfmesh.isanimated;
-       vertex3f = rsurface.modelvertex3f;
        numsurfacelist = model->nummodelsurfaces;
        surfacelist = model->sortedmodelsurfaces;
        surfaces = model->data_surfaces;
-       for (surfacelistindex = 0;surfacelistindex < numsurfacelist;surfacelistindex++)
+
+       bih = NULL;
+       bih_triangles_count = -1;
+       if(!dynamic)
        {
-               surfaceindex = surfacelist[surfacelistindex];
-               surface = surfaces + surfaceindex;
-               // check cull box first because it rejects more than any other check
-               if (!dynamic && !BoxesOverlap(surface->mins, surface->maxs, localmins, localmaxs))
-                       continue;
-               // skip transparent surfaces
-               texture = surface->texture;
-               if (texture->currentmaterialflags & (MATERIALFLAG_BLENDED | MATERIALFLAG_NODEPTHTEST | MATERIALFLAG_SKY | MATERIALFLAG_SHORTDEPTHRANGE | MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION))
-                       continue;
-               if (texture->surfaceflags & Q3SURFACEFLAG_NOMARKS)
-                       continue;
-               decalsurfaceindex = ent == r_refdef.scene.worldentity ? surfaceindex : -1;
-               numvertices = surface->num_vertices;
-               numtriangles = surface->num_triangles;
-               for (triangleindex = 0, e = model->surfmesh.data_element3i + 3*surface->num_firsttriangle;triangleindex < numtriangles;triangleindex++, e += 3)
+               if(model->render_bih.numleafs)
+                       bih = &model->render_bih;
+               else if(model->collision_bih.numleafs)
+                       bih = &model->collision_bih;
+       }
+       if(bih)
+               bih_triangles_count = BIH_GetTriangleListForBox(bih, sizeof(bih_triangles) / sizeof(*bih_triangles), bih_triangles, bih_surfaces, localmins, localmaxs);
+       if(bih_triangles_count == 0)
+               return;
+       if(bih_triangles_count > (int) (sizeof(bih_triangles) / sizeof(*bih_triangles))) // hit too many, likely bad anyway
+               return;
+       if(bih_triangles_count > 0)
+       {
+               for (triangleindex = 0; triangleindex < bih_triangles_count; ++triangleindex)
                {
-                       for (cornerindex = 0;cornerindex < 3;cornerindex++)
-                       {
-                               index = 3*e[cornerindex];
-                               VectorCopy(vertex3f + index, v[cornerindex]);
-                       }
-                       // cull backfaces
-                       //TriangleNormal(v[0], v[1], v[2], normal);
-                       //if (DotProduct(normal, localnormal) < 0.0f)
-                       //      continue;
-                       // clip by each of the box planes formed from the projection matrix
-                       // if anything survives, we emit the decal
-                       numpoints = PolygonF_Clip(3        , v[0]        , planes[0][0], planes[0][1], planes[0][2], planes[0][3], 1.0f/64.0f, sizeof(points[0])/sizeof(points[0][0]), points[1][0]);
-                       if (numpoints < 3)
-                               continue;
-                       numpoints = PolygonF_Clip(numpoints, points[1][0], planes[1][0], planes[1][1], planes[1][2], planes[1][3], 1.0f/64.0f, sizeof(points[0])/sizeof(points[0][0]), points[0][0]);
-                       if (numpoints < 3)
+                       surfaceindex = bih_surfaces[triangleindex];
+                       surface = surfaces + surfaceindex;
+                       texture = surface->texture;
+                       if (texture->currentmaterialflags & (MATERIALFLAG_BLENDED | MATERIALFLAG_NODEPTHTEST | MATERIALFLAG_SKY | MATERIALFLAG_SHORTDEPTHRANGE | MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION))
                                continue;
-                       numpoints = PolygonF_Clip(numpoints, points[0][0], planes[2][0], planes[2][1], planes[2][2], planes[2][3], 1.0f/64.0f, sizeof(points[0])/sizeof(points[0][0]), points[1][0]);
-                       if (numpoints < 3)
+                       if (texture->surfaceflags & Q3SURFACEFLAG_NOMARKS)
                                continue;
-                       numpoints = PolygonF_Clip(numpoints, points[1][0], planes[3][0], planes[3][1], planes[3][2], planes[3][3], 1.0f/64.0f, sizeof(points[0])/sizeof(points[0][0]), points[0][0]);
-                       if (numpoints < 3)
+                       R_DecalSystem_SplatTriangle(decalsystem, r, g, b, a, s1, t1, s2, t2, decalsequence, dynamic, planes, &projection, bih_triangles[triangleindex], surfaceindex);
+               }
+       }
+       else
+       {
+               for (surfacelistindex = 0;surfacelistindex < numsurfacelist;surfacelistindex++)
+               {
+                       surfaceindex = surfacelist[surfacelistindex];
+                       surface = surfaces + surfaceindex;
+                       // check cull box first because it rejects more than any other check
+                       if (!dynamic && !BoxesOverlap(surface->mins, surface->maxs, localmins, localmaxs))
                                continue;
-                       numpoints = PolygonF_Clip(numpoints, points[0][0], planes[4][0], planes[4][1], planes[4][2], planes[4][3], 1.0f/64.0f, sizeof(points[0])/sizeof(points[0][0]), points[1][0]);
-                       if (numpoints < 3)
+                       // skip transparent surfaces
+                       texture = surface->texture;
+                       if (texture->currentmaterialflags & (MATERIALFLAG_BLENDED | MATERIALFLAG_NODEPTHTEST | MATERIALFLAG_SKY | MATERIALFLAG_SHORTDEPTHRANGE | MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION))
                                continue;
-                       numpoints = PolygonF_Clip(numpoints, points[1][0], planes[5][0], planes[5][1], planes[5][2], planes[5][3], 1.0f/64.0f, sizeof(points[0])/sizeof(points[0][0]), v[0]);
-                       if (numpoints < 3)
+                       if (texture->surfaceflags & Q3SURFACEFLAG_NOMARKS)
                                continue;
-                       // some part of the triangle survived, so we have to accept it...
-                       if (dynamic)
-                       {
-                               // dynamic always uses the original triangle
-                               numpoints = 3;
-                               for (cornerindex = 0;cornerindex < 3;cornerindex++)
-                               {
-                                       index = 3*e[cornerindex];
-                                       VectorCopy(vertex3f + index, v[cornerindex]);
-                               }
-                       }
-                       for (cornerindex = 0;cornerindex < numpoints;cornerindex++)
-                       {
-                               // convert vertex positions to texcoords
-                               Matrix4x4_Transform(&projection, v[cornerindex], temp);
-                               tc[cornerindex][0] = (temp[1]+1.0f)*0.5f * (s2-s1) + s1;
-                               tc[cornerindex][1] = (temp[2]+1.0f)*0.5f * (t2-t1) + t1;
-                               // calculate distance fade from the projection origin
-                               f = a * (1.0f-fabs(temp[0])) * cl_decals_newsystem_intensitymultiplier.value;
-                               f = bound(0.0f, f, 1.0f);
-                               c[cornerindex][0] = r * f;
-                               c[cornerindex][1] = g * f;
-                               c[cornerindex][2] = b * f;
-                               c[cornerindex][3] = 1.0f;
-                               //VectorMA(v[cornerindex], cl_decals_bias.value, localnormal, v[cornerindex]);
-                       }
-                       if (dynamic)
-                               R_DecalSystem_SpawnTriangle(decalsystem, v[0], v[1], v[2], tc[0], tc[1], tc[2], c[0], c[1], c[2], triangleindex+surface->num_firsttriangle, surfaceindex, decalsequence);
-                       else
-                               for (cornerindex = 0;cornerindex < numpoints-2;cornerindex++)
-                                       R_DecalSystem_SpawnTriangle(decalsystem, v[0], v[cornerindex+1], v[cornerindex+2], tc[0], tc[cornerindex+1], tc[cornerindex+2], c[0], c[cornerindex+1], c[cornerindex+2], -1, surfaceindex, decalsequence);
+                       numtriangles = surface->num_triangles;
+                       for (triangleindex = 0; triangleindex < numtriangles; triangleindex++)
+                               R_DecalSystem_SplatTriangle(decalsystem, r, g, b, a, s1, t1, s2, t2, decalsequence, dynamic, planes, &projection, triangleindex + surface->num_firsttriangle, surfaceindex);
                }
        }
 }
@@ -11797,7 +10593,7 @@ static void R_DrawModelDecals_FadeEntity(entity_render_t *ent)
 
        for (i = 0, decal = decalsystem->decals;i < numdecals;i++, decal++)
        {
-               if (decal->color4ub[0][3])
+               if (decal->color4f[0][3])
                {
                        decal->lived += frametime;
                        if (killsequence - decal->decalsequence > 0 || decal->lived >= lifetime)
@@ -11809,13 +10605,13 @@ static void R_DrawModelDecals_FadeEntity(entity_render_t *ent)
                }
        }
        decal = decalsystem->decals;
-       while (numdecals > 0 && !decal[numdecals-1].color4ub[0][3])
+       while (numdecals > 0 && !decal[numdecals-1].color4f[0][3])
                numdecals--;
 
        // collapse the array by shuffling the tail decals into the gaps
        for (;;)
        {
-               while (decalsystem->freedecal < numdecals && decal[decalsystem->freedecal].color4ub[0][3])
+               while (decalsystem->freedecal < numdecals && decal[decalsystem->freedecal].color4f[0][3])
                        decalsystem->freedecal++;
                if (decalsystem->freedecal == numdecals)
                        break;
@@ -11838,7 +10634,6 @@ static void R_DrawModelDecals_Entity(entity_render_t *ent)
        decalsystem_t *decalsystem = &ent->decalsystem;
        int numdecals;
        tridecal_t *decal;
-       float fadedelay;
        float faderate;
        float alpha;
        float *v3f;
@@ -11872,7 +10667,6 @@ static void R_DrawModelDecals_Entity(entity_render_t *ent)
        decalsystem->lastupdatetime = cl.time;
        decal = decalsystem->decals;
 
-       fadedelay = cl_decals_time.value;
        faderate = 1.0f / max(0.001f, cl_decals_fadetime.value);
 
        // update vertex positions for animated models
@@ -11881,7 +10675,7 @@ static void R_DrawModelDecals_Entity(entity_render_t *ent)
        t2f = decalsystem->texcoord2f;
        for (i = 0, decal = decalsystem->decals;i < numdecals;i++, decal++)
        {
-               if (!decal->color4ub[0][3])
+               if (!decal->color4f[0][3])
                        continue;
 
                if (surfacevisible && !surfacevisible[decal->surfaceindex])
@@ -11889,24 +10683,21 @@ static void R_DrawModelDecals_Entity(entity_render_t *ent)
 
                // update color values for fading decals
                if (decal->lived >= cl_decals_time.value)
-               {
                        alpha = 1 - faderate * (decal->lived - cl_decals_time.value);
-                       alpha *= (1.0f/255.0f);
-               }
                else
-                       alpha = 1.0f/255.0f;
+                       alpha = 1.0f;
 
-               c4f[ 0] = decal->color4ub[0][0] * alpha;
-               c4f[ 1] = decal->color4ub[0][1] * alpha;
-               c4f[ 2] = decal->color4ub[0][2] * alpha;
+               c4f[ 0] = decal->color4f[0][0] * alpha;
+               c4f[ 1] = decal->color4f[0][1] * alpha;
+               c4f[ 2] = decal->color4f[0][2] * alpha;
                c4f[ 3] = 1;
-               c4f[ 4] = decal->color4ub[1][0] * alpha;
-               c4f[ 5] = decal->color4ub[1][1] * alpha;
-               c4f[ 6] = decal->color4ub[1][2] * alpha;
+               c4f[ 4] = decal->color4f[1][0] * alpha;
+               c4f[ 5] = decal->color4f[1][1] * alpha;
+               c4f[ 6] = decal->color4f[1][2] * alpha;
                c4f[ 7] = 1;
-               c4f[ 8] = decal->color4ub[2][0] * alpha;
-               c4f[ 9] = decal->color4ub[2][1] * alpha;
-               c4f[10] = decal->color4ub[2][2] * alpha;
+               c4f[ 8] = decal->color4f[2][0] * alpha;
+               c4f[ 9] = decal->color4f[2][1] * alpha;
+               c4f[10] = decal->color4f[2][2] * alpha;
                c4f[11] = 1;
 
                t2f[0] = decal->texcoord2f[0][0];
@@ -11917,12 +10708,12 @@ static void R_DrawModelDecals_Entity(entity_render_t *ent)
                t2f[5] = decal->texcoord2f[2][1];
 
                // update vertex positions for animated models
-               if (decal->triangleindex >= 0 && decal->triangleindex < rsurface.modelnum_triangles)
+               if (decal->triangleindex >= 0 && decal->triangleindex < rsurface.modelnumtriangles)
                {
                        e = rsurface.modelelement3i + 3*decal->triangleindex;
-                       VectorCopy(rsurface.vertex3f + 3*e[0], v3f);
-                       VectorCopy(rsurface.vertex3f + 3*e[1], v3f + 3);
-                       VectorCopy(rsurface.vertex3f + 3*e[2], v3f + 6);
+                       VectorCopy(rsurface.modelvertex3f + 3*e[0], v3f);
+                       VectorCopy(rsurface.modelvertex3f + 3*e[1], v3f + 3);
+                       VectorCopy(rsurface.modelvertex3f + 3*e[2], v3f + 6);
                }
                else
                {
@@ -11931,6 +10722,16 @@ static void R_DrawModelDecals_Entity(entity_render_t *ent)
                        VectorCopy(decal->vertex3f[2], v3f + 6);
                }
 
+               if (r_refdef.fogenabled)
+               {
+                       alpha = RSurf_FogVertex(v3f);
+                       VectorScale(c4f, alpha, c4f);
+                       alpha = RSurf_FogVertex(v3f + 3);
+                       VectorScale(c4f + 4, alpha, c4f + 4);
+                       alpha = RSurf_FogVertex(v3f + 6);
+                       VectorScale(c4f + 8, alpha, c4f + 8);
+               }
+
                v3f += 9;
                c4f += 12;
                t2f += 6;
@@ -11941,32 +10742,11 @@ static void R_DrawModelDecals_Entity(entity_render_t *ent)
        {
                r_refdef.stats.drawndecals += numtris;
 
-               if (r_refdef.fogenabled)
-               {
-                       switch(vid.renderpath)
-                       {
-                       case RENDERPATH_GL20:
-                       case RENDERPATH_CGGL:
-                       case RENDERPATH_GL13:
-                       case RENDERPATH_GL11:
-                               for (i = 0, v3f = decalsystem->vertex3f, c4f = decalsystem->color4f;i < numtris*3;i++, v3f += 3, c4f += 4)
-                               {
-                                       alpha = RSurf_FogVertex(v3f);
-                                       c4f[0] *= alpha;
-                                       c4f[1] *= alpha;
-                                       c4f[2] *= alpha;
-                               }
-                               break;
-                       }
-               }
-
                // 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);
-               R_Mesh_ResetTextureState();
-               R_Mesh_VertexPointer(decalsystem->vertex3f, 0, 0);
-               R_Mesh_TexCoordPointer(0, 2, decalsystem->texcoord2f, 0, 0);
-               R_Mesh_ColorPointer(decalsystem->color4f, 0, 0);
+//             R_Mesh_ResetTextureState();
+               R_Mesh_PrepareVertices_Generic_Arrays(numtris * 3, decalsystem->vertex3f, decalsystem->color4f, decalsystem->texcoord2f);
                GL_DepthMask(false);
                GL_DepthRange(0, 1);
                GL_PolygonOffset(rsurface.basepolygonfactor + r_polygonoffset_decals_factor.value, rsurface.basepolygonoffset + r_polygonoffset_decals_offset.value);
@@ -11974,7 +10754,7 @@ static void R_DrawModelDecals_Entity(entity_render_t *ent)
                GL_CullFace(GL_NONE);
                GL_BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
                R_SetupShader_Generic(decalskinframe->base, NULL, GL_MODULATE, 1);
-               R_Mesh_Draw(0, numtris * 3, 0, numtris, decalsystem->element3i, decalsystem->element3s, 0, 0);
+               R_Mesh_Draw(0, numtris * 3, 0, numtris, decalsystem->element3i, NULL, 0, decalsystem->element3s, NULL, 0);
        }
 }
 
@@ -12014,52 +10794,98 @@ static void R_DrawModelDecals(void)
        }
 }
 
+extern cvar_t mod_collision_bih;
 void R_DrawDebugModel(void)
 {
        entity_render_t *ent = rsurface.entity;
        int i, j, k, l, flagsmask;
-       const int *elements;
-       q3mbrush_t *brush;
        const msurface_t *surface;
        dp_model_t *model = ent->model;
        vec3_t v;
 
+       switch(vid.renderpath)
+       {
+       case RENDERPATH_GL11:
+       case RENDERPATH_GL13:
+       case RENDERPATH_GL20:
+               break;
+       case RENDERPATH_D3D9:
+               //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               return;
+       case RENDERPATH_D3D10:
+               Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               return;
+       case RENDERPATH_D3D11:
+               Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               return;
+       case RENDERPATH_SOFT:
+               //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               return;
+       case RENDERPATH_GLES2:
+               //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
+               return;
+       }
+
        flagsmask = MATERIALFLAG_SKY | MATERIALFLAG_WALL;
 
-       R_Mesh_ColorPointer(NULL, 0, 0);
-       R_Mesh_ResetTextureState();
+//     R_Mesh_ResetTextureState();
        R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
        GL_DepthRange(0, 1);
        GL_DepthTest(!r_showdisabledepthtest.integer);
        GL_DepthMask(false);
        GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
 
-       if (r_showcollisionbrushes.value > 0 && model->brush.num_brushes)
+       if (r_showcollisionbrushes.value > 0 && model->collision_bih.numleafs)
        {
+               int triangleindex;
+               int bihleafindex;
+               qboolean cullbox = ent == r_refdef.scene.worldentity;
+               const q3mbrush_t *brush;
+               const bih_t *bih = &model->collision_bih;
+               const bih_leaf_t *bihleaf;
+               float vertex3f[3][3];
                GL_PolygonOffset(r_refdef.polygonfactor + r_showcollisionbrushes_polygonfactor.value, r_refdef.polygonoffset + r_showcollisionbrushes_polygonoffset.value);
-               for (i = 0, brush = model->brush.data_brushes + model->firstmodelbrush;i < model->nummodelbrushes;i++, brush++)
+               cullbox = false;
+               for (bihleafindex = 0, bihleaf = bih->leafs;bihleafindex < bih->numleafs;bihleafindex++, bihleaf++)
                {
-                       if (brush->colbrushf && brush->colbrushf->numtriangles)
-                       {
-                               R_Mesh_VertexPointer(brush->colbrushf->points->v, 0, 0);
-                               GL_Color((i & 31) * (1.0f / 32.0f) * r_refdef.view.colorscale, ((i >> 5) & 31) * (1.0f / 32.0f) * r_refdef.view.colorscale, ((i >> 10) & 31) * (1.0f / 32.0f) * r_refdef.view.colorscale, r_showcollisionbrushes.value);
-                               R_Mesh_Draw(0, brush->colbrushf->numpoints, 0, brush->colbrushf->numtriangles, brush->colbrushf->elements, NULL, 0, 0);
-                       }
-               }
-               for (i = 0, surface = model->data_surfaces + model->firstmodelsurface;i < model->nummodelsurfaces;i++, surface++)
-               {
-                       if (surface->num_collisiontriangles)
+                       if (cullbox && R_CullBox(bihleaf->mins, bihleaf->maxs))
+                               continue;
+                       switch (bihleaf->type)
                        {
-                               R_Mesh_VertexPointer(surface->data_collisionvertex3f, 0, 0);
-                               GL_Color((i & 31) * (1.0f / 32.0f) * r_refdef.view.colorscale, ((i >> 5) & 31) * (1.0f / 32.0f) * r_refdef.view.colorscale, ((i >> 10) & 31) * (1.0f / 32.0f) * r_refdef.view.colorscale, r_showcollisionbrushes.value);
-                               R_Mesh_Draw(0, surface->num_collisionvertices, 0, surface->num_collisiontriangles, surface->data_collisionelement3i, NULL, 0, 0);
+                       case BIH_BRUSH:
+                               brush = model->brush.data_brushes + bihleaf->itemindex;
+                               if (brush->colbrushf && brush->colbrushf->numtriangles)
+                               {
+                                       GL_Color((bihleafindex & 31) * (1.0f / 32.0f) * r_refdef.view.colorscale, ((bihleafindex >> 5) & 31) * (1.0f / 32.0f) * r_refdef.view.colorscale, ((bihleafindex >> 10) & 31) * (1.0f / 32.0f) * r_refdef.view.colorscale, r_showcollisionbrushes.value);
+                                       R_Mesh_PrepareVertices_Generic_Arrays(brush->colbrushf->numpoints, brush->colbrushf->points->v, NULL, NULL);
+                                       R_Mesh_Draw(0, brush->colbrushf->numpoints, 0, brush->colbrushf->numtriangles, brush->colbrushf->elements, NULL, 0, NULL, NULL, 0);
+                               }
+                               break;
+                       case BIH_COLLISIONTRIANGLE:
+                               triangleindex = bihleaf->itemindex;
+                               VectorCopy(model->brush.data_collisionvertex3f + 3*model->brush.data_collisionelement3i[triangleindex*3+0], vertex3f[0]);
+                               VectorCopy(model->brush.data_collisionvertex3f + 3*model->brush.data_collisionelement3i[triangleindex*3+1], vertex3f[1]);
+                               VectorCopy(model->brush.data_collisionvertex3f + 3*model->brush.data_collisionelement3i[triangleindex*3+2], vertex3f[2]);
+                               GL_Color((bihleafindex & 31) * (1.0f / 32.0f) * r_refdef.view.colorscale, ((bihleafindex >> 5) & 31) * (1.0f / 32.0f) * r_refdef.view.colorscale, ((bihleafindex >> 10) & 31) * (1.0f / 32.0f) * r_refdef.view.colorscale, r_showcollisionbrushes.value);
+                               R_Mesh_PrepareVertices_Generic_Arrays(3, vertex3f[0], NULL, NULL);
+                               R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
+                               break;
+                       case BIH_RENDERTRIANGLE:
+                               triangleindex = bihleaf->itemindex;
+                               VectorCopy(model->surfmesh.data_vertex3f + 3*model->surfmesh.data_element3i[triangleindex*3+0], vertex3f[0]);
+                               VectorCopy(model->surfmesh.data_vertex3f + 3*model->surfmesh.data_element3i[triangleindex*3+1], vertex3f[1]);
+                               VectorCopy(model->surfmesh.data_vertex3f + 3*model->surfmesh.data_element3i[triangleindex*3+2], vertex3f[2]);
+                               GL_Color((bihleafindex & 31) * (1.0f / 32.0f) * r_refdef.view.colorscale, ((bihleafindex >> 5) & 31) * (1.0f / 32.0f) * r_refdef.view.colorscale, ((bihleafindex >> 10) & 31) * (1.0f / 32.0f) * r_refdef.view.colorscale, r_showcollisionbrushes.value);
+                               R_Mesh_PrepareVertices_Generic_Arrays(3, vertex3f[0], NULL, NULL);
+                               R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
+                               break;
                        }
                }
        }
 
        GL_PolygonOffset(r_refdef.polygonfactor, r_refdef.polygonoffset);
 
-       if (r_showtris.integer || r_shownormals.integer)
+       if (r_showtris.integer || (r_shownormals.value != 0))
        {
                if (r_showdisabledepthtest.integer)
                {
@@ -12078,7 +10904,7 @@ void R_DrawDebugModel(void)
                        rsurface.texture = R_GetCurrentTexture(surface->texture);
                        if ((rsurface.texture->currentmaterialflags & flagsmask) && surface->num_triangles)
                        {
-                               RSurf_PrepareVerticesForBatch(true, true, 1, &surface);
+                               RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX | BATCHNEED_ARRAY_NORMAL | BATCHNEED_ARRAY_VECTOR | BATCHNEED_NOGAPS, 1, &surface);
                                if (r_showtris.value > 0)
                                {
                                        if (!rsurface.texture->currentlayers->depthmask)
@@ -12087,65 +10913,61 @@ void R_DrawDebugModel(void)
                                                GL_Color(r_refdef.view.colorscale, r_refdef.view.colorscale, r_refdef.view.colorscale, r_showtris.value);
                                        else
                                                GL_Color(0, r_refdef.view.colorscale, 0, r_showtris.value);
-                                       elements = (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);
+                                       R_Mesh_PrepareVertices_Generic_Arrays(rsurface.batchnumvertices, rsurface.batchvertex3f, NULL, NULL);
                                        qglPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
-                                       //R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_firsttriangle, surface->num_triangles, model->surfmesh.data_element3i, NULL, 0, 0);
-                                       R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_firsttriangle, surface->num_triangles, rsurface.modelelement3i, rsurface.modelelement3s, rsurface.modelelement3i_bufferobject, rsurface.modelelement3s_bufferobject);
+                                       RSurf_DrawBatch();
                                        qglPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
                                        CHECKGLERROR
                                }
                                if (r_shownormals.value < 0)
                                {
                                        qglBegin(GL_LINES);
-                                       for (k = 0, l = surface->num_firstvertex;k < surface->num_vertices;k++, l++)
+                                       for (k = 0, l = rsurface.batchfirstvertex;k < rsurface.batchnumvertices;k++, l++)
                                        {
-                                               VectorCopy(rsurface.vertex3f + l * 3, v);
-                                               GL_Color(r_refdef.view.colorscale, 0, 0, 1);
+                                               VectorCopy(rsurface.batchvertex3f + l * 3, v);
+                                               GL_Color(0, 0, r_refdef.view.colorscale, 1);
                                                qglVertex3f(v[0], v[1], v[2]);
-                                               VectorMA(v, -r_shownormals.value, rsurface.svector3f + l * 3, v);
-                                               GL_Color(r_refdef.view.colorscale, 1, 1, 1);
+                                               VectorMA(v, -r_shownormals.value, rsurface.batchnormal3f + l * 3, v);
+                                               GL_Color(r_refdef.view.colorscale, r_refdef.view.colorscale, r_refdef.view.colorscale, 1);
                                                qglVertex3f(v[0], v[1], v[2]);
                                        }
                                        qglEnd();
                                        CHECKGLERROR
                                }
-                               if (r_shownormals.value > 0)
+                               if (r_shownormals.value > 0 && rsurface.batchsvector3f)
                                {
                                        qglBegin(GL_LINES);
-                                       for (k = 0, l = surface->num_firstvertex;k < surface->num_vertices;k++, l++)
+                                       for (k = 0, l = rsurface.batchfirstvertex;k < rsurface.batchnumvertices;k++, l++)
                                        {
-                                               VectorCopy(rsurface.vertex3f + l * 3, v);
+                                               VectorCopy(rsurface.batchvertex3f + l * 3, v);
                                                GL_Color(r_refdef.view.colorscale, 0, 0, 1);
                                                qglVertex3f(v[0], v[1], v[2]);
-                                               VectorMA(v, r_shownormals.value, rsurface.svector3f + l * 3, v);
-                                               GL_Color(r_refdef.view.colorscale, 1, 1, 1);
+                                               VectorMA(v, r_shownormals.value, rsurface.batchsvector3f + l * 3, v);
+                                               GL_Color(r_refdef.view.colorscale, r_refdef.view.colorscale, r_refdef.view.colorscale, 1);
                                                qglVertex3f(v[0], v[1], v[2]);
                                        }
                                        qglEnd();
                                        CHECKGLERROR
                                        qglBegin(GL_LINES);
-                                       for (k = 0, l = surface->num_firstvertex;k < surface->num_vertices;k++, l++)
+                                       for (k = 0, l = rsurface.batchfirstvertex;k < rsurface.batchnumvertices;k++, l++)
                                        {
-                                               VectorCopy(rsurface.vertex3f + l * 3, v);
+                                               VectorCopy(rsurface.batchvertex3f + l * 3, v);
                                                GL_Color(0, r_refdef.view.colorscale, 0, 1);
                                                qglVertex3f(v[0], v[1], v[2]);
-                                               VectorMA(v, r_shownormals.value, rsurface.tvector3f + l * 3, v);
-                                               GL_Color(r_refdef.view.colorscale, 1, 1, 1);
+                                               VectorMA(v, r_shownormals.value, rsurface.batchtvector3f + l * 3, v);
+                                               GL_Color(r_refdef.view.colorscale, r_refdef.view.colorscale, r_refdef.view.colorscale, 1);
                                                qglVertex3f(v[0], v[1], v[2]);
                                        }
                                        qglEnd();
                                        CHECKGLERROR
                                        qglBegin(GL_LINES);
-                                       for (k = 0, l = surface->num_firstvertex;k < surface->num_vertices;k++, l++)
+                                       for (k = 0, l = rsurface.batchfirstvertex;k < rsurface.batchnumvertices;k++, l++)
                                        {
-                                               VectorCopy(rsurface.vertex3f + l * 3, v);
+                                               VectorCopy(rsurface.batchvertex3f + l * 3, v);
                                                GL_Color(0, 0, r_refdef.view.colorscale, 1);
                                                qglVertex3f(v[0], v[1], v[2]);
-                                               VectorMA(v, r_shownormals.value, rsurface.normal3f + l * 3, v);
-                                               GL_Color(r_refdef.view.colorscale, 1, 1, 1);
+                                               VectorMA(v, r_shownormals.value, rsurface.batchnormal3f + l * 3, v);
+                                               GL_Color(r_refdef.view.colorscale, r_refdef.view.colorscale, r_refdef.view.colorscale, 1);
                                                qglVertex3f(v[0], v[1], v[2]);
                                        }
                                        qglEnd();
@@ -12162,8 +10984,7 @@ int r_maxsurfacelist = 0;
 const msurface_t **r_surfacelist = NULL;
 void R_DrawWorldSurfaces(qboolean skysurfaces, qboolean writedepth, qboolean depthonly, qboolean debug, qboolean prepass)
 {
-       int i, j, endj, f, flagsmask;
-       texture_t *t;
+       int i, j, endj, flagsmask;
        dp_model_t *model = r_refdef.scene.worldmodel;
        msurface_t *surfaces;
        unsigned char *update;
@@ -12209,8 +11030,8 @@ void R_DrawWorldSurfaces(qboolean skysurfaces, qboolean writedepth, qboolean dep
                return;
        }
 
-       f = 0;
-       t = NULL;
+       rsurface.lightmaptexture = NULL;
+       rsurface.deluxemaptexture = NULL;
        rsurface.uselightmaptexture = false;
        rsurface.texture = NULL;
        rsurface.rtlight = NULL;
@@ -12244,7 +11065,6 @@ void R_DrawWorldSurfaces(qboolean skysurfaces, qboolean writedepth, qboolean dep
                return;
        }
        R_QueueWorldSurfaceList(numsurfacelist, r_surfacelist, flagsmask, writedepth, depthonly, prepass);
-       GL_AlphaTest(false);
 
        // add to stats if desired
        if (r_speeds.integer && !skysurfaces && !depthonly)
@@ -12259,8 +11079,7 @@ void R_DrawWorldSurfaces(qboolean skysurfaces, qboolean writedepth, qboolean dep
 
 void R_DrawModelSurfaces(entity_render_t *ent, qboolean skysurfaces, qboolean writedepth, qboolean depthonly, qboolean debug, qboolean prepass)
 {
-       int i, j, endj, f, flagsmask;
-       texture_t *t;
+       int i, j, endj, flagsmask;
        dp_model_t *model = ent->model;
        msurface_t *surfaces;
        unsigned char *update;
@@ -12286,13 +11105,33 @@ void R_DrawModelSurfaces(entity_render_t *ent, qboolean skysurfaces, qboolean wr
        else if (prepass)
                RSurf_ActiveModelEntity(ent, true, true, true);
        else if (depthonly)
-               RSurf_ActiveModelEntity(ent, false, false, false);
+       {
+               switch (vid.renderpath)
+               {
+               case RENDERPATH_GL20:
+               case RENDERPATH_D3D9:
+               case RENDERPATH_D3D10:
+               case RENDERPATH_D3D11:
+               case RENDERPATH_SOFT:
+               case RENDERPATH_GLES2:
+                       RSurf_ActiveModelEntity(ent, model->wantnormals, model->wanttangents, false);
+                       break;
+               case RENDERPATH_GL13:
+               case RENDERPATH_GL11:
+                       RSurf_ActiveModelEntity(ent, model->wantnormals, false, false);
+                       break;
+               }
+       }
        else
        {
                switch (vid.renderpath)
                {
                case RENDERPATH_GL20:
-               case RENDERPATH_CGGL:
+               case RENDERPATH_D3D9:
+               case RENDERPATH_D3D10:
+               case RENDERPATH_D3D11:
+               case RENDERPATH_SOFT:
+               case RENDERPATH_GLES2:
                        RSurf_ActiveModelEntity(ent, true, true, false);
                        break;
                case RENDERPATH_GL13:
@@ -12330,8 +11169,8 @@ void R_DrawModelSurfaces(entity_render_t *ent, qboolean skysurfaces, qboolean wr
                return;
        }
 
-       f = 0;
-       t = NULL;
+       rsurface.lightmaptexture = NULL;
+       rsurface.deluxemaptexture = NULL;
        rsurface.uselightmaptexture = false;
        rsurface.texture = NULL;
        rsurface.rtlight = NULL;
@@ -12363,7 +11202,6 @@ void R_DrawModelSurfaces(entity_render_t *ent, qboolean skysurfaces, qboolean wr
                        if (update[j])
                                R_BuildLightMap(ent, surfaces + j);
        R_QueueModelSurfaceList(ent, numsurfacelist, r_surfacelist, flagsmask, writedepth, depthonly, prepass);
-       GL_AlphaTest(false);
 
        // add to stats if desired
        if (r_speeds.integer && !skysurfaces && !depthonly)
@@ -12388,6 +11226,8 @@ void R_DrawCustomSurface(skinframe_t *skinframe, const matrix4x4_t *texmatrix, i
        texture.basematerialflags = materialflags | MATERIALFLAG_CUSTOMSURFACE | MATERIALFLAG_WALL;
        texture.currentskinframe = skinframe;
        texture.currenttexmatrix = *texmatrix; // requires MATERIALFLAG_CUSTOMSURFACE
+       texture.offsetmapping = OFFSETMAPPING_OFF;
+       texture.offsetscale = 1;
        texture.specularscalemod = 1;
        texture.specularpowermod = 1;
 
@@ -12399,6 +11239,8 @@ void R_DrawCustomSurface(skinframe_t *skinframe, const matrix4x4_t *texmatrix, i
 
        // now render it
        rsurface.texture = R_GetCurrentTexture(surface.texture);
+       rsurface.lightmaptexture = NULL;
+       rsurface.deluxemaptexture = NULL;
        rsurface.uselightmaptexture = false;
        R_DrawModelTextureSurfaceList(1, &surfacelist, writedepth, prepass);
 }
@@ -12409,7 +11251,6 @@ void R_DrawCustomSurface_Texture(texture_t *texture, const matrix4x4_t *texmatri
        const msurface_t *surfacelist = &surface;
 
        // fake enough texture and surface state to render this geometry
-
        surface.texture = texture;
        surface.num_triangles = numtriangles;
        surface.num_firsttriangle = firsttriangle;
@@ -12418,6 +11259,8 @@ void R_DrawCustomSurface_Texture(texture_t *texture, const matrix4x4_t *texmatri
 
        // now render it
        rsurface.texture = R_GetCurrentTexture(surface.texture);
+       rsurface.lightmaptexture = NULL;
+       rsurface.deluxemaptexture = NULL;
        rsurface.uselightmaptexture = false;
        R_DrawModelTextureSurfaceList(1, &surfacelist, writedepth, prepass);
 }