]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - gl_rmain.c
found out why the water plane issue happend: namely, when a water plane is backface...
[xonotic/darkplaces.git] / gl_rmain.c
index d9d954777246199ae7ac188530ee2c498dc09c80..626762f51ddc91735eeaaecdf1e3ce03200cb73e 100644 (file)
@@ -148,6 +148,10 @@ 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"};
@@ -805,7 +809,11 @@ static const char *builtinshaderstring =
 "\n"
 "void main(void)\n"
 "{\n"
+"#ifdef USEVIEWTINT\n"
 "      gl_FragColor = gl_Color;\n"
+"#else\n"
+"      gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);\n"
+"#endif\n"
 "#ifdef USEDIFFUSE\n"
 "      gl_FragColor *= texture2D(Texture_First, TexCoord1);\n"
 "#endif\n"
@@ -1148,15 +1156,14 @@ static const char *builtinshaderstring =
 "      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"
+"      // 2 sample offset mapping (only 2 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"
+"      OffsetVector *= 0.5;\n"
 "      TexCoord -= OffsetVector * texture2D(Texture_Normal, TexCoord).a;\n"
 "      TexCoord -= OffsetVector * texture2D(Texture_Normal, TexCoord).a;\n"
 "      return TexCoord;\n"
@@ -1670,6 +1677,7 @@ static const char *builtinshaderstring =
 "      lightnormal.x = dot(lightnormal_modelspace, myhalf3(VectorS));\n"
 "      lightnormal.y = dot(lightnormal_modelspace, myhalf3(VectorT));\n"
 "      lightnormal.z = dot(lightnormal_modelspace, myhalf3(VectorR));\n"
+"      lightnormal = normalize(lightnormal); // VectorS/T/R are not always perfectly normalized, and EXACTSPECULARMATH is very picky about this\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"
@@ -1868,7 +1876,7 @@ const char *builtincgshaderstring =
 "out float4 gl_FragColor : COLOR\n"
 ")\n"
 "{\n"
-"//    float3 temp = float3(Depth,Depth*(65536.0/255.0),Depth*(16777216.0/255.0));\n"
+"//    float4 temp = float4(Depth,Depth*(65536.0/255.0),Depth*(16777216.0/255.0),0.0);\n"
 "      float4 temp = float4(Depth,Depth*256.0,Depth*65536.0,0.0);\n"
 "      temp.yz -= floor(temp.yz);\n"
 "      gl_FragColor = temp;\n"
@@ -2086,7 +2094,11 @@ const char *builtincgshaderstring =
 "out float4 gl_FragColor : COLOR\n"
 ")\n"
 "{\n"
+"#ifdef USEVIEWTINT\n"
 "      gl_FragColor = gl_FrontColor;\n"
+"#else\n"
+"      gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);\n"
+"#endif\n"
 "#ifdef USEDIFFUSE\n"
 "      gl_FragColor *= tex2D(Texture_First, TexCoord1);\n"
 "#endif\n"
@@ -2189,7 +2201,7 @@ const char *builtincgshaderstring =
 "      float2 ScreenScaleRefractReflectIW = ScreenScaleRefractReflect.xy * (1.0 / ModelViewProjectionPosition.w);\n"
 "      //float2 ScreenTexCoord = (ModelViewProjectionPosition.xy + normalize(tex2D(Texture_Normal, TexCoord).rgb - float3(0.5,0.5,0.5)).xy * DistortScaleRefractReflect.xy * 100) * ScreenScaleRefractReflectIW + ScreenCenterRefractReflect.xy;\n"
 "      float2 SafeScreenTexCoord = ModelViewProjectionPosition.xy * ScreenScaleRefractReflectIW + ScreenCenterRefractReflect.xy;\n"
-"      float2 ScreenTexCoord = SafeScreenTexCoord + float2(normalize(tex2D(Texture_Normal, TexCoord).rgb - float3(0.5,0.5,0.5))).xy * DistortScaleRefractReflect.xy;\n"
+"      float2 ScreenTexCoord = SafeScreenTexCoord + normalize(tex2D(Texture_Normal, TexCoord).rgb - float3(0.5,0.5,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"
@@ -3210,7 +3222,7 @@ const char *builtincgshaderstring =
 "#ifdef MODE_FAKELIGHT\n"
 "#define SHADING\n"
 "half3 lightnormal = half3(normalize(EyeVector));\n"
-"half3 lightcolor = half3(1.0);\n"
+"half3 lightcolor = half3(1.0,1.0,1.0);\n"
 "#endif // MODE_FAKELIGHT\n"
 "\n"
 "\n"
@@ -3333,7 +3345,7 @@ 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_VIEWTINT = 1<<2, ///< view tint (postprocessing only), use vertex colors (generic 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
@@ -3345,22 +3357,21 @@ typedef enum shaderpermutation_e
        SHADERPERMUTATION_BLOOM = 1<<11, ///< bloom (postprocessing only)
        SHADERPERMUTATION_SPECULAR = 1<<12, ///< (lightsource or deluxemapping) render specular effects
        SHADERPERMUTATION_POSTPROCESSING = 1<<13, ///< user defined postprocessing (postprocessing only)
