]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - gl_rmain.c
fix bugs with bbox vs bbox traces (the collision box's planes didn't have correct...
[xonotic/darkplaces.git] / gl_rmain.c
index a7dc2c3e0cb5ec48246e620dca1a4c75c114d282..4bc35ef45e34b8fbab9280557a1ae4f13099cd7a 100644 (file)
@@ -99,11 +99,12 @@ cvar_t gl_fogend = {0, "gl_fogend","0", "nehahra fog end distance (for Nehahra c
 cvar_t r_textureunits = {0, "r_textureunits", "32", "number of hardware texture units reported by driver (note: setting this to 1 turns off gl_combine)"};
 
 cvar_t r_glsl = {0, "r_glsl", "1", "enables use of OpenGL 2.0 pixel shaders for lighting"};
-cvar_t r_glsl_offsetmapping = {0, "r_glsl_offsetmapping", "0", "enables offset mapping effect (also known as parallax mapping or sometimes as virtual displacement mapping, not as good as relief mapping or silohuette mapping but much faster), can cause strange artifacts on many textures, requires bumpmaps for depth information (normalmaps can have depth information as alpha channel, but most do not)"};
-cvar_t r_glsl_offsetmapping_scale = {0, "r_glsl_offsetmapping_scale", "-0.04", "how deep the offset mapping effect is, and whether it is inward or outward"};
-cvar_t r_glsl_offsetmapping_bias = {0, "r_glsl_offsetmapping_bias", "0.04", "pushes the effect closer/further"};
+cvar_t r_glsl_offsetmapping = {0, "r_glsl_offsetmapping", "0", "offset mapping effect (also known as parallax mapping or virtual displacement mapping)"};
+cvar_t r_glsl_offsetmapping_reliefmapping = {0, "r_glsl_offsetmapping_reliefmapping", "0", "relief mapping effect (higher quality)"};
+cvar_t r_glsl_offsetmapping_scale = {0, "r_glsl_offsetmapping_scale", "0.04", "how deep the offset mapping effect is"};
 cvar_t r_glsl_usehalffloat = {0, "r_glsl_usehalffloat", "0", "use half and hvec variables in GLSL shader for a speed gain (NVIDIA only)"};
 cvar_t r_glsl_surfacenormalize = {0, "r_glsl_surfacenormalize", "1", "normalize bumpmap texels in GLSL shader, produces a more rounded look on small bumps and dents"};
+cvar_t r_glsl_deluxemapping = {0, "r_glsl_deluxemapping", "1", "use per pixel lighting on deluxemap-compiled q3bsp maps (or a value of 2 forces deluxemap shading even without deluxemaps)"};
 
 cvar_t r_lerpsprites = {CVAR_SAVE, "r_lerpsprites", "1", "enables animation smoothing on sprites (requires r_lerpmodels 1)"};
 cvar_t r_lerpmodels = {CVAR_SAVE, "r_lerpmodels", "1", "enables animation smoothing on models"};
@@ -428,9 +429,15 @@ static const char *builtinshaderstring =
 "#endif\n"
 "\n"
 "varying vec2 TexCoord;\n"
+"#if !defined(MODE_LIGHTSOURCE) && !defined(MODE_LIGHTDIRECTION)\n"
+"varying vec2 TexCoordLightmap;\n"
+"#endif\n"
 "\n"
-"#ifdef USELIGHTSOURCE\n"
+"#ifdef MODE_LIGHTSOURCE\n"
 "varying myhvec3 CubeVector;\n"
+"#endif\n"
+"\n"
+"#if defined(MODE_LIGHTSOURCE) || defined(MODE_LIGHTDIRECTION)\n"
 "varying vec3 LightVector;\n"
 "#endif\n"
 "\n"
@@ -438,13 +445,19 @@ static const char *builtinshaderstring =
 "varying vec3 EyeVector;\n"
 "#endif\n"
 "\n"
+"#ifdef MODE_LIGHTDIRECTIONMAP_MODELSPACE\n"
+"varying myhvec3 VectorS; // direction of S texcoord (sometimes crudely called tangent)\n"
+"varying myhvec3 VectorT; // direction of T texcoord (sometimes crudely called binormal)\n"
+"varying myhvec3 VectorR; // direction of R texcoord (surface normal)\n"
+"#endif\n"
+"\n"
 "\n"
 "\n"
 "\n"
 "// vertex shader specific:\n"
 "#ifdef VERTEX_SHADER\n"
 "\n"
-"#ifdef USELIGHTSOURCE\n"
+"#ifdef MODE_LIGHTSOURCE\n"
 "uniform vec3 LightPosition;\n"
 "#endif\n"
 "\n"
@@ -452,14 +465,22 @@ static const char *builtinshaderstring =
 "uniform vec3 EyePosition;\n"
 "#endif\n"
 "\n"
+"#ifdef MODE_LIGHTDIRECTION\n"
+"uniform myhvec3 LightDir;\n"
+"#endif\n"
+"\n"
 "// TODO: get rid of tangentt (texcoord2) and use a crossproduct to regenerate it from tangents (texcoord1) and normal (texcoord3)\n"
 "\n"
 "void main(void)\n"
 "{\n"
 "      // copy the surface texcoord\n"
 "      TexCoord = vec2(gl_TextureMatrix[0] * gl_MultiTexCoord0);\n"
+"#if !defined(MODE_LIGHTSOURCE) && !defined(MODE_LIGHTDIRECTION)\n"
+"      TexCoordLightmap = vec2(gl_MultiTexCoord4);\n"
+"#endif\n"
+"      gl_FrontColor = gl_Color;\n"
 "\n"
-"#ifdef USELIGHTSOURCE\n"
+"#ifdef MODE_LIGHTSOURCE\n"
 "      // transform vertex position into light attenuation/cubemap space\n"
 "      // (-1 to +1 across the light box)\n"
 "      CubeVector = vec3(gl_TextureMatrix[3] * gl_Vertex);\n"
@@ -473,6 +494,12 @@ static const char *builtinshaderstring =
 "      LightVector.z = dot(lightminusvertex, gl_MultiTexCoord3.xyz);\n"
 "#endif\n"
 "\n"
+"#ifdef MODE_LIGHTDIRECTION\n"
+"      LightVector.x = dot(LightDir, gl_MultiTexCoord1.xyz);\n"
+"      LightVector.y = dot(LightDir, gl_MultiTexCoord2.xyz);\n"
+"      LightVector.z = dot(LightDir, gl_MultiTexCoord3.xyz);\n"
+"#endif\n"
+"\n"
 "#if defined(USESPECULAR) || defined(USEFOG) || defined(USEOFFSETMAPPING)\n"
 "      // transform unnormalized eye direction into tangent space\n"
 "      vec3 eyeminusvertex = EyePosition - gl_Vertex.xyz;\n"
@@ -481,6 +508,12 @@ static const char *builtinshaderstring =
 "      EyeVector.z = dot(eyeminusvertex, gl_MultiTexCoord3.xyz);\n"
 "#endif\n"
 "\n"
+"#ifdef MODE_LIGHTDIRECTIONMAP_MODELSPACE\n"
+"      VectorS = gl_MultiTexCoord1.xyz;\n"
+"      VectorT = gl_MultiTexCoord2.xyz;\n"
+"      VectorR = gl_MultiTexCoord3.xyz;\n"
+"#endif\n"
+"\n"
 "      // transform vertex to camera space, using ftransform to match non-VS\n"
 "      // rendering\n"
 "      gl_Position = ftransform();\n"
@@ -500,12 +533,29 @@ static const char *builtinshaderstring =
 "uniform myhalf OffsetMapping_Bias;\n"
 "#endif\n"
 "\n"
-"#if defined(USELIGHTSOURCE) || defined(USEDELUXEMAPPING)\n"
+"#if defined(MODE_LIGHTSOURCE) || defined(MODE_LIGHTDIRECTIONMAP_MODELSPACE) || defined(MODE_LIGHTDIRECTIONMAP_TANGENTSPACE) || defined(MODE_LIGHTDIRECTION) || defined(USEOFFSETMAPPING)\n"
 "uniform sampler2D Texture_Normal;\n"
 "#endif\n"
 "\n"
+"#ifdef MODE_LIGHTDIRECTION\n"
+"uniform myhvec3 AmbientColor;\n"
+"uniform myhvec3 DiffuseColor;\n"
+"uniform myhvec3 SpecularColor;\n"
+"#endif\n"
+"\n"
 "uniform sampler2D Texture_Color;\n"
 "\n"
+"#if !defined(MODE_LIGHTSOURCE) && !defined(MODE_LIGHTDIRECTION)\n"
+"uniform sampler2D Texture_Lightmap;\n"
+"#endif\n"
+"#if defined(MODE_LIGHTDIRECTIONMAP_MODELSPACE) || defined(MODE_LIGHTDIRECTIONMAP_TANGENTSPACE)\n"
+"uniform sampler2D Texture_Deluxemap;\n"
+"#endif\n"
+"\n"
+"#ifdef USEGLOW\n"
+"uniform sampler2D Texture_Glow;\n"
+"#endif\n"
+"\n"
 "#ifdef USECOLORMAPPING\n"
 "uniform sampler2D Texture_Pants;\n"
 "uniform sampler2D Texture_Shirt;\n"
@@ -526,20 +576,71 @@ static const char *builtinshaderstring =
 "#endif\n"
 "\n"
 "#ifdef USEFOG\n"
+"uniform myhvec3 FogColor;\n"
 "uniform myhalf FogRangeRecip;\n"
 "uniform sampler2D Texture_FogMask;\n"
 "#endif\n"
 "\n"
+"#ifdef USEEASTEREGG\n"
+"void main(void)\n"
+"{\n"
+"      gl_FragColor = myhvec4(0, 0, 0, 1);;\n"
+"      int i;\n"
+"      float o;\n"
+"      vec2 p = vec2(CubeVector.x * 16.0, CubeVector.y * 16.0);\n"
+"      vec2 c = vec2(CubeVector.x * 16.0, CubeVector.y * 16.0);\n"
+"      for (i = 0;i < 1000 && dot(p,p) < 4.0;i = i + 1)\n"
+"      {\n"
+"              o = p.x * p.x - p.y * p.y;\n"
+"              p.y = 2.0 * p.x * p.y;\n"
+"              p.x = o;\n"
+"              p += c;\n"
+"      }\n"
+"      o = float(i) * 0.314;\n"
+"      if (i < 1000)\n"
+"              gl_FragColor = vec4(cos(o), sin(o), sin(o * 0.2), 1);\n"
+"}\n"
+"#else // USEEASTEREGG\n"
+"\n"
+"\n"
+"\n"
 "void main(void)\n"
 "{\n"
 "      // apply offsetmapping\n"
 "#ifdef USEOFFSETMAPPING\n"
-"      // this is 3 sample because of ATI Radeon 9500-9800/X300 limits\n"
-"      myhvec2 OffsetVector = normalize(EyeVector).xy * vec2(-0.333, 0.333);\n"
-"      myhvec2 TexCoordOffset = TexCoord + OffsetVector * (OffsetMapping_Bias + OffsetMapping_Scale * texture2D(Texture_Normal, TexCoord).w);\n"
-"      TexCoordOffset += OffsetVector * (OffsetMapping_Bias + OffsetMapping_Scale * texture2D(Texture_Normal, TexCoordOffset).w);\n"
-"      TexCoordOffset += OffsetVector * (OffsetMapping_Bias + OffsetMapping_Scale * texture2D(Texture_Normal, TexCoordOffset).w);\n"
+"      myhvec2 TexCoordOffset = TexCoord;\n"
 "#define TexCoord TexCoordOffset\n"
+"\n"
+"      myhvec3 eyedir = myhvec3(normalize(EyeVector));\n"
+"      myhalf depthbias = 1.0 - eyedir.z; // should this be a -?\n"
+"      depthbias = 1.0 - depthbias * depthbias;\n"
+"\n"
+"#ifdef USEOFFSETMAPPING_RELIEFMAPPING\n"
+"      // 14 sample relief mapping: linear search and then binary search\n"
+"      myhvec3 OffsetVector = myhvec3(EyeVector.xy * (1.0 / EyeVector.z) * depthbias * OffsetMapping_Scale * myhvec2(-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"
+"      if (RT.z > texture2D(Texture_Normal, RT.xy).a) RT += 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"
+"      if (RT.z > texture2D(Texture_Normal, RT.xy).a) RT += 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"
+"      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"
+"      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"
+"      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"
+"      // 3 sample offset mapping (only 3 samples because of ATI Radeon 9500-9800/X300 limits)\n"
+"      myhvec2 OffsetVector = myhvec2((EyeVector.xy * (1.0 / EyeVector.z) * depthbias) * OffsetMapping_Scale * myhvec2(-0.333, 0.333));\n"
+"      TexCoord -= OffsetVector * texture2D(Texture_Normal, TexCoord).a;\n"
+"      TexCoord -= OffsetVector * texture2D(Texture_Normal, TexCoord).a;\n"
+"      TexCoord -= OffsetVector * texture2D(Texture_Normal, TexCoord).a;\n"
+"#endif\n"
 "#endif\n"
 "\n"
 "      // combine the diffuse textures (base, pants, shirt)\n"
@@ -551,7 +652,7 @@ static const char *builtinshaderstring =
 "\n"
 "\n"
 "\n"
-"#ifdef USELIGHTSOURCE\n"
+"#ifdef MODE_LIGHTSOURCE\n"
 "      // light source\n"
 "\n"
 "      // get the surface normal and light normal\n"
@@ -590,61 +691,106 @@ static const char *builtinshaderstring =
 "\n"
 "\n"
 "\n"
+"#elif defined(MODE_LIGHTDIRECTION)\n"
+"      // directional model lighting\n"
+"\n"
+"      // get the surface normal and light normal\n"
+"#ifdef SURFACENORMALIZE\n"
+"      myhvec3 surfacenormal = normalize(myhvec3(texture2D(Texture_Normal, TexCoord)) - 0.5);\n"
 "#else\n"
-"#ifdef USEDELUXEMAPPING\n"
-"      // deluxemap lightmapping\n"
+"      myhvec3 surfacenormal = -1.0 + 2.0 * myhvec3(texture2D(Texture_Normal, TexCoord));\n"
+"#endif\n"
+"      myhvec3 diffusenormal = myhvec3(normalize(LightVector));\n"
+"\n"
+"\n"
+"      // calculate directional shading\n"
+"      color.rgb *= AmbientColor + DiffuseColor * max(dot(surfacenormal, diffusenormal), 0.0);\n"
+"#ifdef USESPECULAR\n"
+"      myhvec3 specularnormal = myhvec3(normalize(diffusenormal + myhvec3(normalize(EyeVector))));\n"
+"      color.rgb += myhvec3(texture2D(Texture_Gloss, TexCoord)) * SpecularColor * pow(max(dot(surfacenormal, specularnormal), 0.0), SpecularPower);\n"
+"#endif\n"
+"\n"
+"\n"
+"\n"
+"\n"
+"#elif defined(MODE_LIGHTDIRECTIONMAP_MODELSPACE)\n"
+"      // deluxemap lightmapping using light vectors in modelspace (evil q3map2)\n"
 "\n"
 "      // get the surface normal and light normal\n"
 "#ifdef SURFACENORMALIZE\n"
 "      myhvec3 surfacenormal = normalize(myhvec3(texture2D(Texture_Normal, TexCoord)) - 0.5);\n"
-"      myhvec3 diffusenormal = normalize(myhvec3(texture2D(Texture_Deluxemap, TexCoordLightmap)));\n"
 "#else\n"
 "      myhvec3 surfacenormal = -1.0 + 2.0 * myhvec3(texture2D(Texture_Normal, TexCoord));\n"
-"      myhvec3 diffusenormal = myhvec3(texture2D(Texture_Deluxemap, TexCoordLightmap));\n"
 "#endif\n"
+"      myhvec3 diffusenormal_modelspace = myhvec3(texture2D(Texture_Deluxemap, TexCoordLightmap)) - 0.5;\n"
+"      myhvec3 diffusenormal = normalize(myhvec3(dot(diffusenormal_modelspace, VectorS), dot(diffusenormal_modelspace, VectorT), dot(diffusenormal_modelspace, VectorR)));\n"
 "\n"
 "      // calculate directional shading\n"
-"      color.rgb *= (AmbientScale + DiffuseScale * max(dot(surfacenormal, diffusenormal), 0.0));\n"
+"      myhvec3 tempcolor = color.rgb * (DiffuseScale * max(dot(surfacenormal, diffusenormal), 0.0));\n"
 "#ifdef USESPECULAR\n"
 "      myhvec3 specularnormal = myhvec3(normalize(diffusenormal + myhvec3(normalize(EyeVector))));\n"
-"      color.rgb += myhvec3(texture2D(Texture_Gloss, TexCoord)) * SpecularScale * pow(max(dot(surfacenormal, specularnormal), 0.0), SpecularPower);\n"
+"      tempcolor += myhvec3(texture2D(Texture_Gloss, TexCoord)) * SpecularScale * pow(max(dot(surfacenormal, specularnormal), 0.0), SpecularPower);\n"
 "#endif\n"
 "\n"
 "      // apply lightmap color\n"
-"      color.rgb *= myhvec3(texture2D(Texture_Lightmap, TexCoordLightmap));\n"
+"      color.rgb = tempcolor * myhvec3(texture2D(Texture_Lightmap, TexCoordLightmap)) + color.rgb * myhvec3(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"
+"#ifdef SURFACENORMALIZE\n"
+"      myhvec3 surfacenormal = normalize(myhvec3(texture2D(Texture_Normal, TexCoord)) - 0.5);\n"
+"      myhvec3 diffusenormal = normalize(myhvec3(texture2D(Texture_Deluxemap, TexCoordLightmap)) - 0.5);\n"
 "#else\n"
-"      // apply lightmap color\n"
-"      color.rgb *= myhvec3(texture2D(Texture_Lightmap, TexCoordLightmap)) * DiffuseScale;\n"
+"      myhvec3 surfacenormal = -1.0 + 2.0 * myhvec3(texture2D(Texture_Normal, TexCoord));\n"
+"      myhvec3 diffusenormal = -1.0 + 2.0 * myhvec3(texture2D(Texture_Deluxemap, TexCoordLightmap));\n"
 "#endif\n"
+"\n"
+"      // calculate directional shading\n"
+"      myhvec3 tempcolor = color.rgb * (DiffuseScale * max(dot(surfacenormal, diffusenormal), 0.0));\n"
+"#ifdef USESPECULAR\n"
+"      myhvec3 specularnormal = myhvec3(normalize(diffusenormal + myhvec3(normalize(EyeVector))));\n"
+"      tempcolor += myhvec3(texture2D(Texture_Gloss, TexCoord)) * SpecularScale * pow(max(dot(surfacenormal, specularnormal), 0.0), SpecularPower);\n"
 "#endif\n"
 "\n"
+"      // apply lightmap color\n"
+"      color.rgb = tempcolor * myhvec3(texture2D(Texture_Lightmap, TexCoordLightmap)) + color.rgb * myhvec3(AmbientScale);\n"
+"\n"
 "\n"
 "\n"
 "\n"
+"#else // MODE none (lightmap)\n"
+"      // apply lightmap color\n"
+"      color.rgb *= myhvec3(texture2D(Texture_Lightmap, TexCoordLightmap)) * DiffuseScale + myhvec3(AmbientScale);\n"
+"#endif // MODE\n"
+"\n"
+"#ifdef USEGLOW\n"
+"      color.rgb += myhvec3(texture2D(Texture_Glow, TexCoord));\n"
+"#endif\n"
+"\n"
 "#ifdef USEFOG\n"
 "      // apply fog\n"
-"      myhalf fog = texture2D(Texture_FogMask, myhvec2(length(EyeVector)*FogRangeRecip, 0)).x;\n"
-"      color.rgb = color.rgb * (1 - fog) + FogColor * fog;\n"
+"      myhalf fog = texture2D(Texture_FogMask, myhvec2(length(EyeVector)*FogRangeRecip, 0.0)).x;\n"
+"      color.rgb = color.rgb * fog + FogColor * (1.0 - fog);\n"
 "#endif\n"
 "\n"
-"      gl_FragColor = color;\n"
+"      gl_FragColor = color * gl_Color;\n"
 "}\n"
+"#endif // !USEEASTEREGG\n"
 "\n"
 "#endif\n"
 ;
 
-// the loaded GLSL shader file for compiling shader permutations as needed
-static char *shaderstring = NULL;
-
 void R_GLSL_CompilePermutation(int permutation)
 {
        r_glsl_permutation_t *p = r_glsl_permutations + permutation;
        int vertstrings_count;
        int fragstrings_count;
+       char *shaderstring;
        const char *vertstrings_list[SHADERPERMUTATION_COUNT+1];
        const char *fragstrings_list[SHADERPERMUTATION_COUNT+1];
        char permutationname[256];
@@ -662,11 +808,17 @@ void R_GLSL_CompilePermutation(int permutation)
                fragstrings_list[fragstrings_count++] = "#define MODE_LIGHTSOURCE\n";
                strlcat(permutationname, " lightsource", sizeof(permutationname));
        }
-       if (permutation & SHADERPERMUTATION_MODE_LIGHTDIRECTIONMAP)
+       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\n";
-               fragstrings_list[fragstrings_count++] = "#define MODE_LIGHTDIRECTIONMAP\n";
-               strlcat(permutationname, " lightdirectionmap", sizeof(permutationname));
+               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)
        {
@@ -710,6 +862,12 @@ void R_GLSL_CompilePermutation(int permutation)
                fragstrings_list[fragstrings_count++] = "#define USEOFFSETMAPPING\n";
                strlcat(permutationname, " offsetmapping", sizeof(permutationname));
        }
+       if (permutation & SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING)
+       {
+               vertstrings_list[vertstrings_count++] = "#define USEOFFSETMAPPING_RELIEFMAPPING\n";
+               fragstrings_list[fragstrings_count++] = "#define USEOFFSETMAPPING_RELIEFMAPPING\n";
+               strlcat(permutationname, " OFFSETMAPPING_RELIEFMAPPING", sizeof(permutationname));
+       }
        if (permutation & SHADERPERMUTATION_SURFACENORMALIZE)
        {
                vertstrings_list[vertstrings_count++] = "#define SURFACENORMALIZE\n";
@@ -722,8 +880,10 @@ void R_GLSL_CompilePermutation(int permutation)
                fragstrings_list[fragstrings_count++] = "#define GEFORCEFX\n";
                strlcat(permutationname, " halffloat", sizeof(permutationname));
        }
+       shaderstring = (char *)FS_LoadFile("glsl/default.glsl", r_main_mempool, false, NULL);
        if (shaderstring)
        {
+               Con_DPrintf("GLSL shader text loaded from disk\n");
                vertstrings_list[vertstrings_count++] = shaderstring;
                fragstrings_list[fragstrings_count++] = shaderstring;
        }
@@ -759,7 +919,6 @@ void R_GLSL_CompilePermutation(int permutation)
                p->loc_SpecularPower       = qglGetUniformLocationARB(p->program, "SpecularPower");
                p->loc_SpecularScale       = qglGetUniformLocationARB(p->program, "SpecularScale");
                p->loc_OffsetMapping_Scale = qglGetUniformLocationARB(p->program, "OffsetMapping_Scale");
-               p->loc_OffsetMapping_Bias  = qglGetUniformLocationARB(p->program, "OffsetMapping_Bias");
                p->loc_AmbientColor        = qglGetUniformLocationARB(p->program, "AmbientColor");
                p->loc_DiffuseColor        = qglGetUniformLocationARB(p->program, "DiffuseColor");
                p->loc_SpecularColor       = qglGetUniformLocationARB(p->program, "SpecularColor");
@@ -779,6 +938,17 @@ void R_GLSL_CompilePermutation(int permutation)
        }
        else
                Con_Printf("permutation%s failed for shader %s, some features may not work properly!\n", permutationname, "glsl/default.glsl");
