added r_shadow_usenormalmap cvar which allows you to disable directional shading...
authorhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Thu, 29 Mar 2007 00:39:50 +0000 (00:39 +0000)
committerhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Thu, 29 Mar 2007 00:39:50 +0000 (00:39 +0000)
added an optimized ambient-without-diffuse path in the GLSL shader to help r_shadow_usenormalmap 0 performance

git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@7017 d7cf8633-e32d-0410-b094-e92efae38249

gl_rmain.c
r_shadow.c
render.h

index 0b70390..afccccc 100644 (file)
@@ -69,11 +69,11 @@ 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_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", "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_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_glsl = {CVAR_SAVE, "r_glsl", "1", "enables use of OpenGL 2.0 pixel shaders for lighting"};
+cvar_t r_glsl_offsetmapping = {CVAR_SAVE, "r_glsl_offsetmapping", "0", "offset mapping effect (also known as parallax mapping or virtual displacement mapping)"};
+cvar_t r_glsl_offsetmapping_reliefmapping = {CVAR_SAVE, "r_glsl_offsetmapping_reliefmapping", "0", "relief mapping effect (higher quality)"};
+cvar_t r_glsl_offsetmapping_scale = {CVAR_SAVE, "r_glsl_offsetmapping_scale", "0.04", "how deep the offset mapping effect is"};
+cvar_t r_glsl_deluxemapping = {CVAR_SAVE, "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"};
 
 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"};
@@ -143,7 +143,7 @@ rtexture_t *r_texture_fogattenuation;
 //rtexture_t *r_texture_fogintensity;
 
 // information about each possible shader permutation
 //rtexture_t *r_texture_fogintensity;
 
 // information about each possible shader permutation
-r_glsl_permutation_t r_glsl_permutations[SHADERPERMUTATION_COUNT];
+r_glsl_permutation_t r_glsl_permutations[SHADERPERMUTATION_MAX];
 // currently selected permutation
 r_glsl_permutation_t *r_glsl_permutation;
 
 // currently selected permutation
 r_glsl_permutation_t *r_glsl_permutation;
 
@@ -478,10 +478,12 @@ static const char *builtinshaderstring =
 "// fragment shader specific:\n"
 "#ifdef FRAGMENT_SHADER\n"
 "\n"
 "// fragment shader specific:\n"
 "#ifdef FRAGMENT_SHADER\n"
 "\n"
+"// 11 textures, we can only use up to 16 on DX9-class hardware\n"
 "uniform sampler2D Texture_Normal;\n"
 "uniform sampler2D Texture_Color;\n"
 "uniform sampler2D Texture_Gloss;\n"
 "uniform samplerCube Texture_Cube;\n"
 "uniform sampler2D Texture_Normal;\n"
 "uniform sampler2D Texture_Color;\n"
 "uniform sampler2D Texture_Gloss;\n"
 "uniform samplerCube Texture_Cube;\n"
+"uniform sampler2D Texture_Attenuation;\n"
 "uniform sampler2D Texture_FogMask;\n"
 "uniform sampler2D Texture_Pants;\n"
 "uniform sampler2D Texture_Shirt;\n"
 "uniform sampler2D Texture_FogMask;\n"
 "uniform sampler2D Texture_Pants;\n"
 "uniform sampler2D Texture_Shirt;\n"
@@ -509,13 +511,8 @@ static const char *builtinshaderstring =
 "uniform myhalf SpecularScale;\n"
 "uniform myhalf SpecularPower;\n"
 "\n"
 "uniform myhalf SpecularScale;\n"
 "uniform myhalf SpecularPower;\n"
 "\n"
-"void main(void)\n"
+"vec2 OffsetMapping(vec2 TexCoord)\n"
 "{\n"
 "{\n"
-"      // apply offsetmapping\n"
-"#ifdef USEOFFSETMAPPING\n"
-"      vec2 TexCoordOffset = TexCoord;\n"
-"#define TexCoord TexCoordOffset\n"
-"\n"
 "      vec3 eyedir = vec3(normalize(EyeVector));\n"
 "      float depthbias = 1.0 - eyedir.z; // should this be a -?\n"
 "      depthbias = 1.0 - depthbias * depthbias;\n"
 "      vec3 eyedir = vec3(normalize(EyeVector));\n"
 "      float depthbias = 1.0 - eyedir.z; // should this be a -?\n"
 "      depthbias = 1.0 - depthbias * depthbias;\n"
@@ -541,7 +538,7 @@ 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"
 "      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"
-"#elif 1\n"
+"#else\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(normalize(EyeVector.xy)) * OffsetMapping_Scale * vec2(-0.333, 0.333);\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(normalize(EyeVector.xy)) * OffsetMapping_Scale * vec2(-0.333, 0.333);\n"
@@ -550,39 +547,16 @@ static const char *builtinshaderstring =
 "      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 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"
+"      return TexCoord;\n"
+"}\n"
+"\n"
+"void main(void)\n"
+"{\n"
+"      // apply offsetmapping\n"
+"#ifdef USEOFFSETMAPPING\n"
+"      vec2 TexCoordOffset = OffsetMapping(TexCoord);\n"
+"#define TexCoord TexCoordOffset\n"
 "#endif\n"
 "\n"
 "      // combine the diffuse textures (base, pants, shirt)\n"
 "#endif\n"
 "\n"
 "      // combine the diffuse textures (base, pants, shirt)\n"
@@ -597,15 +571,28 @@ static const char *builtinshaderstring =
 "#ifdef MODE_LIGHTSOURCE\n"
 "      // light source\n"
 "\n"
 "#ifdef MODE_LIGHTSOURCE\n"
 "      // light source\n"
 "\n"
-"      // get the surface normal and light normal\n"
+"      // calculate surface normal, light normal, and specular normal\n"
+"      // compute color intensity for the two textures (colormap and glossmap)\n"
+"      // scale by light color and attenuation as efficiently as possible\n"
+"      // (do as much scalar math as possible rather than vector math)\n"
+"#ifdef USESPECULAR\n"
 "      myhvec3 surfacenormal = normalize(myhvec3(texture2D(Texture_Normal, TexCoord)) - myhvec3(0.5));\n"
 "      myhvec3 diffusenormal = myhvec3(normalize(LightVector));\n"
 "      myhvec3 surfacenormal = normalize(myhvec3(texture2D(Texture_Normal, TexCoord)) - myhvec3(0.5));\n"
 "      myhvec3 diffusenormal = myhvec3(normalize(LightVector));\n"
+"      myhvec3 specularnormal = normalize(diffusenormal + myhvec3(normalize(EyeVector)));\n"
 "\n"
 "      // calculate directional shading\n"
 "\n"
 "      // calculate directional shading\n"
-"      color.rgb *= AmbientScale + DiffuseScale * myhalf(max(float(dot(surfacenormal, diffusenormal)), 0.0));\n"
-"#ifdef USESPECULAR\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"
+"      color.rgb = LightColor * myhalf(texture2D(Texture_Attenuation, length(CubeVector))) * (color.rgb * (AmbientScale + DiffuseScale * myhalf(max(float(dot(surfacenormal, diffusenormal)), 0.0))) + (SpecularScale * pow(myhalf(max(float(dot(surfacenormal, specularnormal)), 0.0)), SpecularPower)) * myhvec3(texture2D(Texture_Gloss, TexCoord)));\n"
+"#else\n"
+"#ifdef USEDIFFUSE\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 = LightColor * myhalf(texture2D(Texture_Attenuation, length(CubeVector))) * color.rgb * (AmbientScale + DiffuseScale * myhalf(max(float(dot(surfacenormal, diffusenormal)), 0.0)));\n"
+"#else\n"
+"      // calculate directionless shading\n"
+"      color.rgb = color.rgb * LightColor * myhalf(texture2D(Texture_Attenuation, length(CubeVector)));\n"
+"#endif\n"
 "#endif\n"
 "\n"
 "#ifdef USECUBEFILTER\n"
 "#endif\n"
 "\n"
 "#ifdef USECUBEFILTER\n"
@@ -614,20 +601,6 @@ static const char *builtinshaderstring =
 "      color.rgb *= myhvec3(textureCube(Texture_Cube, CubeVector));\n"
 "#endif\n"
 "\n"
 "      color.rgb *= myhvec3(textureCube(Texture_Cube, CubeVector));\n"
 "#endif\n"
 "\n"
-"      // apply light color\n"
-"      color.rgb *= LightColor;\n"
-"\n"
-"      // apply attenuation\n"
-"      //\n"
-"      // the attenuation is (1-(x*x+y*y+z*z)) which gives a large bright\n"
-"      // center and sharp falloff at the edge, this is about the most efficient\n"
-"      // we can get away with as far as providing illumination.\n"
-"      //\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 *= myhalf(max(1.0 - dot(CubeVector, CubeVector), 0.0));\n"
-"      color.rgb *= myhalf(max(2.0 - 2.0 * length(CubeVector), 0.0) / (1 + dot(CubeVector, CubeVector)));\n"
-"\n"
 "\n"
 "\n"
 "\n"
 "\n"
 "\n"
 "\n"
@@ -706,6 +679,7 @@ const char *permutationinfo[][2] =
        {"#define USEGLOW\n", " glow"},
        {"#define USEFOG\n", " fog"},
        {"#define USECOLORMAPPING\n", " colormapping"},
        {"#define USEGLOW\n", " glow"},
        {"#define USEFOG\n", " fog"},
        {"#define USECOLORMAPPING\n", " colormapping"},
+       {"#define USEDIFFUSE\n", " diffuse"},
        {"#define USESPECULAR\n", " specular"},
        {"#define USECUBEFILTER\n", " cubefilter"},
        {"#define USEOFFSETMAPPING\n", " offsetmapping"},
        {"#define USESPECULAR\n", " specular"},
        {"#define USECUBEFILTER\n", " cubefilter"},
        {"#define USEOFFSETMAPPING\n", " offsetmapping"},
