]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - gl_rmain.c
CSQC fixes (less broken, still not spec compliant)
[xonotic/darkplaces.git] / gl_rmain.c
index ec2dc227f46bece8c1e9d4f6a4c615e36d6192b8..8022dcc835d30e37e1af4bf642a5625eea04d5fa 100644 (file)
@@ -43,6 +43,7 @@ cvar_t r_showcollisionbrushes = {0, "r_showcollisionbrushes", "0", "draws collis
 cvar_t r_showcollisionbrushes_polygonfactor = {0, "r_showcollisionbrushes_polygonfactor", "-1", "expands outward the brush polygons a little bit, used to make collision brushes appear infront of walls"};
 cvar_t r_showcollisionbrushes_polygonoffset = {0, "r_showcollisionbrushes_polygonoffset", "0", "nudges brush polygon depth in hardware depth units, used to make collision brushes appear infront of walls"};
 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_drawviewmodel = {0, "r_drawviewmodel","1", "draw your weapon model"};
 cvar_t r_speeds = {0, "r_speeds","0", "displays rendering statistics and per-subsystem timings"};
@@ -78,6 +79,11 @@ 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_power = {CVAR_SAVE, "r_bloom_power", "2", "how much to darken the image before blurring to make the bloom effect"};
 
+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_bloomintensity = {CVAR_SAVE, "r_hdr_bloomintensity", "0.5", "amount of bloom"};
+cvar_t r_hdr_glowintensity = {CVAR_SAVE, "r_hdr_glowintensity", "1", "how bright light emitting textures should appear"};
+
 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"};
 
 cvar_t developer_texturelogging = {0, "developer_texturelogging", "0", "produces a textures.log file containing names of skins and map textures the engine tried to load"};
@@ -328,6 +334,18 @@ static const char *builtinshaderstring =
 "\n"
 "// common definitions between vertex shader and fragment shader:\n"
 "\n"
+"#ifdef __GLSL_CG_DATA_TYPES\n"
+"#define myhalf half\n"
+"#define myhvec2 hvec2\n"
+"#define myhvec3 hvec3\n"
+"#define myhvec4 hvec4\n"
+"#else\n"
+"#define myhalf float\n"
+"#define myhvec2 vec2\n"
+"#define myhvec3 vec3\n"
+"#define myhvec4 vec4\n"
+"#endif\n"
+"\n"
 "varying vec2 TexCoord;\n"
 "varying vec2 TexCoordLightmap;\n"
 "\n"
@@ -422,22 +440,25 @@ static const char *builtinshaderstring =
 "uniform sampler2D Texture_Deluxemap;\n"
 "uniform sampler2D Texture_Glow;\n"
 "\n"
-"uniform vec3 LightColor;\n"
-"uniform vec3 AmbientColor;\n"
-"uniform vec3 DiffuseColor;\n"
-"uniform vec3 SpecularColor;\n"
-"uniform vec3 Color_Pants;\n"
-"uniform vec3 Color_Shirt;\n"
-"uniform vec3 FogColor;\n"
+"uniform myhvec3 LightColor;\n"
+"uniform myhvec3 AmbientColor;\n"
+"uniform myhvec3 DiffuseColor;\n"
+"uniform myhvec3 SpecularColor;\n"
+"uniform myhvec3 Color_Pants;\n"
+"uniform myhvec3 Color_Shirt;\n"
+"uniform myhvec3 FogColor;\n"
+"\n"
+"uniform myhalf GlowScale;\n"
+"uniform myhalf SceneBrightness;\n"
 "\n"
 "uniform float OffsetMapping_Scale;\n"
 "uniform float OffsetMapping_Bias;\n"
 "uniform float FogRangeRecip;\n"
 "\n"
-"uniform float AmbientScale;\n"
-"uniform float DiffuseScale;\n"
-"uniform float SpecularScale;\n"
-"uniform float SpecularPower;\n"
+"uniform myhalf AmbientScale;\n"
+"uniform myhalf DiffuseScale;\n"
+"uniform myhalf SpecularScale;\n"
+"uniform myhalf SpecularPower;\n"
 "\n"
 "void main(void)\n"
 "{\n"
@@ -452,7 +473,9 @@ static const char *builtinshaderstring =
 "\n"
 "#ifdef USEOFFSETMAPPING_RELIEFMAPPING\n"
 "      // 14 sample relief mapping: linear search and then binary search\n"
-"      vec3 OffsetVector = vec3(EyeVector.xy * (1.0 / EyeVector.z) * depthbias * OffsetMapping_Scale * vec2(-0.1, 0.1), -0.1);\n"
+"      //vec3 OffsetVector = vec3(EyeVector.xy * (1.0 / EyeVector.z) * depthbias * OffsetMapping_Scale * vec2(-0.1, 0.1), -0.1);\n"
+"      //vec3 OffsetVector = vec3(normalize(EyeVector.xy) * OffsetMapping_Scale * vec2(-0.1, 0.1), -0.1);\n"
+"      vec3 OffsetVector = vec3(eyedir.xy * OffsetMapping_Scale * vec2(-0.1, 0.1), -0.1);\n"
 "      vec3 RT = vec3(TexCoord - OffsetVector.xy * 10.0, 1.0) + OffsetVector;\n"
 "      if (RT.z > texture2D(Texture_Normal, RT.xy).a) RT += OffsetVector;\n"
 "      if (RT.z > texture2D(Texture_Normal, RT.xy).a) RT += OffsetVector;\n"
@@ -469,20 +492,54 @@ static const char *builtinshaderstring =
 "      if (RT.z > texture2D(Texture_Normal, RT.xy).a) RT += OffsetVector;OffsetVector *= 0.5;RT -= OffsetVector;\n"
 "      if (RT.z > texture2D(Texture_Normal, RT.xy).a) RT += OffsetVector;OffsetVector *= 0.5;RT -= OffsetVector;\n"
 "      TexCoord = RT.xy;\n"
-"#else\n"
+"#elif 1\n"
 "      // 3 sample offset mapping (only 3 samples because of ATI Radeon 9500-9800/X300 limits)\n"
-"      vec2 OffsetVector = vec2((EyeVector.xy * (1.0 / EyeVector.z) * depthbias) * OffsetMapping_Scale * vec2(-0.333, 0.333));\n"
+"      //vec2 OffsetVector = vec2(EyeVector.xy * (1.0 / EyeVector.z) * depthbias) * OffsetMapping_Scale * vec2(-0.333, 0.333);\n"
+"      //vec2 OffsetVector = vec2(normalize(EyeVector.xy)) * OffsetMapping_Scale * vec2(-0.333, 0.333);\n"
+"      vec2 OffsetVector = vec2(eyedir.xy) * OffsetMapping_Scale * vec2(-0.333, 0.333);\n"
 "      //TexCoord += OffsetVector * 3.0;\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"
+"#elif 0\n"
+"      // 10 sample offset mapping\n"
+"      //vec2 OffsetVector = vec2(EyeVector.xy * (1.0 / EyeVector.z) * depthbias) * OffsetMapping_Scale * vec2(-0.333, 0.333);\n"
+"      //vec2 OffsetVector = vec2(normalize(EyeVector.xy)) * OffsetMapping_Scale * vec2(-0.333, 0.333);\n"
+"      vec2 OffsetVector = vec2(eyedir.xy) * OffsetMapping_Scale * vec2(-0.1, 0.1);\n"
+"      //TexCoord += OffsetVector * 3.0;\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"
+"      TexCoord -= OffsetVector * texture2D(Texture_Normal, TexCoord).a;\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"
+"      TexCoord -= OffsetVector * texture2D(Texture_Normal, TexCoord).a;\n"
+"      TexCoord -= OffsetVector * texture2D(Texture_Normal, TexCoord).a;\n"
+"      TexCoord -= OffsetVector * texture2D(Texture_Normal, TexCoord).a;\n"
+"#elif 1\n"
+"      // parallax mapping as described in the paper\n"
+"      // 'Parallax Mapping with Offset Limiting: A Per-Pixel Approximation of Uneven Surfaces' by Terry Welsh\n"
+"      // The paper provides code in the ARB fragment program assembly language\n"
+"      // I translated it to GLSL but may have done something wrong - SavageX\n"
+"      // LordHavoc: removed bias and simplified to one line\n"
+"      // LordHavoc: this is just a single sample offsetmapping...\n"
+"      TexCoordOffset += vec2(eyedir.x, -1.0 * eyedir.y) * OffsetMapping_Scale * texture2D(Texture_Normal, TexCoord).a;\n"
+"#else\n"
+"      // parallax mapping as described in the paper\n"
+"      // 'Parallax Mapping with Offset Limiting: A Per-Pixel Approximation of Uneven Surfaces' by Terry Welsh\n"
+"      // The paper provides code in the ARB fragment program assembly language\n"
+"      // I translated it to GLSL but may have done something wrong - SavageX\n"
+"      float height = texture2D(Texture_Normal, TexCoord).a;\n"
+"      height = (height - 0.5) * OffsetMapping_Scale; // bias and scale\n"
+"      TexCoordOffset += height * vec2(eyedir.x, -1.0 * eyedir.y);\n"
 "#endif\n"
 "#endif\n"
 "\n"
 "      // combine the diffuse textures (base, pants, shirt)\n"
-"      vec4 color = vec4(texture2D(Texture_Color, TexCoord));\n"
+"      myhvec4 color = myhvec4(texture2D(Texture_Color, TexCoord));\n"
 "#ifdef USECOLORMAPPING\n"
-"      color.rgb += vec3(texture2D(Texture_Pants, TexCoord)) * Color_Pants + vec3(texture2D(Texture_Shirt, TexCoord)) * Color_Shirt;\n"
+"      color.rgb += myhvec3(texture2D(Texture_Pants, TexCoord)) * Color_Pants + myhvec3(texture2D(Texture_Shirt, TexCoord)) * Color_Shirt;\n"
 "#endif\n"
 "\n"
 "\n"
@@ -492,20 +549,20 @@ static const char *builtinshaderstring =
 "      // light source\n"
 "\n"
 "      // get the surface normal and light normal\n"
-"      vec3 surfacenormal = normalize(vec3(texture2D(Texture_Normal, TexCoord)) - 0.5);\n"
-"      vec3 diffusenormal = vec3(normalize(LightVector));\n"
+"      myhvec3 surfacenormal = normalize(myhvec3(texture2D(Texture_Normal, TexCoord)) - myhvec3(0.5));\n"
+"      myhvec3 diffusenormal = myhvec3(normalize(LightVector));\n"
 "\n"
 "      // calculate directional shading\n"
-"      color.rgb *= (AmbientScale + DiffuseScale * max(dot(surfacenormal, diffusenormal), 0.0));\n"
+"      color.rgb *= AmbientScale + DiffuseScale * myhalf(max(float(dot(surfacenormal, diffusenormal)), 0.0));\n"
 "#ifdef USESPECULAR\n"
-"      vec3 specularnormal = vec3(normalize(diffusenormal + vec3(normalize(EyeVector))));\n"
-"      color.rgb += vec3(texture2D(Texture_Gloss, TexCoord)) * SpecularScale * pow(max(dot(surfacenormal, specularnormal), 0.0), SpecularPower);\n"
+"      myhvec3 specularnormal = normalize(diffusenormal + myhvec3(normalize(EyeVector)));\n"
+"      color.rgb += myhvec3(texture2D(Texture_Gloss, TexCoord)) * SpecularScale * pow(myhalf(max(float(dot(surfacenormal, specularnormal)), 0.0)), SpecularPower);\n"
 "#endif\n"
 "\n"
 "#ifdef USECUBEFILTER\n"
 "      // apply light cubemap filter\n"
 "      //color.rgb *= normalize(CubeVector) * 0.5 + 0.5;//vec3(textureCube(Texture_Cube, CubeVector));\n"
-"      color.rgb *= vec3(textureCube(Texture_Cube, CubeVector));\n"
+"      color.rgb *= myhvec3(textureCube(Texture_Cube, CubeVector));\n"
 "#endif\n"
 "\n"
 "      // apply light color\n"
@@ -519,7 +576,7 @@ static const char *builtinshaderstring =
 "      //\n"
 "      // pow(1-(x*x+y*y+z*z), 4) is far more realistic but needs large lights to\n"
 "      // provide significant illumination, large = slow = pain.\n"
-"      color.rgb *= max(1.0 - dot(CubeVector, CubeVector), 0.0);\n"
+"      color.rgb *= myhalf(max(1.0 - dot(CubeVector, CubeVector), 0.0));\n"
 "\n"
 "\n"
 "\n"
@@ -528,85 +585,87 @@ static const char *builtinshaderstring =
 "      // directional model lighting\n"
 "\n"
 "      // get the surface normal and light normal\n"
-"      vec3 surfacenormal = normalize(vec3(texture2D(Texture_Normal, TexCoord)) - 0.5);\n"
-"      vec3 diffusenormal = vec3(normalize(LightVector));\n"
+"      myhvec3 surfacenormal = normalize(myhvec3(texture2D(Texture_Normal, TexCoord)) - myhvec3(0.5));\n"
+"      myhvec3 diffusenormal = myhvec3(normalize(LightVector));\n"
 "\n"
 "      // calculate directional shading\n"
-"      color.rgb *= AmbientColor + DiffuseColor * max(dot(surfacenormal, diffusenormal), 0.0);\n"
+"      color.rgb *= AmbientColor + DiffuseColor * myhalf(max(float(dot(surfacenormal, diffusenormal)), 0.0));\n"
 "#ifdef USESPECULAR\n"
-"      vec3 specularnormal = vec3(normalize(diffusenormal + vec3(normalize(EyeVector))));\n"
-"      color.rgb += vec3(texture2D(Texture_Gloss, TexCoord)) * SpecularColor * pow(max(dot(surfacenormal, specularnormal), 0.0), SpecularPower);\n"
+"      myhvec3 specularnormal = normalize(diffusenormal + myhvec3(normalize(EyeVector)));\n"
+"      color.rgb += myhvec3(texture2D(Texture_Gloss, TexCoord)) * SpecularColor * pow(myhalf(max(float(dot(surfacenormal, specularnormal)), 0.0)), SpecularPower);\n"
 "#endif\n"
 "\n"
 "\n"
 "\n"
 "\n"
-"#elif defined(MODE_LIGHTDIRECTIONMAP_MODELSPACE)\n"
+"#elif defined(MODE_LIGHTDIRECTIONMAP_MODELSPACE) || defined(MODE_LIGHTDIRECTIONMAP_TANGENTSPACE)\n"
 "      // deluxemap lightmapping using light vectors in modelspace (evil q3map2)\n"
 "\n"
 "      // get the surface normal and light normal\n"
-"      vec3 surfacenormal = normalize(vec3(texture2D(Texture_Normal, TexCoord)) - 0.5);\n"
-"      vec3 diffusenormal_modelspace = vec3(texture2D(Texture_Deluxemap, TexCoordLightmap)) - 0.5;\n"
-"      vec3 diffusenormal = normalize(vec3(dot(diffusenormal_modelspace, VectorS), dot(diffusenormal_modelspace, VectorT), dot(diffusenormal_modelspace, VectorR)));\n"
+"      myhvec3 surfacenormal = normalize(myhvec3(texture2D(Texture_Normal, TexCoord)) - myhvec3(0.5));\n"
 "\n"
-"      // calculate directional shading\n"
-"      vec3 tempcolor = color.rgb * (DiffuseScale * max(dot(surfacenormal, diffusenormal), 0.0));\n"
-"#ifdef USESPECULAR\n"
-"      vec3 specularnormal = vec3(normalize(diffusenormal + vec3(normalize(EyeVector))));\n"
-"      tempcolor += vec3(texture2D(Texture_Gloss, TexCoord)) * SpecularScale * pow(max(dot(surfacenormal, specularnormal), 0.0), SpecularPower);\n"
+"#ifdef MODE_LIGHTDIRECTIONMAP_MODELSPACE\n"
+"      myhvec3 diffusenormal_modelspace = myhvec3(texture2D(Texture_Deluxemap, TexCoordLightmap)) - myhvec3(0.5);\n"
+"      myhvec3 diffusenormal = normalize(myhvec3(dot(diffusenormal_modelspace, myhvec3(VectorS)), dot(diffusenormal_modelspace, myhvec3(VectorT)), dot(diffusenormal_modelspace, myhvec3(VectorR))));\n"
+"#else\n"
+"      myhvec3 diffusenormal = normalize(myhvec3(texture2D(Texture_Deluxemap, TexCoordLightmap)) - myhvec3(0.5));\n"
 "#endif\n"
-"\n"
-"      // apply lightmap color\n"
-"      color.rgb = tempcolor * vec3(texture2D(Texture_Lightmap, TexCoordLightmap)) + color.rgb * vec3(AmbientScale);\n"
-"\n"
-"\n"
-"\n"
-"\n"
-"#elif defined(MODE_LIGHTDIRECTIONMAP_TANGENTSPACE)\n"
-"      // deluxemap lightmapping using light vectors in tangentspace\n"
-"\n"
-"      // get the surface normal and light normal\n"
-"      vec3 surfacenormal = normalize(vec3(texture2D(Texture_Normal, TexCoord)) - 0.5);\n"
-"      vec3 diffusenormal = normalize(vec3(texture2D(Texture_Deluxemap, TexCoordLightmap)) - 0.5);\n"
-"\n"
 "      // calculate directional shading\n"
-"      vec3 tempcolor = color.rgb * (DiffuseScale * max(dot(surfacenormal, diffusenormal), 0.0));\n"
+"      myhvec3 tempcolor = color.rgb * (DiffuseScale * myhalf(max(float(dot(surfacenormal, diffusenormal)), 0.0)));\n"
 "#ifdef USESPECULAR\n"
-"      vec3 specularnormal = vec3(normalize(diffusenormal + vec3(normalize(EyeVector))));\n"
-"      tempcolor += vec3(texture2D(Texture_Gloss, TexCoord)) * SpecularScale * pow(max(dot(surfacenormal, specularnormal), 0.0), SpecularPower);\n"
+"      myhvec3 specularnormal = myhvec3(normalize(diffusenormal + myhvec3(normalize(EyeVector))));\n"
+"      tempcolor += myhvec3(texture2D(Texture_Gloss, TexCoord)) * SpecularScale * pow(myhalf(max(float(dot(surfacenormal, specularnormal)), 0.0)), SpecularPower);\n"
 "#endif\n"
 "\n"
 "      // apply lightmap color\n"
-"      color.rgb = tempcolor * vec3(texture2D(Texture_Lightmap, TexCoordLightmap)) + color.rgb * vec3(AmbientScale);\n"
-"\n"
-"\n"
+"      color.rgb = tempcolor * myhvec3(texture2D(Texture_Lightmap, TexCoordLightmap)) + color.rgb * AmbientScale;\n"
 "\n"
 "\n"
 "#else // MODE none (lightmap)\n"
 "      // apply lightmap color\n"
-"      color.rgb *= vec3(texture2D(Texture_Lightmap, TexCoordLightmap)) * DiffuseScale + vec3(AmbientScale);\n"
+"      color.rgb *= myhvec3(texture2D(Texture_Lightmap, TexCoordLightmap)) * DiffuseScale + myhvec3(AmbientScale);\n"
 "#endif // MODE\n"
 "\n"
-"      color *= gl_Color;\n"
+"      color *= myhvec4(gl_Color);\n"
 "\n"
 "#ifdef USEGLOW\n"
-"      color.rgb += vec3(texture2D(Texture_Glow, TexCoord));\n"
+"      color.rgb += myhvec3(texture2D(Texture_Glow, TexCoord)) * GlowScale;\n"
 "#endif\n"
 "\n"
 "#ifdef USEFOG\n"
 "      // apply fog\n"
-"      float fog = texture2D(Texture_FogMask, vec2(length(EyeVectorModelSpace)*FogRangeRecip, 0.0)).x;\n"
+"      myhalf fog = myhalf(texture2D(Texture_FogMask, myhvec2(length(EyeVectorModelSpace)*FogRangeRecip, 0.0)).x);\n"
 "      color.rgb = color.rgb * fog + FogColor * (1.0 - fog);\n"
 "#endif\n"
 "\n"
-"      gl_FragColor = color;\n"
+"      color.rgb *= SceneBrightness;\n"
+"\n"
+"      gl_FragColor = vec4(color);\n"
 "}\n"
 "\n"
 "#endif // FRAGMENT_SHADER\n"
 ;
 
+// NOTE: MUST MATCH ORDER OF SHADERPERMUTATION_* DEFINES!
+const char *permutationinfo[][2] =
+{
+       {"#define MODE_LIGHTSOURCE\n", " lightsource"},
+       {"#define MODE_LIGHTDIRECTIONMAP_MODELSPACE\n", " lightdirectionmap_modelspace"},
+       {"#define MODE_LIGHTDIRECTIONMAP_TANGENTSPACE\n", " lightdirectionmap_tangentspace"},
+       {"#define MODE_LIGHTDIRECTION\n", " lightdirection"},
+       {"#define USEGLOW\n", " glow"},
+       {"#define USEFOG\n", " fog"},
+       {"#define USECOLORMAPPING\n", " colormapping"},
+       {"#define USESPECULAR\n", " specular"},
+       {"#define USECUBEFILTER\n", " cubefilter"},
+       {"#define USEOFFSETMAPPING\n", " offsetmapping"},
+       {"#define USEOFFSETMAPPING_RELIEFMAPPING\n", " reliefmapping"},
+       {NULL, NULL}
+};
+
 void R_GLSL_CompilePermutation(int permutation)
 {
+       int i;
        r_glsl_permutation_t *p = r_glsl_permutations + permutation;
        int vertstrings_count;
        int fragstrings_count;
@@ -622,71 +681,20 @@ void R_GLSL_CompilePermutation(int permutation)
        vertstrings_count = 1;
        fragstrings_count = 1;
        permutationname[0] = 0;
-       if (permutation & SHADERPERMUTATION_MODE_LIGHTSOURCE)
-       {
-               vertstrings_list[vertstrings_count++] = "#define MODE_LIGHTSOURCE\n";
-               fragstrings_list[fragstrings_count++] = "#define MODE_LIGHTSOURCE\n";
-               strlcat(permutationname, " lightsource", sizeof(permutationname));
-       }
-       if (permutation & SHADERPERMUTATION_MODE_LIGHTDIRECTIONMAP_MODELSPACE)
-       {
-               vertstrings_list[vertstrings_count++] = "#define MODE_LIGHTDIRECTIONMAP_MODELSPACE\n";
-               fragstrings_list[fragstrings_count++] = "#define MODE_LIGHTDIRECTIONMAP_MODELSPACE\n";
-               strlcat(permutationname, " lightdirectionmap_modelspace", sizeof(permutationname));
-       }
-       if (permutation & SHADERPERMUTATION_MODE_LIGHTDIRECTIONMAP_TANGENTSPACE)
-       {
-               vertstrings_list[vertstrings_count++] = "#define MODE_LIGHTDIRECTIONMAP_TANGENTSPACE\n";
-               fragstrings_list[fragstrings_count++] = "#define MODE_LIGHTDIRECTIONMAP_TANGENTSPACE\n";
-               strlcat(permutationname, " lightdirectionmap_tangentspace", sizeof(permutationname));
-       }
-       if (permutation & SHADERPERMUTATION_MODE_LIGHTDIRECTION)
-       {
-               vertstrings_list[vertstrings_count++] = "#define MODE_LIGHTDIRECTION\n";
-               fragstrings_list[fragstrings_count++] = "#define MODE_LIGHTDIRECTION\n";
-               strlcat(permutationname, " lightdirection", sizeof(permutationname));
-       }
-       if (permutation & SHADERPERMUTATION_GLOW)
-       {
-               vertstrings_list[vertstrings_count++] = "#define USEGLOW\n";
-               fragstrings_list[fragstrings_count++] = "#define USEGLOW\n";
-               strlcat(permutationname, " glow", sizeof(permutationname));
-       }
-       if (permutation & SHADERPERMUTATION_COLORMAPPING)
-       {
-               vertstrings_list[vertstrings_count++] = "#define USECOLORMAPPING\n";
-               fragstrings_list[fragstrings_count++] = "#define USECOLORMAPPING\n";
-               strlcat(permutationname, " colormapping", sizeof(permutationname));
-       }
-       if (permutation & SHADERPERMUTATION_SPECULAR)
-       {
-               vertstrings_list[vertstrings_count++] = "#define USESPECULAR\n";
-               fragstrings_list[fragstrings_count++] = "#define USESPECULAR\n";
-               strlcat(permutationname, " specular", sizeof(permutationname));
-       }
-       if (permutation & SHADERPERMUTATION_FOG)
-       {
-               vertstrings_list[vertstrings_count++] = "#define USEFOG\n";
-               fragstrings_list[fragstrings_count++] = "#define USEFOG\n";
-               strlcat(permutationname, " fog", sizeof(permutationname));
-       }
-       if (permutation & SHADERPERMUTATION_CUBEFILTER)
-       {
-               vertstrings_list[vertstrings_count++] = "#define USECUBEFILTER\n";
-               fragstrings_list[fragstrings_count++] = "#define USECUBEFILTER\n";
-               strlcat(permutationname, " cubefilter", sizeof(permutationname));
-       }
-       if (permutation & SHADERPERMUTATION_OFFSETMAPPING)
-       {
-               vertstrings_list[vertstrings_count++] = "#define USEOFFSETMAPPING\n";
-               fragstrings_list[fragstrings_count++] = "#define USEOFFSETMAPPING\n";
-               strlcat(permutationname, " offsetmapping", sizeof(permutationname));
-       }
-       if (permutation & SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING)
+       for (i = 0;permutationinfo[i][0];i++)
        {
-               vertstrings_list[vertstrings_count++] = "#define USEOFFSETMAPPING_RELIEFMAPPING\n";
-               fragstrings_list[fragstrings_count++] = "#define USEOFFSETMAPPING_RELIEFMAPPING\n";
-               strlcat(permutationname, " OFFSETMAPPING_RELIEFMAPPING", sizeof(permutationname));
+               if (permutation & (1<<i))
+               {
+                       vertstrings_list[vertstrings_count++] = permutationinfo[i][0];
+                       fragstrings_list[fragstrings_count++] = permutationinfo[i][0];
+                       strlcat(permutationname, permutationinfo[i][1], sizeof(permutationname));
+               }
+               else
+               {
+                       // keep line numbers correct
+                       vertstrings_list[vertstrings_count++] = "\n";
+                       fragstrings_list[fragstrings_count++] = "\n";
+               }
        }
        shaderstring = (char *)FS_LoadFile("glsl/default.glsl", r_main_mempool, false, NULL);
        if (shaderstring)
@@ -726,6 +734,8 @@ void R_GLSL_CompilePermutation(int permutation)
                p->loc_DiffuseScale        = qglGetUniformLocationARB(p->program, "DiffuseScale");
                p->loc_SpecularPower       = qglGetUniformLocationARB(p->program, "SpecularPower");
                p->loc_SpecularScale       = qglGetUniformLocationARB(p->program, "SpecularScale");
+               p->loc_GlowScale           = qglGetUniformLocationARB(p->program, "GlowScale");
+               p->loc_SceneBrightness     = qglGetUniformLocationARB(p->program, "SceneBrightness");
                p->loc_OffsetMapping_Scale = qglGetUniformLocationARB(p->program, "OffsetMapping_Scale");
                p->loc_AmbientColor        = qglGetUniformLocationARB(p->program, "AmbientColor");
                p->loc_DiffuseColor        = qglGetUniformLocationARB(p->program, "DiffuseColor");
@@ -781,9 +791,9 @@ int R_SetupSurfaceShader(const vec3_t lightcolorbase, qboolean modellighting)
                {
                        if (modellighting)
                                permutation |= SHADERPERMUTATION_MODE_LIGHTDIRECTION;
-                       else
+                       else if (r_glsl_deluxemapping.integer >= 1 && rsurface_lightmaptexture)
                        {
-                               if (r_glsl_deluxemapping.integer >= 1 && r_refdef.worldmodel && r_refdef.worldmodel->brushq3.deluxemapping && rsurface_lightmaptexture)
+                               if (r_refdef.worldmodel && r_refdef.worldmodel->brushq3.deluxemapping)
                                {
                                        if (r_refdef.worldmodel->brushq3.deluxemapping_modelspace)
                                                permutation |= SHADERPERMUTATION_MODE_LIGHTDIRECTIONMAP_MODELSPACE;
@@ -872,6 +882,8 @@ int R_SetupSurfaceShader(const vec3_t lightcolorbase, qboolean modellighting)
        //if (r_glsl_permutation->loc_Texture_Lightmap >= 0) R_Mesh_TexBind(7, R_GetTexture(r_texture_white));
        //if (r_glsl_permutation->loc_Texture_Deluxemap >= 0) R_Mesh_TexBind(8, R_GetTexture(r_texture_blanknormalmap));
        if (r_glsl_permutation->loc_Texture_Glow >= 0) R_Mesh_TexBind(9, R_GetTexture(rsurface_texture->skin.glow));
+       if (r_glsl_permutation->loc_GlowScale >= 0) qglUniform1fARB(r_glsl_permutation->loc_GlowScale, r_hdr_glowintensity.value);
+       if (r_glsl_permutation->loc_SceneBrightness >= 0) qglUniform1fARB(r_glsl_permutation->loc_SceneBrightness, r_view.colorscale);
        if (r_glsl_permutation->loc_FogColor >= 0)
        {
                // additive passes are only darkened by fog, not tinted
@@ -954,7 +966,7 @@ void gl_main_newmap(void)
                l = (int)strlen(entname) - 4;
                if (l >= 0 && !strcmp(entname + l, ".bsp"))
                {
-                       strcpy(entname + l, ".ent");
+                       memcpy(entname + l, ".ent", 5);
                        if ((entities = (char *)FS_LoadFile(entname, tempmempool, true, NULL)))
                        {
                                CL_ParseEntityLump(entities);
@@ -983,6 +995,7 @@ void GL_Main_Init(void)
        Cvar_RegisterVariable(&r_showcollisionbrushes_polygonfactor);
        Cvar_RegisterVariable(&r_showcollisionbrushes_polygonoffset);
        Cvar_RegisterVariable(&r_showdisabledepthtest);
+       Cvar_RegisterVariable(&r_drawportals);
        Cvar_RegisterVariable(&r_drawentities);
        Cvar_RegisterVariable(&r_drawviewmodel);
        Cvar_RegisterVariable(&r_speeds);
@@ -1005,6 +1018,10 @@ void GL_Main_Init(void)
        Cvar_RegisterVariable(&r_bloom_blur);
        Cvar_RegisterVariable(&r_bloom_resolution);
        Cvar_RegisterVariable(&r_bloom_power);
+       Cvar_RegisterVariable(&r_hdr);
+       Cvar_RegisterVariable(&r_hdr_scenebrightness);
+       Cvar_RegisterVariable(&r_hdr_bloomintensity);
+       Cvar_RegisterVariable(&r_hdr_glowintensity);
        Cvar_RegisterVariable(&r_smoothnormals_areaweighting);
        Cvar_RegisterVariable(&developer_texturelogging);
        Cvar_RegisterVariable(&gl_lightmaps);
@@ -1021,7 +1038,6 @@ extern void GL_Main_Init(void);
 extern void R_Shadow_Init(void);
 extern void R_Sky_Init(void);
 extern void GL_Surf_Init(void);
-extern void R_Crosshairs_Init(void);
 extern void R_Light_Init(void);
 extern void R_Particles_Init(void);
 extern void R_Explosion_Init(void);
@@ -1039,11 +1055,10 @@ void Render_Init(void)
        R_Shadow_Init();
        R_Sky_Init();
        GL_Surf_Init();
-       R_Crosshairs_Init();
+       Sbar_Init();
        R_Light_Init();
        R_Particles_Init();
        R_Explosion_Init();
-       Sbar_Init();
        R_LightningBeams_Init();
        Mod_RenderInit();
 }
@@ -1332,98 +1347,122 @@ static void R_View_SetFrustum(void)
 
 void R_View_Update(void)
 {
+       R_View_SetFrustum();
+       R_View_WorldVisibility();
+       R_View_UpdateEntityVisible();
+}
+
+void R_ResetViewRendering(void)
+{
+       if (gl_support_fragment_shader)
+       {
+               qglUseProgramObjectARB(0);CHECKGLERROR
+       }
+
        // GL is weird because it's bottom to top, r_view.y is top to bottom
        qglViewport(r_view.x, vid.height - (r_view.y + r_view.height), r_view.width, r_view.height);CHECKGLERROR
+       GL_SetupView_Mode_Ortho(0, 0, 1, 1, -10, 100);
        GL_Scissor(r_view.x, r_view.y, r_view.width, r_view.height);
        GL_ColorMask(r_view.colormask[0], r_view.colormask[1], r_view.colormask[2], 1);
-       R_View_SetFrustum();
-       R_View_WorldVisibility();
-       R_View_UpdateEntityVisible();
+       GL_ScissorTest(true);
+       GL_DepthMask(true);
+       GL_DepthTest(true);
+       R_Mesh_Matrix(&identitymatrix);
+       R_Mesh_ResetTextureState();
 }
 
-static void R_BlendView(void)
+void R_RenderScene(void);
+
+void R_Bloom_MakeTexture(qboolean darken)
 {
        int screenwidth, screenheight;
-       qboolean dobloom;
-       qboolean doblend;
+       int screentexturewidth, screentextureheight;
+       int bloomtexturewidth, bloomtextureheight;
+       int bloomwidth, bloomheight, x, range;
+       float xoffset, yoffset, r;
        float vertex3f[12];
        float texcoord2f[3][8];
 
+       // set bloomwidth and bloomheight to the bloom resolution that will be
+       // used (often less than the screen resolution for faster rendering)
+       bloomwidth = bound(1, r_bloom_resolution.integer, r_view.width);
+       bloomheight = bound(1, bloomwidth * r_view.height / r_view.width, r_view.height);
+
        // set the (poorly named) screenwidth and screenheight variables to
        // a power of 2 at least as large as the screen, these will define the
        // size of the texture to allocate
        for (screenwidth = 1;screenwidth < vid.width;screenwidth *= 2);
        for (screenheight = 1;screenheight < vid.height;screenheight *= 2);
 
-       doblend = r_refdef.viewblend[3] >= 0.01f;
-       dobloom = r_bloom.integer && screenwidth <= gl_max_texture_size && screenheight <= gl_max_texture_size && r_bloom_resolution.value >= 32 && r_bloom_power.integer >= 1 && r_bloom_power.integer < 100 && r_bloom_blur.value >= 0 && r_bloom_blur.value < 512;
+       r_refdef.stats.bloom++;
 
-       if (!dobloom && !doblend)
-               return;
+       // allocate textures as needed
+       // TODO: reallocate these when size settings change
+       if (!r_bloom_texture_screen)
+               r_bloom_texture_screen = R_LoadTexture2D(r_main_texturepool, "screen", screenwidth, screenheight, NULL, TEXTYPE_RGBA, TEXF_FORCENEAREST | TEXF_CLAMP | TEXF_ALWAYSPRECACHE, NULL);
+       if (!r_bloom_texture_bloom)
+               r_bloom_texture_bloom = R_LoadTexture2D(r_main_texturepool, "bloom", screenwidth, screenheight, NULL, TEXTYPE_RGBA, TEXF_FORCELINEAR | TEXF_CLAMP | TEXF_ALWAYSPRECACHE, NULL);
+
+       screentexturewidth = R_TextureWidth(r_bloom_texture_screen);
+       screentextureheight = R_TextureHeight(r_bloom_texture_screen);
+       bloomtexturewidth = R_TextureWidth(r_bloom_texture_bloom);
+       bloomtextureheight = R_TextureHeight(r_bloom_texture_bloom);
 
-       GL_SetupView_Mode_Ortho(0, 0, 1, 1, -10, 100);
-       GL_DepthMask(true);
-       GL_DepthTest(false);
-       R_Mesh_Matrix(&identitymatrix);
        // vertex coordinates for a quad that covers the screen exactly
        vertex3f[0] = 0;vertex3f[1] = 0;vertex3f[2] = 0;
        vertex3f[3] = 1;vertex3f[4] = 0;vertex3f[5] = 0;
        vertex3f[6] = 1;vertex3f[7] = 1;vertex3f[8] = 0;
        vertex3f[9] = 0;vertex3f[10] = 1;vertex3f[11] = 0;
+
+       // set up a texcoord array for the full resolution screen image
+       // (we have to keep this around to copy back during final render)
+       texcoord2f[0][0] = 0;
+       texcoord2f[0][1] = (float)r_view.height / (float)screentextureheight;
+       texcoord2f[0][2] = (float)r_view.width / (float)screentexturewidth;
+       texcoord2f[0][3] = (float)r_view.height / (float)screentextureheight;
+       texcoord2f[0][4] = (float)r_view.width / (float)screentexturewidth;
+       texcoord2f[0][5] = 0;
+       texcoord2f[0][6] = 0;
+       texcoord2f[0][7] = 0;
+
+       // set up a texcoord array for the reduced resolution bloom image
+       // (which will be additive blended over the screen image)
+       texcoord2f[1][0] = 0;
+       texcoord2f[1][1] = (float)bloomheight / (float)bloomtextureheight;
+       texcoord2f[1][2] = (float)bloomwidth / (float)bloomtexturewidth;
+       texcoord2f[1][3] = (float)bloomheight / (float)bloomtextureheight;
+       texcoord2f[1][4] = (float)bloomwidth / (float)bloomtexturewidth;
+       texcoord2f[1][5] = 0;
+       texcoord2f[1][6] = 0;
+       texcoord2f[1][7] = 0;
+
+       R_ResetViewRendering();
+       GL_DepthTest(false);
        R_Mesh_VertexPointer(vertex3f);
        R_Mesh_ColorPointer(NULL);
-       R_Mesh_ResetTextureState();
-       if (dobloom)
+
+       R_Mesh_TexCoordPointer(0, 2, texcoord2f[0]);
+       R_Mesh_TexBind(0, R_GetTexture(r_bloom_texture_screen));
+
+       // copy view into the screen texture
+       GL_ActiveTexture(0);
+       CHECKGLERROR
+       qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_view.x, vid.height - (r_view.y + r_view.height), r_view.width, r_view.height);CHECKGLERROR
+       r_refdef.stats.bloom_copypixels += r_view.width * r_view.height;
+
+       // now scale it down to the bloom texture size
+       CHECKGLERROR
+       qglViewport(r_view.x, vid.height - (r_view.y + bloomheight), bloomwidth, bloomheight);CHECKGLERROR
+       GL_BlendFunc(GL_ONE, GL_ZERO);
+       GL_Color(1, 1, 1, 1);
+       // TODO: optimize with multitexture or GLSL
+       R_Mesh_Draw(0, 4, 2, polygonelements);
+       r_refdef.stats.bloom_drawpixels += bloomwidth * bloomheight;
+
+       if (darken)
        {
-               int bloomwidth, bloomheight, x, range;
-               float xoffset, yoffset, r;
-               r_refdef.stats.bloom++;
-               // allocate textures as needed
-               if (!r_bloom_texture_screen)
-                       r_bloom_texture_screen = R_LoadTexture2D(r_main_texturepool, "screen", screenwidth, screenheight, NULL, TEXTYPE_RGBA, TEXF_FORCENEAREST | TEXF_CLAMP | TEXF_ALWAYSPRECACHE, NULL);
-               if (!r_bloom_texture_bloom)
-                       r_bloom_texture_bloom = R_LoadTexture2D(r_main_texturepool, "bloom", screenwidth, screenheight, NULL, TEXTYPE_RGBA, TEXF_FORCELINEAR | TEXF_CLAMP | TEXF_ALWAYSPRECACHE, NULL);
-               // set bloomwidth and bloomheight to the bloom resolution that will be
-               // used (often less than the screen resolution for faster rendering)
-               bloomwidth = min(r_view.width, r_bloom_resolution.integer);
-               bloomheight = min(r_view.height, bloomwidth * r_view.height / r_view.width);
-               // set up a texcoord array for the full resolution screen image
-               // (we have to keep this around to copy back during final render)
-               texcoord2f[0][0] = 0;
-               texcoord2f[0][1] = (float)r_view.height / (float)screenheight;
-               texcoord2f[0][2] = (float)r_view.width / (float)screenwidth;
-               texcoord2f[0][3] = (float)r_view.height / (float)screenheight;
-               texcoord2f[0][4] = (float)r_view.width / (float)screenwidth;
-               texcoord2f[0][5] = 0;
-               texcoord2f[0][6] = 0;
-               texcoord2f[0][7] = 0;
-               // set up a texcoord array for the reduced resolution bloom image
-               // (which will be additive blended over the screen image)
-               texcoord2f[1][0] = 0;
-               texcoord2f[1][1] = (float)bloomheight / (float)screenheight;
-               texcoord2f[1][2] = (float)bloomwidth / (float)screenwidth;
-               texcoord2f[1][3] = (float)bloomheight / (float)screenheight;
-               texcoord2f[1][4] = (float)bloomwidth / (float)screenwidth;
-               texcoord2f[1][5] = 0;
-               texcoord2f[1][6] = 0;
-               texcoord2f[1][7] = 0;
-               R_Mesh_TexCoordPointer(0, 2, texcoord2f[0]);
-               R_Mesh_TexBind(0, R_GetTexture(r_bloom_texture_screen));
-               // copy view into the full resolution screen image texture
-               GL_ActiveTexture(0);
-               CHECKGLERROR
-               qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_view.x, vid.height - (r_view.y + r_view.height), r_view.width, r_view.height);CHECKGLERROR
-               r_refdef.stats.bloom_copypixels += r_view.width * r_view.height;
-               // now scale it down to the bloom size and raise to a power of itself
-               // to darken it (this leaves the really bright stuff bright, and
-               // everything else becomes very dark)
-               // TODO: optimize with multitexture or GLSL
-               CHECKGLERROR
-               qglViewport(r_view.x, vid.height - (r_view.y + bloomheight), bloomwidth, bloomheight);CHECKGLERROR
-               GL_BlendFunc(GL_ONE, GL_ZERO);
-               GL_Color(1, 1, 1, 1);
-               R_Mesh_Draw(0, 4, 2, polygonelements);
-               r_refdef.stats.bloom_drawpixels += bloomwidth * bloomheight;
+               // raise to a power of itself to darken it (this leaves the really
+               // bright stuff bright, and everything else becomes very dark)
                // render multiple times with a multiply blendfunc to raise to a power
                GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
                for (x = 1;x < r_bloom_power.integer;x++)
@@ -1431,86 +1470,204 @@ static void R_BlendView(void)
                        R_Mesh_Draw(0, 4, 2, polygonelements);
                        r_refdef.stats.bloom_drawpixels += bloomwidth * bloomheight;
                }
-               // we now have a darkened bloom image in the framebuffer, copy it into
-               // the bloom image texture for more processing
+       }
+
+       // we now have a darkened bloom image in the framebuffer
+       // copy it into the bloom image texture for more processing
+       R_Mesh_TexBind(0, R_GetTexture(r_bloom_texture_bloom));
+       R_Mesh_TexCoordPointer(0, 2, texcoord2f[2]);
+       GL_ActiveTexture(0);
+       CHECKGLERROR
+       qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_view.x, vid.height - (r_view.y + bloomheight), bloomwidth, bloomheight);CHECKGLERROR
+       r_refdef.stats.bloom_copypixels += bloomwidth * bloomheight;
+
+       // blend on at multiple vertical offsets to achieve a vertical blur
+       // TODO: do offset blends using GLSL
+       range = r_bloom_blur.integer * bloomwidth / 320;
+       GL_BlendFunc(GL_ONE, GL_ZERO);
+       for (x = -range;x <= range;x++)
+       {
+               xoffset = 0 / (float)bloomwidth * (float)bloomwidth / (float)screenwidth;
+               yoffset = x / (float)bloomheight * (float)bloomheight / (float)screenheight;
+               // compute a texcoord array with the specified x and y offset
+               texcoord2f[2][0] = xoffset+0;
+               texcoord2f[2][1] = yoffset+(float)bloomheight / (float)screenheight;
+               texcoord2f[2][2] = xoffset+(float)bloomwidth / (float)screenwidth;
+               texcoord2f[2][3] = yoffset+(float)bloomheight / (float)screenheight;
+               texcoord2f[2][4] = xoffset+(float)bloomwidth / (float)screenwidth;
+               texcoord2f[2][5] = yoffset+0;
+               texcoord2f[2][6] = xoffset+0;
+               texcoord2f[2][7] = yoffset+0;
+               // this r value looks like a 'dot' particle, fading sharply to
+               // black at the edges
+               // (probably not realistic but looks good enough)
+               r = r_bloom_intensity.value/(range*2+1)*(1 - x*x/(float)(range*range));
+               if (r < 0.01f)
+                       continue;
+               GL_Color(r, r, r, 1);
+               R_Mesh_Draw(0, 4, 2, polygonelements);
+               r_refdef.stats.bloom_drawpixels += bloomwidth * bloomheight;
+               GL_BlendFunc(GL_ONE, GL_ONE);
+       }
+
+       // copy the vertically blurred bloom view to a texture
+       GL_ActiveTexture(0);
+       CHECKGLERROR
+       qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_view.x, vid.height - (r_view.y + bloomheight), bloomwidth, bloomheight);CHECKGLERROR
+       r_refdef.stats.bloom_copypixels += bloomwidth * bloomheight;
+
+       // blend the vertically blurred image at multiple offsets horizontally
+       // to finish the blur effect
+       // TODO: do offset blends using GLSL
+       range = r_bloom_blur.integer * bloomwidth / 320;
+       GL_BlendFunc(GL_ONE, GL_ZERO);
+       for (x = -range;x <= range;x++)
+       {
+               xoffset = x / (float)bloomwidth * (float)bloomwidth / (float)screenwidth;
+               yoffset = 0 / (float)bloomheight * (float)bloomheight / (float)screenheight;
+               // compute a texcoord array with the specified x and y offset
+               texcoord2f[2][0] = xoffset+0;
+               texcoord2f[2][1] = yoffset+(float)bloomheight / (float)screenheight;
+               texcoord2f[2][2] = xoffset+(float)bloomwidth / (float)screenwidth;
+               texcoord2f[2][3] = yoffset+(float)bloomheight / (float)screenheight;
+               texcoord2f[2][4] = xoffset+(float)bloomwidth / (float)screenwidth;
+               texcoord2f[2][5] = yoffset+0;
+               texcoord2f[2][6] = xoffset+0;
+               texcoord2f[2][7] = yoffset+0;
+               // this r value looks like a 'dot' particle, fading sharply to
+               // black at the edges
+               // (probably not realistic but looks good enough)
+               r = r_bloom_intensity.value/(range*2+1)*(1 - x*x/(float)(range*range));
+               if (r < 0.01f)
+                       continue;
+               GL_Color(r, r, r, 1);
+               R_Mesh_Draw(0, 4, 2, polygonelements);
+               r_refdef.stats.bloom_drawpixels += bloomwidth * bloomheight;
+               GL_BlendFunc(GL_ONE, GL_ONE);
+       }
+
+       // copy the blurred bloom view to a texture
+       GL_ActiveTexture(0);
+       CHECKGLERROR
+       qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_view.x, vid.height - (r_view.y + bloomheight), bloomwidth, bloomheight);CHECKGLERROR
+       r_refdef.stats.bloom_copypixels += bloomwidth * bloomheight;
+}
+
+void R_HDR_RenderBloomTexture(void)
+{
+       int oldwidth, oldheight;
+
+       oldwidth = r_view.width;
+       oldheight = r_view.height;
+       r_view.width = bound(1, r_bloom_resolution.integer, min(r_view.width, gl_max_texture_size));
+       r_view.height = r_view.width * oldheight / oldwidth;
+
+       // TODO: support GL_EXT_framebuffer_object rather than reusing the framebuffer?  it might improve SLI performance.
+       // FIXME: change global lightmapintensity and light intensity according to r_hdr_bloomintensity cvar
+       // FIXME: change global lightmapintensity and light intensity according to r_hdr_scenebrightness cvar
+       // TODO: add exposure compensation features
+
+       r_view.colorscale = r_hdr_bloomintensity.value * r_hdr_scenebrightness.value;
+       R_RenderScene();
+
+       R_ResetViewRendering();
+
+       R_Bloom_MakeTexture(false);
+
+       R_ClearScreen();
+       if (r_timereport_active)
+               R_TimeReport("clear");
+
+       // restore the view settings
+       r_view.width = oldwidth;
+       r_view.height = oldheight;
+
+       // go back to full view area
+       R_ResetViewRendering();
+}
+
+static void R_BlendView(void)
+{
+       int screenwidth, screenheight;
+       int bloomwidth, bloomheight;
+       qboolean dobloom;
+       qboolean dohdr;
+       qboolean doblend;
+       float vertex3f[12];
+       float texcoord2f[3][8];
+
+       // set the (poorly named) screenwidth and screenheight variables to
+       // a power of 2 at least as large as the screen, these will define the
+       // size of the texture to allocate
+       for (screenwidth = 1;screenwidth < vid.width;screenwidth *= 2);
+       for (screenheight = 1;screenheight < vid.height;screenheight *= 2);
+
+       doblend = r_refdef.viewblend[3] >= 0.01f;
+       dobloom = !r_hdr.integer && r_bloom.integer && screenwidth <= gl_max_texture_size && screenheight <= gl_max_texture_size && r_bloom_resolution.value >= 32 && r_bloom_power.integer >= 1 && r_bloom_power.integer < 100 && r_bloom_blur.value >= 0 && r_bloom_blur.value < 512;
+       dohdr = r_hdr.integer && screenwidth <= gl_max_texture_size && screenheight <= gl_max_texture_size && r_bloom_resolution.value >= 32 && r_bloom_power.integer >= 1 && r_bloom_power.integer < 100 && r_bloom_blur.value >= 0 && r_bloom_blur.value < 512;
+
+       if (!dobloom && !dohdr && !doblend)
+               return;
+
+       // vertex coordinates for a quad that covers the screen exactly
+       vertex3f[0] = 0;vertex3f[1] = 0;vertex3f[2] = 0;
+       vertex3f[3] = 1;vertex3f[4] = 0;vertex3f[5] = 0;
+       vertex3f[6] = 1;vertex3f[7] = 1;vertex3f[8] = 0;
+       vertex3f[9] = 0;vertex3f[10] = 1;vertex3f[11] = 0;
+
+       // set bloomwidth and bloomheight to the bloom resolution that will be
+       // used (often less than the screen resolution for faster rendering)
+       bloomwidth = min(r_view.width, r_bloom_resolution.integer);
+       bloomheight = min(r_view.height, bloomwidth * r_view.height / r_view.width);
+       // set up a texcoord array for the full resolution screen image
+       // (we have to keep this around to copy back during final render)
+       texcoord2f[0][0] = 0;
+       texcoord2f[0][1] = (float)r_view.height / (float)screenheight;
+       texcoord2f[0][2] = (float)r_view.width / (float)screenwidth;
+       texcoord2f[0][3] = (float)r_view.height / (float)screenheight;
+       texcoord2f[0][4] = (float)r_view.width / (float)screenwidth;
+       texcoord2f[0][5] = 0;
+       texcoord2f[0][6] = 0;
+       texcoord2f[0][7] = 0;
+       // set up a texcoord array for the reduced resolution bloom image
+       // (which will be additive blended over the screen image)
+       texcoord2f[1][0] = 0;
+       texcoord2f[1][1] = (float)bloomheight / (float)screenheight;
+       texcoord2f[1][2] = (float)bloomwidth / (float)screenwidth;
+       texcoord2f[1][3] = (float)bloomheight / (float)screenheight;
+       texcoord2f[1][4] = (float)bloomwidth / (float)screenwidth;
+       texcoord2f[1][5] = 0;
+       texcoord2f[1][6] = 0;
+       texcoord2f[1][7] = 0;
+
+       if (dohdr)
+       {
+               // render high dynamic range bloom effect
+               // the bloom texture was made earlier this render, so we just need to
+               // blend it onto the screen...
+               R_ResetViewRendering();
+               GL_DepthTest(false);
+               R_Mesh_VertexPointer(vertex3f);
+               R_Mesh_ColorPointer(NULL);
+               GL_Color(1, 1, 1, 1);
+               GL_BlendFunc(GL_ONE, GL_ONE);
                R_Mesh_TexBind(0, R_GetTexture(r_bloom_texture_bloom));
-               R_Mesh_TexCoordPointer(0, 2, texcoord2f[2]);
-               GL_ActiveTexture(0);
-               CHECKGLERROR
-               qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_view.x, vid.height - (r_view.y + bloomheight), bloomwidth, bloomheight);CHECKGLERROR
-               r_refdef.stats.bloom_copypixels += bloomwidth * bloomheight;
-               // blend on at multiple vertical offsets to achieve a vertical blur
-               // TODO: do offset blends using GLSL
-               range = r_bloom_blur.integer * bloomwidth / 320;
-               GL_BlendFunc(GL_ONE, GL_ZERO);
-               for (x = -range;x <= range;x++)
-               {
-                       xoffset = 0 / (float)bloomwidth * (float)bloomwidth / (float)screenwidth;
-                       yoffset = x / (float)bloomheight * (float)bloomheight / (float)screenheight;
-                       // compute a texcoord array with the specified x and y offset
-                       texcoord2f[2][0] = xoffset+0;
-                       texcoord2f[2][1] = yoffset+(float)bloomheight / (float)screenheight;
-                       texcoord2f[2][2] = xoffset+(float)bloomwidth / (float)screenwidth;
-                       texcoord2f[2][3] = yoffset+(float)bloomheight / (float)screenheight;
-                       texcoord2f[2][4] = xoffset+(float)bloomwidth / (float)screenwidth;
-                       texcoord2f[2][5] = yoffset+0;
-                       texcoord2f[2][6] = xoffset+0;
-                       texcoord2f[2][7] = yoffset+0;
-                       // this r value looks like a 'dot' particle, fading sharply to
-                       // black at the edges
-                       // (probably not realistic but looks good enough)
-                       r = r_bloom_intensity.value/(range*2+1)*(1 - x*x/(float)(range*range));
-                       if (r < 0.01f)
-                               continue;
-                       GL_Color(r, r, r, 1);
-                       R_Mesh_Draw(0, 4, 2, polygonelements);
-                       r_refdef.stats.bloom_drawpixels += bloomwidth * bloomheight;
-                       GL_BlendFunc(GL_ONE, GL_ONE);
-               }
-               // copy the vertically blurred bloom view to a texture
-               GL_ActiveTexture(0);
-               CHECKGLERROR
-               qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_view.x, vid.height - (r_view.y + bloomheight), bloomwidth, bloomheight);CHECKGLERROR
-               r_refdef.stats.bloom_copypixels += bloomwidth * bloomheight;
-               // blend the vertically blurred image at multiple offsets horizontally
-               // to finish the blur effect
-               // TODO: do offset blends using GLSL
-               range = r_bloom_blur.integer * bloomwidth / 320;
-               GL_BlendFunc(GL_ONE, GL_ZERO);
-               for (x = -range;x <= range;x++)
-               {
-                       xoffset = x / (float)bloomwidth * (float)bloomwidth / (float)screenwidth;
-                       yoffset = 0 / (float)bloomheight * (float)bloomheight / (float)screenheight;
-                       // compute a texcoord array with the specified x and y offset
-                       texcoord2f[2][0] = xoffset+0;
-                       texcoord2f[2][1] = yoffset+(float)bloomheight / (float)screenheight;
-                       texcoord2f[2][2] = xoffset+(float)bloomwidth / (float)screenwidth;
-                       texcoord2f[2][3] = yoffset+(float)bloomheight / (float)screenheight;
-                       texcoord2f[2][4] = xoffset+(float)bloomwidth / (float)screenwidth;
-                       texcoord2f[2][5] = yoffset+0;
-                       texcoord2f[2][6] = xoffset+0;
-                       texcoord2f[2][7] = yoffset+0;
-                       // this r value looks like a 'dot' particle, fading sharply to
-                       // black at the edges
-                       // (probably not realistic but looks good enough)
-                       r = r_bloom_intensity.value/(range*2+1)*(1 - x*x/(float)(range*range));
-                       if (r < 0.01f)
-                               continue;
-                       GL_Color(r, r, r, 1);
-                       R_Mesh_Draw(0, 4, 2, polygonelements);
-                       r_refdef.stats.bloom_drawpixels += bloomwidth * bloomheight;
-                       GL_BlendFunc(GL_ONE, GL_ONE);
-               }
-               // copy the blurred bloom view to a texture
-               GL_ActiveTexture(0);
-               CHECKGLERROR
-               qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_view.x, vid.height - (r_view.y + bloomheight), bloomwidth, bloomheight);CHECKGLERROR
-               r_refdef.stats.bloom_copypixels += bloomwidth * bloomheight;
-               // go back to full view area
-               qglViewport(r_view.x, vid.height - (r_view.y + r_view.height), r_view.width, r_view.height);CHECKGLERROR
+               R_Mesh_TexCoordPointer(0, 2, texcoord2f[1]);
+               R_Mesh_Draw(0, 4, 2, polygonelements);
+               r_refdef.stats.bloom_drawpixels += r_view.width * r_view.height;
+       }
+       if (dobloom)
+       {
+               // render simple bloom effect
+               // make the bloom texture
+               R_Bloom_MakeTexture(true);
                // put the original screen image back in place and blend the bloom
                // texture on it
-               GL_Color(1,1,1,1);
+               R_ResetViewRendering();
+               GL_DepthTest(false);
+               R_Mesh_VertexPointer(vertex3f);
+               R_Mesh_ColorPointer(NULL);
+               GL_Color(1, 1, 1, 1);
                GL_BlendFunc(GL_ONE, GL_ZERO);
                // do both in one pass if possible
                R_Mesh_TexBind(0, R_GetTexture(r_bloom_texture_screen));
@@ -1536,7 +1693,10 @@ static void R_BlendView(void)
        if (doblend)
        {
                // apply a color tint to the whole view
-               R_Mesh_ResetTextureState();
+               R_ResetViewRendering();
+               GL_DepthTest(false);
+               R_Mesh_VertexPointer(vertex3f);
+               R_Mesh_ColorPointer(NULL);
                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, 2, polygonelements);
@@ -1648,46 +1808,17 @@ void R_RenderView(void)
        if (r_timereport_active)
                R_TimeReport("visibility");
 
-       R_ClearScreen();
-       if (r_timereport_active)
-               R_TimeReport("clear");
-
-       R_RenderScene();
-
-       R_BlendView();
-       if (r_timereport_active)
-               R_TimeReport("blendview");
-
-       GL_Scissor(0, 0, vid.width, vid.height);
-       GL_ScissorTest(false);
-       CHECKGLERROR
-}
-
-//[515]: csqc
-void CSQC_R_ClearScreen (void)
-{
-       if (!r_refdef.entities/* || !r_refdef.worldmodel*/)
-               return; //Host_Error ("R_RenderView: NULL worldmodel");
-
-       CHECKGLERROR
-       GL_ScissorTest(true);
-       GL_DepthMask(true);
-       if (r_timereport_active)
-               R_TimeReport("setup");
-
-       R_View_Update();
-       if (r_timereport_active)
-               R_TimeReport("visibility");
+       R_ResetViewRendering();
 
        R_ClearScreen();
        if (r_timereport_active)
                R_TimeReport("clear");
-       CHECKGLERROR
-}
 