+       if (shaderstring)
+               Mem_Free(shaderstring);
+}
+
+void R_GLSL_Restart_f(void)
+{
+       int i;
+       for (i = 0;i < SHADERPERMUTATION_COUNT;i++)
+               if (r_glsl_permutations[i].program)
+                       GL_Backend_FreeProgram(r_glsl_permutations[i].program);
+       memset(r_glsl_permutations, 0, sizeof(r_glsl_permutations));
 }
 
 void R_SetupSurfaceShader(const entity_render_t *ent, const texture_t *texture, const vec3_t modelorg, const vec3_t lightcolorbase, qboolean modellighting)
@@ -797,20 +967,19 @@ void R_SetupSurfaceShader(const entity_render_t *ent, const texture_t *texture,
                if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
                        permutation |= SHADERPERMUTATION_CUBEFILTER;
        }
-       else if (modellighting)
-       {
-               permutation |= SHADERPERMUTATION_MODE_LIGHTDIRECTION;
-               if (texture->skin.glow)
-                       permutation |= SHADERPERMUTATION_GLOW;
-       }
-       else if (false)
-       {
-               permutation |= SHADERPERMUTATION_MODE_LIGHTDIRECTIONMAP;
-               if (texture->skin.glow)
-                       permutation |= SHADERPERMUTATION_GLOW;
-       }
        else
        {
+               if (modellighting)
+                       permutation |= SHADERPERMUTATION_MODE_LIGHTDIRECTION;
+               else if (r_glsl_deluxemapping.integer >= 1 && r_refdef.worldmodel && r_refdef.worldmodel->brushq3.deluxemapping)
+               {
+                       if (r_refdef.worldmodel->brushq3.deluxemapping_modelspace)
+                               permutation |= SHADERPERMUTATION_MODE_LIGHTDIRECTIONMAP_MODELSPACE;
+                       else
+                               permutation |= SHADERPERMUTATION_MODE_LIGHTDIRECTIONMAP_TANGENTSPACE;
+               }
+               else if (r_glsl_deluxemapping.integer >= 2) // fake mode
+                       permutation |= SHADERPERMUTATION_MODE_LIGHTDIRECTIONMAP_TANGENTSPACE;
                if (texture->skin.glow)
                        permutation |= SHADERPERMUTATION_GLOW;
        }