-       SHADERPERMUTATION_EXACTSPECULARMATH = 1<<14, ///< (lightsource or deluxemapping) use exact reflection map for specular effects, as opposed to the usual OpenGL approximation
-       SHADERPERMUTATION_REFLECTION = 1<<15, ///< normalmap-perturbed reflection of the scene infront of the surface, preformed as an overlay on the surface
-       SHADERPERMUTATION_OFFSETMAPPING = 1<<16, ///< adjust texcoords to roughly simulate a displacement mapped surface
-       SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING = 1<<17, ///< adjust texcoords to accurately simulate a displacement mapped surface (requires OFFSETMAPPING to also be set!)
-       SHADERPERMUTATION_SHADOWMAP2D = 1<<18, ///< (lightsource) use shadowmap texture as light filter
-       SHADERPERMUTATION_SHADOWMAPPCF = 1<<19, ///< (lightsource) use percentage closer filtering on shadowmap test results
-       SHADERPERMUTATION_SHADOWMAPPCF2 = 1<<20, ///< (lightsource) use higher quality percentage closer filtering on shadowmap test results
-       SHADERPERMUTATION_SHADOWSAMPLER = 1<<21, ///< (lightsource) use hardware shadowmap test
-       SHADERPERMUTATION_SHADOWMAPVSDCT = 1<<22, ///< (lightsource) use virtual shadow depth cube texture for shadowmap indexing
-       SHADERPERMUTATION_SHADOWMAPORTHO = 1<<23, //< (lightsource) use orthographic shadowmap projection
-       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_NORMALMAPSCROLLBLEND = 1<<27, // (water) counter-direction normalmaps scrolling
-       SHADERPERMUTATION_LIMIT = 1<<28, ///< size of permutations array
-       SHADERPERMUTATION_COUNT = 28 ///< size of shaderpermutationinfo array
+       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_SHADOWMAP2D = 1<<17, ///< (lightsource) use shadowmap texture as light filter
+       SHADERPERMUTATION_SHADOWMAPPCF = 1<<18, ///< (lightsource) use percentage closer filtering on shadowmap test results
+       SHADERPERMUTATION_SHADOWMAPPCF2 = 1<<19, ///< (lightsource) use higher quality percentage closer filtering on shadowmap test results
+       SHADERPERMUTATION_SHADOWSAMPLER = 1<<20, ///< (lightsource) use hardware shadowmap test
+       SHADERPERMUTATION_SHADOWMAPVSDCT = 1<<21, ///< (lightsource) use virtual shadow depth cube texture for shadowmap indexing
+       SHADERPERMUTATION_SHADOWMAPORTHO = 1<<22, //< (lightsource) use orthographic shadowmap projection
+       SHADERPERMUTATION_DEFERREDLIGHTMAP = 1<<23, ///< (lightmap) read Texture_ScreenDiffuse/Specular textures and add them on top of lightmapping
+       SHADERPERMUTATION_ALPHAKILL = 1<<24, ///< (deferredgeometry) discard pixel if diffuse texture alpha below 0.5
+       SHADERPERMUTATION_REFLECTCUBE = 1<<25, ///< fake reflections using global cubemap (not HDRI light probe)
+       SHADERPERMUTATION_NORMALMAPSCROLLBLEND = 1<<26, // (water) counter-direction normalmaps scrolling
+       SHADERPERMUTATION_LIMIT = 1<<27, ///< size of permutations array
+       SHADERPERMUTATION_COUNT = 27 ///< size of shaderpermutationinfo array
 }
 shaderpermutation_t;
 
@@ -3381,7 +3392,6 @@ shaderpermutationinfo_t shaderpermutationinfo[SHADERPERMUTATION_COUNT] =
        {"#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"},
@@ -3397,7 +3407,7 @@ shaderpermutationinfo_t shaderpermutationinfo[SHADERPERMUTATION_COUNT] =
        {"#define USENORMALMAPSCROLLBLEND\n", " normalmapscrollblend"},
 };
 