@@ -717,14 +691,14 @@ void R_GLSL_CompilePermutation(const char *filename, int permutation)
 {
        int i;
        qboolean shaderfound;
 {
        int i;
        qboolean shaderfound;
-       r_glsl_permutation_t *p = r_glsl_permutations + (permutation & SHADERPERMUTATION_COUNTMASK);
+       r_glsl_permutation_t *p = r_glsl_permutations + (permutation & SHADERPERMUTATION_MASK);
        int vertstrings_count;
        int geomstrings_count;
        int fragstrings_count;
        char *shaderstring;
        int vertstrings_count;
        int geomstrings_count;
        int fragstrings_count;
        char *shaderstring;
-       const char *vertstrings_list[SHADERPERMUTATION_COUNT+1];
-       const char *geomstrings_list[SHADERPERMUTATION_COUNT+1];
-       const char *fragstrings_list[SHADERPERMUTATION_COUNT+1];
+       const char *vertstrings_list[32+1];
+       const char *geomstrings_list[32+1];
+       const char *fragstrings_list[32+1];
        char permutationname[256];
        if (p->compiled)
                return;
        char permutationname[256];
        if (p->compiled)
                return;
@@ -792,6 +766,7 @@ void R_GLSL_CompilePermutation(const char *filename, int permutation)
                p->loc_Texture_Color       = qglGetUniformLocationARB(p->program, "Texture_Color");
                p->loc_Texture_Gloss       = qglGetUniformLocationARB(p->program, "Texture_Gloss");
                p->loc_Texture_Cube        = qglGetUniformLocationARB(p->program, "Texture_Cube");
                p->loc_Texture_Color       = qglGetUniformLocationARB(p->program, "Texture_Color");
                p->loc_Texture_Gloss       = qglGetUniformLocationARB(p->program, "Texture_Gloss");
                p->loc_Texture_Cube        = qglGetUniformLocationARB(p->program, "Texture_Cube");
+               p->loc_Texture_Attenuation = qglGetUniformLocationARB(p->program, "Texture_Attenuation");
                p->loc_Texture_FogMask     = qglGetUniformLocationARB(p->program, "Texture_FogMask");
                p->loc_Texture_Pants       = qglGetUniformLocationARB(p->program, "Texture_Pants");
                p->loc_Texture_Shirt       = qglGetUniformLocationARB(p->program, "Texture_Shirt");
                p->loc_Texture_FogMask     = qglGetUniformLocationARB(p->program, "Texture_FogMask");
                p->loc_Texture_Pants       = qglGetUniformLocationARB(p->program, "Texture_Pants");
                p->loc_Texture_Shirt       = qglGetUniformLocationARB(p->program, "Texture_Shirt");
@@ -827,6 +802,7 @@ void R_GLSL_CompilePermutation(const char *filename, int permutation)
                if (p->loc_Texture_Lightmap >= 0)  qglUniform1iARB(p->loc_Texture_Lightmap, 7);
                if (p->loc_Texture_Deluxemap >= 0) qglUniform1iARB(p->loc_Texture_Deluxemap, 8);
                if (p->loc_Texture_Glow >= 0)      qglUniform1iARB(p->loc_Texture_Glow, 9);
                if (p->loc_Texture_Lightmap >= 0)  qglUniform1iARB(p->loc_Texture_Lightmap, 7);
                if (p->loc_Texture_Deluxemap >= 0) qglUniform1iARB(p->loc_Texture_Deluxemap, 8);
                if (p->loc_Texture_Glow >= 0)      qglUniform1iARB(p->loc_Texture_Glow, 9);
+               if (p->loc_Texture_Attenuation >= 0) qglUniform1iARB(p->loc_Texture_Attenuation, 10);
                CHECKGLERROR
                qglUseProgramObjectARB(0);CHECKGLERROR
        }
                CHECKGLERROR
                qglUseProgramObjectARB(0);CHECKGLERROR
        }
@@ -839,21 +815,23 @@ void R_GLSL_CompilePermutation(const char *filename, int permutation)
 void R_GLSL_Restart_f(void)
 {
        int i;
 void R_GLSL_Restart_f(void)
 {
        int i;
-       for (i = 0;i < SHADERPERMUTATION_COUNT;i++)
+       for (i = 0;i < SHADERPERMUTATION_MAX;i++)
                if (r_glsl_permutations[i].program)
                        GL_Backend_FreeProgram(r_glsl_permutations[i].program);
        memset(r_glsl_permutations, 0, sizeof(r_glsl_permutations));
 }
 
                if (r_glsl_permutations[i].program)
                        GL_Backend_FreeProgram(r_glsl_permutations[i].program);
        memset(r_glsl_permutations, 0, sizeof(r_glsl_permutations));
 }
 
-int R_SetupSurfaceShader(const vec3_t lightcolorbase, qboolean modellighting)
+extern rtexture_t *r_shadow_attenuationgradienttexture;
+extern rtexture_t *r_shadow_attenuation2dtexture;
+extern rtexture_t *r_shadow_attenuation3dtexture;
+int R_SetupSurfaceShader(const vec3_t lightcolorbase, qboolean modellighting, float ambientscale, float diffusescale, float specularscale)
 {
        // select a permutation of the lighting shader appropriate to this
        // combination of texture, entity, light source, and fogging, only use the
        // minimum features necessary to avoid wasting rendering time in the
        // fragment shader on features that are not being used
        const char *shaderfilename = NULL;
 {
        // select a permutation of the lighting shader appropriate to this
        // combination of texture, entity, light source, and fogging, only use the
        // minimum features necessary to avoid wasting rendering time in the
        // fragment shader on features that are not being used
        const char *shaderfilename = NULL;
-       int permutation = 0;
-       float specularscale = rsurface_texture->specularscale;
+       unsigned int permutation = 0;
        r_glsl_permutation = NULL;
        // TODO: implement geometry-shader based shadow volumes someday
        if (r_shadow_rtlight)
        r_glsl_permutation = NULL;
        // TODO: implement geometry-shader based shadow volumes someday
        if (r_shadow_rtlight)
@@ -861,11 +839,12 @@ int R_SetupSurfaceShader(const vec3_t lightcolorbase, qboolean modellighting)
                // light source
                shaderfilename = "glsl/default.glsl";
                permutation = SHADERPERMUTATION_MODE_LIGHTSOURCE | SHADERPERMUTATION_USES_VERTEXSHADER | SHADERPERMUTATION_USES_FRAGMENTSHADER;
                // light source
                shaderfilename = "glsl/default.glsl";
                permutation = SHADERPERMUTATION_MODE_LIGHTSOURCE | SHADERPERMUTATION_USES_VERTEXSHADER | SHADERPERMUTATION_USES_FRAGMENTSHADER;
-               specularscale *= r_shadow_rtlight->specularscale;
                if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
                        permutation |= SHADERPERMUTATION_CUBEFILTER;
                if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
                        permutation |= SHADERPERMUTATION_CUBEFILTER;
+               if (diffusescale > 0)
+                       permutation |= SHADERPERMUTATION_DIFFUSE;
                if (specularscale > 0)
                if (specularscale > 0)
-                       permutation |= SHADERPERMUTATION_SPECULAR;
+                       permutation |= SHADERPERMUTATION_SPECULAR | SHADERPERMUTATION_DIFFUSE;
                if (r_refdef.fogenabled)
                        permutation |= SHADERPERMUTATION_FOG;
                if (rsurface_texture->colormapping)
                if (r_refdef.fogenabled)
                        permutation |= SHADERPERMUTATION_FOG;
                if (rsurface_texture->colormapping)
@@ -956,30 +935,30 @@ int R_SetupSurfaceShader(const vec3_t lightcolorbase, qboolean modellighting)
                                permutation |= SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING;
                }
        }
                                permutation |= SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING;
                }
        }
-       if (!r_glsl_permutations[permutation & SHADERPERMUTATION_COUNTMASK].program)
+       if (!r_glsl_permutations[permutation & SHADERPERMUTATION_MASK].program)
        {
        {
-               if (!r_glsl_permutations[permutation & SHADERPERMUTATION_COUNTMASK].compiled)
+               if (!r_glsl_permutations[permutation & SHADERPERMUTATION_MASK].compiled)
                        R_GLSL_CompilePermutation(shaderfilename, permutation);
                        R_GLSL_CompilePermutation(shaderfilename, permutation);
-               if (!r_glsl_permutations[permutation & SHADERPERMUTATION_COUNTMASK].program)
+               if (!r_glsl_permutations[permutation & SHADERPERMUTATION_MASK].program)
                {
                        // remove features until we find a valid permutation
                {
                        // remove features until we find a valid permutation
-                       int i;
-                       for (i = SHADERPERMUTATION_COUNT-1;;i>>=1)
+                       unsigned int i;
+                       for (i = SHADERPERMUTATION_MASK;;i>>=1)
                        {
                        {
+                               if (!i)
+                                       return 0; // utterly failed
                                // reduce i more quickly whenever it would not remove any bits
                                if (permutation < i)
                                        continue;
                                permutation &= i;
                                // reduce i more quickly whenever it would not remove any bits
                                if (permutation < i)
                                        continue;
                                permutation &= i;
-                               if (!r_glsl_permutations[permutation & SHADERPERMUTATION_COUNTMASK].compiled)
+                               if (!r_glsl_permutations[permutation & SHADERPERMUTATION_MASK].compiled)
                                        R_GLSL_CompilePermutation(shaderfilename, permutation);
                                        R_GLSL_CompilePermutation(shaderfilename, permutation);
-                               if (r_glsl_permutations[permutation & SHADERPERMUTATION_COUNTMASK].program)
+                               if (r_glsl_permutations[permutation & SHADERPERMUTATION_MASK].program)
                                        break;
                                        break;
-                               if (!i)
-                                       return 0; // utterly failed
                        }
                }
        }
                        }
                }
        }
-       r_glsl_permutation = r_glsl_permutations + (permutation & SHADERPERMUTATION_COUNTMASK);
+       r_glsl_permutation = r_glsl_permutations + (permutation & SHADERPERMUTATION_MASK);
        CHECKGLERROR
        qglUseProgramObjectARB(r_glsl_permutation->program);CHECKGLERROR
        R_Mesh_TexMatrix(0, &rsurface_texture->currenttexmatrix);
        CHECKGLERROR
        qglUseProgramObjectARB(r_glsl_permutation->program);CHECKGLERROR
        R_Mesh_TexMatrix(0, &rsurface_texture->currenttexmatrix);