@@ -821,7 +990,11 @@ void R_SetupSurfaceShader(const entity_render_t *ent, const texture_t *texture,
        if (texture->colormapping)
                permutation |= SHADERPERMUTATION_COLORMAPPING;
        if (r_glsl_offsetmapping.integer)
+       {
                permutation |= SHADERPERMUTATION_OFFSETMAPPING;
+               if (r_glsl_offsetmapping_reliefmapping.integer)
+                       permutation |= SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING;
+       }
        if (r_glsl_surfacenormalize.integer)
                permutation |= SHADERPERMUTATION_SURFACENORMALIZE;
        if (r_glsl_usehalffloat.integer)
@@ -865,20 +1038,34 @@ void R_SetupSurfaceShader(const entity_render_t *ent, const texture_t *texture,
        }
        else if (permutation & SHADERPERMUTATION_MODE_LIGHTDIRECTION)
        {
-               if (r_glsl_permutation->loc_AmbientColor >= 0)
-                       qglUniform3fARB(r_glsl_permutation->loc_AmbientColor, ent->modellight_ambient[0], ent->modellight_ambient[1], ent->modellight_ambient[2]);
-               if (r_glsl_permutation->loc_DiffuseColor >= 0)
-                       qglUniform3fARB(r_glsl_permutation->loc_DiffuseColor, ent->modellight_diffuse[0], ent->modellight_diffuse[1], ent->modellight_diffuse[2]);
-               if (r_glsl_permutation->loc_SpecularColor >= 0)
-                       qglUniform3fARB(r_glsl_permutation->loc_SpecularColor, ent->modellight_diffuse[0] * texture->specularscale, ent->modellight_diffuse[1] * texture->specularscale, ent->modellight_diffuse[2] * texture->specularscale);
-               if (r_glsl_permutation->loc_LightDir >= 0)
-                       qglUniform3fARB(r_glsl_permutation->loc_LightDir, ent->modellight_lightdir[0], ent->modellight_lightdir[1], ent->modellight_lightdir[2]);
+               if (texture->currentmaterialflags & MATERIALFLAG_FULLBRIGHT)
+               {
+                       if (r_glsl_permutation->loc_AmbientColor >= 0)
+                               qglUniform3fARB(r_glsl_permutation->loc_AmbientColor, 1, 1, 1);
+                       if (r_glsl_permutation->loc_DiffuseColor >= 0)
+                               qglUniform3fARB(r_glsl_permutation->loc_DiffuseColor, 0, 0, 0);
+                       if (r_glsl_permutation->loc_SpecularColor >= 0)
+                               qglUniform3fARB(r_glsl_permutation->loc_SpecularColor, 0, 0, 0);
+                       if (r_glsl_permutation->loc_LightDir >= 0)
+                               qglUniform3fARB(r_glsl_permutation->loc_LightDir, 0, 0, -1);
+               }
+               else
+               {
+                       if (r_glsl_permutation->loc_AmbientColor >= 0)
+                               qglUniform3fARB(r_glsl_permutation->loc_AmbientColor, ent->modellight_ambient[0], ent->modellight_ambient[1], ent->modellight_ambient[2]);
+                       if (r_glsl_permutation->loc_DiffuseColor >= 0)
+                               qglUniform3fARB(r_glsl_permutation->loc_DiffuseColor, ent->modellight_diffuse[0], ent->modellight_diffuse[1], ent->modellight_diffuse[2]);
+                       if (r_glsl_permutation->loc_SpecularColor >= 0)
+                               qglUniform3fARB(r_glsl_permutation->loc_SpecularColor, ent->modellight_diffuse[0] * texture->specularscale, ent->modellight_diffuse[1] * texture->specularscale, ent->modellight_diffuse[2] * texture->specularscale);
+                       if (r_glsl_permutation->loc_LightDir >= 0)
+                               qglUniform3fARB(r_glsl_permutation->loc_LightDir, ent->modellight_lightdir[0], ent->modellight_lightdir[1], ent->modellight_lightdir[2]);
+               }
        }
        else
        {
                if (r_glsl_permutation->loc_AmbientScale >= 0) qglUniform1fARB(r_glsl_permutation->loc_AmbientScale, r_ambient.value * 2.0f / 128.0f);
                if (r_glsl_permutation->loc_DiffuseScale >= 0) qglUniform1fARB(r_glsl_permutation->loc_DiffuseScale, r_lightmapintensity * 2.0f);
-               if (r_glsl_permutation->loc_SpecularScale >= 0) qglUniform1fARB(r_glsl_permutation->loc_SpecularScale, specularscale * 2.0f);
+               if (r_glsl_permutation->loc_SpecularScale >= 0) qglUniform1fARB(r_glsl_permutation->loc_SpecularScale, r_lightmapintensity * specularscale * 2.0f);
        }
        if (r_glsl_permutation->loc_Texture_Normal >= 0) R_Mesh_TexBind(0, R_GetTexture(texture->skin.nmap));
        if (r_glsl_permutation->loc_Texture_Color >= 0) R_Mesh_TexBind(1, R_GetTexture(texture->basetexture));
@@ -896,12 +1083,23 @@ void R_SetupSurfaceShader(const entity_render_t *ent, const texture_t *texture,
                        qglUniform3fARB(r_glsl_permutation->loc_FogColor, fogcolor[0], fogcolor[1], fogcolor[2]);
        }
        if (r_glsl_permutation->loc_EyePosition >= 0) qglUniform3fARB(r_glsl_permutation->loc_EyePosition, modelorg[0], modelorg[1], modelorg[2]);
-       if (r_glsl_permutation->loc_Color_Pants >= 0) qglUniform3fARB(r_glsl_permutation->loc_Color_Pants, ent->colormap_pantscolor[0], ent->colormap_pantscolor[1], ent->colormap_pantscolor[2]);
-       if (r_glsl_permutation->loc_Color_Shirt >= 0) qglUniform3fARB(r_glsl_permutation->loc_Color_Shirt, ent->colormap_shirtcolor[0], ent->colormap_shirtcolor[1], ent->colormap_shirtcolor[2]);
+       if (r_glsl_permutation->loc_Color_Pants >= 0)
+       {
+               if (texture->skin.pants)
+                       qglUniform3fARB(r_glsl_permutation->loc_Color_Pants, ent->colormap_pantscolor[0], ent->colormap_pantscolor[1], ent->colormap_pantscolor[2]);
+               else
+                       qglUniform3fARB(r_glsl_permutation->loc_Color_Pants, 0, 0, 0);
+       }
+       if (r_glsl_permutation->loc_Color_Shirt >= 0)
+       {
+               if (texture->skin.shirt)
+                       qglUniform3fARB(r_glsl_permutation->loc_Color_Shirt, ent->colormap_shirtcolor[0], ent->colormap_shirtcolor[1], ent->colormap_shirtcolor[2]);
+               else
+                       qglUniform3fARB(r_glsl_permutation->loc_Color_Shirt, 0, 0, 0);
+       }
        if (r_glsl_permutation->loc_FogRangeRecip >= 0) qglUniform1fARB(r_glsl_permutation->loc_FogRangeRecip, fograngerecip);
        if (r_glsl_permutation->loc_SpecularPower >= 0) qglUniform1fARB(r_glsl_permutation->loc_SpecularPower, texture->specularpower);
        if (r_glsl_permutation->loc_OffsetMapping_Scale >= 0) qglUniform1fARB(r_glsl_permutation->loc_OffsetMapping_Scale, r_glsl_offsetmapping_scale.value);
-       if (r_glsl_permutation->loc_OffsetMapping_Bias >= 0) qglUniform1fARB(r_glsl_permutation->loc_OffsetMapping_Bias, r_glsl_offsetmapping_bias.value);
        CHECKGLERROR
 }
 