-/// this enum is multiplied by SHADERPERMUTATION_MODEBASE
+// this enum selects which of the glslshadermodeinfo entries should be used
 typedef enum shadermode_e
 {
        SHADERMODE_GENERIC, ///< (particles/HUD/etc) vertex color, optionally multiplied by one texture
@@ -3446,7 +3456,7 @@ 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_DEPTH_OR_SHADOW\n", " depth/shadow"},
        {"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"},
@@ -3468,10 +3478,11 @@ 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"},
+       {"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"},
@@ -3584,19 +3595,66 @@ r_glsl_permutation_t;
 
 #define SHADERPERMUTATION_HASHSIZE 256
 
-// this called both on R_GLSL_CompileShader and R_HLSL_CacheShader
-// this function adds more "#define" to supply static parms, could be used by game-specific code part
-int vertstrings_count = 0;
-int geomstrings_count = 0;
-int fragstrings_count = 0;
-const char *vertstrings_list[64+3];
-const char *geomstrings_list[64+3];
-const char *fragstrings_list[64+3];
+
+// 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)
 {
-       if (mode == SHADERMODE_POSTPROCESS && (permutation & SHADERPERMUTATION_SATURATION))
-               if (r_glsl_saturation_redcompensate.integer)
-                       fragstrings_list[fragstrings_count++] = "#define SATURATION_REDCOMPENSATE\n";
+       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
@@ -3666,6 +3724,12 @@ static void R_GLSL_CompilePermutation(r_glsl_permutation_t *p, unsigned int mode
        shadermodeinfo_t *modeinfo = glslshadermodeinfo + mode;
        char *vertexstring, *geometrystring, *fragmentstring;
        char permutationname[256];
+       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;
@@ -3681,9 +3745,6 @@ static void R_GLSL_CompilePermutation(r_glsl_permutation_t *p, unsigned int mode
 
        // the first pretext is which type of shader to compile as
        // (later these will all be bound together as a program object)
-       vertstrings_count = 0;
-       geomstrings_count = 0;
-       fragstrings_count = 0;
        vertstrings_list[vertstrings_count++] = "#define VERTEX_SHADER\n";
        geomstrings_list[geomstrings_count++] = "#define GEOMETRY_SHADER\n";
        fragstrings_list[fragstrings_count++] = "#define FRAGMENT_SHADER\n";
@@ -3715,6 +3776,12 @@ 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;
@@ -4098,6 +4165,12 @@ static void R_CG_CompilePermutation(r_cg_permutation_t *p, unsigned int mode, un
        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;
@@ -4116,9 +4189,6 @@ static void R_CG_CompilePermutation(r_cg_permutation_t *p, unsigned int mode, un
 
        // the first pretext is which type of shader to compile as
        // (later these will all be bound together as a program object)
-       vertstrings_count = 0;
-       geomstrings_count = 0;
-       fragstrings_count = 0;
        vertstrings_list[vertstrings_count++] = "#define VERTEX_SHADER\n";
        geomstrings_list[geomstrings_count++] = "#define GEOMETRY_SHADER\n";
        fragstrings_list[fragstrings_count++] = "#define FRAGMENT_SHADER\n";
@@ -4152,6 +4222,12 @@ 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++)
@@ -4721,6 +4797,12 @@ static void R_HLSL_CompilePermutation(r_hlsl_permutation_t *p, unsigned int mode
        char *vertstring, *geomstring, *fragstring;
        char permutationname[256];
        char cachename[256];
+       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;
@@ -4747,9 +4829,6 @@ static void R_HLSL_CompilePermutation(r_hlsl_permutation_t *p, unsigned int mode
 
        // the first pretext is which type of shader to compile as
        // (later these will all be bound together as a program object)
-       vertstrings_count = 0;
-       geomstrings_count = 0;
-       fragstrings_count = 0;
        vertstrings_list[vertstrings_count++] = "#define VERTEX_SHADER\n";
        geomstrings_list[geomstrings_count++] = "#define GEOMETRY_SHADER\n";
        fragstrings_list[fragstrings_count++] = "#define FRAGMENT_SHADER\n";
@@ -4783,6 +4862,12 @@ static void R_HLSL_CompilePermutation(r_hlsl_permutation_t *p, unsigned int mode
 
        // 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++)
@@ -5065,7 +5150,7 @@ void R_SetupShader_Generic(rtexture_t *first, rtexture_t *second, int texturemod
        {
        case RENDERPATH_D3D9:
 #ifdef SUPPORTD3D
-               R_SetupShader_SetPermutationHLSL(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))));
+               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
@@ -5077,14 +5162,14 @@ void R_SetupShader_Generic(rtexture_t *first, rtexture_t *second, int texturemod
                Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
                break;
        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))));
+               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(GL20TU_FIRST , first );
                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))));
+               R_SetupShader_SetPermutationCG(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))));
                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
 #endif
@@ -5377,11 +5462,7 @@ 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_texture_fogheighttexture ? SHADERPERMUTATION_FOGHEIGHTTEXTURE : (r_refdef.fogplaneviewabove ? SHADERPERMUTATION_FOGOUTSIDE : SHADERPERMUTATION_FOGINSIDE);
                if (rsurface.texture->colormapping)
@@ -5474,11 +5555,7 @@ 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_texture_fogheighttexture ? SHADERPERMUTATION_FOGHEIGHTTEXTURE : (r_refdef.fogplaneviewabove ? SHADERPERMUTATION_FOGOUTSIDE : SHADERPERMUTATION_FOGINSIDE);
                if (rsurface.texture->colormapping)
@@ -5600,11 +5677,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                        mode = SHADERMODE_FAKELIGHT;
                        permutation |= SHADERPERMUTATION_DIFFUSE;
                        if (specularscale > 0)
-                       {
                                permutation |= SHADERPERMUTATION_SPECULAR | SHADERPERMUTATION_DIFFUSE;
-                               if (r_shadow_glossexact.integer)
-                                       permutation |= SHADERPERMUTATION_EXACTSPECULARMATH;
-                       }
                }
                else if (r_glsl_deluxemapping.integer >= 1 && rsurface.uselightmaptexture && r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brushq3.deluxemapping)
                {
@@ -5615,11 +5688,7 @@ 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;
-                       }
                }
                else if (r_glsl_deluxemapping.integer >= 2 && rsurface.uselightmaptexture)
                {
@@ -5627,11 +5696,7 @@ 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;
-                       }
                }
                else if (rsurface.uselightmaptexture)
                {
@@ -5685,7 +5750,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
 
                        // additive passes are only darkened by fog, not tinted
                        hlslPSSetParameter3f(D3DPSREGISTER_FogColor, 0, 0, 0);
-                       hlslPSSetParameter1f(D3DPSREGISTER_SpecularPower, rsurface.texture->specularpower * ((permutation & SHADERPERMUTATION_EXACTSPECULARMATH) ? 0.25f : 1.0f));
+                       hlslPSSetParameter1f(D3DPSREGISTER_SpecularPower, rsurface.texture->specularpower * (r_shadow_glossexact.integer ? 0.25f : 1.0f));
                }
                else
                {
@@ -5723,7 +5788,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                        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 * ((permutation & SHADERPERMUTATION_EXACTSPECULARMATH) ? 0.25f : 1.0f));
+                       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]);
                }
@@ -5827,7 +5892,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                        // 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_SpecularPower >= 0) qglUniform1fARB(r_glsl_permutation->loc_SpecularPower, rsurface.texture->specularpower * ((permutation & SHADERPERMUTATION_EXACTSPECULARMATH) ? 0.25f : 1.0f));
+                       if (r_glsl_permutation->loc_SpecularPower >= 0) qglUniform1fARB(r_glsl_permutation->loc_SpecularPower, rsurface.texture->specularpower * (r_shadow_glossexact.integer ? 0.25f : 1.0f));
                }
                else
                {
@@ -5837,12 +5902,12 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                        }
                        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) * 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]);