@@ -987,19 +966,30 @@ int R_SetupSurfaceShader(const vec3_t lightcolorbase, qboolean modellighting)
        {
                if (r_glsl_permutation->loc_Texture_Cube >= 0 && r_shadow_rtlight) R_Mesh_TexBindCubeMap(3, R_GetTexture(r_shadow_rtlight->currentcubemap));
                if (r_glsl_permutation->loc_LightPosition >= 0) qglUniform3fARB(r_glsl_permutation->loc_LightPosition, r_shadow_entitylightorigin[0], r_shadow_entitylightorigin[1], r_shadow_entitylightorigin[2]);
        {
                if (r_glsl_permutation->loc_Texture_Cube >= 0 && r_shadow_rtlight) R_Mesh_TexBindCubeMap(3, R_GetTexture(r_shadow_rtlight->currentcubemap));
                if (r_glsl_permutation->loc_LightPosition >= 0) qglUniform3fARB(r_glsl_permutation->loc_LightPosition, r_shadow_entitylightorigin[0], r_shadow_entitylightorigin[1], r_shadow_entitylightorigin[2]);
-               if (r_glsl_permutation->loc_LightColor >= 0) qglUniform3fARB(r_glsl_permutation->loc_LightColor, lightcolorbase[0], lightcolorbase[1], lightcolorbase[2]);
-               if (r_glsl_permutation->loc_AmbientScale >= 0) qglUniform1fARB(r_glsl_permutation->loc_AmbientScale, r_shadow_rtlight->ambientscale);
-               if (r_glsl_permutation->loc_DiffuseScale >= 0) qglUniform1fARB(r_glsl_permutation->loc_DiffuseScale, r_shadow_rtlight->diffusescale);
-               if (r_glsl_permutation->loc_SpecularScale >= 0) qglUniform1fARB(r_glsl_permutation->loc_SpecularScale, specularscale);
+               if (permutation & SHADERPERMUTATION_DIFFUSE)
+               {
+                       if (r_glsl_permutation->loc_LightColor >= 0) qglUniform3fARB(r_glsl_permutation->loc_LightColor, lightcolorbase[0], lightcolorbase[1], lightcolorbase[2]);
+                       if (r_glsl_permutation->loc_AmbientScale >= 0) qglUniform1fARB(r_glsl_permutation->loc_AmbientScale, ambientscale);
+                       if (r_glsl_permutation->loc_DiffuseScale >= 0) qglUniform1fARB(r_glsl_permutation->loc_DiffuseScale, diffusescale);
+                       if (r_glsl_permutation->loc_SpecularScale >= 0) qglUniform1fARB(r_glsl_permutation->loc_SpecularScale, specularscale);
+               }
+               else
+               {
+                       // ambient only is simpler
+                       if (r_glsl_permutation->loc_LightColor >= 0) qglUniform3fARB(r_glsl_permutation->loc_LightColor, lightcolorbase[0] * ambientscale, lightcolorbase[1] * ambientscale, lightcolorbase[2] * ambientscale);
+                       if (r_glsl_permutation->loc_AmbientScale >= 0) qglUniform1fARB(r_glsl_permutation->loc_AmbientScale, 1);
+                       if (r_glsl_permutation->loc_DiffuseScale >= 0) qglUniform1fARB(r_glsl_permutation->loc_DiffuseScale, 0);
+                       if (r_glsl_permutation->loc_SpecularScale >= 0) qglUniform1fARB(r_glsl_permutation->loc_SpecularScale, 0);
+               }
        }
        else if (permutation & SHADERPERMUTATION_MODE_LIGHTDIRECTION)
        {
                if (r_glsl_permutation->loc_AmbientColor >= 0)
        }
        else if (permutation & SHADERPERMUTATION_MODE_LIGHTDIRECTION)
        {
                if (r_glsl_permutation->loc_AmbientColor >= 0)
-                       qglUniform3fARB(r_glsl_permutation->loc_AmbientColor, rsurface_entity->modellight_ambient[0], rsurface_entity->modellight_ambient[1], rsurface_entity->modellight_ambient[2]);
+                       qglUniform3fARB(r_glsl_permutation->loc_AmbientColor, rsurface_entity->modellight_ambient[0] * ambientscale, rsurface_entity->modellight_ambient[1] * ambientscale, rsurface_entity->modellight_ambient[2] * ambientscale);
                if (r_glsl_permutation->loc_DiffuseColor >= 0)
                if (r_glsl_permutation->loc_DiffuseColor >= 0)
-                       qglUniform3fARB(r_glsl_permutation->loc_DiffuseColor, rsurface_entity->modellight_diffuse[0], rsurface_entity->modellight_diffuse[1], rsurface_entity->modellight_diffuse[2]);
+                       qglUniform3fARB(r_glsl_permutation->loc_DiffuseColor, rsurface_entity->modellight_diffuse[0] * diffusescale, rsurface_entity->modellight_diffuse[1] * diffusescale, rsurface_entity->modellight_diffuse[2] * diffusescale);
                if (r_glsl_permutation->loc_SpecularColor >= 0)
                if (r_glsl_permutation->loc_SpecularColor >= 0)
-                       qglUniform3fARB(r_glsl_permutation->loc_SpecularColor, rsurface_entity->modellight_diffuse[0] * rsurface_texture->specularscale, rsurface_entity->modellight_diffuse[1] * rsurface_texture->specularscale, rsurface_entity->modellight_diffuse[2] * rsurface_texture->specularscale);
+                       qglUniform3fARB(r_glsl_permutation->loc_SpecularColor, rsurface_entity->modellight_diffuse[0] * specularscale, rsurface_entity->modellight_diffuse[1] * specularscale, rsurface_entity->modellight_diffuse[2] * specularscale);
                if (r_glsl_permutation->loc_LightDir >= 0)
                        qglUniform3fARB(r_glsl_permutation->loc_LightDir, rsurface_entity->modellight_lightdir[0], rsurface_entity->modellight_lightdir[1], rsurface_entity->modellight_lightdir[2]);
        }
                if (r_glsl_permutation->loc_LightDir >= 0)
                        qglUniform3fARB(r_glsl_permutation->loc_LightDir, rsurface_entity->modellight_lightdir[0], rsurface_entity->modellight_lightdir[1], rsurface_entity->modellight_lightdir[2]);
        }
@@ -1013,6 +1003,7 @@ int R_SetupSurfaceShader(const vec3_t lightcolorbase, qboolean modellighting)
        if (r_glsl_permutation->loc_Texture_Color >= 0) R_Mesh_TexBind(1, R_GetTexture(rsurface_texture->basetexture));
        if (r_glsl_permutation->loc_Texture_Gloss >= 0) R_Mesh_TexBind(2, R_GetTexture(rsurface_texture->glosstexture));
        //if (r_glsl_permutation->loc_Texture_Cube >= 0 && permutation & SHADERPERMUTATION_MODE_LIGHTSOURCE) R_Mesh_TexBindCubeMap(3, R_GetTexture(r_shadow_rtlight->currentcubemap));
        if (r_glsl_permutation->loc_Texture_Color >= 0) R_Mesh_TexBind(1, R_GetTexture(rsurface_texture->basetexture));
        if (r_glsl_permutation->loc_Texture_Gloss >= 0) R_Mesh_TexBind(2, R_GetTexture(rsurface_texture->glosstexture));
        //if (r_glsl_permutation->loc_Texture_Cube >= 0 && permutation & SHADERPERMUTATION_MODE_LIGHTSOURCE) R_Mesh_TexBindCubeMap(3, R_GetTexture(r_shadow_rtlight->currentcubemap));
+       if (r_glsl_permutation->loc_Texture_Attenuation >= 0) R_Mesh_TexBind(10, R_GetTexture(r_shadow_attenuationgradienttexture));
        if (r_glsl_permutation->loc_Texture_FogMask >= 0) R_Mesh_TexBind(4, R_GetTexture(r_texture_fogattenuation));
        if (r_glsl_permutation->loc_Texture_Pants >= 0) R_Mesh_TexBind(5, R_GetTexture(rsurface_texture->currentskinframe->pants));
        if (r_glsl_permutation->loc_Texture_Shirt >= 0) R_Mesh_TexBind(6, R_GetTexture(rsurface_texture->currentskinframe->shirt));
        if (r_glsl_permutation->loc_Texture_FogMask >= 0) R_Mesh_TexBind(4, R_GetTexture(r_texture_fogattenuation));
        if (r_glsl_permutation->loc_Texture_Pants >= 0) R_Mesh_TexBind(5, R_GetTexture(rsurface_texture->currentskinframe->pants));
        if (r_glsl_permutation->loc_Texture_Shirt >= 0) R_Mesh_TexBind(6, R_GetTexture(rsurface_texture->currentskinframe->shirt));