@@ -921,29 +1119,11 @@ void gl_main_start(void)
                R_BuildNormalizationCube();
        }
        R_BuildFogTexture();
-       shaderstring = NULL;
-       if (gl_support_fragment_shader)
-       {
-               shaderstring = (char *)FS_LoadFile("glsl/default.glsl", r_main_mempool, false, NULL);
-               if (shaderstring)
-                       Con_Printf("GLSL shader text loaded from disk\n");
-               // if we couldn't load the shader file, fall back to builtin shader
-               if (!shaderstring)
-               {
-                       if (shaderstring)
-                               Con_Printf("GLSL shader text loaded from fallback\n");
-                       shaderstring = Mem_Alloc(r_main_mempool, strlen(builtinshaderstring) + 1);
-                       strcpy(shaderstring, builtinshaderstring);
-               }
-       }
-       if (shaderstring)
-               Con_Printf("GLSL shader text loaded\n");
        memset(r_glsl_permutations, 0, sizeof(r_glsl_permutations));
 }
 
 void gl_main_shutdown(void)
 {
-       int i;
        R_FreeTexturePool(&r_main_texturepool);
        r_bloom_texture_screen = NULL;
        r_bloom_texture_bloom = NULL;
@@ -952,13 +1132,7 @@ void gl_main_shutdown(void)
        r_texture_black = NULL;
        r_texture_whitecube = NULL;
        r_texture_normalizationcube = NULL;
-       if (shaderstring)
-               Mem_Free(shaderstring);
-       shaderstring = NULL;
-       for (i = 0;i < SHADERPERMUTATION_COUNT;i++)
-               if (r_glsl_permutations[i].program)
-                       GL_Backend_FreeProgram(r_glsl_permutations[i].program);
-       memset(r_glsl_permutations, 0, sizeof(r_glsl_permutations));
+       R_GLSL_Restart_f();
 }
 
 extern void CL_ParseEntityLump(char *entitystring);