+                               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 * 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) qglUniform3fARB(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) 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, 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) 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_LightColor >= 0) qglUniform3fARB(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) qglUniform3fARB(r_glsl_permutation->loc_LightDir, rsurface.modellight_lightdir[0], rsurface.modellight_lightdir[1], rsurface.modellight_lightdir[2]);
                        }
                        else
@@ -5868,7 +5933,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                        if (r_glsl_permutation->loc_ReflectColor >= 0) qglUniform4fARB(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) 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_SpecularPower >= 0) qglUniform1fARB(r_glsl_permutation->loc_SpecularPower, rsurface.texture->specularpower * (r_shadow_glossexact.integer ? 0.25f : 1.0f));
                        if (r_glsl_permutation->loc_NormalmapScrollBlend >= 0) qglUniform2fARB(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);qglUniformMatrix4fvARB(r_glsl_permutation->loc_TexMatrix, 1, false, m16f);}
@@ -5996,7 +6061,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
 
                        // 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_SpecularPower) cgGLSetParameter1f(r_cg_permutation->fp_SpecularPower, rsurface.texture->specularpower * ((permutation & SHADERPERMUTATION_EXACTSPECULARMATH) ? 0.25f : 1.0f));CHECKCGERROR
+                       if (r_cg_permutation->fp_SpecularPower) cgGLSetParameter1f(r_cg_permutation->fp_SpecularPower, rsurface.texture->specularpower * (r_shadow_glossexact.integer ? 0.25f : 1.0f));CHECKCGERROR
                }
                else
                {
@@ -6038,7 +6103,7 @@ void R_SetupShader_Surface(const vec3_t lightcolorbase, qboolean modellighting,
                        if (r_cg_permutation->fp_ReflectColor) cgGLSetParameter4fv(r_cg_permutation->fp_ReflectColor, rsurface.texture->reflectcolor4f[0], rsurface.texture->reflectcolor4f[1], rsurface.texture->reflectcolor4f[2], rsurface.texture->reflectcolor4f[3] * rsurface.texture->lightmapcolor[3]);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_SpecularPower) cgGLSetParameter1f(r_cg_permutation->fp_SpecularPower, rsurface.texture->specularpower * (r_shadow_glossexact.integer ? 0.25f : 1.0f));CHECKCGERROR
                        if (r_cg_permutation->fp_NormalmapScrollBlend) cgGLSetParameter2f(r_cg_permutation->fp_NormalmapScrollBlend, rsurface.texture->r_water_waterscroll[0], rsurface.texture->r_water_waterscroll[1]);
                }
                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
@@ -6149,11 +6214,7 @@ 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_usingshadowmap2d)
        {
                permutation |= SHADERPERMUTATION_SHADOWMAP2D;
@@ -6183,7 +6244,7 @@ void R_SetupShader_DeferredLight(const rtlight_t *rtlight)
                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) * ((permutation & SHADERPERMUTATION_EXACTSPECULARMATH) ? 0.25f : 1.0f));
+               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);
 
@@ -6210,7 +6271,7 @@ void R_SetupShader_DeferredLight(const rtlight_t *rtlight)
                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_SpecularPower             >= 0) qglUniform1fARB(       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) 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);
 
@@ -6231,7 +6292,7 @@ void R_SetupShader_DeferredLight(const rtlight_t *rtlight)
                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_SpecularPower            ) cgGLSetParameter1f(r_cg_permutation->fp_SpecularPower, (r_shadow_gloss.integer == 2 ? r_shadow_gloss2exponent.value : r_shadow_glossexponent.value) * (r_shadow_glossexact.integer ? 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
 
@@ -7347,6 +7408,11 @@ 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);
@@ -7837,7 +7903,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, ent->modellight_lightdir, org, LP_LIGHTMAP | LP_RTWORLD | LP_DYNLIGHT);
+                       }
+                       else
+                               r_refdef.scene.worldmodel->brush.LightPoint(r_refdef.scene.worldmodel, org, ent->modellight_ambient, ent->modellight_diffuse, tempdiffusenormal);
+
                        if(ent->flags & RENDER_EQUALIZE)
                        {
                                // first fix up ambient lighting...
@@ -9091,13 +9168,18 @@ void R_HDR_RenderBloomTexture(void)
 {
        int oldwidth, oldheight;
        float oldcolorscale;
+       int 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)
@@ -9117,7 +9199,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;
@@ -9130,6 +9212,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;
@@ -9253,10 +9336,14 @@ 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();
                GL_Color(1, 1, 1, 1);
@@ -9392,7 +9479,7 @@ 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)
@@ -9556,17 +9643,28 @@ R_RenderView
 */
 void R_RenderView(void)
 {
+       matrix4x4_t originalmatrix = r_refdef.view.matrix, offsetmatrix;
        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]
@@ -9580,12 +9678,17 @@ 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 || 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;
 
@@ -9638,6 +9741,8 @@ void R_RenderView(void)
        GL_Scissor(0, 0, vid.width, vid.height);
        GL_ScissorTest(false);
 
+       r_refdef.view.matrix = originalmatrix;
+
        CHECKGLERROR
 }
 
@@ -9938,7 +10043,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];
@@ -10108,7 +10213,6 @@ 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);
        memcpy(color4f, nomodelcolor4f, sizeof(float[6*4]));
        for (i = 0, c = color4f;i < 6;i++, c += 4)
        {
@@ -10128,7 +10232,8 @@ void R_DrawNoModel_TransparentCallback(const entity_render_t *ent, const rtlight
                        c[2] = (c[2] * f1 + r_refdef.fogcolor[2] * f2);
                }
        }