@@ -1053,9 +1044,9 @@ int R_SetupSurfaceShader(const vec3_t lightcolorbase, qboolean modellighting)
 
 void R_SwitchSurfaceShader(int permutation)
 {
 
 void R_SwitchSurfaceShader(int permutation)
 {
-       if (r_glsl_permutation != r_glsl_permutations + (permutation & SHADERPERMUTATION_COUNTMASK))
+       if (r_glsl_permutation != r_glsl_permutations + (permutation & SHADERPERMUTATION_MASK))
        {
        {
-               r_glsl_permutation = r_glsl_permutations + (permutation & SHADERPERMUTATION_COUNTMASK);
+               r_glsl_permutation = r_glsl_permutations + (permutation & SHADERPERMUTATION_MASK);
                CHECKGLERROR
                qglUseProgramObjectARB(r_glsl_permutation->program);
                CHECKGLERROR
                CHECKGLERROR
                qglUseProgramObjectARB(r_glsl_permutation->program);
                CHECKGLERROR
@@ -2736,6 +2727,7 @@ void R_UpdateTextureInfo(const entity_render_t *ent, texture_t *t)
        t->backgroundbasetexture = t->backgroundnumskinframes ? ((!t->colormapping && t->backgroundcurrentskinframe->merged) ? t->backgroundcurrentskinframe->merged : t->backgroundcurrentskinframe->base) : r_texture_white;
        t->backgroundglosstexture = r_texture_white;
        t->specularpower = r_shadow_glossexponent.value;
        t->backgroundbasetexture = t->backgroundnumskinframes ? ((!t->colormapping && t->backgroundcurrentskinframe->merged) ? t->backgroundcurrentskinframe->merged : t->backgroundcurrentskinframe->base) : r_texture_white;
        t->backgroundglosstexture = r_texture_white;
        t->specularpower = r_shadow_glossexponent.value;
+       // TODO: store reference values for these in the texture?
        t->specularscale = 0;
        if (r_shadow_gloss.integer > 0)
        {
        t->specularscale = 0;
        if (r_shadow_gloss.integer > 0)
        {
@@ -3553,7 +3545,7 @@ static void R_DrawTextureSurfaceList_GL20(int texturenumsurfaces, msurface_t **t
                R_Mesh_ResetTextureState();
        }
 
                R_Mesh_ResetTextureState();
        }
 
-       R_SetupSurfaceShader(vec3_origin, rsurface_lightmode == 2);
+       R_SetupSurfaceShader(vec3_origin, rsurface_lightmode == 2, 1, 1, rsurface_texture->specularscale);
        if (!r_glsl_permutation)
                return;
 
        if (!r_glsl_permutation)
                return;
 
index 29016e0..9a8397d 100644 (file)
@@ -198,6 +198,7 @@ int r_shadow_rtlight_numfrustumplanes;
 mplane_t r_shadow_rtlight_frustumplanes[12+6+6]; // see R_Shadow_ComputeShadowCasterCullingPlanes
 
 rtexturepool_t *r_shadow_texturepool;
 mplane_t r_shadow_rtlight_frustumplanes[12+6+6]; // see R_Shadow_ComputeShadowCasterCullingPlanes
 
 rtexturepool_t *r_shadow_texturepool;
+rtexture_t *r_shadow_attenuationgradienttexture;
 rtexture_t *r_shadow_attenuation2dtexture;
 rtexture_t *r_shadow_attenuation3dtexture;
 
 rtexture_t *r_shadow_attenuation2dtexture;
 rtexture_t *r_shadow_attenuation3dtexture;
 
@@ -210,12 +211,13 @@ rtexturepool_t *r_shadow_filters_texturepool;
 cvar_t r_shadow_bumpscale_basetexture = {0, "r_shadow_bumpscale_basetexture", "0", "generate fake bumpmaps from diffuse textures at this bumpyness, try 4 to match tenebrae, higher values increase depth, requires r_restart to take effect"};
 cvar_t r_shadow_bumpscale_bumpmap = {0, "r_shadow_bumpscale_bumpmap", "4", "what magnitude to interpret _bump.tga textures as, higher values increase depth, requires r_restart to take effect"};
 cvar_t r_shadow_debuglight = {0, "r_shadow_debuglight", "-1", "renders only one light, for level design purposes or debugging"};
 cvar_t r_shadow_bumpscale_basetexture = {0, "r_shadow_bumpscale_basetexture", "0", "generate fake bumpmaps from diffuse textures at this bumpyness, try 4 to match tenebrae, higher values increase depth, requires r_restart to take effect"};
 cvar_t r_shadow_bumpscale_bumpmap = {0, "r_shadow_bumpscale_bumpmap", "4", "what magnitude to interpret _bump.tga textures as, higher values increase depth, requires r_restart to take effect"};
 cvar_t r_shadow_debuglight = {0, "r_shadow_debuglight", "-1", "renders only one light, for level design purposes or debugging"};
+cvar_t r_shadow_usenormalmap = {CVAR_SAVE, "r_shadow_usenormalmap", "1", "enables use of directional shading on lights"};
 cvar_t r_shadow_gloss = {CVAR_SAVE, "r_shadow_gloss", "1", "0 disables gloss (specularity) rendering, 1 uses gloss if textures are found, 2 forces a flat metallic specular effect on everything without textures (similar to tenebrae)"};
 cvar_t r_shadow_gloss2intensity = {0, "r_shadow_gloss2intensity", "0.125", "how bright the forced flat gloss should look if r_shadow_gloss is 2"};
 cvar_t r_shadow_glossintensity = {0, "r_shadow_glossintensity", "1", "how bright textured glossmaps should look if r_shadow_gloss is 1 or 2"};
 cvar_t r_shadow_glossexponent = {0, "r_shadow_glossexponent", "32", "how 'sharp' the gloss should appear (specular power)"};
 cvar_t r_shadow_gloss = {CVAR_SAVE, "r_shadow_gloss", "1", "0 disables gloss (specularity) rendering, 1 uses gloss if textures are found, 2 forces a flat metallic specular effect on everything without textures (similar to tenebrae)"};
 cvar_t r_shadow_gloss2intensity = {0, "r_shadow_gloss2intensity", "0.125", "how bright the forced flat gloss should look if r_shadow_gloss is 2"};
 cvar_t r_shadow_glossintensity = {0, "r_shadow_glossintensity", "1", "how bright textured glossmaps should look if r_shadow_gloss is 1 or 2"};
 cvar_t r_shadow_glossexponent = {0, "r_shadow_glossexponent", "32", "how 'sharp' the gloss should appear (specular power)"};
-cvar_t r_shadow_lightattenuationpower = {0, "r_shadow_lightattenuationpower", "0.5", "changes attenuation texture generation (does not affect r_glsl lighting)"};
-cvar_t r_shadow_lightattenuationscale = {0, "r_shadow_lightattenuationscale", "1", "changes attenuation texture generation (does not affect r_glsl lighting)"};
+cvar_t r_shadow_lightattenuationdividebias = {0, "r_shadow_lightattenuationdividebias", "1", "changes attenuation texture generation"};
+cvar_t r_shadow_lightattenuationlinearscale = {0, "r_shadow_lightattenuationlinearscale", "2", "changes attenuation texture generation"};
 cvar_t r_shadow_lightintensityscale = {0, "r_shadow_lightintensityscale", "1", "renders all world lights brighter or darker"};
 cvar_t r_shadow_lightradiusscale = {0, "r_shadow_lightradiusscale", "1", "renders all world lights larger or smaller"};
 cvar_t r_shadow_portallight = {0, "r_shadow_portallight", "1", "use portal culling to exactly determine lit triangles when compiling world lights"};
 cvar_t r_shadow_lightintensityscale = {0, "r_shadow_lightintensityscale", "1", "renders all world lights brighter or darker"};
 cvar_t r_shadow_lightradiusscale = {0, "r_shadow_lightradiusscale", "1", "renders all world lights larger or smaller"};
 cvar_t r_shadow_portallight = {0, "r_shadow_portallight", "1", "use portal culling to exactly determine lit triangles when compiling world lights"};
@@ -247,7 +249,16 @@ cvar_t r_editlights_cursorpushoff = {0, "r_editlights_cursorpushoff", "4", "how
 cvar_t r_editlights_cursorgrid = {0, "r_editlights_cursorgrid", "4", "snaps cursor to this grid size"};
 cvar_t r_editlights_quakelightsizescale = {CVAR_SAVE, "r_editlights_quakelightsizescale", "1", "changes size of light entities loaded from a map"};
 
 cvar_t r_editlights_cursorgrid = {0, "r_editlights_cursorgrid", "4", "snaps cursor to this grid size"};
 cvar_t r_editlights_quakelightsizescale = {CVAR_SAVE, "r_editlights_quakelightsizescale", "1", "changes size of light entities loaded from a map"};
 
-float r_shadow_attenpower, r_shadow_attenscale;
+// note the table actually includes one more value, just to avoid the need to clamp the distance index due to minor math error
+#define ATTENTABLESIZE 256
+// 1D gradient, 2D circle and 3D sphere attenuation textures
+#define ATTEN1DSIZE 32
+#define ATTEN2DSIZE 64
+#define ATTEN3DSIZE 32
+
+static float r_shadow_attendividebias; // r_shadow_lightattenuationdividebias
+static float r_shadow_attenlinearscale; // r_shadow_lightattenuationlinearscale
+static float r_shadow_attentable[ATTENTABLESIZE+1];
 
 rtlight_t *r_shadow_compilingrtlight;
 dlight_t *r_shadow_worldlightchain;
 
 rtlight_t *r_shadow_compilingrtlight;
 dlight_t *r_shadow_worldlightchain;
@@ -282,6 +293,7 @@ void r_shadow_start(void)
 {
        // allocate vertex processing arrays
        numcubemaps = 0;
 {
        // allocate vertex processing arrays
        numcubemaps = 0;
+       r_shadow_attenuationgradienttexture = NULL;
        r_shadow_attenuation2dtexture = NULL;
        r_shadow_attenuation3dtexture = NULL;
        r_shadow_texturepool = NULL;
        r_shadow_attenuation2dtexture = NULL;
        r_shadow_attenuation3dtexture = NULL;
        r_shadow_texturepool = NULL;
@@ -317,6 +329,7 @@ void r_shadow_shutdown(void)
 {
        R_Shadow_UncompileWorldLights();
        numcubemaps = 0;
 {
        R_Shadow_UncompileWorldLights();
        numcubemaps = 0;
+       r_shadow_attenuationgradienttexture = NULL;
        r_shadow_attenuation2dtexture = NULL;
        r_shadow_attenuation3dtexture = NULL;
        R_FreeTexturePool(&r_shadow_texturepool);
        r_shadow_attenuation2dtexture = NULL;
        r_shadow_attenuation3dtexture = NULL;
        R_FreeTexturePool(&r_shadow_texturepool);
@@ -382,8 +395,8 @@ void R_Shadow_Help_f(void)
 "r_shadow_gloss 0/1/2 : no gloss, gloss textures only, force gloss\n"
 "r_shadow_gloss2intensity : brightness of forced gloss\n"
 "r_shadow_glossintensity : brightness of textured gloss\n"
 "r_shadow_gloss 0/1/2 : no gloss, gloss textures only, force gloss\n"
 "r_shadow_gloss2intensity : brightness of forced gloss\n"
 "r_shadow_glossintensity : brightness of textured gloss\n"
-"r_shadow_lightattenuationpower : used to generate attenuation texture\n"
-"r_shadow_lightattenuationscale : used to generate attenuation texture\n"
+"r_shadow_lightattenuationlinearscale : used to generate attenuation texture\n"
+"r_shadow_lightattenuationdividebias : used to generate attenuation texture\n"
 "r_shadow_lightintensityscale : scale rendering brightness of all lights\n"
 "r_shadow_lightradiusscale : scale rendering radius of all lights\n"
 "r_shadow_portallight : use portal visibility for static light precomputation\n"
 "r_shadow_lightintensityscale : scale rendering brightness of all lights\n"
 "r_shadow_lightradiusscale : scale rendering radius of all lights\n"
 "r_shadow_portallight : use portal visibility for static light precomputation\n"
@@ -411,13 +424,14 @@ void R_Shadow_Init(void)
 {
        Cvar_RegisterVariable(&r_shadow_bumpscale_basetexture);
        Cvar_RegisterVariable(&r_shadow_bumpscale_bumpmap);
 {
        Cvar_RegisterVariable(&r_shadow_bumpscale_basetexture);
        Cvar_RegisterVariable(&r_shadow_bumpscale_bumpmap);
+       Cvar_RegisterVariable(&r_shadow_usenormalmap);
        Cvar_RegisterVariable(&r_shadow_debuglight);
        Cvar_RegisterVariable(&r_shadow_gloss);
        Cvar_RegisterVariable(&r_shadow_gloss2intensity);
        Cvar_RegisterVariable(&r_shadow_glossintensity);
        Cvar_RegisterVariable(&r_shadow_glossexponent);
        Cvar_RegisterVariable(&r_shadow_debuglight);
        Cvar_RegisterVariable(&r_shadow_gloss);
        Cvar_RegisterVariable(&r_shadow_gloss2intensity);
        Cvar_RegisterVariable(&r_shadow_glossintensity);
        Cvar_RegisterVariable(&r_shadow_glossexponent);
-       Cvar_RegisterVariable(&r_shadow_lightattenuationpower);
-       Cvar_RegisterVariable(&r_shadow_lightattenuationscale);
+       Cvar_RegisterVariable(&r_shadow_lightattenuationdividebias);
+       Cvar_RegisterVariable(&r_shadow_lightattenuationlinearscale);
        Cvar_RegisterVariable(&r_shadow_lightintensityscale);
        Cvar_RegisterVariable(&r_shadow_lightradiusscale);
        Cvar_RegisterVariable(&r_shadow_portallight);
        Cvar_RegisterVariable(&r_shadow_lightintensityscale);
        Cvar_RegisterVariable(&r_shadow_lightradiusscale);
        Cvar_RegisterVariable(&r_shadow_portallight);
@@ -903,60 +917,54 @@ void R_Shadow_RenderVolume(int numvertices, int numtriangles, const float *verte
        CHECKGLERROR
 }
 
        CHECKGLERROR
 }
 
+static unsigned char R_Shadow_MakeTextures_SamplePoint(float x, float y, float z)
+{
+       float dist = sqrt(x*x+y*y+z*z);
+       float intensity = dist < 1 ? ((1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist)) : 0;
+       return (unsigned char)bound(0, intensity * 256.0f, 255);
+}
+
 static void R_Shadow_MakeTextures(void)
 {
 static void R_Shadow_MakeTextures(void)
 {
-       int x, y, z, d;
-       float v[3], intensity;
+       int x, y, z;
+       float intensity, dist;
        unsigned char *data;
        unsigned char *data;
+       unsigned int palette[256];
        R_FreeTexturePool(&r_shadow_texturepool);
        r_shadow_texturepool = R_AllocTexturePool();
        R_FreeTexturePool(&r_shadow_texturepool);
        r_shadow_texturepool = R_AllocTexturePool();
-       r_shadow_attenpower = r_shadow_lightattenuationpower.value;
-       r_shadow_attenscale = r_shadow_lightattenuationscale.value;
-#define ATTEN2DSIZE 64
-#define ATTEN3DSIZE 32
-       data = (unsigned char *)Mem_Alloc(tempmempool, max(ATTEN3DSIZE*ATTEN3DSIZE*ATTEN3DSIZE*4, ATTEN2DSIZE*ATTEN2DSIZE*4));
+       r_shadow_attenlinearscale = r_shadow_lightattenuationlinearscale.value;
+       r_shadow_attendividebias = r_shadow_lightattenuationdividebias.value;
+       // note this code could suffer byte order issues except that it is multiplying by an integer that reads the same both ways
+       for (x = 0;x < 256;x++)
+               palette[x] = x * 0x01010101;
+       data = (unsigned char *)Mem_Alloc(tempmempool, max(max(ATTEN3DSIZE*ATTEN3DSIZE*ATTEN3DSIZE, ATTEN2DSIZE*ATTEN2DSIZE), ATTEN1DSIZE));
+       // the table includes one additional value to avoid the need to clamp indexing due to minor math errors
+       for (x = 0;x <= ATTENTABLESIZE;x++)
+       {
+               dist = (x + 0.5f) * (1.0f / ATTENTABLESIZE) * (1.0f / 0.9375);
+               intensity = dist < 1 ? ((1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist)) : 0;
+               r_shadow_attentable[x] = bound(0, intensity, 1);
+       }
+       // 1D gradient texture
+       for (x = 0;x < ATTEN1DSIZE;x++)
+               data[x] = R_Shadow_MakeTextures_SamplePoint((x + 0.5f) * (1.0f / ATTEN1DSIZE) * (1.0f / 0.9375), 0, 0);
+       r_shadow_attenuationgradienttexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation1d", ATTEN1DSIZE, 1, data, TEXTYPE_PALETTE, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, palette);
+       // 2D circle texture
        for (y = 0;y < ATTEN2DSIZE;y++)
        for (y = 0;y < ATTEN2DSIZE;y++)
-       {
                for (x = 0;x < ATTEN2DSIZE;x++)
                for (x = 0;x < ATTEN2DSIZE;x++)
-               {
-                       v[0] = ((x + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
-                       v[1] = ((y + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
-                       v[2] = 0;
-                       intensity = 1.0f - sqrt(DotProduct(v, v));
-                       if (intensity > 0)
-                               intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
-                       d = (int)bound(0, intensity, 255);
-                       data[(y*ATTEN2DSIZE+x)*4+0] = d;
-                       data[(y*ATTEN2DSIZE+x)*4+1] = d;
-                       data[(y*ATTEN2DSIZE+x)*4+2] = d;
-                       data[(y*ATTEN2DSIZE+x)*4+3] = d;
-               }
-       }
-       r_shadow_attenuation2dtexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation2d", ATTEN2DSIZE, ATTEN2DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
+                       data[y*ATTEN2DSIZE+x] = R_Shadow_MakeTextures_SamplePoint(((x + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375), ((y + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375), 0);
+       r_shadow_attenuation2dtexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation2d", ATTEN2DSIZE, ATTEN2DSIZE, data, TEXTYPE_PALETTE, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, palette);
+       // 3D sphere texture
        if (r_shadow_texture3d.integer && gl_texture3d)
        {
                for (z = 0;z < ATTEN3DSIZE;z++)
        if (r_shadow_texture3d.integer && gl_texture3d)
        {
                for (z = 0;z < ATTEN3DSIZE;z++)
-               {
                        for (y = 0;y < ATTEN3DSIZE;y++)
                        for (y = 0;y < ATTEN3DSIZE;y++)
-                       {
                                for (x = 0;x < ATTEN3DSIZE;x++)
                                for (x = 0;x < ATTEN3DSIZE;x++)
-                               {
-                                       v[0] = ((x + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
-                                       v[1] = ((y + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
-                                       v[2] = ((z + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
-                                       intensity = 1.0f - sqrt(DotProduct(v, v));
-                                       if (intensity > 0)
-                                               intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
-                                       d = (int)bound(0, intensity, 255);
-                                       data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+0] = d;
-                                       data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+1] = d;
-                                       data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+2] = d;
-                                       data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+3] = d;
-                               }
-                       }
-               }
-               r_shadow_attenuation3dtexture = R_LoadTexture3D(r_shadow_texturepool, "attenuation3d", ATTEN3DSIZE, ATTEN3DSIZE, ATTEN3DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
+                                       data[(z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x] = R_Shadow_MakeTextures_SamplePoint(((x + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375), ((y + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375), ((z + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375));
+               r_shadow_attenuation3dtexture = R_LoadTexture3D(r_shadow_texturepool, "attenuation3d", ATTEN3DSIZE, ATTEN3DSIZE, ATTEN3DSIZE, data, TEXTYPE_PALETTE, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, palette);
        }
        }
+       else
+               r_shadow_attenuation3dtexture = NULL;
        Mem_Free(data);
 }
 
        Mem_Free(data);
 }
 
@@ -990,8 +998,8 @@ void R_Shadow_RenderMode_Begin(void)
 
        if (!r_shadow_attenuation2dtexture
         || (!r_shadow_attenuation3dtexture && r_shadow_texture3d.integer)
 
        if (!r_shadow_attenuation2dtexture
         || (!r_shadow_attenuation3dtexture && r_shadow_texture3d.integer)
-        || r_shadow_lightattenuationpower.value != r_shadow_attenpower
-        || r_shadow_lightattenuationscale.value != r_shadow_attenscale)
+        || r_shadow_lightattenuationdividebias.value != r_shadow_attendividebias
+        || r_shadow_lightattenuationlinearscale.value != r_shadow_attenlinearscale)
                R_Shadow_MakeTextures();
 
        CHECKGLERROR
                R_Shadow_MakeTextures();
 
        CHECKGLERROR
@@ -1259,92 +1267,155 @@ static void R_Shadow_RenderLighting_Light_Vertex_Shading(int firstvertex, int nu
        float dist, dot, distintensity, shadeintensity, v[3], n[3];
        if (r_textureunits.integer >= 3)
        {
        float dist, dot, distintensity, shadeintensity, v[3], n[3];
        if (r_textureunits.integer >= 3)
        {
-               for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
+               if (VectorLength2(diffusecolor) > 0)
                {
                {
-                       Matrix4x4_Transform(&r_shadow_entitytolight, vertex3f, v);
-                       Matrix4x4_Transform3x3(&r_shadow_entitytolight, normal3f, n);
-                       if ((dot = DotProduct(n, v)) < 0)
+                       for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
                        {
                        {
-                               shadeintensity = -dot / sqrt(VectorLength2(v) * VectorLength2(n));
-                               color4f[0] = (ambientcolor[0] + shadeintensity * diffusecolor[0]);
-                               color4f[1] = (ambientcolor[1] + shadeintensity * diffusecolor[1]);
-                               color4f[2] = (ambientcolor[2] + shadeintensity * diffusecolor[2]);
+                               Matrix4x4_Transform(&r_shadow_entitytolight, vertex3f, v);
+                               Matrix4x4_Transform3x3(&r_shadow_entitytolight, normal3f, n);
+                               if ((dot = DotProduct(n, v)) < 0)
+                               {
+                                       shadeintensity = -dot / sqrt(VectorLength2(v) * VectorLength2(n));
+                                       VectorMA(ambientcolor, shadeintensity, diffusecolor, color4f);
+                               }
+                               else
+                                       VectorCopy(ambientcolor, color4f);
                                if (r_refdef.fogenabled)
                                {
                                        float f = VERTEXFOGTABLE(VectorDistance(v, rsurface_modelorg));
                                        VectorScale(color4f, f, color4f);
                                }
                                if (r_refdef.fogenabled)
                                {
                                        float f = VERTEXFOGTABLE(VectorDistance(v, rsurface_modelorg));
                                        VectorScale(color4f, f, color4f);
                                }
+                               color4f[3] = 1;
+                       }
+               }
+               else
+               {
+                       for (;numverts > 0;numverts--, vertex3f += 3, color4f += 4)
+                       {
+                               VectorCopy(ambientcolor, color4f);
+                               if (r_refdef.fogenabled)
+                               {
+                                       float f;
+                                       Matrix4x4_Transform(&r_shadow_entitytolight, vertex3f, v);
+                                       f = VERTEXFOGTABLE(VectorDistance(v, rsurface_modelorg));
+                                       VectorScale(color4f, f, color4f);
+                               }
+                               color4f[3] = 1;
                        }
                        }
-                       else
-                               VectorClear(color4f);
-                       color4f[3] = 1;
                }
        }
        else if (r_textureunits.integer >= 2)
        {
                }
        }
        else if (r_textureunits.integer >= 2)
        {
-               for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
+               if (VectorLength2(diffusecolor) > 0)
                {
                {
-                       Matrix4x4_Transform(&r_shadow_entitytolight, vertex3f, v);
-                       if ((dist = fabs(v[2])) < 1)
+                       for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
                        {
                        {
-                               distintensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale;
-                               Matrix4x4_Transform3x3(&r_shadow_entitytolight, normal3f, n);
-                               if ((dot = DotProduct(n, v)) < 0)
+                               Matrix4x4_Transform(&r_shadow_entitytolight, vertex3f, v);
+                               if ((dist = fabs(v[2])) < 1 && (distintensity = r_shadow_attentable[(int)(dist * ATTENTABLESIZE)]))
                                {
                                {
-                                       shadeintensity = -dot / sqrt(VectorLength2(v) * VectorLength2(n));
-                                       color4f[0] = (ambientcolor[0] + shadeintensity * diffusecolor[0]) * distintensity;
-                                       color4f[1] = (ambientcolor[1] + shadeintensity * diffusecolor[1]) * distintensity;
-                                       color4f[2] = (ambientcolor[2] + shadeintensity * diffusecolor[2]) * distintensity;
+                                       Matrix4x4_Transform3x3(&r_shadow_entitytolight, normal3f, n);
+                                       if ((dot = DotProduct(n, v)) < 0)
+                                       {
+                                               shadeintensity = -dot / sqrt(VectorLength2(v) * VectorLength2(n));
+                                               color4f[0] = (ambientcolor[0] + shadeintensity * diffusecolor[0]) * distintensity;
+                                               color4f[1] = (ambientcolor[1] + shadeintensity * diffusecolor[1]) * distintensity;
+                                               color4f[2] = (ambientcolor[2] + shadeintensity * diffusecolor[2]) * distintensity;
+                                       }
+                                       else
+                                       {
+                                               color4f[0] = ambientcolor[0] * distintensity;
+                                               color4f[1] = ambientcolor[1] * distintensity;
+                                               color4f[2] = ambientcolor[2] * distintensity;
+                                       }
+                                       if (r_refdef.fogenabled)
+                                       {
+                                               float f = VERTEXFOGTABLE(VectorDistance(v, rsurface_modelorg));
+                                               VectorScale(color4f, f, color4f);
+                                       }
                                }
                                else
                                }
                                else
+                                       VectorClear(color4f);
+                               color4f[3] = 1;
+                       }
+               }
+               else
+               {
+                       for (;numverts > 0;numverts--, vertex3f += 3, color4f += 4)
+                       {
+                               Matrix4x4_Transform(&r_shadow_entitytolight, vertex3f, v);
+                               if ((dist = fabs(v[2])) < 1 && (distintensity = r_shadow_attentable[(int)(dist * ATTENTABLESIZE)]))
                                {
                                        color4f[0] = ambientcolor[0] * distintensity;
                                        color4f[1] = ambientcolor[1] * distintensity;
                                        color4f[2] = ambientcolor[2] * distintensity;
                                {
                                        color4f[0] = ambientcolor[0] * distintensity;
                                        color4f[1] = ambientcolor[1] * distintensity;
                                        color4f[2] = ambientcolor[2] * distintensity;
+                                       if (r_refdef.fogenabled)
+                                       {
+                                               float f = VERTEXFOGTABLE(VectorDistance(v, rsurface_modelorg));
+                                               VectorScale(color4f, f, color4f);
+                                       }
                                }
                                }
-                               if (r_refdef.fogenabled)
-                               {
-                                       float f = VERTEXFOGTABLE(VectorDistance(v, rsurface_modelorg));
-                                       VectorScale(color4f, f, color4f);
-                               }
+                               else
+                                       VectorClear(color4f);
+                               color4f[3] = 1;
                        }
                        }
-                       else
-                               VectorClear(color4f);
-                       color4f[3] = 1;
                }
        }
        else
        {
                }
        }
        else
        {
-               for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
+               if (VectorLength2(diffusecolor) > 0)
                {
                {
-                       Matrix4x4_Transform(&r_shadow_entitytolight, vertex3f, v);
-                       if ((dist = DotProduct(v, v)) < 1)
+                       for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
                        {
                        {
-                               dist = sqrt(dist);
-                               distintensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale;
-                               Matrix4x4_Transform3x3(&r_shadow_entitytolight, normal3f, n);
-                               if ((dot = DotProduct(n, v)) < 0)
+                               Matrix4x4_Transform(&r_shadow_entitytolight, vertex3f, v);
+                               if ((dist = VectorLength(v)) < 1 && (distintensity = r_shadow_attentable[(int)(dist * ATTENTABLESIZE)]))
                                {
                                {
-                                       shadeintensity = -dot / sqrt(VectorLength2(v) * VectorLength2(n));
-                                       color4f[0] = (ambientcolor[0] + shadeintensity * diffusecolor[0]) * distintensity;
-                                       color4f[1] = (ambientcolor[1] + shadeintensity * diffusecolor[1]) * distintensity;
-                                       color4f[2] = (ambientcolor[2] + shadeintensity * diffusecolor[2]) * distintensity;
+                                       distintensity = (1 - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist);
+                                       Matrix4x4_Transform3x3(&r_shadow_entitytolight, normal3f, n);
+                                       if ((dot = DotProduct(n, v)) < 0)
+                                       {
+                                               shadeintensity = -dot / sqrt(VectorLength2(v) * VectorLength2(n));
+                                               color4f[0] = (ambientcolor[0] + shadeintensity * diffusecolor[0]) * distintensity;
+                                               color4f[1] = (ambientcolor[1] + shadeintensity * diffusecolor[1]) * distintensity;
+                                               color4f[2] = (ambientcolor[2] + shadeintensity * diffusecolor[2]) * distintensity;
+                                       }
+                                       else
+                                       {
+                                               color4f[0] = ambientcolor[0] * distintensity;
+                                               color4f[1] = ambientcolor[1] * distintensity;
+                                               color4f[2] = ambientcolor[2] * distintensity;
+                                       }
+                                       if (r_refdef.fogenabled)
+                                       {
+                                               float f = VERTEXFOGTABLE(VectorDistance(v, rsurface_modelorg));
+                                               VectorScale(color4f, f, color4f);
+                                       }
                                }
                                else
                                }
                                else
+                                       VectorClear(color4f);
+                               color4f[3] = 1;
+                       }
+               }
+               else
+               {
+                       for (;numverts > 0;numverts--, vertex3f += 3, color4f += 4)
+                       {
+                               Matrix4x4_Transform(&r_shadow_entitytolight, vertex3f, v);
+                               if ((dist = VectorLength(v)) < 1 && (distintensity = r_shadow_attentable[(int)(dist * ATTENTABLESIZE)]))
                                {
                                {
+                                       distintensity = (1 - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist);
                                        color4f[0] = ambientcolor[0] * distintensity;
                                        color4f[1] = ambientcolor[1] * distintensity;
                                        color4f[2] = ambientcolor[2] * distintensity;
                                        color4f[0] = ambientcolor[0] * distintensity;
                                        color4f[1] = ambientcolor[1] * distintensity;
                                        color4f[2] = ambientcolor[2] * distintensity;
+                                       if (r_refdef.fogenabled)
+                                       {
+                                               float f = VERTEXFOGTABLE(VectorDistance(v, rsurface_modelorg));
+                                               VectorScale(color4f, f, color4f);
+                                       }
                                }
                                }
-                               if (r_refdef.fogenabled)
-                               {
-                                       float f = VERTEXFOGTABLE(VectorDistance(v, rsurface_modelorg));
-                                       VectorScale(color4f, f, color4f);
-                               }
+                               else
+                                       VectorClear(color4f);
+                               color4f[3] = 1;
                        }
                        }
-                       else
-                               VectorClear(color4f);
-                       color4f[3] = 1;
                }
        }
 }
                }
        }
 }
@@ -1393,7 +1464,7 @@ static void R_Shadow_GenTexCoords_Specular_NormalCubeMap(int firstvertex, int nu
        }
 }
 
        }
 }
 
-static void R_Shadow_RenderLighting_VisibleLighting(int firstvertex, int numvertices, int numtriangles, const int *element3i, const vec3_t lightcolorbase, const vec3_t lightcolorpants, const vec3_t lightcolorshirt, rtexture_t *basetexture, rtexture_t *pantstexture, rtexture_t *shirttexture, rtexture_t *normalmaptexture, rtexture_t *glosstexture, float specularscale, qboolean dopants, qboolean doshirt)
+static void R_Shadow_RenderLighting_VisibleLighting(int firstvertex, int numvertices, int numtriangles, const int *element3i, const vec3_t lightcolorbase, const vec3_t lightcolorpants, const vec3_t lightcolorshirt, rtexture_t *basetexture, rtexture_t *pantstexture, rtexture_t *shirttexture, rtexture_t *normalmaptexture, rtexture_t *glosstexture, float ambientscale, float diffusescale, float specularscale, qboolean dopants, qboolean doshirt)
 {
        // used to display how many times a surface is lit for level design purposes
        GL_Color(0.1 * r_view.colorscale, 0.025 * r_view.colorscale, 0, 1);
 {
        // used to display how many times a surface is lit for level design purposes
        GL_Color(0.1 * r_view.colorscale, 0.025 * r_view.colorscale, 0, 1);
@@ -1402,10 +1473,10 @@ static void R_Shadow_RenderLighting_VisibleLighting(int firstvertex, int numvert
        R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
 }
 
        R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
 }
 
-static void R_Shadow_RenderLighting_Light_GLSL(int firstvertex, int numvertices, int numtriangles, const int *element3i, const vec3_t lightcolorbase, const vec3_t lightcolorpants, const vec3_t lightcolorshirt, rtexture_t *basetexture, rtexture_t *pantstexture, rtexture_t *shirttexture, rtexture_t *normalmaptexture, rtexture_t *glosstexture, float specularscale, qboolean dopants, qboolean doshirt)
+static void R_Shadow_RenderLighting_Light_GLSL(int firstvertex, int numvertices, int numtriangles, const int *element3i, const vec3_t lightcolorbase, const vec3_t lightcolorpants, const vec3_t lightcolorshirt, rtexture_t *basetexture, rtexture_t *pantstexture, rtexture_t *shirttexture, rtexture_t *normalmaptexture, rtexture_t *glosstexture, float ambientscale, float diffusescale, float specularscale, qboolean dopants, qboolean doshirt)
 {
        // ARB2 GLSL shader path (GFFX5200, Radeon 9500)
 {
        // ARB2 GLSL shader path (GFFX5200, Radeon 9500)
-       R_SetupSurfaceShader(lightcolorbase, false);
+       R_SetupSurfaceShader(lightcolorbase, false, ambientscale, diffusescale, specularscale);
        R_Mesh_TexCoordPointer(0, 2, rsurface_model->surfmesh.data_texcoordtexture2f);
        R_Mesh_TexCoordPointer(1, 3, rsurface_svector3f);
        R_Mesh_TexCoordPointer(2, 3, rsurface_tvector3f);
        R_Mesh_TexCoordPointer(0, 2, rsurface_model->surfmesh.data_texcoordtexture2f);
        R_Mesh_TexCoordPointer(1, 3, rsurface_svector3f);
        R_Mesh_TexCoordPointer(2, 3, rsurface_tvector3f);
@@ -1871,32 +1942,32 @@ static void R_Shadow_RenderLighting_Light_Dot3_SpecularPass(int firstvertex, int
        R_Shadow_RenderLighting_Light_Dot3_Finalize(firstvertex, numvertices, numtriangles, element3i, lightcolorbase[0] * colorscale, lightcolorbase[1] * colorscale, lightcolorbase[2] * colorscale);
 }
 
        R_Shadow_RenderLighting_Light_Dot3_Finalize(firstvertex, numvertices, numtriangles, element3i, lightcolorbase[0] * colorscale, lightcolorbase[1] * colorscale, lightcolorbase[2] * colorscale);
 }
 
-static void R_Shadow_RenderLighting_Light_Dot3(int firstvertex, int numvertices, int numtriangles, const int *element3i, const vec3_t lightcolorbase, const vec3_t lightcolorpants, const vec3_t lightcolorshirt, rtexture_t *basetexture, rtexture_t *pantstexture, rtexture_t *shirttexture, rtexture_t *normalmaptexture, rtexture_t *glosstexture, float specularscale, qboolean dopants, qboolean doshirt)
+static void R_Shadow_RenderLighting_Light_Dot3(int firstvertex, int numvertices, int numtriangles, const int *element3i, const vec3_t lightcolorbase, const vec3_t lightcolorpants, const vec3_t lightcolorshirt, rtexture_t *basetexture, rtexture_t *pantstexture, rtexture_t *shirttexture, rtexture_t *normalmaptexture, rtexture_t *glosstexture, float ambientscale, float diffusescale, float specularscale, qboolean dopants, qboolean doshirt)
 {
        // ARB path (any Geforce, any Radeon)
 {
        // ARB path (any Geforce, any Radeon)
-       qboolean doambient = r_shadow_rtlight->ambientscale > 0;
-       qboolean dodiffuse = r_shadow_rtlight->diffusescale > 0;
+       qboolean doambient = ambientscale > 0;
+       qboolean dodiffuse = diffusescale > 0;
        qboolean dospecular = specularscale > 0;
        if (!doambient && !dodiffuse && !dospecular)
                return;
        R_Mesh_ColorPointer(NULL);
        if (doambient)
        qboolean dospecular = specularscale > 0;
        if (!doambient && !dodiffuse && !dospecular)
                return;
        R_Mesh_ColorPointer(NULL);
        if (doambient)
-               R_Shadow_RenderLighting_Light_Dot3_AmbientPass(firstvertex, numvertices, numtriangles, element3i, lightcolorbase, basetexture, r_shadow_rtlight->ambientscale * r_view.colorscale);
+               R_Shadow_RenderLighting_Light_Dot3_AmbientPass(firstvertex, numvertices, numtriangles, element3i, lightcolorbase, basetexture, ambientscale * r_view.colorscale);
        if (dodiffuse)
        if (dodiffuse)
-               R_Shadow_RenderLighting_Light_Dot3_DiffusePass(firstvertex, numvertices, numtriangles, element3i, lightcolorbase, basetexture, normalmaptexture, r_shadow_rtlight->diffusescale * r_view.colorscale);
+               R_Shadow_RenderLighting_Light_Dot3_DiffusePass(firstvertex, numvertices, numtriangles, element3i, lightcolorbase, basetexture, normalmaptexture, diffusescale * r_view.colorscale);
        if (dopants)
        {
                if (doambient)
        if (dopants)
        {
                if (doambient)
-                       R_Shadow_RenderLighting_Light_Dot3_AmbientPass(firstvertex, numvertices, numtriangles, element3i, lightcolorpants, pantstexture, r_shadow_rtlight->ambientscale * r_view.colorscale);
+                       R_Shadow_RenderLighting_Light_Dot3_AmbientPass(firstvertex, numvertices, numtriangles, element3i, lightcolorpants, pantstexture, ambientscale * r_view.colorscale);
                if (dodiffuse)
                if (dodiffuse)
-                       R_Shadow_RenderLighting_Light_Dot3_DiffusePass(firstvertex, numvertices, numtriangles, element3i, lightcolorpants, pantstexture, normalmaptexture, r_shadow_rtlight->diffusescale * r_view.colorscale);
+                       R_Shadow_RenderLighting_Light_Dot3_DiffusePass(firstvertex, numvertices, numtriangles, element3i, lightcolorpants, pantstexture, normalmaptexture, diffusescale * r_view.colorscale);
        }
        if (doshirt)
        {
                if (doambient)
        }
        if (doshirt)
        {
                if (doambient)
-                       R_Shadow_RenderLighting_Light_Dot3_AmbientPass(firstvertex, numvertices, numtriangles, element3i, lightcolorshirt, shirttexture, r_shadow_rtlight->ambientscale * r_view.colorscale);
+                       R_Shadow_RenderLighting_Light_Dot3_AmbientPass(firstvertex, numvertices, numtriangles, element3i, lightcolorshirt, shirttexture, ambientscale * r_view.colorscale);
                if (dodiffuse)
                if (dodiffuse)
-                       R_Shadow_RenderLighting_Light_Dot3_DiffusePass(firstvertex, numvertices, numtriangles, element3i, lightcolorshirt, shirttexture, normalmaptexture, r_shadow_rtlight->diffusescale * r_view.colorscale);
+                       R_Shadow_RenderLighting_Light_Dot3_DiffusePass(firstvertex, numvertices, numtriangles, element3i, lightcolorshirt, shirttexture, normalmaptexture, diffusescale * r_view.colorscale);
        }
        if (dospecular)
                R_Shadow_RenderLighting_Light_Dot3_SpecularPass(firstvertex, numvertices, numtriangles, element3i, lightcolorbase, glosstexture, normalmaptexture, specularscale * r_view.colorscale);
        }
        if (dospecular)
                R_Shadow_RenderLighting_Light_Dot3_SpecularPass(firstvertex, numvertices, numtriangles, element3i, lightcolorbase, glosstexture, normalmaptexture, specularscale * r_view.colorscale);
@@ -1990,7 +2061,7 @@ void R_Shadow_RenderLighting_Light_Vertex_Pass(const model_t *model, int firstve
        }
 }
 
        }
 }
 
-static void R_Shadow_RenderLighting_Light_Vertex(int firstvertex, int numvertices, int numtriangles, const int *element3i, const vec3_t lightcolorbase, const vec3_t lightcolorpants, const vec3_t lightcolorshirt, rtexture_t *basetexture, rtexture_t *pantstexture, rtexture_t *shirttexture, rtexture_t *normalmaptexture, rtexture_t *glosstexture, float specularscale, qboolean dopants, qboolean doshirt)
+static void R_Shadow_RenderLighting_Light_Vertex(int firstvertex, int numvertices, int numtriangles, const int *element3i, const vec3_t lightcolorbase, const vec3_t lightcolorpants, const vec3_t lightcolorshirt, rtexture_t *basetexture, rtexture_t *pantstexture, rtexture_t *shirttexture, rtexture_t *normalmaptexture, rtexture_t *glosstexture, float ambientscale, float diffusescale, float specularscale, qboolean dopants, qboolean doshirt)
 {
        // OpenGL 1.1 path (anything)
        model_t *model = rsurface_entity->model;
 {
        // OpenGL 1.1 path (anything)
        model_t *model = rsurface_entity->model;
@@ -1998,12 +2069,12 @@ static void R_Shadow_RenderLighting_Light_Vertex(int firstvertex, int numvertice
        float ambientcolorpants[3], diffusecolorpants[3];
        float ambientcolorshirt[3], diffusecolorshirt[3];
        rmeshstate_t m;
        float ambientcolorpants[3], diffusecolorpants[3];
        float ambientcolorshirt[3], diffusecolorshirt[3];
        rmeshstate_t m;
-       VectorScale(lightcolorbase, r_shadow_rtlight->ambientscale * 2 * r_view.colorscale, ambientcolorbase);
-       VectorScale(lightcolorbase, r_shadow_rtlight->diffusescale * 2 * r_view.colorscale, diffusecolorbase);
-       VectorScale(lightcolorpants, r_shadow_rtlight->ambientscale * 2 * r_view.colorscale, ambientcolorpants);
-       VectorScale(lightcolorpants, r_shadow_rtlight->diffusescale * 2 * r_view.colorscale, diffusecolorpants);
-       VectorScale(lightcolorshirt, r_shadow_rtlight->ambientscale * 2 * r_view.colorscale, ambientcolorshirt);
-       VectorScale(lightcolorshirt, r_shadow_rtlight->diffusescale * 2 * r_view.colorscale, diffusecolorshirt);
+       VectorScale(lightcolorbase, ambientscale * 2 * r_view.colorscale, ambientcolorbase);
+       VectorScale(lightcolorbase, diffusescale * 2 * r_view.colorscale, diffusecolorbase);
+       VectorScale(lightcolorpants, ambientscale * 2 * r_view.colorscale, ambientcolorpants);
+       VectorScale(lightcolorpants, diffusescale * 2 * r_view.colorscale, diffusecolorpants);
+       VectorScale(lightcolorshirt, ambientscale * 2 * r_view.colorscale, ambientcolorshirt);
+       VectorScale(lightcolorshirt, diffusescale * 2 * r_view.colorscale, diffusecolorshirt);
        GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
        R_Mesh_ColorPointer(rsurface_array_color4f);
        memset(&m, 0, sizeof(m));
        GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
        R_Mesh_ColorPointer(rsurface_array_color4f);
        memset(&m, 0, sizeof(m));
@@ -2025,7 +2096,7 @@ static void R_Shadow_RenderLighting_Light_Vertex(int firstvertex, int numvertice
                }
        }
        R_Mesh_TextureState(&m);
                }
        }
        R_Mesh_TextureState(&m);
-       R_Mesh_TexBind(0, R_GetTexture(basetexture));
+       //R_Mesh_TexBind(0, R_GetTexture(basetexture));
        R_Shadow_RenderLighting_Light_Vertex_Pass(model, firstvertex, numvertices, numtriangles, element3i, diffusecolorbase, ambientcolorbase);
        if (dopants)
        {
        R_Shadow_RenderLighting_Light_Vertex_Pass(model, firstvertex, numvertices, numtriangles, element3i, diffusecolorbase, ambientcolorbase);
        if (dopants)
        {
@@ -2041,13 +2112,23 @@ static void R_Shadow_RenderLighting_Light_Vertex(int firstvertex, int numvertice
 
 void R_Shadow_RenderLighting(int firstvertex, int numvertices, int numtriangles, const int *element3i)
 {
 
 void R_Shadow_RenderLighting(int firstvertex, int numvertices, int numtriangles, const int *element3i)
 {
+       float ambientscale, diffusescale, specularscale;
        // FIXME: support MATERIALFLAG_NODEPTHTEST
        vec3_t lightcolorbase, lightcolorpants, lightcolorshirt;
        // calculate colors to render this texture with
        lightcolorbase[0] = r_shadow_rtlight->currentcolor[0] * rsurface_entity->colormod[0] * rsurface_texture->currentalpha;
        lightcolorbase[1] = r_shadow_rtlight->currentcolor[1] * rsurface_entity->colormod[1] * rsurface_texture->currentalpha;
        lightcolorbase[2] = r_shadow_rtlight->currentcolor[2] * rsurface_entity->colormod[2] * rsurface_texture->currentalpha;
        // FIXME: support MATERIALFLAG_NODEPTHTEST
        vec3_t lightcolorbase, lightcolorpants, lightcolorshirt;
        // calculate colors to render this texture with
        lightcolorbase[0] = r_shadow_rtlight->currentcolor[0] * rsurface_entity->colormod[0] * rsurface_texture->currentalpha;
        lightcolorbase[1] = r_shadow_rtlight->currentcolor[1] * rsurface_entity->colormod[1] * rsurface_texture->currentalpha;
        lightcolorbase[2] = r_shadow_rtlight->currentcolor[2] * rsurface_entity->colormod[2] * rsurface_texture->currentalpha;
-       if ((r_shadow_rtlight->ambientscale + r_shadow_rtlight->diffusescale) * VectorLength2(lightcolorbase) + (r_shadow_rtlight->specularscale * rsurface_texture->specularscale) * VectorLength2(lightcolorbase) < (1.0f / 1048576.0f))
+       ambientscale = r_shadow_rtlight->ambientscale;
+       diffusescale = r_shadow_rtlight->diffusescale;
+       specularscale = ambientscale, diffusescale, specularscale;
+       if (!r_shadow_usenormalmap.integer)
+       {
+               ambientscale += 1.0f * diffusescale;
+               diffusescale = 0;
+               specularscale = 0;
+       }
+       if ((ambientscale + diffusescale) * VectorLength2(lightcolorbase) + specularscale * VectorLength2(lightcolorbase) < (1.0f / 1048576.0f))
                return;
        GL_DepthTest(!(rsurface_texture->currentmaterialflags & MATERIALFLAG_NODEPTHTEST));
        GL_CullFace((rsurface_texture->currentmaterialflags & MATERIALFLAG_NOCULLFACE) ? GL_NONE : GL_FRONT); // quake is backwards, this culls back faces
                return;
        GL_DepthTest(!(rsurface_texture->currentmaterialflags & MATERIALFLAG_NODEPTHTEST));
        GL_CullFace((rsurface_texture->currentmaterialflags & MATERIALFLAG_NOCULLFACE) ? GL_NONE : GL_FRONT); // quake is backwards, this culls back faces
@@ -2075,16 +2156,16 @@ void R_Shadow_RenderLighting(int firstvertex, int numvertices, int numtriangles,
                {
                case R_SHADOW_RENDERMODE_VISIBLELIGHTING:
                        GL_DepthTest(!(rsurface_texture->currentmaterialflags & MATERIALFLAG_NODEPTHTEST) && !r_showdisabledepthtest.integer);
                {
                case R_SHADOW_RENDERMODE_VISIBLELIGHTING:
                        GL_DepthTest(!(rsurface_texture->currentmaterialflags & MATERIALFLAG_NODEPTHTEST) && !r_showdisabledepthtest.integer);
-                       R_Shadow_RenderLighting_VisibleLighting(firstvertex, numvertices, numtriangles, element3i, lightcolorbase, lightcolorpants, lightcolorshirt, rsurface_texture->basetexture, rsurface_texture->currentskinframe->pants, rsurface_texture->currentskinframe->shirt, rsurface_texture->currentskinframe->nmap, rsurface_texture->glosstexture, r_shadow_rtlight->specularscale * rsurface_texture->specularscale, dopants, doshirt);
+                       R_Shadow_RenderLighting_VisibleLighting(firstvertex, numvertices, numtriangles, element3i, lightcolorbase, lightcolorpants, lightcolorshirt, rsurface_texture->basetexture, rsurface_texture->currentskinframe->pants, rsurface_texture->currentskinframe->shirt, rsurface_texture->currentskinframe->nmap, rsurface_texture->glosstexture, ambientscale, diffusescale, specularscale, dopants, doshirt);
                        break;
                case R_SHADOW_RENDERMODE_LIGHT_GLSL:
                        break;
                case R_SHADOW_RENDERMODE_LIGHT_GLSL:
-                       R_Shadow_RenderLighting_Light_GLSL(firstvertex, numvertices, numtriangles, element3i, lightcolorbase, lightcolorpants, lightcolorshirt, rsurface_texture->basetexture, rsurface_texture->currentskinframe->pants, rsurface_texture->currentskinframe->shirt, rsurface_texture->currentskinframe->nmap, rsurface_texture->glosstexture, r_shadow_rtlight->specularscale * rsurface_texture->specularscale, dopants, doshirt);
+                       R_Shadow_RenderLighting_Light_GLSL(firstvertex, numvertices, numtriangles, element3i, lightcolorbase, lightcolorpants, lightcolorshirt, rsurface_texture->basetexture, rsurface_texture->currentskinframe->pants, rsurface_texture->currentskinframe->shirt, rsurface_texture->currentskinframe->nmap, rsurface_texture->glosstexture, ambientscale, diffusescale, specularscale, dopants, doshirt);
                        break;
                case R_SHADOW_RENDERMODE_LIGHT_DOT3:
                        break;
                case R_SHADOW_RENDERMODE_LIGHT_DOT3:
-                       R_Shadow_RenderLighting_Light_Dot3(firstvertex, numvertices, numtriangles, element3i, lightcolorbase, lightcolorpants, lightcolorshirt, rsurface_texture->basetexture, rsurface_texture->currentskinframe->pants, rsurface_texture->currentskinframe->shirt, rsurface_texture->currentskinframe->nmap, rsurface_texture->glosstexture, r_shadow_rtlight->specularscale * rsurface_texture->specularscale, dopants, doshirt);
+                       R_Shadow_RenderLighting_Light_Dot3(firstvertex, numvertices, numtriangles, element3i, lightcolorbase, lightcolorpants, lightcolorshirt, rsurface_texture->basetexture, rsurface_texture->currentskinframe->pants, rsurface_texture->currentskinframe->shirt, rsurface_texture->currentskinframe->nmap, rsurface_texture->glosstexture, ambientscale, diffusescale, specularscale, dopants, doshirt);
                        break;
                case R_SHADOW_RENDERMODE_LIGHT_VERTEX:
                        break;
                case R_SHADOW_RENDERMODE_LIGHT_VERTEX:
-                       R_Shadow_RenderLighting_Light_Vertex(firstvertex, numvertices, numtriangles, element3i, lightcolorbase, lightcolorpants, lightcolorshirt, rsurface_texture->basetexture, rsurface_texture->currentskinframe->pants, rsurface_texture->currentskinframe->shirt, rsurface_texture->currentskinframe->nmap, rsurface_texture->glosstexture, r_shadow_rtlight->specularscale * rsurface_texture->specularscale, dopants, doshirt);
+                       R_Shadow_RenderLighting_Light_Vertex(firstvertex, numvertices, numtriangles, element3i, lightcolorbase, lightcolorpants, lightcolorshirt, rsurface_texture->basetexture, rsurface_texture->currentskinframe->pants, rsurface_texture->currentskinframe->shirt, rsurface_texture->currentskinframe->nmap, rsurface_texture->glosstexture, ambientscale, diffusescale, specularscale, dopants, doshirt);
                        break;
                default:
                        Con_Printf("R_Shadow_RenderLighting: unknown r_shadow_rendermode %i\n", r_shadow_rendermode);
                        break;
                default:
                        Con_Printf("R_Shadow_RenderLighting: unknown r_shadow_rendermode %i\n", r_shadow_rendermode);
@@ -2097,16 +2178,16 @@ void R_Shadow_RenderLighting(int firstvertex, int numvertices, int numtriangles,
                {
                case R_SHADOW_RENDERMODE_VISIBLELIGHTING:
                        GL_DepthTest(!(rsurface_texture->currentmaterialflags & MATERIALFLAG_NODEPTHTEST) && !r_showdisabledepthtest.integer);
                {
                case R_SHADOW_RENDERMODE_VISIBLELIGHTING:
                        GL_DepthTest(!(rsurface_texture->currentmaterialflags & MATERIALFLAG_NODEPTHTEST) && !r_showdisabledepthtest.integer);
-                       R_Shadow_RenderLighting_VisibleLighting(firstvertex, numvertices, numtriangles, element3i, lightcolorbase, vec3_origin, vec3_origin, rsurface_texture->basetexture, r_texture_black, r_texture_black, rsurface_texture->currentskinframe->nmap, rsurface_texture->glosstexture, r_shadow_rtlight->specularscale * rsurface_texture->specularscale, false, false);
+                       R_Shadow_RenderLighting_VisibleLighting(firstvertex, numvertices, numtriangles, element3i, lightcolorbase, vec3_origin, vec3_origin, rsurface_texture->basetexture, r_texture_black, r_texture_black, rsurface_texture->currentskinframe->nmap, rsurface_texture->glosstexture, ambientscale, diffusescale, specularscale, false, false);
                        break;
                case R_SHADOW_RENDERMODE_LIGHT_GLSL:
                        break;
                case R_SHADOW_RENDERMODE_LIGHT_GLSL:
-                       R_Shadow_RenderLighting_Light_GLSL(firstvertex, numvertices, numtriangles, element3i, lightcolorbase, vec3_origin, vec3_origin, rsurface_texture->basetexture, r_texture_black, r_texture_black, rsurface_texture->currentskinframe->nmap, rsurface_texture->glosstexture, r_shadow_rtlight->specularscale * rsurface_texture->specularscale, false, false);
+                       R_Shadow_RenderLighting_Light_GLSL(firstvertex, numvertices, numtriangles, element3i, lightcolorbase, vec3_origin, vec3_origin, rsurface_texture->basetexture, r_texture_black, r_texture_black, rsurface_texture->currentskinframe->nmap, rsurface_texture->glosstexture, ambientscale, diffusescale, specularscale, false, false);
                        break;
                case R_SHADOW_RENDERMODE_LIGHT_DOT3:
                        break;
                case R_SHADOW_RENDERMODE_LIGHT_DOT3:
-                       R_Shadow_RenderLighting_Light_Dot3(firstvertex, numvertices, numtriangles, element3i, lightcolorbase, vec3_origin, vec3_origin, rsurface_texture->basetexture, r_texture_black, r_texture_black, rsurface_texture->currentskinframe->nmap, rsurface_texture->glosstexture, r_shadow_rtlight->specularscale * rsurface_texture->specularscale, false, false);
+                       R_Shadow_RenderLighting_Light_Dot3(firstvertex, numvertices, numtriangles, element3i, lightcolorbase, vec3_origin, vec3_origin, rsurface_texture->basetexture, r_texture_black, r_texture_black, rsurface_texture->currentskinframe->nmap, rsurface_texture->glosstexture, ambientscale, diffusescale, specularscale, false, false);
                        break;
                case R_SHADOW_RENDERMODE_LIGHT_VERTEX:
                        break;
                case R_SHADOW_RENDERMODE_LIGHT_VERTEX:
-                       R_Shadow_RenderLighting_Light_Vertex(firstvertex, numvertices, numtriangles, element3i, lightcolorbase, vec3_origin, vec3_origin, rsurface_texture->basetexture, r_texture_black, r_texture_black, rsurface_texture->currentskinframe->nmap, rsurface_texture->glosstexture, r_shadow_rtlight->specularscale * rsurface_texture->specularscale, false, false);
+                       R_Shadow_RenderLighting_Light_Vertex(firstvertex, numvertices, numtriangles, element3i, lightcolorbase, vec3_origin, vec3_origin, rsurface_texture->basetexture, r_texture_black, r_texture_black, rsurface_texture->currentskinframe->nmap, rsurface_texture->glosstexture, ambientscale, diffusescale, specularscale, false, false);
                        break;
                default:
                        Con_Printf("R_Shadow_RenderLighting: unknown r_shadow_rendermode %i\n", r_shadow_rendermode);
                        break;
                default:
                        Con_Printf("R_Shadow_RenderLighting: unknown r_shadow_rendermode %i\n", r_shadow_rendermode);
index 852a25b..e84837f 100644 (file)
--- a/render.h
+++ b/render.h
@@ -244,13 +244,14 @@ void RSurf_DrawBatch_Simple(int texturenumsurfaces, msurface_t **texturesurfacel
 #define SHADERPERMUTATION_GLOW (1<<4) // (lightmap) blend in an additive glow texture
 #define SHADERPERMUTATION_FOG (1<<5) // tint the color by fog color or black if using additive blend mode
 #define SHADERPERMUTATION_COLORMAPPING (1<<6) // indicates this is a colormapped skin
 #define SHADERPERMUTATION_GLOW (1<<4) // (lightmap) blend in an additive glow texture
 #define SHADERPERMUTATION_FOG (1<<5) // tint the color by fog color or black if using additive blend mode
 #define SHADERPERMUTATION_COLORMAPPING (1<<6) // indicates this is a colormapped skin
-#define SHADERPERMUTATION_SPECULAR (1<<7) // (lightsource or deluxemapping) render specular effects
-#define SHADERPERMUTATION_CUBEFILTER (1<<8) // (lightsource) use cubemap light filter
-#define SHADERPERMUTATION_OFFSETMAPPING (1<<9) // adjust texcoords to roughly simulate a displacement mapped surface
-#define SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING (1<<10) // adjust texcoords to accurately simulate a displacement mapped surface (requires OFFSETMAPPING to also be set!)
+#define SHADERPERMUTATION_DIFFUSE (1<<7) // (lightsource) whether to use directional shading
+#define SHADERPERMUTATION_SPECULAR (1<<8) // (lightsource or deluxemapping) render specular effects
+#define SHADERPERMUTATION_CUBEFILTER (1<<9) // (lightsource) use cubemap light filter
+#define SHADERPERMUTATION_OFFSETMAPPING (1<<10) // adjust texcoords to roughly simulate a displacement mapped surface
+#define SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING (1<<11) // adjust texcoords to accurately simulate a displacement mapped surface (requires OFFSETMAPPING to also be set!)
 
 
-#define SHADERPERMUTATION_COUNT (1<<11) // how many permutations are possible
-#define SHADERPERMUTATION_COUNTMASK (SHADERPERMUTATION_COUNT - 1) // mask of valid indexing bits for r_glsl_permutations[] array
+#define SHADERPERMUTATION_MAX (1<<12) // how many permutations are possible
+#define SHADERPERMUTATION_MASK (SHADERPERMUTATION_MAX - 1) // mask of valid indexing bits for r_glsl_permutations[] array
 
 // these are additional flags used only by R_GLSL_CompilePermutation
 #define SHADERPERMUTATION_USES_VERTEXSHADER (1<<29)
 
 // these are additional flags used only by R_GLSL_CompilePermutation
 #define SHADERPERMUTATION_USES_VERTEXSHADER (1<<29)
@@ -267,6 +268,7 @@ typedef struct r_glsl_permutation_s
        int loc_Texture_Color;
        int loc_Texture_Gloss;
        int loc_Texture_Cube;
        int loc_Texture_Color;
        int loc_Texture_Gloss;
        int loc_Texture_Cube;
+       int loc_Texture_Attenuation;
        int loc_Texture_FogMask;
        int loc_Texture_Pants;
        int loc_Texture_Shirt;
        int loc_Texture_FogMask;
        int loc_Texture_Pants;
        int loc_Texture_Shirt;
@@ -295,12 +297,12 @@ typedef struct r_glsl_permutation_s
 r_glsl_permutation_t;
 
 // information about each possible shader permutation
 r_glsl_permutation_t;
 
 // information about each possible shader permutation
-extern r_glsl_permutation_t r_glsl_permutations[SHADERPERMUTATION_COUNT];
+extern r_glsl_permutation_t r_glsl_permutations[SHADERPERMUTATION_MAX];
 // currently selected permutation
 extern r_glsl_permutation_t *r_glsl_permutation;
 
 void R_GLSL_CompilePermutation(const char *shaderfilename, int permutation);
 // currently selected permutation
 extern r_glsl_permutation_t *r_glsl_permutation;
 
 void R_GLSL_CompilePermutation(const char *shaderfilename, int permutation);
-int R_SetupSurfaceShader(const vec3_t lightcolorbase, qboolean modellighting);
+int R_SetupSurfaceShader(const vec3_t lightcolorbase, qboolean modellighting, float ambientscale, float diffusescale, float specularscale);
 void R_SwitchSurfaceShader(int permutation);
 
 #endif
 void R_SwitchSurfaceShader(int permutation);
 
 #endif