@@ -991,6 +1165,7 @@ void GL_Main_Init(void)
 {
        r_main_mempool = Mem_AllocPool("Renderer", 0, NULL);
 
+       Cmd_AddCommand("r_glsl_restart", R_GLSL_Restart_f, "unloads GLSL shaders, they will then be reloaded as needed\n");
        FOG_registercvars(); // FIXME: move this fog stuff to client?
        Cvar_RegisterVariable(&r_nearclip);
        Cvar_RegisterVariable(&r_showtris);
@@ -1012,10 +1187,11 @@ void GL_Main_Init(void)
        Cvar_RegisterVariable(&r_textureunits);
        Cvar_RegisterVariable(&r_glsl);
        Cvar_RegisterVariable(&r_glsl_offsetmapping);
+       Cvar_RegisterVariable(&r_glsl_offsetmapping_reliefmapping);
        Cvar_RegisterVariable(&r_glsl_offsetmapping_scale);
-       Cvar_RegisterVariable(&r_glsl_offsetmapping_bias);
        Cvar_RegisterVariable(&r_glsl_usehalffloat);
        Cvar_RegisterVariable(&r_glsl_surfacenormalize);
+       Cvar_RegisterVariable(&r_glsl_deluxemapping);
        Cvar_RegisterVariable(&r_lerpsprites);
        Cvar_RegisterVariable(&r_lerpmodels);
        Cvar_RegisterVariable(&r_waterscroll);
@@ -1411,6 +1587,8 @@ static void R_BlendView(void)
        qboolean dobloom;
        qboolean doblend;
        rmeshstate_t m;
+       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
@@ -1429,10 +1607,10 @@ static void R_BlendView(void)
        GL_DepthTest(false);
        R_Mesh_Matrix(&identitymatrix);
        // vertex coordinates for a quad that covers the screen exactly
-       varray_vertex3f[0] = 0;varray_vertex3f[1] = 0;varray_vertex3f[2] = 0;
-       varray_vertex3f[3] = 1;varray_vertex3f[4] = 0;varray_vertex3f[5] = 0;
-       varray_vertex3f[6] = 1;varray_vertex3f[7] = 1;varray_vertex3f[8] = 0;
-       varray_vertex3f[9] = 0;varray_vertex3f[10] = 1;varray_vertex3f[11] = 0;
+       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;
        if (dobloom)
        {
                int bloomwidth, bloomheight, x, dobloomblend, range;
@@ -1449,27 +1627,27 @@ static void R_BlendView(void)
                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)
-               varray_texcoord2f[0][0] = 0;
-               varray_texcoord2f[0][1] = (float)r_view_height / (float)screenheight;
-               varray_texcoord2f[0][2] = (float)r_view_width / (float)screenwidth;
-               varray_texcoord2f[0][3] = (float)r_view_height / (float)screenheight;
-               varray_texcoord2f[0][4] = (float)r_view_width / (float)screenwidth;
-               varray_texcoord2f[0][5] = 0;
-               varray_texcoord2f[0][6] = 0;
-               varray_texcoord2f[0][7] = 0;
+               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)
-               varray_texcoord2f[1][0] = 0;
-               varray_texcoord2f[1][1] = (float)bloomheight / (float)screenheight;
-               varray_texcoord2f[1][2] = (float)bloomwidth / (float)screenwidth;
-               varray_texcoord2f[1][3] = (float)bloomheight / (float)screenheight;
-               varray_texcoord2f[1][4] = (float)bloomwidth / (float)screenwidth;
-               varray_texcoord2f[1][5] = 0;
-               varray_texcoord2f[1][6] = 0;
-               varray_texcoord2f[1][7] = 0;
+               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;
                memset(&m, 0, sizeof(m));