-       R_Mesh_ResetTextureState();
+//     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);
 }
@@ -10785,6 +10890,7 @@ void R_Mesh_ResizeArrays(int newvertices)
 
 void RSurf_ActiveWorldEntity(void)
 {
+       int newvertices;
        dp_model_t *model = r_refdef.scene.worldmodel;
        //if (rsurface.entity == r_refdef.scene.worldentity)
        //      return;
@@ -10795,8 +10901,9 @@ void RSurf_ActiveWorldEntity(void)
        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);
+       newvertices = max(model->surfmesh.num_vertices, model->surfmesh.num_triangles);
+       if (rsurface.array_size < newvertices)
+               R_Mesh_ResizeArrays(newvertices);
        rsurface.matrix = identitymatrix;
        rsurface.inversematrix = identitymatrix;
        rsurface.matrixscale = 1;
@@ -10900,6 +11007,7 @@ void RSurf_ActiveWorldEntity(void)
 
 void RSurf_ActiveModelEntity(const entity_render_t *ent, qboolean wantnormals, qboolean wanttangents, qboolean prepass)
 {
+       int newvertices;
        dp_model_t *model = ent->model;
        //if (rsurface.entity == ent && (!model->surfmesh.isanimated || (!wantnormals && !wanttangents)))
        //      return;
@@ -10910,8 +11018,9 @@ void RSurf_ActiveModelEntity(const entity_render_t *ent, qboolean wantnormals, q
        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);
+       newvertices = max(model->surfmesh.num_vertices, model->surfmesh.num_triangles);
+       if (rsurface.array_size < newvertices)
+               R_Mesh_ResizeArrays(newvertices);
        rsurface.matrix = ent->matrix;
        rsurface.inversematrix = ent->inversematrix;
        rsurface.matrixscale = Matrix4x4_ScaleFromMatrix(&rsurface.matrix);
@@ -11081,7 +11190,7 @@ void RSurf_ActiveModelEntity(const entity_render_t *ent, qboolean wantnormals, q
 
 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)
 {
-       int i;
+       int newvertices;
 
        rsurface.entity = r_refdef.scene.worldentity;
        rsurface.skeleton = NULL;
@@ -11091,8 +11200,9 @@ void RSurf_ActiveCustomEntity(const matrix4x4_t *matrix, const matrix4x4_t *inve
        rsurface.ent_flags = entflags;
        rsurface.modelnumvertices = numvertices;
        rsurface.modelnumtriangles = numtriangles;
-       if (rsurface.array_size < rsurface.modelnumvertices)
-               R_Mesh_ResizeArrays(rsurface.modelnumvertices);
+       newvertices = max(rsurface.modelnumvertices, rsurface.modelnumtriangles);
+       if (rsurface.array_size < newvertices)
+               R_Mesh_ResizeArrays(newvertices);
        rsurface.matrix = *matrix;
        rsurface.inversematrix = *inversematrix;
        rsurface.matrixscale = Matrix4x4_ScaleFromMatrix(&rsurface.matrix);
@@ -11221,25 +11331,6 @@ void RSurf_ActiveCustomEntity(const matrix4x4_t *matrix, const matrix4x4_t *inve
                        rsurface.modeltvector3f = rsurface.array_modeltvector3f;
                }
        }
-
-       // now convert arrays into vertexmesh structs
-       for (i = 0;i < numvertices;i++)
-       {
-               VectorCopy(rsurface.modelvertex3f + 3*i, rsurface.array_modelvertexposition[i].vertex3f);
-               VectorCopy(rsurface.modelvertex3f + 3*i, rsurface.array_modelvertexmesh[i].vertex3f);
-               if (rsurface.modelsvector3f)
-                       VectorCopy(rsurface.modelsvector3f + 3*i, rsurface.array_modelvertexmesh[i].svector3f);
-               if (rsurface.modeltvector3f)
-                       VectorCopy(rsurface.modeltvector3f + 3*i, rsurface.array_modelvertexmesh[i].tvector3f);
-               if (rsurface.modelnormal3f)
-                       VectorCopy(rsurface.modelnormal3f + 3*i, rsurface.array_modelvertexmesh[i].normal3f);
-               if (rsurface.modellightmapcolor4f)
-                       Vector4Scale(rsurface.modellightmapcolor4f + 4*i, 255.0f, rsurface.array_modelvertexmesh[i].color4ub);
-               if (rsurface.modeltexcoordtexture2f)
-                       Vector2Copy(rsurface.modeltexcoordtexture2f + 2*i, rsurface.array_modelvertexmesh[i].texcoordtexture2f);
-               if (rsurface.modeltexcoordlightmap2f)
-                       Vector2Copy(rsurface.modeltexcoordlightmap2f + 2*i, rsurface.array_modelvertexmesh[i].texcoordlightmap2f);
-       }
 }
 
 float RSurf_FogPoint(const float *v)
@@ -11331,8 +11422,6 @@ void RSurf_PrepareVerticesForBatch(int batchneed, int texturenumsurfaces, const
                        endvertex = surfaceendvertex;
                numtriangles += surfacenumtriangles;
        }
-       if (!numtriangles)
-               return;
 
        // we now know the vertex range used, and if there are any gaps in it
        rsurface.batchfirstvertex = firstvertex;
@@ -11776,8 +11865,8 @@ void RSurf_PrepareVerticesForBatch(int batchneed, int texturenumsurfaces, const
                                }
                        }
                        // if we get here, BATCHNEED_ARRAY_NORMAL and BATCHNEED_ARRAY_VECTOR are in batchneed, so no need to check