-//[515]: csqc
-void CSQC_R_RenderScene (void)
-{
+       // this produces a bloom texture to be used in R_BlendView() later
+       if (r_hdr.integer)
+               R_HDR_RenderBloomTexture();
+
+       r_view.colorscale = r_hdr_scenebrightness.value;
        R_RenderScene();
 
        R_BlendView();
@@ -1701,8 +1832,11 @@ void CSQC_R_RenderScene (void)
 
 extern void R_DrawLightningBeams (void);
 extern void VM_AddPolygonsToMeshQueue (void);
+extern void R_DrawPortals (void);
 void R_RenderScene(void)
 {
+       DrawQ_Finish();
+
        // don't let sound skip if going slow
        if (r_refdef.extraupdate)
                S_ExtraUpdate ();
@@ -1716,6 +1850,8 @@ void R_RenderScene(void)
        qglPolygonOffset(r_refdef.polygonfactor, r_refdef.polygonoffset);CHECKGLERROR
        qglEnable(GL_POLYGON_OFFSET_FILL);CHECKGLERROR
 
+       R_ResetViewRendering();
+
        R_MeshQueue_BeginScene();
 
        if (r_refdef.rtworldshadows || r_refdef.rtdlightshadows)
@@ -1796,6 +1932,13 @@ void R_RenderScene(void)
        }
        VM_AddPolygonsToMeshQueue();
 
+       if (r_drawportals.integer)
+       {
+               R_DrawPortals();
+               if (r_timereport_active)
+                       R_TimeReport("portals");
+       }
+
        if (gl_support_fragment_shader)
        {
                qglUseProgramObjectARB(0);CHECKGLERROR
@@ -1815,12 +1958,6 @@ void R_RenderScene(void)
                if (r_timereport_active)
                        R_TimeReport("coronas");
        }
-       if(cl.csqc_vidvars.drawcrosshair)
-       {
-               R_DrawWorldCrosshair();
-               if (r_timereport_active)
-                       R_TimeReport("crosshair");
-       }
 
        // don't let sound skip if going slow
        if (r_refdef.extraupdate)
@@ -2029,22 +2166,25 @@ void R_DrawSprite(int blendfunc1, int blendfunc2, rtexture_t *texture, rtexture_
        R_Mesh_ResetTextureState();
        R_Mesh_TexBind(0, R_GetTexture(texture));
        R_Mesh_TexCoordPointer(0, 2, spritetexcoord2f);
-       GL_Color(cr * ifog, cg * ifog, cb * ifog, ca);
+       // FIXME: fixed function path can't properly handle r_view.colorscale > 1
+       GL_Color(cr * ifog * r_view.colorscale, cg * ifog * r_view.colorscale, cb * ifog * r_view.colorscale, ca);
        R_Mesh_Draw(0, 4, 2, polygonelements);
 
        if (blendfunc2 == GL_ONE_MINUS_SRC_ALPHA)
        {
                R_Mesh_TexBind(0, R_GetTexture(fogtexture));
                GL_BlendFunc(blendfunc1, GL_ONE);
-               GL_Color(r_refdef.fogcolor[0] * fog, r_refdef.fogcolor[1] * fog, r_refdef.fogcolor[2] * fog, ca);
+               GL_Color(r_refdef.fogcolor[0] * fog * r_view.colorscale, r_refdef.fogcolor[1] * fog * r_view.colorscale, r_refdef.fogcolor[2] * fog * r_view.colorscale, ca);
                R_Mesh_Draw(0, 4, 2, polygonelements);
        }
 }
 
-int R_Mesh_AddVertex3f(rmesh_t *mesh, const float *v)
+int R_Mesh_AddVertex(rmesh_t *mesh, float x, float y, float z)
 {
        int i;
        float *vertex3f;
+       float v[3];
+       VectorSet(v, x, y, z);
        for (i = 0, vertex3f = mesh->vertex3f;i < mesh->numvertices;i++, vertex3f += 3)
                if (VectorDistance2(v, vertex3f) < mesh->epsilon2)
                        break;
@@ -2065,12 +2205,33 @@ void R_Mesh_AddPolygon3f(rmesh_t *mesh, int numvertices, float *vertex3f)
 {
        int i;
        int *e, element[3];
-       element[0] = R_Mesh_AddVertex3f(mesh, vertex3f);vertex3f += 3;
-       element[1] = R_Mesh_AddVertex3f(mesh, vertex3f);vertex3f += 3;
+       element[0] = R_Mesh_AddVertex(mesh, vertex3f[0], vertex3f[1], vertex3f[2]);vertex3f += 3;
+       element[1] = R_Mesh_AddVertex(mesh, vertex3f[0], vertex3f[1], vertex3f[2]);vertex3f += 3;
        e = mesh->element3i + mesh->numtriangles * 3;
        for (i = 0;i < numvertices - 2;i++, vertex3f += 3)
        {
-               element[2] = R_Mesh_AddVertex3f(mesh, vertex3f);
+               element[2] = R_Mesh_AddVertex(mesh, vertex3f[0], vertex3f[1], vertex3f[2]);
+               if (mesh->numtriangles < mesh->maxtriangles)
+               {
+                       *e++ = element[0];
+                       *e++ = element[1];
+                       *e++ = element[2];
+                       mesh->numtriangles++;
+               }
+               element[1] = element[2];
+       }
+}
+
+void R_Mesh_AddPolygon3d(rmesh_t *mesh, int numvertices, double *vertex3d)
+{
+       int i;
+       int *e, element[3];
+       element[0] = R_Mesh_AddVertex(mesh, vertex3d[0], vertex3d[1], vertex3d[2]);vertex3d += 3;
+       element[1] = R_Mesh_AddVertex(mesh, vertex3d[0], vertex3d[1], vertex3d[2]);vertex3d += 3;
+       e = mesh->element3i + mesh->numtriangles * 3;
+       for (i = 0;i < numvertices - 2;i++, vertex3d += 3)
+       {
+               element[2] = R_Mesh_AddVertex(mesh, vertex3d[0], vertex3d[1], vertex3d[2]);
                if (mesh->numtriangles < mesh->maxtriangles)
                {
                        *e++ = element[0];
@@ -2082,29 +2243,37 @@ void R_Mesh_AddPolygon3f(rmesh_t *mesh, int numvertices, float *vertex3f)
        }
 }
 
+#define R_MESH_PLANE_DIST_EPSILON (1.0 / 32.0)
 void R_Mesh_AddBrushMeshFromPlanes(rmesh_t *mesh, int numplanes, mplane_t *planes)
 {
        int planenum, planenum2;
        int w;
        int tempnumpoints;
        mplane_t *plane, *plane2;
-       float temppoints[2][256*3];
+       double maxdist;
+       double temppoints[2][256*3];
+       // figure out how large a bounding box we need to properly compute this brush
+       maxdist = 0;
+       for (w = 0;w < numplanes;w++)
+               maxdist = max(maxdist, planes[w].dist);
+       // now make it large enough to enclose the entire brush, and round it off to a reasonable multiple of 1024
+       maxdist = floor(maxdist * (4.0 / 1024.0) + 1) * 1024.0;
        for (planenum = 0, plane = planes;planenum < numplanes;planenum++, plane++)
        {
                w = 0;
                tempnumpoints = 4;
-               PolygonF_QuadForPlane(temppoints[w], plane->normal[0], plane->normal[1], plane->normal[2], plane->normal[3], 1024.0*1024.0*1024.0);
+               PolygonD_QuadForPlane(temppoints[w], plane->normal[0], plane->normal[1], plane->normal[2], plane->dist, maxdist);
                for (planenum2 = 0, plane2 = planes;planenum2 < numplanes && tempnumpoints >= 3;planenum2++, plane2++)
                {
                        if (planenum2 == planenum)
                                continue;
-                       PolygonF_Divide(tempnumpoints, temppoints[w], plane2->normal[0], plane2->normal[1], plane2->normal[2], plane2->dist, 1.0/32.0, 0, NULL, NULL, 256, temppoints[!w], &tempnumpoints, NULL);
+                       PolygonD_Divide(tempnumpoints, temppoints[w], plane2->normal[0], plane2->normal[1], plane2->normal[2], plane2->dist, R_MESH_PLANE_DIST_EPSILON, 0, NULL, NULL, 256, temppoints[!w], &tempnumpoints, NULL);
                        w = !w;
                }
                if (tempnumpoints < 3)
                        continue;
                // generate elements forming a triangle fan for this polygon
-               R_Mesh_AddPolygon3f(mesh, tempnumpoints, temppoints[w]);
+               R_Mesh_AddPolygon3d(mesh, tempnumpoints, temppoints[w]);
        }
 }
 
@@ -2113,7 +2282,7 @@ static void R_DrawCollisionBrush(const colbrushf_t *brush)
        int i;
        R_Mesh_VertexPointer(brush->points->v);
        i = (int)(((size_t)brush) / sizeof(colbrushf_t));
-       GL_Color((i & 31) * (1.0f / 32.0f), ((i >> 5) & 31) * (1.0f / 32.0f), ((i >> 10) & 31) * (1.0f / 32.0f), 0.2f);
+       GL_Color((i & 31) * (1.0f / 32.0f) * r_view.colorscale, ((i >> 5) & 31) * (1.0f / 32.0f) * r_view.colorscale, ((i >> 10) & 31) * (1.0f / 32.0f) * r_view.colorscale, 0.2f);
        GL_LockArrays(0, brush->numpoints);
        R_Mesh_Draw(0, brush->numpoints, brush->numtriangles, brush->elements);
        GL_LockArrays(0, 0);
@@ -2126,7 +2295,7 @@ static void R_DrawCollisionSurface(const entity_render_t *ent, const msurface_t
                return;
        R_Mesh_VertexPointer(surface->data_collisionvertex3f);
        i = (int)(((size_t)surface) / sizeof(msurface_t));
-       GL_Color((i & 31) * (1.0f / 32.0f), ((i >> 5) & 31) * (1.0f / 32.0f), ((i >> 10) & 31) * (1.0f / 32.0f), 0.2f);
+       GL_Color((i & 31) * (1.0f / 32.0f) * r_view.colorscale, ((i >> 5) & 31) * (1.0f / 32.0f) * r_view.colorscale, ((i >> 10) & 31) * (1.0f / 32.0f) * r_view.colorscale, 0.2f);
        GL_LockArrays(0, surface->num_collisionvertices);
        R_Mesh_Draw(0, surface->num_collisionvertices, surface->num_collisiontriangles, surface->data_collisionelement3i);
        GL_LockArrays(0, 0);
@@ -2142,9 +2311,9 @@ static void R_Texture_AddLayer(texture_t *t, qboolean depthmask, int blendfunc1,
        layer->blendfunc2 = blendfunc2;
        layer->texture = texture;
        layer->texmatrix = *matrix;
-       layer->color[0] = r;
-       layer->color[1] = g;
-       layer->color[2] = b;
+       layer->color[0] = r * r_view.colorscale;
+       layer->color[1] = g * r_view.colorscale;
+       layer->color[2] = b * r_view.colorscale;
        layer->color[3] = a;
 }
 
@@ -2288,7 +2457,7 @@ void R_UpdateTextureInfo(const entity_render_t *ent, texture_t *t)
                                        }
                                }
                                if (t->skin.glow != NULL)
-                                       R_Texture_AddLayer(t, false, GL_SRC_ALPHA, GL_ONE, TEXTURELAYERTYPE_TEXTURE, t->skin.glow, &t->currenttexmatrix, 1, 1, 1, t->currentalpha);
+                                       R_Texture_AddLayer(t, false, GL_SRC_ALPHA, GL_ONE, TEXTURELAYERTYPE_TEXTURE, t->skin.glow, &t->currenttexmatrix, r_hdr_glowintensity.value, r_hdr_glowintensity.value, r_hdr_glowintensity.value, t->currentalpha);
                                if (r_refdef.fogenabled && !(t->currentmaterialflags & MATERIALFLAG_ADD))
                                {
                                        // if this is opaque use alpha blend which will darken the earlier
@@ -2497,7 +2666,7 @@ void RSurf_PrepareVerticesForBatch(qboolean generatenormals, qboolean generateta
                                for (i = 0;i < 4;i++)
                                        Matrix4x4_Transform(&imatrix1, (rsurface_vertex3f + 3 * surface->num_firstvertex) + (j+i)*3, v[i]);
                                for (i = 0;i < 4;i++)
-                                       VectorMAMAMAM(1, center, v[i][0], forward, v[i][1], right, v[i][2], up, rsurface_array_modelvertex3f + (surface->num_firstvertex+i+j) * 3);
+                                       VectorMAMAMAM(1, center, v[i][0], forward, v[i][1], right, v[i][2], up, rsurface_array_deformedvertex3f + (surface->num_firstvertex+i+j) * 3);
                        }
                        Mod_BuildNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, rsurface_modelvertex3f, rsurface_model->surfmesh.data_element3i + surface->num_firsttriangle * 3, rsurface_array_deformednormal3f, r_smoothnormals_areaweighting.integer);
                        Mod_BuildTextureVectorsFromNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, rsurface_modelvertex3f, rsurface_model->surfmesh.data_texcoordtexture2f, rsurface_array_deformednormal3f, rsurface_model->surfmesh.data_element3i + surface->num_firsttriangle * 3, rsurface_array_deformedsvector3f, rsurface_array_deformedtvector3f, r_smoothnormals_areaweighting.integer);
@@ -2601,7 +2770,7 @@ static void RSurf_DrawBatch_ShowSurfaces(int texturenumsurfaces, msurface_t **te
        {
                const msurface_t *surface = texturesurfacelist[texturesurfaceindex];
                int k = (int)(((size_t)surface) / sizeof(msurface_t));
-               GL_Color((k & 15) * (1.0f / 16.0f), ((k >> 4) & 15) * (1.0f / 16.0f), ((k >> 8) & 15) * (1.0f / 16.0f), 0.2f);
+               GL_Color((k & 15) * (1.0f / 16.0f) * r_view.colorscale, ((k >> 4) & 15) * (1.0f / 16.0f) * r_view.colorscale, ((k >> 8) & 15) * (1.0f / 16.0f) * r_view.colorscale, 0.2f);
                GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
                R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, (rsurface_model->surfmesh.data_element3i + 3 * surface->num_firsttriangle));
        }
@@ -2810,7 +2979,7 @@ static void R_DrawTextureSurfaceList_Sky(int texturenumsurfaces, msurface_t **te
        // level, so don't use it then either.
        if (rsurface_model->type == mod_brushq1 && r_q1bsp_skymasking.integer && !r_viewcache.world_novis)
        {
-               GL_Color(r_refdef.fogcolor[0], r_refdef.fogcolor[1], r_refdef.fogcolor[2], 1);
+               GL_Color(r_refdef.fogcolor[0] * r_view.colorscale, r_refdef.fogcolor[1] * r_view.colorscale, r_refdef.fogcolor[2] * r_view.colorscale, 1);
                R_Mesh_ColorPointer(NULL);
                R_Mesh_ResetTextureState();
                if (skyrendermasked)
@@ -3389,11 +3558,11 @@ void R_DrawSurfaces(entity_render_t *ent, qboolean skysurfaces)
                                if (r_showtris.integer)
                                {
                                        if (!rsurface_texture->currentlayers->depthmask)
-                                               GL_Color(r_showtris.value, 0, 0, 1);
+                                               GL_Color(r_showtris.value * r_view.colorscale, 0, 0, 1);
                                        else if (ent == r_refdef.worldentity)
-                                               GL_Color(r_showtris.value, r_showtris.value, r_showtris.value, 1);
+                                               GL_Color(r_showtris.value * r_view.colorscale, r_showtris.value * r_view.colorscale, r_showtris.value * r_view.colorscale, 1);
                                        else
-                                               GL_Color(0, r_showtris.value, 0, 1);
+                                               GL_Color(0, r_showtris.value * r_view.colorscale, 0, 1);
                                        elements = (ent->model->surfmesh.data_element3i + 3 * surface->num_firsttriangle);
                                        CHECKGLERROR
                                        qglBegin(GL_LINES);
@@ -3408,7 +3577,7 @@ void R_DrawSurfaces(entity_render_t *ent, qboolean skysurfaces)
                                }
                                if (r_shownormals.integer)
                                {
-                                       GL_Color(r_shownormals.value, 0, 0, 1);
+                                       GL_Color(r_shownormals.value * r_view.colorscale, 0, 0, 1);
                                        qglBegin(GL_LINES);
                                        for (k = 0, l = surface->num_firstvertex;k < surface->num_vertices;k++, l++)
                                        {
@@ -3419,7 +3588,7 @@ void R_DrawSurfaces(entity_render_t *ent, qboolean skysurfaces)
                                        }
                                        qglEnd();
                                        CHECKGLERROR
-                                       GL_Color(0, 0, r_shownormals.value, 1);
+                                       GL_Color(0, 0, r_shownormals.value * r_view.colorscale, 1);
                                        qglBegin(GL_LINES);
                                        for (k = 0, l = surface->num_firstvertex;k < surface->num_vertices;k++, l++)
                                        {
@@ -3430,7 +3599,7 @@ void R_DrawSurfaces(entity_render_t *ent, qboolean skysurfaces)
                                        }
                                        qglEnd();
                                        CHECKGLERROR
-                                       GL_Color(0, r_shownormals.value, 0, 1);
+                                       GL_Color(0, r_shownormals.value * r_view.colorscale, 0, 1);
                                        qglBegin(GL_LINES);
                                        for (k = 0, l = surface->num_firstvertex;k < surface->num_vertices;k++, l++)
                                        {