-               m.pointer_vertex = varray_vertex3f;
-               m.pointer_texcoord[0] = varray_texcoord2f[0];
+               m.pointer_vertex = vertex3f;
+               m.pointer_texcoord[0] = texcoord2f[0];
                m.tex[0] = R_GetTexture(r_bloom_texture_screen);
                R_Mesh_State(&m);
                // copy view into the full resolution screen image texture
@@ -1495,9 +1673,9 @@ static void R_BlendView(void)
                // we now have a darkened bloom image in the framebuffer, copy it into
                // the bloom image texture for more processing
                memset(&m, 0, sizeof(m));
-               m.pointer_vertex = varray_vertex3f;
+               m.pointer_vertex = vertex3f;
                m.tex[0] = R_GetTexture(r_bloom_texture_bloom);
-               m.pointer_texcoord[0] = varray_texcoord2f[2];
+               m.pointer_texcoord[0] = texcoord2f[2];
                R_Mesh_State(&m);
                GL_ActiveTexture(0);
                qglCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, r_view_x, vid.height - (r_view_y + bloomheight), bloomwidth, bloomheight);
@@ -1511,14 +1689,14 @@ static void R_BlendView(void)
                        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
-                       varray_texcoord2f[2][0] = xoffset+0;
-                       varray_texcoord2f[2][1] = yoffset+(float)bloomheight / (float)screenheight;
-                       varray_texcoord2f[2][2] = xoffset+(float)bloomwidth / (float)screenwidth;
-                       varray_texcoord2f[2][3] = yoffset+(float)bloomheight / (float)screenheight;
-                       varray_texcoord2f[2][4] = xoffset+(float)bloomwidth / (float)screenwidth;
-                       varray_texcoord2f[2][5] = yoffset+0;
-                       varray_texcoord2f[2][6] = xoffset+0;
-                       varray_texcoord2f[2][7] = yoffset+0;
+                       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)
@@ -1544,14 +1722,14 @@ static void R_BlendView(void)
                        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
-                       varray_texcoord2f[2][0] = xoffset+0;
-                       varray_texcoord2f[2][1] = yoffset+(float)bloomheight / (float)screenheight;
-                       varray_texcoord2f[2][2] = xoffset+(float)bloomwidth / (float)screenwidth;
-                       varray_texcoord2f[2][3] = yoffset+(float)bloomheight / (float)screenheight;
-                       varray_texcoord2f[2][4] = xoffset+(float)bloomwidth / (float)screenwidth;
-                       varray_texcoord2f[2][5] = yoffset+0;
-                       varray_texcoord2f[2][6] = xoffset+0;
-                       varray_texcoord2f[2][7] = yoffset+0;
+                       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)
@@ -1572,9 +1750,9 @@ static void R_BlendView(void)
                // put the original screen image back in place and blend the bloom
                // texture on it
                memset(&m, 0, sizeof(m));
-               m.pointer_vertex = varray_vertex3f;
+               m.pointer_vertex = vertex3f;
                m.tex[0] = R_GetTexture(r_bloom_texture_screen);
-               m.pointer_texcoord[0] = varray_texcoord2f[0];
+               m.pointer_texcoord[0] = texcoord2f[0];
 #if 0
                dobloomblend = false;
 #else
@@ -1584,7 +1762,7 @@ static void R_BlendView(void)
                        dobloomblend = false;
                        m.texcombinergb[1] = GL_ADD;
                        m.tex[1] = R_GetTexture(r_bloom_texture_bloom);
-                       m.pointer_texcoord[1] = varray_texcoord2f[1];
+                       m.pointer_texcoord[1] = texcoord2f[1];
                }
                else
                        dobloomblend = true;
@@ -1598,9 +1776,9 @@ static void R_BlendView(void)
                if (dobloomblend)
                {
                        memset(&m, 0, sizeof(m));
-                       m.pointer_vertex = varray_vertex3f;
+                       m.pointer_vertex = vertex3f;
                        m.tex[0] = R_GetTexture(r_bloom_texture_bloom);
-                       m.pointer_texcoord[0] = varray_texcoord2f[1];
+                       m.pointer_texcoord[0] = texcoord2f[1];
                        R_Mesh_State(&m);
                        GL_BlendFunc(GL_ONE, GL_ONE);
                        GL_Color(1,1,1,1);
@@ -1612,7 +1790,7 @@ static void R_BlendView(void)
        {
                // apply a color tint to the whole view
                memset(&m, 0, sizeof(m));
-               m.pointer_vertex = varray_vertex3f;
+               m.pointer_vertex = vertex3f;
                R_Mesh_State(&m);
                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]);