-                       Mod_BuildNormals(rsurface.batchfirstvertex, rsurface.batchnumvertices, rsurface.batchnumtriangles, rsurface.array_batchvertex3f, rsurface.batchelement3i + 3 * rsurface.batchfirsttriangle, rsurface.array_batchnormal3f, true);
-                       Mod_BuildTextureVectorsFromNormals(rsurface.batchfirstvertex, rsurface.batchnumvertices, rsurface.batchnumtriangles, rsurface.array_batchvertex3f, rsurface.batchtexcoordtexture2f, rsurface.array_batchnormal3f, rsurface.batchelement3i + 3 * rsurface.batchfirsttriangle, rsurface.array_batchsvector3f, rsurface.array_batchtvector3f, true);
+                       Mod_BuildNormals(rsurface.batchfirstvertex, rsurface.batchnumvertices, rsurface.batchnumtriangles, rsurface.array_batchvertex3f, rsurface.batchelement3i + 3 * rsurface.batchfirsttriangle, rsurface.array_batchnormal3f, r_smoothnormals_areaweighting.integer != 0);
+                       Mod_BuildTextureVectorsFromNormals(rsurface.batchfirstvertex, rsurface.batchnumvertices, rsurface.batchnumtriangles, rsurface.array_batchvertex3f, rsurface.batchtexcoordtexture2f, rsurface.array_batchnormal3f, rsurface.batchelement3i + 3 * rsurface.batchfirsttriangle, rsurface.array_batchsvector3f, rsurface.array_batchtvector3f, r_smoothnormals_areaweighting.integer != 0);
                        rsurface.batchvertex3f = rsurface.array_batchvertex3f;
                        rsurface.batchvertex3f_vertexbuffer = NULL;
                        rsurface.batchvertex3f_bufferoffset = 0;