@@ -2068,6 +2246,7 @@ void R_DrawSprite(int blendfunc1, int blendfunc2, rtexture_t *texture, rtexture_
 {
        float fog = 0.0f, ifog;
        rmeshstate_t m;
+       float vertex3f[12];
 
        if (fogenabled)
                fog = VERTEXFOGTABLE(VectorDistance(origin, r_vieworigin));
@@ -2078,23 +2257,23 @@ void R_DrawSprite(int blendfunc1, int blendfunc2, rtexture_t *texture, rtexture_
        GL_DepthMask(false);
        GL_DepthTest(!depthdisable);
 
-       varray_vertex3f[ 0] = origin[0] + left[0] * scalex2 + up[0] * scaley1;
-       varray_vertex3f[ 1] = origin[1] + left[1] * scalex2 + up[1] * scaley1;
-       varray_vertex3f[ 2] = origin[2] + left[2] * scalex2 + up[2] * scaley1;
-       varray_vertex3f[ 3] = origin[0] + left[0] * scalex2 + up[0] * scaley2;
-       varray_vertex3f[ 4] = origin[1] + left[1] * scalex2 + up[1] * scaley2;
-       varray_vertex3f[ 5] = origin[2] + left[2] * scalex2 + up[2] * scaley2;
-       varray_vertex3f[ 6] = origin[0] + left[0] * scalex1 + up[0] * scaley2;
-       varray_vertex3f[ 7] = origin[1] + left[1] * scalex1 + up[1] * scaley2;
-       varray_vertex3f[ 8] = origin[2] + left[2] * scalex1 + up[2] * scaley2;
-       varray_vertex3f[ 9] = origin[0] + left[0] * scalex1 + up[0] * scaley1;
-       varray_vertex3f[10] = origin[1] + left[1] * scalex1 + up[1] * scaley1;
-       varray_vertex3f[11] = origin[2] + left[2] * scalex1 + up[2] * scaley1;
+       vertex3f[ 0] = origin[0] + left[0] * scalex2 + up[0] * scaley1;
+       vertex3f[ 1] = origin[1] + left[1] * scalex2 + up[1] * scaley1;
+       vertex3f[ 2] = origin[2] + left[2] * scalex2 + up[2] * scaley1;
+       vertex3f[ 3] = origin[0] + left[0] * scalex2 + up[0] * scaley2;
+       vertex3f[ 4] = origin[1] + left[1] * scalex2 + up[1] * scaley2;
+       vertex3f[ 5] = origin[2] + left[2] * scalex2 + up[2] * scaley2;
+       vertex3f[ 6] = origin[0] + left[0] * scalex1 + up[0] * scaley2;
+       vertex3f[ 7] = origin[1] + left[1] * scalex1 + up[1] * scaley2;
+       vertex3f[ 8] = origin[2] + left[2] * scalex1 + up[2] * scaley2;
+       vertex3f[ 9] = origin[0] + left[0] * scalex1 + up[0] * scaley1;
+       vertex3f[10] = origin[1] + left[1] * scalex1 + up[1] * scaley1;
+       vertex3f[11] = origin[2] + left[2] * scalex1 + up[2] * scaley1;
 
        memset(&m, 0, sizeof(m));
        m.tex[0] = R_GetTexture(texture);
        m.pointer_texcoord[0] = spritetexcoord2f;
-       m.pointer_vertex = varray_vertex3f;
+       m.pointer_vertex = vertex3f;
        R_Mesh_State(&m);
        GL_Color(cr * ifog, cg * ifog, cb * ifog, ca);
        R_Mesh_Draw(0, 4, 2, polygonelements);
@@ -2355,6 +2534,29 @@ void R_UpdateAllTextureInfo(entity_render_t *ent)
                        R_UpdateTextureInfo(ent, ent->model->data_textures + i);
 }
 
+int rsurface_array_size = 0;
+float *rsurface_array_vertex3f = NULL;
+float *rsurface_array_svector3f = NULL;
+float *rsurface_array_tvector3f = NULL;
+float *rsurface_array_normal3f = NULL;
+float *rsurface_array_color4f = NULL;
+float *rsurface_array_texcoord3f = NULL;
+
+void R_Mesh_ResizeArrays(int newvertices)
+{
+       if (rsurface_array_size >= newvertices)
+               return;
+       if (rsurface_array_vertex3f)
+               Mem_Free(rsurface_array_vertex3f);
+       rsurface_array_size = (newvertices + 1023) & ~1023;
+       rsurface_array_vertex3f = Mem_Alloc(r_main_mempool, rsurface_array_size * sizeof(float[19]));
+       rsurface_array_svector3f = rsurface_array_vertex3f + rsurface_array_size * 3;
+       rsurface_array_tvector3f = rsurface_array_vertex3f + rsurface_array_size * 6;
+       rsurface_array_normal3f = rsurface_array_vertex3f + rsurface_array_size * 9;
+       rsurface_array_color4f = rsurface_array_vertex3f + rsurface_array_size * 12;
+       rsurface_array_texcoord3f = rsurface_array_vertex3f + rsurface_array_size * 16;
+}
+
 float *rsurface_vertex3f;
 float *rsurface_svector3f;
 float *rsurface_tvector3f;
@@ -2363,15 +2565,17 @@ float *rsurface_lightmapcolor4f;
 
 void RSurf_SetVertexPointer(const entity_render_t *ent, const texture_t *texture, const msurface_t *surface, const vec3_t modelorg, qboolean generatenormals, qboolean generatetangents)
 {
+       if (rsurface_array_size < surface->groupmesh->num_vertices)
+               R_Mesh_ResizeArrays(surface->groupmesh->num_vertices);
        if ((ent->frameblend[0].lerp != 1 || ent->frameblend[0].frame != 0) && (surface->groupmesh->data_morphvertex3f || surface->groupmesh->data_vertexboneweights))
        {
-               rsurface_vertex3f = varray_vertex3f;
+               rsurface_vertex3f = rsurface_array_vertex3f;
                Mod_Alias_GetMesh_Vertex3f(ent->model, ent->frameblend, surface->groupmesh, rsurface_vertex3f);
                if (generatetangents || (texture->textureflags & (Q3TEXTUREFLAG_AUTOSPRITE | Q3TEXTUREFLAG_AUTOSPRITE2)))
                {
-                       rsurface_svector3f = varray_svector3f;
-                       rsurface_tvector3f = varray_tvector3f;
-                       rsurface_normal3f = varray_normal3f;
+                       rsurface_svector3f = rsurface_array_svector3f;
+                       rsurface_tvector3f = rsurface_array_tvector3f;
+                       rsurface_normal3f = rsurface_array_normal3f;
                        Mod_BuildTextureVectorsAndNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, rsurface_vertex3f, surface->groupmesh->data_texcoordtexture2f, surface->groupmesh->data_element3i + surface->num_firsttriangle * 3, rsurface_svector3f, rsurface_tvector3f, rsurface_normal3f, r_smoothnormals_areaweighting.integer);
                }
                else
@@ -2380,7 +2584,7 @@ void RSurf_SetVertexPointer(const entity_render_t *ent, const texture_t *texture
                        rsurface_tvector3f = NULL;
                        if (generatenormals)
                        {
-                               rsurface_normal3f = varray_normal3f;
+                               rsurface_normal3f = rsurface_array_normal3f;
                                Mod_BuildNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, rsurface_vertex3f, surface->groupmesh->data_element3i + 3 * surface->num_firsttriangle, rsurface_normal3f, r_smoothnormals_areaweighting.integer);
                        }
                        else
@@ -2426,12 +2630,12 @@ void RSurf_SetVertexPointer(const entity_render_t *ent, const texture_t *texture
                                VectorSet(up, 0, 0, 1);
                        }
                        for (i = 0;i < 4;i++)
-                               VectorMAMAMAM(1, center, v[i][0], forward, v[i][1], right, v[i][2], up, varray_vertex3f + (surface->num_firstvertex+i+j) * 3);
+                               VectorMAMAMAM(1, center, v[i][0], forward, v[i][1], right, v[i][2], up, rsurface_array_vertex3f + (surface->num_firstvertex+i+j) * 3);
                }
-               rsurface_vertex3f = varray_vertex3f;
-               rsurface_svector3f = varray_svector3f;
-               rsurface_tvector3f = varray_tvector3f;
-               rsurface_normal3f = varray_normal3f;
+               rsurface_vertex3f = rsurface_array_vertex3f;
+               rsurface_svector3f = rsurface_array_svector3f;
+               rsurface_tvector3f = rsurface_array_tvector3f;
+               rsurface_normal3f = rsurface_array_normal3f;
                Mod_BuildTextureVectorsAndNormals(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, rsurface_vertex3f, surface->groupmesh->data_texcoordtexture2f, surface->groupmesh->data_element3i + surface->num_firsttriangle * 3, rsurface_svector3f, rsurface_tvector3f, rsurface_normal3f, r_smoothnormals_areaweighting.integer);
        }
        R_Mesh_VertexPointer(rsurface_vertex3f);
@@ -2453,7 +2657,7 @@ static void RSurf_DrawLightmap(const entity_render_t *ent, const texture_t *text
        if (lightmode >= 2)
        {
                // model lighting
-               vec4_t ambientcolor;
+               vec3_t ambientcolor;
                vec3_t diffusecolor;
                vec3_t lightdir;
                VectorCopy(ent->modellight_lightdir, lightdir);
@@ -2468,24 +2672,22 @@ static void RSurf_DrawLightmap(const entity_render_t *ent, const texture_t *text
                        int numverts = surface->num_vertices;
                        v = rsurface_vertex3f + 3 * surface->num_firstvertex;
                        c2 = rsurface_normal3f + 3 * surface->num_firstvertex;
-                       c = varray_color4f + 4 * surface->num_firstvertex;
+                       c = rsurface_array_color4f + 4 * surface->num_firstvertex;
                        // q3-style directional shading
                        for (i = 0;i < numverts;i++, v += 3, c2 += 3, c += 4)
                        {
                                if ((f = DotProduct(c2, lightdir)) > 0)
-                               {
                                        VectorMA(ambientcolor, f, diffusecolor, c);
-                                       c[3] = a;
-                               }
                                else
-                                       VectorCopy4(ambientcolor, c);
+                                       VectorCopy(ambientcolor, c);
+                               c[3] = a;
                        }
                        r = 1;
                        g = 1;
                        b = 1;
                        a = 1;
                        applycolor = false;
-                       rsurface_lightmapcolor4f = varray_color4f;
+                       rsurface_lightmapcolor4f = rsurface_array_color4f;
                }
                else
                {
@@ -2499,7 +2701,7 @@ static void RSurf_DrawLightmap(const entity_render_t *ent, const texture_t *text
        {
                if (surface->lightmapinfo && surface->lightmapinfo->stainsamples)
                {
-                       for (i = 0, c = varray_color4f + 4 * surface->num_firstvertex;i < surface->num_vertices;i++, c += 4)
+                       for (i = 0, c = rsurface_array_color4f + 4 * surface->num_firstvertex;i < surface->num_vertices;i++, c += 4)
                        {
                                if (surface->lightmapinfo->samples)
                                {
@@ -2529,7 +2731,7 @@ static void RSurf_DrawLightmap(const entity_render_t *ent, const texture_t *text
                                else
                                        VectorClear(c);
                        }
-                       rsurface_lightmapcolor4f = varray_color4f;
+                       rsurface_lightmapcolor4f = rsurface_array_color4f;
                }
                else
                        rsurface_lightmapcolor4f = surface->groupmesh->data_lightmapcolor4f;
@@ -2540,7 +2742,7 @@ static void RSurf_DrawLightmap(const entity_render_t *ent, const texture_t *text
        {
                if (rsurface_lightmapcolor4f)
                {
-                       for (i = 0, v = (rsurface_vertex3f + 3 * surface->num_firstvertex), c = (rsurface_lightmapcolor4f + 4 * surface->num_firstvertex), c2 = (varray_color4f + 4 * surface->num_firstvertex);i < surface->num_vertices;i++, v += 3, c += 4, c2 += 4)
+                       for (i = 0, v = (rsurface_vertex3f + 3 * surface->num_firstvertex), c = (rsurface_lightmapcolor4f + 4 * surface->num_firstvertex), c2 = (rsurface_array_color4f + 4 * surface->num_firstvertex);i < surface->num_vertices;i++, v += 3, c += 4, c2 += 4)
                        {
                                f = 1 - VERTEXFOGTABLE(VectorDistance(v, modelorg));
                                c2[0] = c[0] * f;
@@ -2551,7 +2753,7 @@ static void RSurf_DrawLightmap(const entity_render_t *ent, const texture_t *text
                }
                else
                {
-                       for (i = 0, v = (rsurface_vertex3f + 3 * surface->num_firstvertex), c2 = (varray_color4f + 4 * surface->num_firstvertex);i < surface->num_vertices;i++, v += 3, c2 += 4)
+                       for (i = 0, v = (rsurface_vertex3f + 3 * surface->num_firstvertex), c2 = (rsurface_array_color4f + 4 * surface->num_firstvertex);i < surface->num_vertices;i++, v += 3, c2 += 4)
                        {
                                f = 1 - VERTEXFOGTABLE(VectorDistance(v, modelorg));
                                c2[0] = f;
@@ -2560,18 +2762,18 @@ static void RSurf_DrawLightmap(const entity_render_t *ent, const texture_t *text
                                c2[3] = 1;
                        }
                }
-               rsurface_lightmapcolor4f = varray_color4f;
+               rsurface_lightmapcolor4f = rsurface_array_color4f;
        }
        if (applycolor && rsurface_lightmapcolor4f)
        {
-               for (i = 0, c = (rsurface_lightmapcolor4f + 4 * surface->num_firstvertex), c2 = (varray_color4f + 4 * surface->num_firstvertex);i < surface->num_vertices;i++, c += 4, c2 += 4)
+               for (i = 0, c = (rsurface_lightmapcolor4f + 4 * surface->num_firstvertex), c2 = (rsurface_array_color4f + 4 * surface->num_firstvertex);i < surface->num_vertices;i++, c += 4, c2 += 4)
                {
                        c2[0] = c[0] * r;
                        c2[1] = c[1] * g;
                        c2[2] = c[2] * b;
                        c2[3] = c[3] * a;
                }
-               rsurface_lightmapcolor4f = varray_color4f;
+               rsurface_lightmapcolor4f = rsurface_array_color4f;
        }
        R_Mesh_ColorPointer(rsurface_lightmapcolor4f);
        GL_Color(r, g, b, a);
@@ -2694,8 +2896,7 @@ static void R_DrawTextureSurfaceList(const entity_render_t *ent, texture_t *text
                                {
                                        R_Mesh_TexBind(7, R_GetTexture(surface->lightmaptexture));
                                        if (r_glsl_permutation->loc_Texture_Deluxemap >= 0)
-                                               R_Mesh_TexBind(8, R_GetTexture(r_texture_blanknormalmap));
-                                               //R_Mesh_TexBind(8, R_GetTexture(surface->deluxemaptexture));
+                                               R_Mesh_TexBind(8, R_GetTexture(surface->deluxemaptexture));
                                        R_Mesh_ColorPointer(NULL);
                                }
                                else
@@ -2746,7 +2947,7 @@ static void R_DrawTextureSurfaceList(const entity_render_t *ent, texture_t *text
                                m.tex[1] = R_GetTexture(layer->texture);
                                m.texmatrix[1] = layer->texmatrix;
                                m.texrgbscale[1] = layertexrgbscale;
-                               m.pointer_color = varray_color4f;
+                               m.pointer_color = rsurface_array_color4f;
                                R_Mesh_State(&m);
                                if (lightmode == 2)
                                {
@@ -2783,7 +2984,7 @@ static void R_DrawTextureSurfaceList(const entity_render_t *ent, texture_t *text
                                memset(&m, 0, sizeof(m));
                                m.tex[0] = R_GetTexture(layer->texture);
                                m.texmatrix[0] = layer->texmatrix;
-                               m.pointer_color = varray_color4f;
+                               m.pointer_color = rsurface_array_color4f;
                                m.texrgbscale[0] = layertexrgbscale;
                                R_Mesh_State(&m);
                                if (lightmode == 2)
@@ -2818,7 +3019,7 @@ static void R_DrawTextureSurfaceList(const entity_render_t *ent, texture_t *text
                                memset(&m, 0, sizeof(m));
                                m.tex[0] = R_GetTexture(layer->texture);
                                m.texmatrix[0] = layer->texmatrix;
-                               m.pointer_color = varray_color4f;
+                               m.pointer_color = rsurface_array_color4f;
                                m.texrgbscale[0] = layertexrgbscale;
                                R_Mesh_State(&m);
                                for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
@@ -2833,7 +3034,7 @@ static void R_DrawTextureSurfaceList(const entity_render_t *ent, texture_t *text
                                m.tex[0] = R_GetTexture(layer->texture);
                                m.texmatrix[0] = layer->texmatrix;
                                m.texrgbscale[0] = layertexrgbscale;
-                               m.pointer_color = varray_color4f;
+                               m.pointer_color = rsurface_array_color4f;
                                R_Mesh_State(&m);
                                if (lightmode == 2)
                                {
@@ -2858,7 +3059,7 @@ static void R_DrawTextureSurfaceList(const entity_render_t *ent, texture_t *text
                                memset(&m, 0, sizeof(m));
                                m.tex[0] = R_GetTexture(layer->texture);
                                m.texmatrix[0] = layer->texmatrix;
-                               m.pointer_color = varray_color4f;
+                               m.pointer_color = rsurface_array_color4f;
                                m.texrgbscale[0] = layertexrgbscale;
                                R_Mesh_State(&m);
                                for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
@@ -2884,8 +3085,8 @@ static void R_DrawTextureSurfaceList(const entity_render_t *ent, texture_t *text
                                        RSurf_SetVertexPointer(ent, texture, surface, modelorg, false, false);
                                        if (layer->texture)
                                                R_Mesh_TexCoordPointer(0, 2, surface->groupmesh->data_texcoordtexture2f);
-                                       R_Mesh_ColorPointer(varray_color4f);
-                                       for (i = 0, v = (rsurface_vertex3f + 3 * surface->num_firstvertex), c = (varray_color4f + 4 * surface->num_firstvertex);i < surface->num_vertices;i++, v += 3, c += 4)
+                                       R_Mesh_ColorPointer(rsurface_array_color4f);
+                                       for (i = 0, v = (rsurface_vertex3f + 3 * surface->num_firstvertex), c = (rsurface_array_color4f + 4 * surface->num_firstvertex);i < surface->num_vertices;i++, v += 3, c += 4)
                                        {
                                                f = VERTEXFOGTABLE(VectorDistance(v, modelorg));
                                                c[0] = layercolor[0];