@@ -11881,14 +11970,14 @@ void RSurf_PrepareVerticesForBatch(int batchneed, int texturenumsurfaces, const
                        rsurface.batchvertex3f_bufferoffset = 0;
                        if(batchneed & (BATCHNEED_ARRAY_NORMAL | BATCHNEED_ARRAY_VECTOR)) // otherwise these can stay NULL
                        {
-                               Mod_BuildNormals(rsurface.batchfirstvertex, rsurface.batchnumvertices, rsurface.batchnumtriangles, rsurface.array_batchvertex3f, rsurface.batchelement3i + 3 * rsurface.batchfirsttriangle, rsurface.array_batchnormal3f, true);
+                               Mod_BuildNormals(rsurface.batchfirstvertex, rsurface.batchnumvertices, rsurface.batchnumtriangles, rsurface.array_batchvertex3f, rsurface.batchelement3i + 3 * rsurface.batchfirsttriangle, rsurface.array_batchnormal3f, r_smoothnormals_areaweighting.integer != 0);
                                rsurface.batchnormal3f = rsurface.array_batchnormal3f;
                                rsurface.batchnormal3f_vertexbuffer = NULL;
                                rsurface.batchnormal3f_bufferoffset = 0;
                        }
                        if(batchneed & BATCHNEED_ARRAY_VECTOR) // otherwise these can stay NULL
                        {
-                               Mod_BuildTextureVectorsFromNormals(rsurface.batchfirstvertex, rsurface.batchnumvertices, rsurface.batchnumtriangles, rsurface.array_batchvertex3f, rsurface.batchtexcoordtexture2f, rsurface.array_batchnormal3f, rsurface.batchelement3i + 3 * rsurface.batchfirsttriangle, rsurface.array_batchsvector3f, rsurface.array_batchtvector3f, true);
+                               Mod_BuildTextureVectorsFromNormals(rsurface.batchfirstvertex, rsurface.batchnumvertices, rsurface.batchnumtriangles, rsurface.array_batchvertex3f, rsurface.batchtexcoordtexture2f, rsurface.array_batchnormal3f, rsurface.batchelement3i + 3 * rsurface.batchfirsttriangle, rsurface.array_batchsvector3f, rsurface.array_batchtvector3f, r_smoothnormals_areaweighting.integer != 0);
                                rsurface.batchsvector3f = rsurface.array_batchsvector3f;
                                rsurface.batchsvector3f_vertexbuffer = NULL;
                                rsurface.batchsvector3f_bufferoffset = 0;
@@ -11914,7 +12003,7 @@ void RSurf_PrepareVerticesForBatch(int batchneed, int texturenumsurfaces, const
                        rsurface.batchnormal3f_bufferoffset = 0;
                        if(batchneed & BATCHNEED_ARRAY_VECTOR) // otherwise these can stay NULL
                        {
-                               Mod_BuildTextureVectorsFromNormals(rsurface.batchfirstvertex, rsurface.batchnumvertices, rsurface.batchnumtriangles, rsurface.array_batchvertex3f, rsurface.batchtexcoordtexture2f, rsurface.array_batchnormal3f, rsurface.batchelement3i + 3 * rsurface.batchfirsttriangle, rsurface.array_batchsvector3f, rsurface.array_batchtvector3f, true);
+                               Mod_BuildTextureVectorsFromNormals(rsurface.batchfirstvertex, rsurface.batchnumvertices, rsurface.batchnumtriangles, rsurface.array_batchvertex3f, rsurface.batchtexcoordtexture2f, rsurface.array_batchnormal3f, rsurface.batchelement3i + 3 * rsurface.batchfirsttriangle, rsurface.array_batchsvector3f, rsurface.array_batchtvector3f, r_smoothnormals_areaweighting.integer != 0);
                                rsurface.batchsvector3f = rsurface.array_batchsvector3f;
                                rsurface.batchsvector3f_vertexbuffer = NULL;
                                rsurface.batchsvector3f_bufferoffset = 0;
@@ -11945,7 +12034,7 @@ void RSurf_PrepareVerticesForBatch(int batchneed, int texturenumsurfaces, const
                                VectorMA(rsurface.batchvertex3f + 3*j, scale, rsurface.batchnormal3f + 3*j, rsurface.array_batchvertex3f + 3*j);
                        }
                        // if we get here, BATCHNEED_ARRAY_NORMAL is in batchneed, so no need to check
-                       Mod_BuildNormals(rsurface.batchfirstvertex, rsurface.batchnumvertices, rsurface.batchnumtriangles, rsurface.array_batchvertex3f, rsurface.batchelement3i + 3 * rsurface.batchfirsttriangle, rsurface.array_batchnormal3f, true);
+                       Mod_BuildNormals(rsurface.batchfirstvertex, rsurface.batchnumvertices, rsurface.batchnumtriangles, rsurface.array_batchvertex3f, rsurface.batchelement3i + 3 * rsurface.batchfirsttriangle, rsurface.array_batchnormal3f, r_smoothnormals_areaweighting.integer != 0);
                        rsurface.batchvertex3f = rsurface.array_batchvertex3f;
                        rsurface.batchvertex3f_vertexbuffer = NULL;
                        rsurface.batchvertex3f_bufferoffset = 0;
@@ -11954,7 +12043,7 @@ void RSurf_PrepareVerticesForBatch(int batchneed, int texturenumsurfaces, const
                        rsurface.batchnormal3f_bufferoffset = 0;
                        if(batchneed & BATCHNEED_ARRAY_VECTOR) // otherwise these can stay NULL
                        {
-                               Mod_BuildTextureVectorsFromNormals(rsurface.batchfirstvertex, rsurface.batchnumvertices, rsurface.batchnumtriangles, rsurface.array_batchvertex3f, rsurface.batchtexcoordtexture2f, rsurface.array_batchnormal3f, rsurface.batchelement3i + 3 * rsurface.batchfirsttriangle, rsurface.array_batchsvector3f, rsurface.array_batchtvector3f, true);
+                               Mod_BuildTextureVectorsFromNormals(rsurface.batchfirstvertex, rsurface.batchnumvertices, rsurface.batchnumtriangles, rsurface.array_batchvertex3f, rsurface.batchtexcoordtexture2f, rsurface.array_batchnormal3f, rsurface.batchelement3i + 3 * rsurface.batchfirsttriangle, rsurface.array_batchsvector3f, rsurface.array_batchtvector3f, r_smoothnormals_areaweighting.integer != 0);
                                rsurface.batchsvector3f = rsurface.array_batchsvector3f;
                                rsurface.batchsvector3f_vertexbuffer = NULL;
                                rsurface.batchsvector3f_bufferoffset = 0;
@@ -11971,7 +12060,7 @@ void RSurf_PrepareVerticesForBatch(int batchneed, int texturenumsurfaces, const
                                VectorMA(rsurface.batchvertex3f + 3*j, scale, rsurface.batchnormal3f + 3*j, rsurface.array_batchvertex3f + 3*j);
                        }
                        // if we get here, BATCHNEED_ARRAY_NORMAL is in batchneed, so no need to check
-                       Mod_BuildNormals(rsurface.batchfirstvertex, rsurface.batchnumvertices, rsurface.batchnumtriangles, rsurface.array_batchvertex3f, rsurface.batchelement3i + 3 * rsurface.batchfirsttriangle, rsurface.array_batchnormal3f, true);
+                       Mod_BuildNormals(rsurface.batchfirstvertex, rsurface.batchnumvertices, rsurface.batchnumtriangles, rsurface.array_batchvertex3f, rsurface.batchelement3i + 3 * rsurface.batchfirsttriangle, rsurface.array_batchnormal3f, r_smoothnormals_areaweighting.integer != 0);
                        rsurface.batchvertex3f = rsurface.array_batchvertex3f;
                        rsurface.batchvertex3f_vertexbuffer = NULL;
                        rsurface.batchvertex3f_bufferoffset = 0;
@@ -11980,7 +12069,7 @@ void RSurf_PrepareVerticesForBatch(int batchneed, int texturenumsurfaces, const
                        rsurface.batchnormal3f_bufferoffset = 0;
                        if(batchneed & BATCHNEED_ARRAY_VECTOR) // otherwise these can stay NULL
                        {
-                               Mod_BuildTextureVectorsFromNormals(rsurface.batchfirstvertex, rsurface.batchnumvertices, rsurface.batchnumtriangles, rsurface.array_batchvertex3f, rsurface.batchtexcoordtexture2f, rsurface.array_batchnormal3f, rsurface.batchelement3i + 3 * rsurface.batchfirsttriangle, rsurface.array_batchsvector3f, rsurface.array_batchtvector3f, true);
+                               Mod_BuildTextureVectorsFromNormals(rsurface.batchfirstvertex, rsurface.batchnumvertices, rsurface.batchnumtriangles, rsurface.array_batchvertex3f, rsurface.batchtexcoordtexture2f, rsurface.array_batchnormal3f, rsurface.batchelement3i + 3 * rsurface.batchfirsttriangle, rsurface.array_batchsvector3f, rsurface.array_batchtvector3f, r_smoothnormals_areaweighting.integer != 0);
                                rsurface.batchsvector3f = rsurface.array_batchsvector3f;
                                rsurface.batchsvector3f_vertexbuffer = NULL;
                                rsurface.batchsvector3f_bufferoffset = 0;
@@ -12123,6 +12212,36 @@ void RSurf_PrepareVerticesForBatch(int batchneed, int texturenumsurfaces, const
 
 void RSurf_DrawBatch(void)
 {
+       // 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);
 }
 
@@ -12134,13 +12253,20 @@ static int RSurf_FindWaterPlaneForSurface(const msurface_t *surface)
        vec3_t vert;
        const float *v;
        r_waterstate_waterplane_t *p;
+       qboolean prepared = false;
        bestd = 0;
        for (planeindex = 0, p = r_waterstate.waterplanes;planeindex < r_waterstate.numwaterplanes;planeindex++, p++)
        {
                if(p->camera_entity != rsurface.texture->camera_entity)
                        continue;
                d = 0;
-               RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX, 1, &surface);
+               if(!prepared)
+               {
+                       RSurf_PrepareVerticesForBatch(BATCHNEED_ARRAY_VERTEX, 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);
@@ -12153,6 +12279,10 @@ static int RSurf_FindWaterPlaneForSurface(const msurface_t *surface)
                }
        }
        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(void)
@@ -12505,16 +12635,23 @@ static void R_DrawTextureSurfaceList_GL20(int texturenumsurfaces, const msurface
        // 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)) && !r_waterstate.renderingscene)
+       if ((rsurface.texture->currentmaterialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_CAMERA | MATERIALFLAG_REFLECTION)))
        {
                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)) && !r_waterstate.renderingscene)
+                       if ((rsurface.texture->currentmaterialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_CAMERA)))
                        {
                                // render water or distortion background
                                GL_DepthMask(true);
@@ -12525,7 +12662,7 @@ static void R_DrawTextureSurfaceList_GL20(int texturenumsurfaces, const msurface
                                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) && !r_waterstate.renderingscene)
+                       else if ((rsurface.texture->currentmaterialflags & MATERIALFLAG_REFLECTION))
                        {
                                // render surface with reflection texture as input
                                GL_DepthMask(writedepth && !(rsurface.texture->currentmaterialflags & MATERIALFLAG_BLENDED));
@@ -12761,7 +12898,7 @@ static void R_DrawTextureSurfaceList_ShowSurfaces(int texturenumsurfaces, const
        float c[4];
 
        GL_AlphaTest(false);
-       R_Mesh_ResetTextureState();
+//     R_Mesh_ResetTextureState();
        R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
 
        if(rsurface.texture && rsurface.texture->currentskinframe)
@@ -13065,7 +13202,7 @@ static void R_DrawSurface_TransparentCallback(const entity_render_t *ent, const
                                GL_BlendFunc(GL_ONE, GL_ZERO);
                                GL_DepthMask(true);
                                GL_AlphaTest(false);
-                               R_Mesh_ResetTextureState();
+//                             R_Mesh_ResetTextureState();
                                R_SetupShader_DepthOrShadow();
                        }
                        RSurf_SetupDepthAndCulling();
@@ -13346,7 +13483,7 @@ void R_DrawLoc_Callback(const entity_render_t *ent, const rtlight_t *rtlight, in
        GL_CullFace(GL_NONE);
        R_EntityMatrix(&identitymatrix);
 
-       R_Mesh_ResetTextureState();
+//     R_Mesh_ResetTextureState();
 
        i = surfacelist[0];
        GL_Color(((i & 0x0007) >> 0) * (1.0f / 7.0f) * r_refdef.view.colorscale,
@@ -13937,9 +14074,9 @@ static void R_DrawModelDecals_Entity(entity_render_t *ent)
                if (decal->triangleindex >= 0 && decal->triangleindex < rsurface.modelnumtriangles)
                {
                        e = rsurface.modelelement3i + 3*decal->triangleindex;
-                       VectorCopy(rsurface.modelvertexposition[e[0]].vertex3f, v3f);
-                       VectorCopy(rsurface.modelvertexposition[e[1]].vertex3f, v3f + 3);
-                       VectorCopy(rsurface.modelvertexposition[e[2]].vertex3f, 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
                {
@@ -13971,7 +14108,7 @@ static void R_DrawModelDecals_Entity(entity_render_t *ent)
                // now render the decals all at once
                // (this assumes they all use one particle font texture!)
                RSurf_ActiveCustomEntity(&rsurface.matrix, &rsurface.inversematrix, rsurface.ent_flags, rsurface.ent_shadertime, 1, 1, 1, 1, numdecals*3, decalsystem->vertex3f, decalsystem->texcoord2f, NULL, NULL, NULL, decalsystem->color4f, numtris, decalsystem->element3i, decalsystem->element3s, false, false);
-               R_Mesh_ResetTextureState();
+//             R_Mesh_ResetTextureState();
                R_Mesh_PrepareVertices_Generic_Arrays(numtris * 3, decalsystem->vertex3f, decalsystem->color4f, decalsystem->texcoord2f);
                GL_DepthMask(false);
                GL_DepthRange(0, 1);
@@ -14049,7 +14186,7 @@ void R_DrawDebugModel(void)
 
        flagsmask = MATERIALFLAG_SKY | MATERIALFLAG_WALL;
 
-       R_Mesh_ResetTextureState();
+//     R_Mesh_ResetTextureState();
        R_SetupShader_Generic(NULL, NULL, GL_MODULATE, 1);
        GL_DepthRange(0, 1);
        GL_DepthTest(!r_showdisabledepthtest.integer);
@@ -14472,7 +14609,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;