cleaning up GLSL code
authorhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Wed, 15 Mar 2006 13:19:36 +0000 (13:19 +0000)
committerhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Wed, 15 Mar 2006 13:19:36 +0000 (13:19 +0000)
moved GLSL shader setup to gl_rmain.c and made it more general, not specifically lighting shaders
cache locations of GLSL shader variables for faster access (instead of looking them up every time it renders something)
GLSL shader reworked to support deluxemap rendering and lightmap rendering, to be used in the future
merged glsl/light.frag and glsl/light.vert into glsl/default.glsl using VERTEX_SHADER and FRAGMENT_SHADER defines to differentiate parts of it

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

gl_rmain.c
r_shadow.c
render.h

index c756c53..505bcaf 100644 (file)
@@ -23,6 +23,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #include "r_shadow.h"
 #include "polygon.h"
 
+mempool_t *r_main_mempool;
+rtexturepool_t *r_main_texturepool;
+
 // used for dlight push checking and other things
 int r_framecount;
 
@@ -95,6 +98,13 @@ cvar_t gl_fogend = {0, "gl_fogend","0", "nehahra fog end distance (for Nehahra c
 
 cvar_t r_textureunits = {0, "r_textureunits", "32", "number of hardware texture units reported by driver (note: setting this to 1 turns off gl_combine)"};
 
+cvar_t r_glsl = {0, "r_glsl", "1", "enables use of OpenGL 2.0 pixel shaders for lighting"};
+cvar_t r_glsl_offsetmapping = {0, "r_glsl_offsetmapping", "0", "enables offset mapping effect (also known as parallax mapping or sometimes as virtual displacement mapping, not as good as relief mapping or silohuette mapping but much faster), can cause strange artifacts on many textures, requires bumpmaps for depth information (normalmaps can have depth information as alpha channel, but most do not)"};
+cvar_t r_glsl_offsetmapping_scale = {0, "r_glsl_offsetmapping_scale", "-0.04", "how deep the offset mapping effect is, and whether it is inward or outward"};
+cvar_t r_glsl_offsetmapping_bias = {0, "r_glsl_offsetmapping_bias", "0.04", "pushes the effect closer/further"};
+cvar_t r_glsl_usehalffloat = {0, "r_glsl_usehalffloat", "0", "use half and hvec variables in GLSL shader for a speed gain (NVIDIA only)"};
+cvar_t r_glsl_surfacenormalize = {0, "r_glsl_surfacenormalize", "1", "normalize bumpmap texels in GLSL shader, produces a more rounded look on small bumps and dents"};
+
 cvar_t r_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_waterscroll = {CVAR_SAVE, "r_waterscroll", "1", "makes water scroll around, value controls how much"};
@@ -113,7 +123,6 @@ cvar_t gl_lightmaps = {0, "gl_lightmaps", "0", "draws only lightmaps, no texture
 
 cvar_t r_test = {0, "r_test", "0", "internal development use only, leave it alone (usually does nothing anyway)"}; // used for testing renderer code changes, otherwise does nothing
 
-rtexturepool_t *r_main_texturepool;
 rtexture_t *r_bloom_texture_screen;
 rtexture_t *r_bloom_texture_bloom;
 rtexture_t *r_texture_blanknormalmap;
@@ -125,6 +134,11 @@ rtexture_t *r_texture_normalizationcube;
 rtexture_t *r_texture_fogattenuation;
 rtexture_t *r_texture_fogintensity;
 
+// information about each possible shader permutation
+r_glsl_permutation_t r_glsl_permutations[SHADERPERMUTATION_COUNT];
+// currently selected permutation
+r_glsl_permutation_t *r_glsl_permutation;
+
 void R_ModulateColors(float *in, float *out, int verts, float r, float g, float b)
 {
        int i;
@@ -394,8 +408,447 @@ static void R_BuildFogTexture(void)
        r_texture_fogintensity = R_LoadTexture2D(r_main_texturepool, "fogintensity", FOGWIDTH, 1, &data2[0][0], TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_FORCELINEAR | TEXF_CLAMP, NULL);
 }
 
+static const char *builtinshaderstring =
+"// ambient+diffuse+specular+normalmap+attenuation+cubemap+fog shader\n"
+"// written by Forest 'LordHavoc' Hale\n"
+"\n"
+"// common definitions between vertex shader and fragment shader:\n"
+"\n"
+"// use half floats if available for math performance\n"
+"#ifdef GEFORCEFX\n"
+"#define myhalf half\n"
+"#define myhvec2 hvec2\n"
+"#define myhvec3 hvec3\n"
+"#define myhvec4 hvec4\n"
+"#else\n"
+"#define myhalf float\n"
+"#define myhvec2 vec2\n"
+"#define myhvec3 vec3\n"
+"#define myhvec4 vec4\n"
+"#endif\n"
+"\n"
+"varying vec2 TexCoord;\n"
+"\n"
+"#ifdef USELIGHTSOURCE\n"
+"varying myhvec3 CubeVector;\n"
+"varying vec3 LightVector;\n"
+"#endif\n"
+"\n"
+"#if defined(USESPECULAR) || defined(USEFOG) || defined(USEOFFSETMAPPING)\n"
+"varying vec3 EyeVector;\n"
+"#endif\n"
+"\n"
+"\n"
+"\n"
+"\n"
+"// vertex shader specific:\n"
+"#ifdef VERTEX_SHADER\n"
+"\n"
+"#ifdef USELIGHTSOURCE\n"
+"uniform vec3 LightPosition;\n"
+"#endif\n"
+"\n"
+"#if defined(USESPECULAR) || defined(USEFOG) || defined(USEOFFSETMAPPING)\n"
+"uniform vec3 EyePosition;\n"
+"#endif\n"
+"\n"
+"// TODO: get rid of tangentt (texcoord2) and use a crossproduct to regenerate it from tangents (texcoord1) and normal (texcoord3)\n"
+"\n"
+"void main(void)\n"
+"{\n"
+"      // copy the surface texcoord\n"
+"      TexCoord = vec2(gl_TextureMatrix[0] * gl_MultiTexCoord0);\n"
+"\n"
+"#ifdef USELIGHTSOURCE\n"
+"      // transform vertex position into light attenuation/cubemap space\n"
+"      // (-1 to +1 across the light box)\n"
+"      CubeVector = vec3(gl_TextureMatrix[3] * gl_Vertex);\n"
+"\n"
+"      // transform unnormalized light direction into tangent space\n"
+"      // (we use unnormalized to ensure that it interpolates correctly and then\n"
+"      //  normalize it per pixel)\n"
+"      vec3 lightminusvertex = LightPosition - gl_Vertex.xyz;\n"
+"      LightVector.x = dot(lightminusvertex, gl_MultiTexCoord1.xyz);\n"
+"      LightVector.y = dot(lightminusvertex, gl_MultiTexCoord2.xyz);\n"
+"      LightVector.z = dot(lightminusvertex, gl_MultiTexCoord3.xyz);\n"
+"#endif\n"
+"\n"
+"#if defined(USESPECULAR) || defined(USEFOG) || defined(USEOFFSETMAPPING)\n"
+"      // transform unnormalized eye direction into tangent space\n"
+"      vec3 eyeminusvertex = EyePosition - gl_Vertex.xyz;\n"
+"      EyeVector.x = dot(eyeminusvertex, gl_MultiTexCoord1.xyz);\n"
+"      EyeVector.y = dot(eyeminusvertex, gl_MultiTexCoord2.xyz);\n"
+"      EyeVector.z = dot(eyeminusvertex, gl_MultiTexCoord3.xyz);\n"
+"#endif\n"
+"\n"
+"      // transform vertex to camera space, using ftransform to match non-VS\n"
+"      // rendering\n"
+"      gl_Position = ftransform();\n"
+"}\n"
+"\n"
+"#endif\n"
+"\n"
+"\n"
+"\n"
+"\n"
+"// fragment shader specific:\n"
+"#ifdef FRAGMENT_SHADER\n"
+"\n"
+"uniform myhvec3 LightColor;\n"
+"#ifdef USEOFFSETMAPPING\n"
+"uniform myhalf OffsetMapping_Scale;\n"
+"uniform myhalf OffsetMapping_Bias;\n"
+"#endif\n"
+"\n"
+"#if defined(USELIGHTSOURCE) || defined(USEDELUXEMAPPING)\n"
+"uniform sampler2D Texture_Normal;\n"
+"#endif\n"
+"\n"
+"uniform sampler2D Texture_Color;\n"
+"\n"
+"#ifdef USECOLORMAPPING\n"
+"uniform sampler2D Texture_Pants;\n"
+"uniform sampler2D Texture_Shirt;\n"
+"uniform myhvec3 Color_Pants;\n"
+"uniform myhvec3 Color_Shirt;\n"
+"#endif\n"
+"\n"
+"uniform myhalf AmbientScale;\n"
+"uniform myhalf DiffuseScale;\n"
+"#ifdef USESPECULAR\n"
+"uniform myhalf SpecularScale;\n"
+"uniform myhalf SpecularPower;\n"
+"uniform sampler2D Texture_Gloss;\n"
+"#endif\n"
+"\n"
+"#ifdef USECUBEFILTER\n"
+"uniform samplerCube Texture_Cube;\n"
+"#endif\n"
+"\n"
+"#ifdef USEFOG\n"
+"uniform myhalf FogRangeRecip;\n"
+"uniform sampler2D Texture_FogMask;\n"
+"#endif\n"
+"\n"
+"void main(void)\n"
+"{\n"
+"      // apply offsetmapping\n"
+"#ifdef USEOFFSETMAPPING\n"
+"      // this is 3 sample because of ATI Radeon 9500-9800/X300 limits\n"
+"      myhvec2 OffsetVector = normalize(EyeVector).xy * vec2(-0.333, 0.333);\n"
+"      myhvec2 TexCoordOffset = TexCoord + OffsetVector * (OffsetMapping_Bias + OffsetMapping_Scale * texture2D(Texture_Normal, TexCoord).w);\n"
+"      TexCoordOffset += OffsetVector * (OffsetMapping_Bias + OffsetMapping_Scale * texture2D(Texture_Normal, TexCoordOffset).w);\n"
+"      TexCoordOffset += OffsetVector * (OffsetMapping_Bias + OffsetMapping_Scale * texture2D(Texture_Normal, TexCoordOffset).w);\n"
+"#define TexCoord TexCoordOffset\n"
+"#endif\n"
+"\n"
+"      // combine the diffuse textures (base, pants, shirt)\n"
+"      myhvec4 color = myhvec4(texture2D(Texture_Color, TexCoord));\n"
+"#ifdef USECOLORMAPPING\n"
+"      color.rgb += myhvec3(texture2D(Texture_Pants, TexCoord)) * Color_Pants + myhvec3(texture2D(Texture_Shirt, TexCoord)) * Color_Shirt;\n"
+"#endif\n"
+"\n"
+"\n"
+"\n"
+"\n"
+"#ifdef USELIGHTSOURCE\n"
+"      // light source\n"
+"\n"
+"      // get the surface normal and light normal\n"
+"#ifdef SURFACENORMALIZE\n"
+"      myhvec3 surfacenormal = normalize(myhvec3(texture2D(Texture_Normal, TexCoord)) - 0.5);\n"
+"#else\n"
+"      myhvec3 surfacenormal = -1.0 + 2.0 * myhvec3(texture2D(Texture_Normal, TexCoord));\n"
+"#endif\n"
+"      myhvec3 diffusenormal = myhvec3(normalize(LightVector));\n"
+"\n"
+"      // calculate directional shading\n"
+"      color.rgb *= (AmbientScale + DiffuseScale * max(dot(surfacenormal, diffusenormal), 0.0));\n"
+"#ifdef USESPECULAR\n"
+"      myhvec3 specularnormal = myhvec3(normalize(diffusenormal + myhvec3(normalize(EyeVector))));\n"
+"      color.rgb += myhvec3(texture2D(Texture_Gloss, TexCoord)) * SpecularScale * pow(max(dot(surfacenormal, specularnormal), 0.0), SpecularPower);\n"
+"#endif\n"
+"\n"
+"#ifdef USECUBEFILTER\n"
+"      // apply light cubemap filter\n"
+"      color.rgb *= myhvec3(textureCube(Texture_Cube, CubeVector));\n"
+"#endif\n"
+"\n"
+"      // apply light color\n"
+"      color.rgb = 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 *= max(1.0 - dot(CubeVector, CubeVector), 0.0);\n"
+"\n"
+"\n"
+"\n"
+"\n"
+"#else\n"
+"#ifdef USEDELUXEMAPPING\n"
+"      // deluxemap lightmapping\n"
+"\n"
+"      // get the surface normal and light normal\n"
+"#ifdef SURFACENORMALIZE\n"
+"      myhvec3 surfacenormal = normalize(myhvec3(texture2D(Texture_Normal, TexCoord)) - 0.5);\n"
+"      myhvec3 diffusenormal = normalize(myhvec3(texture2D(Texture_Deluxemap, TexCoordLightmap)));\n"
+"#else\n"
+"      myhvec3 surfacenormal = -1.0 + 2.0 * myhvec3(texture2D(Texture_Normal, TexCoord));\n"
+"      myhvec3 diffusenormal = myhvec3(texture2D(Texture_Deluxemap, TexCoordLightmap));\n"
+"#endif\n"
+"\n"
+"      // calculate directional shading\n"
+"      color.rgb *= (AmbientScale + DiffuseScale * max(dot(surfacenormal, diffusenormal), 0.0));\n"
+"#ifdef USESPECULAR\n"
+"      myhvec3 specularnormal = myhvec3(normalize(diffusenormal + myhvec3(normalize(EyeVector))));\n"
+"      color.rgb += myhvec3(texture2D(Texture_Gloss, TexCoord)) * SpecularScale * pow(max(dot(surfacenormal, specularnormal), 0.0), SpecularPower);\n"
+"#endif\n"
+"\n"
+"      // apply lightmap color\n"
+"      color.rgb *= myhvec3(texture2D(Texture_Lightmap, TexCoordLightmap));\n"
+"\n"
+"\n"
+"\n"
+"\n"
+"#else\n"
+"      // apply lightmap color\n"
+"      color.rgb *= myhvec3(texture2D(Texture_Lightmap, TexCoordLightmap)) * DiffuseScale;\n"
+"#endif\n"
+"#endif\n"
+"\n"
+"\n"
+"\n"
+"\n"
+"#ifdef USEFOG\n"
+"      // apply fog\n"
+"      myhalf fog = texture2D(Texture_FogMask, myhvec2(length(EyeVector)*FogRangeRecip, 0)).x;\n"
+"      color.rgb = color.rgb * (1 - fog) + FogColor * fog;\n"
+"#endif\n"
+"\n"
+"      gl_FragColor = color;\n"
+"}\n"
+"\n"
+"#endif\n"
+;
+
+// the loaded GLSL shader file for compiling shader permutations as needed
+static char *shaderstring = NULL;
+
+void R_GLSL_CompilePermutation(int permutation)
+{
+       r_glsl_permutation_t *p = r_glsl_permutations + permutation;
+       int vertstrings_count;
+       int fragstrings_count;
+       const char *vertstrings_list[SHADERPERMUTATION_COUNT+1];
+       const char *fragstrings_list[SHADERPERMUTATION_COUNT+1];
+       char permutationname[256];
+       if (p->compiled)
+               return;
+       p->compiled = true;
+       vertstrings_list[0] = "#define VERTEX_SHADER\n";
+       fragstrings_list[0] = "#define FRAGMENT_SHADER\n";
+       vertstrings_count = 1;
+       fragstrings_count = 1;
+       permutationname[0] = 0;
+       if (permutation & SHADERPERMUTATION_DELUXEMAPPING)
+       {
+               vertstrings_list[vertstrings_count++] = "#define USEDELUXEMAP\n";
+               fragstrings_list[fragstrings_count++] = "#define USEDELUXEMAP\n";
+               strlcat(permutationname, " deluxemap", sizeof(permutationname));
+       }
+       if (permutation & SHADERPERMUTATION_LIGHTSOURCE)
+       {
+               vertstrings_list[vertstrings_count++] = "#define USELIGHTSOURCE\n";
+               fragstrings_list[fragstrings_count++] = "#define USELIGHTSOURCE\n";
+               strlcat(permutationname, " lightsource", sizeof(permutationname));
+       }
+       if (permutation & SHADERPERMUTATION_COLORMAPPING)
+       {
+               vertstrings_list[vertstrings_count++] = "#define USECOLORMAPPING\n";
+               fragstrings_list[fragstrings_count++] = "#define USECOLORMAPPING\n";
+               strlcat(permutationname, " colormapping", sizeof(permutationname));
+       }
+       if (permutation & SHADERPERMUTATION_SPECULAR)
+       {
+               vertstrings_list[vertstrings_count++] = "#define USESPECULAR\n";
+               fragstrings_list[fragstrings_count++] = "#define USESPECULAR\n";
+               strlcat(permutationname, " specular", sizeof(permutationname));
+       }
+       if (permutation & SHADERPERMUTATION_FOG)
+       {
+               vertstrings_list[vertstrings_count++] = "#define USEFOG\n";
+               fragstrings_list[fragstrings_count++] = "#define USEFOG\n";
+               strlcat(permutationname, " fog", sizeof(permutationname));
+       }
+       if (permutation & SHADERPERMUTATION_CUBEFILTER)
+       {
+               vertstrings_list[vertstrings_count++] = "#define USECUBEFILTER\n";
+               fragstrings_list[fragstrings_count++] = "#define USECUBEFILTER\n";
+               strlcat(permutationname, " cubefilter", sizeof(permutationname));
+       }
+       if (permutation & SHADERPERMUTATION_OFFSETMAPPING)
+       {
+               vertstrings_list[vertstrings_count++] = "#define USEOFFSETMAPPING\n";
+               fragstrings_list[fragstrings_count++] = "#define USEOFFSETMAPPING\n";
+               strlcat(permutationname, " offsetmapping", sizeof(permutationname));
+       }
+       if (permutation & SHADERPERMUTATION_SURFACENORMALIZE)
+       {
+               vertstrings_list[vertstrings_count++] = "#define SURFACENORMALIZE\n";
+               fragstrings_list[fragstrings_count++] = "#define SURFACENORMALIZE\n";
+               strlcat(permutationname, " surfacenormalize", sizeof(permutationname));
+       }
+       if (permutation & SHADERPERMUTATION_GEFORCEFX)
+       {
+               vertstrings_list[vertstrings_count++] = "#define GEFORCEFX\n";
+               fragstrings_list[fragstrings_count++] = "#define GEFORCEFX\n";
+               strlcat(permutationname, " halffloat", sizeof(permutationname));
+       }
+       if (shaderstring)
+       {
+               vertstrings_list[vertstrings_count++] = shaderstring;
+               fragstrings_list[fragstrings_count++] = shaderstring;
+       }
+       else
+       {
+               vertstrings_list[vertstrings_count++] = builtinshaderstring;
+               fragstrings_list[fragstrings_count++] = builtinshaderstring;
+       }
+       p->program = GL_Backend_CompileProgram(vertstrings_count, vertstrings_list, fragstrings_count, fragstrings_list);
+       if (p->program)
+       {
+               CHECKGLERROR
+               qglUseProgramObjectARB(p->program);
+               p->loc_Texture_Normal      = qglGetUniformLocationARB(p->program, "Texture_Normal");
+               p->loc_Texture_Color       = qglGetUniformLocationARB(p->program, "Texture_Color");
+               p->loc_Texture_Gloss       = qglGetUniformLocationARB(p->program, "Texture_Gloss");
+               p->loc_Texture_Cube        = qglGetUniformLocationARB(p->program, "Texture_Cube");
+               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_FogColor            = qglGetUniformLocationARB(p->program, "FogColor");
+               p->loc_LightPosition       = qglGetUniformLocationARB(p->program, "LightPosition");
+               p->loc_EyePosition         = qglGetUniformLocationARB(p->program, "EyePosition");
+               p->loc_LightColor          = qglGetUniformLocationARB(p->program, "LightColor");
+               p->loc_Color_Pants         = qglGetUniformLocationARB(p->program, "Color_Pants");
+               p->loc_Color_Shirt         = qglGetUniformLocationARB(p->program, "Color_Shirt");
+               p->loc_FogRangeRecip       = qglGetUniformLocationARB(p->program, "FogRangeRecip");
+               p->loc_AmbientScale        = qglGetUniformLocationARB(p->program, "AmbientScale");
+               p->loc_DiffuseScale        = qglGetUniformLocationARB(p->program, "DiffuseScale");
+               p->loc_SpecularPower       = qglGetUniformLocationARB(p->program, "SpecularPower");
+               p->loc_SpecularScale       = qglGetUniformLocationARB(p->program, "SpecularScale");
+               p->loc_OffsetMapping_Scale = qglGetUniformLocationARB(p->program, "OffsetMapping_Scale");
+               p->loc_OffsetMapping_Bias  = qglGetUniformLocationARB(p->program, "OffsetMapping_Bias");
+               if (p->loc_Texture_Normal)   qglUniform1iARB(p->loc_Texture_Normal, 0);
+               if (p->loc_Texture_Color)    qglUniform1iARB(p->loc_Texture_Color, 1);
+               if (p->loc_Texture_Gloss)    qglUniform1iARB(p->loc_Texture_Gloss, 2);
+               if (p->loc_Texture_Cube)     qglUniform1iARB(p->loc_Texture_Cube, 3);
+               if (p->loc_Texture_FogMask)  qglUniform1iARB(p->loc_Texture_FogMask, 4);
+               if (p->loc_Texture_Pants)    qglUniform1iARB(p->loc_Texture_Pants, 5);
+               if (p->loc_Texture_Shirt)    qglUniform1iARB(p->loc_Texture_Shirt, 6);
+               if (p->loc_Texture_Lightmap) qglUniform1iARB(p->loc_Texture_Lightmap, 7);
+               if (p->loc_Texture_Deluxemap) qglUniform1iARB(p->loc_Texture_Deluxemap, 8);
+               qglUseProgramObjectARB(0);
+               CHECKGLERROR
+       }
+       else
+               Con_Printf("permutation%s failed for shader %s, some features may not work properly!\n", permutationname, "glsl/default.glsl");
+}
+
+void R_SetupSurfaceShader(const entity_render_t *ent, const texture_t *texture, 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)
+{
+       // 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
+       int permutation = 0;
+       r_glsl_permutation = NULL;
+       if (r_shadow_rtlight)
+               permutation |= SHADERPERMUTATION_LIGHTSOURCE;
+       else if (false)
+               permutation |= SHADERPERMUTATION_DELUXEMAPPING;
+       if (fogenabled)
+               permutation |= SHADERPERMUTATION_FOG;
+       if ((dopants || doshirt))
+               permutation |= SHADERPERMUTATION_COLORMAPPING;
+       if (specularscale > 0)
+               permutation |= SHADERPERMUTATION_SPECULAR;
+       if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
+               permutation |= SHADERPERMUTATION_CUBEFILTER;
+       if (r_glsl_offsetmapping.integer)
+               permutation |= SHADERPERMUTATION_OFFSETMAPPING;
+       if (r_glsl_surfacenormalize.integer)
+               permutation |= SHADERPERMUTATION_SURFACENORMALIZE;
+       if (r_glsl_usehalffloat.integer)
+               permutation |= SHADERPERMUTATION_GEFORCEFX;
+       if (!r_glsl_permutations[permutation].program)
+       {
+               if (!r_glsl_permutations[permutation].compiled)
+                       R_GLSL_CompilePermutation(permutation);
+               if (!r_glsl_permutations[permutation].program)
+               {
+                       // remove features until we find a valid permutation
+                       int i;
+                       for (i = SHADERPERMUTATION_COUNT-1;;i>>=1)
+                       {
+                               // reduce i more quickly whenever it would not remove any bits
+                               if (permutation < i)
+                                       continue;
+                               permutation &= i;
+                               if (!r_glsl_permutations[permutation].compiled)
+                                       R_GLSL_CompilePermutation(permutation);
+                               if (r_glsl_permutations[permutation].program)
+                                       break;
+                               if (!i)
+                                       return; // utterly failed
+                       }
+               }
+       }
+       r_glsl_permutation = r_glsl_permutations + permutation;
+       CHECKGLERROR
+       qglUseProgramObjectARB(r_glsl_permutation->program);CHECKGLERROR
+       R_Mesh_TexMatrix(0, &texture->currenttexmatrix);
+       if (r_shadow_rtlight)
+               R_Mesh_TexMatrix(3, &r_shadow_entitytolight);
+       if (r_glsl_permutation->loc_Texture_Normal) R_Mesh_TexBind(0, R_GetTexture(normalmaptexture));
+       if (r_glsl_permutation->loc_Texture_Color) R_Mesh_TexBind(1, R_GetTexture(basetexture));
+       if (r_glsl_permutation->loc_FogColor)
+       {
+               // additive passes are only darkened by fog, not tinted
+               if (r_shadow_rtlight || (texture->currentmaterialflags & MATERIALFLAG_ADD))
+                       qglUniform3fARB(r_glsl_permutation->loc_FogColor, 0, 0, 0);
+               else
+                       qglUniform3fARB(r_glsl_permutation->loc_FogColor, fogcolor[0], fogcolor[1], fogcolor[2]);
+       }
+       if (r_glsl_permutation->loc_LightPosition) qglUniform3fARB(r_glsl_permutation->loc_LightPosition, r_shadow_entitylightorigin[0], r_shadow_entitylightorigin[1], r_shadow_entitylightorigin[2]);
+       if (r_glsl_permutation->loc_EyePosition) qglUniform3fARB(r_glsl_permutation->loc_EyePosition, r_shadow_entityeyeorigin[0], r_shadow_entityeyeorigin[1], r_shadow_entityeyeorigin[2]);
+       if (r_glsl_permutation->loc_LightColor) qglUniform3fARB(r_glsl_permutation->loc_LightColor, lightcolorbase[0], lightcolorbase[1], lightcolorbase[2]);
+       if (r_glsl_permutation->loc_Texture_Pants) R_Mesh_TexBind(5, R_GetTexture(pantstexture));
+       if (r_glsl_permutation->loc_Texture_Shirt) R_Mesh_TexBind(6, R_GetTexture(shirttexture));
+       if (r_glsl_permutation->loc_Color_Pants) qglUniform3fARB(r_glsl_permutation->loc_Color_Pants, ent->colormap_pantscolor[0], ent->colormap_pantscolor[1], ent->colormap_pantscolor[2]);
+       if (r_glsl_permutation->loc_Color_Shirt) qglUniform3fARB(r_glsl_permutation->loc_Color_Shirt, ent->colormap_shirtcolor[0], ent->colormap_shirtcolor[1], ent->colormap_shirtcolor[2]);
+       if (r_glsl_permutation->loc_FogRangeRecip) qglUniform1fARB(r_glsl_permutation->loc_FogRangeRecip, fograngerecip);
+       if (r_glsl_permutation->loc_AmbientScale) qglUniform1fARB(r_glsl_permutation->loc_AmbientScale, r_shadow_rtlight->ambientscale);
+       if (r_glsl_permutation->loc_DiffuseScale) qglUniform1fARB(r_glsl_permutation->loc_DiffuseScale, r_shadow_rtlight->diffusescale);
+       if (r_glsl_permutation->loc_Texture_Gloss) R_Mesh_TexBind(2, R_GetTexture(glosstexture));
+       if (r_glsl_permutation->loc_SpecularPower) qglUniform1fARB(r_glsl_permutation->loc_SpecularPower, 8);
+       if (r_glsl_permutation->loc_SpecularScale) qglUniform1fARB(r_glsl_permutation->loc_SpecularScale, specularscale);
+       if (r_glsl_permutation->loc_OffsetMapping_Scale) qglUniform1fARB(r_glsl_permutation->loc_OffsetMapping_Scale, r_glsl_offsetmapping_scale.value);
+       if (r_glsl_permutation->loc_OffsetMapping_Bias) qglUniform1fARB(r_glsl_permutation->loc_OffsetMapping_Bias, r_glsl_offsetmapping_bias.value);
+       CHECKGLERROR
+}
+
 void gl_main_start(void)
 {
+       // use half float math where available (speed gain on NVIDIA GFFX and GF6)
+       if (gl_support_half_float)
+               Cvar_SetValue("r_glsl_usehalffloat", 1);
        r_main_texturepool = R_AllocTexturePool();
        r_bloom_texture_screen = NULL;
        r_bloom_texture_bloom = NULL;
@@ -407,10 +860,29 @@ void gl_main_start(void)
                R_BuildNormalizationCube();
        }
        R_BuildFogTexture();
+       shaderstring = NULL;
+       if (gl_support_fragment_shader)
+       {
+               shaderstring = (char *)FS_LoadFile("glsl/default.glsl", r_main_mempool, false, NULL);
+               if (shaderstring)
+                       Con_Printf("GLSL shader text loaded from disk\n");
+               // if we couldn't load the shader file, fall back to builtin shader
+               if (!shaderstring)
+               {
+                       if (shaderstring)
+                               Con_Printf("GLSL shader text loaded from fallback\n");
+                       shaderstring = Mem_Alloc(r_main_mempool, strlen(builtinshaderstring) + 1);
+                       strcpy(shaderstring, builtinshaderstring);
+               }
+       }
+       if (shaderstring)
+               Con_Printf("GLSL shader text loaded\n");
+       memset(r_glsl_permutations, 0, sizeof(r_glsl_permutations));
 }
 
 void gl_main_shutdown(void)
 {
+       int i;
        R_FreeTexturePool(&r_main_texturepool);
        r_bloom_texture_screen = NULL;
        r_bloom_texture_bloom = NULL;
@@ -419,6 +891,13 @@ void gl_main_shutdown(void)
        r_texture_black = NULL;
        r_texture_whitecube = NULL;
        r_texture_normalizationcube = NULL;
+       if (shaderstring)
+               Mem_Free(shaderstring);
+       shaderstring = NULL;
+       for (i = 0;i < SHADERPERMUTATION_COUNT;i++)
+               if (r_glsl_permutations[i].program)
+                       GL_Backend_FreeProgram(r_glsl_permutations[i].program);
+       memset(r_glsl_permutations, 0, sizeof(r_glsl_permutations));
 }
 
 extern void CL_ParseEntityLump(char *entitystring);
@@ -449,8 +928,9 @@ void gl_main_newmap(void)
 
 void GL_Main_Init(void)
 {
-// FIXME: move this to client?
-       FOG_registercvars();
+       r_main_mempool = Mem_AllocPool("Renderer", 0, NULL);
+
+       FOG_registercvars(); // FIXME: move this fog stuff to client?
        Cvar_RegisterVariable(&r_nearclip);
        Cvar_RegisterVariable(&r_showtris);
        Cvar_RegisterVariable(&r_showtris_polygonoffset);
@@ -469,6 +949,12 @@ void GL_Main_Init(void)
        Cvar_RegisterVariable(&r_dynamic);
        Cvar_RegisterVariable(&r_fullbright);
        Cvar_RegisterVariable(&r_textureunits);
+       Cvar_RegisterVariable(&r_glsl);
+       Cvar_RegisterVariable(&r_glsl_offsetmapping);
+       Cvar_RegisterVariable(&r_glsl_offsetmapping_scale);
+       Cvar_RegisterVariable(&r_glsl_offsetmapping_bias);
+       Cvar_RegisterVariable(&r_glsl_usehalffloat);
+       Cvar_RegisterVariable(&r_glsl_surfacenormalize);
        Cvar_RegisterVariable(&r_lerpsprites);
        Cvar_RegisterVariable(&r_lerpmodels);
        Cvar_RegisterVariable(&r_waterscroll);
index af19c93..5235137 100644 (file)
@@ -199,8 +199,8 @@ cvar_t r_shadow_debuglight = {0, "r_shadow_debuglight", "-1", "renders only one
 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.25", "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_lightattenuationpower = {0, "r_shadow_lightattenuationpower", "0.5", "changes attenuation texture generation (does not affect r_shadow_glsl lighting)"};
-cvar_t r_shadow_lightattenuationscale = {0, "r_shadow_lightattenuationscale", "1", "changes attenuation texture generation (does not affect r_shadow_glsl lighting)"};
+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_lightintensityscale = {0, "r_shadow_lightintensityscale", "1", "renders all world lights brighter or darker"};
 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_projectdistance = {0, "r_shadow_projectdistance", "1000000", "how far to cast shadows"};
@@ -216,13 +216,7 @@ cvar_t r_shadow_realtime_world_compileshadow = {0, "r_shadow_realtime_world_comp
 cvar_t r_shadow_scissor = {0, "r_shadow_scissor", "1", "use scissor optimization of light rendering (restricts rendering to the portion of the screen affected by the light)"};
 cvar_t r_shadow_shadow_polygonfactor = {0, "r_shadow_shadow_polygonfactor", "0", "how much to enlarge shadow volume polygons when rendering (should be 0!)"};
 cvar_t r_shadow_shadow_polygonoffset = {0, "r_shadow_shadow_polygonoffset", "1", "how much to push shadow volumes into the distance when rendering, to reduce chances of zfighting artifacts (should not be less than 0)"};
-cvar_t r_shadow_texture3d = {0, "r_shadow_texture3d", "1", "use 3D voxel textures for spherical attenuation rather than cylindrical (does not affect r_shadow_glsl lighting)"};
-cvar_t r_shadow_glsl = {0, "r_shadow_glsl", "1", "enables use of OpenGL 2.0 pixel shaders for lighting"};
-cvar_t r_shadow_glsl_offsetmapping = {0, "r_shadow_glsl_offsetmapping", "0", "enables offset mapping effect (also known as parallax mapping or sometimes as virtual displacement mapping, not as good as relief mapping or silohuette mapping but much faster), can cause strange artifacts on many textures, requires bumpmaps for depth information (normalmaps can have depth information as alpha channel, but most do not)"};
-cvar_t r_shadow_glsl_offsetmapping_scale = {0, "r_shadow_glsl_offsetmapping_scale", "-0.04", "how deep the offset mapping effect is, and whether it is inward or outward"};
-cvar_t r_shadow_glsl_offsetmapping_bias = {0, "r_shadow_glsl_offsetmapping_bias", "0.04", "pushes the effect closer/further"};
-cvar_t r_shadow_glsl_usehalffloat = {0, "r_shadow_glsl_usehalffloat", "0", "use half and hvec variables in GLSL shader for a speed gain (NVIDIA only)"};
-cvar_t r_shadow_glsl_surfacenormalize = {0, "r_shadow_glsl_surfacenormalize", "1", "normalize bumpmap texels in GLSL shader, produces a more rounded look on small bumps and dents"};
+cvar_t r_shadow_texture3d = {0, "r_shadow_texture3d", "1", "use 3D voxel textures for spherical attenuation rather than cylindrical (does not affect r_glsl lighting)"};
 cvar_t gl_ext_stenciltwoside = {0, "gl_ext_stenciltwoside", "1", "make use of GL_EXT_stenciltwoside extension (NVIDIA only)"};
 cvar_t r_editlights = {0, "r_editlights", "0", "enables .rtlights file editing mode"};
 cvar_t r_editlights_cursordistance = {0, "r_editlights_cursordistance", "1024", "maximum distance of cursor from eye"};
@@ -252,20 +246,6 @@ cubemapinfo_t;
 static int numcubemaps;
 static cubemapinfo_t cubemaps[MAX_CUBEMAPS];
 
-#define SHADERPERMUTATION_COLORMAPPING (1<<0)
-#define SHADERPERMUTATION_SPECULAR (1<<1)
-#define SHADERPERMUTATION_FOG (1<<2)
-#define SHADERPERMUTATION_CUBEFILTER (1<<3)
-#define SHADERPERMUTATION_OFFSETMAPPING (1<<4)
-#define SHADERPERMUTATION_SURFACENORMALIZE (1<<5)
-#define SHADERPERMUTATION_GEFORCEFX (1<<6)
-#define SHADERPERMUTATION_COUNT (1<<7)
-
-// indicates if we have tried compiling this shader permutation yet
-qboolean r_shadow_program_compiledlight[SHADERPERMUTATION_COUNT];
-// GLSL program object number, or 0 if compile failed
-GLhandleARB r_shadow_program_light[SHADERPERMUTATION_COUNT];
-
 void R_Shadow_UncompileWorldLights(void);
 void R_Shadow_ClearWorldLights(void);
 void R_Shadow_SaveWorldLights(void);
@@ -277,281 +257,8 @@ void R_Shadow_ValidateCvars(void);
 static void R_Shadow_MakeTextures(void);
 void R_Shadow_DrawWorldLightShadowVolume(matrix4x4_t *matrix, dlight_t *light);
 
-const char *builtinshader_light_vert =
-"// ambient+diffuse+specular+normalmap+attenuation+cubemap+fog shader\n"
-"// written by Forest 'LordHavoc' Hale\n"
-"\n"
-"// use half floats if available for math performance\n"
-"#ifdef GEFORCEFX\n"
-"#define myhalf half\n"
-"#define myhvec2 hvec2\n"
-"#define myhvec3 hvec3\n"
-"#define myhvec4 hvec4\n"
-"#else\n"
-"#define myhalf float\n"
-"#define myhvec2 vec2\n"
-"#define myhvec3 vec3\n"
-"#define myhvec4 vec4\n"
-"#endif\n"
-"\n"
-"uniform vec3 LightPosition;\n"
-"\n"
-"varying vec2 TexCoord;\n"
-"varying myhvec3 CubeVector;\n"
-"varying vec3 LightVector;\n"
-"\n"
-"#if defined(USESPECULAR) || defined(USEFOG) || defined(USEOFFSETMAPPING)\n"
-"uniform vec3 EyePosition;\n"
-"varying vec3 EyeVector;\n"
-"#endif\n"
-"\n"
-"// TODO: get rid of tangentt (texcoord2) and use a crossproduct to regenerate it from tangents (texcoord1) and normal (texcoord3)\n"
-"\n"
-"void main(void)\n"
-"{\n"
-"      // copy the surface texcoord\n"
-"      TexCoord = vec2(gl_TextureMatrix[0] * gl_MultiTexCoord0);\n"
-"\n"
-"      // transform vertex position into light attenuation/cubemap space\n"
-"      // (-1 to +1 across the light box)\n"
-"      CubeVector = vec3(gl_TextureMatrix[3] * gl_Vertex);\n"
-"\n"
-"      // transform unnormalized light direction into tangent space\n"
-"      // (we use unnormalized to ensure that it interpolates correctly and then\n"
-"      //  normalize it per pixel)\n"
-"      vec3 lightminusvertex = LightPosition - gl_Vertex.xyz;\n"
-"      LightVector.x = dot(lightminusvertex, gl_MultiTexCoord1.xyz);\n"
-"      LightVector.y = dot(lightminusvertex, gl_MultiTexCoord2.xyz);\n"
-"      LightVector.z = dot(lightminusvertex, gl_MultiTexCoord3.xyz);\n"
-"\n"
-"#if defined(USESPECULAR) || defined(USEFOG) || defined(USEOFFSETMAPPING)\n"
-"      // transform unnormalized eye direction into tangent space\n"
-"      vec3 eyeminusvertex = EyePosition - gl_Vertex.xyz;\n"
-"      EyeVector.x = dot(eyeminusvertex, gl_MultiTexCoord1.xyz);\n"
-"      EyeVector.y = dot(eyeminusvertex, gl_MultiTexCoord2.xyz);\n"
-"      EyeVector.z = dot(eyeminusvertex, gl_MultiTexCoord3.xyz);\n"
-"#endif\n"
-"\n"
-"      // transform vertex to camera space, using ftransform to match non-VS\n"
-"      // rendering\n"
-"      gl_Position = ftransform();\n"
-"}\n"
-;
-
-const char *builtinshader_light_frag =
-"// ambient+diffuse+specular+normalmap+attenuation+cubemap+fog shader\n"
-"// written by Forest 'LordHavoc' Hale\n"
-"\n"
-"// use half floats if available for math performance\n"
-"#ifdef GEFORCEFX\n"
-"#define myhalf half\n"
-"#define myhvec2 hvec2\n"
-"#define myhvec3 hvec3\n"
-"#define myhvec4 hvec4\n"
-"#else\n"
-"#define myhalf float\n"
-"#define myhvec2 vec2\n"
-"#define myhvec3 vec3\n"
-"#define myhvec4 vec4\n"
-"#endif\n"
-"\n"
-"uniform myhvec3 LightColor;\n"
-"#ifdef USEOFFSETMAPPING\n"
-"uniform myhalf OffsetMapping_Scale;\n"
-"uniform myhalf OffsetMapping_Bias;\n"
-"#endif\n"
-"#ifdef USESPECULAR\n"
-"uniform myhalf SpecularPower;\n"
-"#endif\n"
-"#ifdef USEFOG\n"
-"uniform myhalf FogRangeRecip;\n"
-"#endif\n"
-"uniform myhalf AmbientScale;\n"
-"uniform myhalf DiffuseScale;\n"
-"#ifdef USESPECULAR\n"
-"uniform myhalf SpecularScale;\n"
-"#endif\n"
-"\n"
-"#ifdef USECOLORMAPPING\n"
-"uniform myhvec3 Color_Pants;\n"
-"uniform myhvec3 Color_Shirt;\n"
-"#endif\n"
-"\n"
-"uniform sampler2D Texture_Normal;\n"
-"uniform sampler2D Texture_Color;\n"
-"uniform sampler2D Texture_Pants;\n"
-"uniform sampler2D Texture_Shirt;\n"
-"#ifdef USESPECULAR\n"
-"uniform sampler2D Texture_Gloss;\n"
-"#endif\n"
-"#ifdef USECUBEFILTER\n"
-"uniform samplerCube Texture_Cube;\n"
-"#endif\n"
-"#ifdef USEFOG\n"
-"uniform sampler2D Texture_FogMask;\n"
-"#endif\n"
-"\n"
-"varying vec2 TexCoord;\n"
-"varying myhvec3 CubeVector;\n"
-"varying vec3 LightVector;\n"
-"#if defined(USESPECULAR) || defined(USEFOG) || defined(USEOFFSETMAPPING)\n"
-"varying vec3 EyeVector;\n"
-"#endif\n"
-"\n"
-"void main(void)\n"
-"{\n"
-"      // 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"
-"      myhalf colorscale = max(1.0 - dot(CubeVector, CubeVector), 0.0);\n"
-"\n"
-"#ifdef USEFOG\n"
-"      // apply fog\n"
-"      colorscale *= texture2D(Texture_FogMask, myhvec2(length(EyeVector)*FogRangeRecip, 0)).x;\n"
-"#endif\n"
-"\n"
-"#ifdef USEOFFSETMAPPING\n"
-"      // this is 3 sample because of ATI Radeon 9500-9800/X300 limits\n"
-"      myhvec2 OffsetVector = normalize(EyeVector).xy * vec2(-0.333, 0.333);\n"
-"      myhvec2 TexCoordOffset = TexCoord + OffsetVector * (OffsetMapping_Bias + OffsetMapping_Scale * texture2D(Texture_Normal, TexCoord).w);\n"
-"      TexCoordOffset += OffsetVector * (OffsetMapping_Bias + OffsetMapping_Scale * texture2D(Texture_Normal, TexCoordOffset).w);\n"
-"      TexCoordOffset += OffsetVector * (OffsetMapping_Bias + OffsetMapping_Scale * texture2D(Texture_Normal, TexCoordOffset).w);\n"
-"#define TexCoord TexCoordOffset\n"
-"#endif\n"
-"\n"
-"      // get the surface normal\n"
-"#ifdef SURFACENORMALIZE\n"
-"      myhvec3 surfacenormal = normalize(myhvec3(texture2D(Texture_Normal, TexCoord)) - 0.5);\n"
-"#else\n"
-"      myhvec3 surfacenormal = -1.0 + 2.0 * myhvec3(texture2D(Texture_Normal, TexCoord));\n"
-"#endif\n"
-"\n"
-"      // calculate shading\n"
-"      myhvec3 diffusenormal = myhvec3(normalize(LightVector));\n"
-"      myhvec4 texturecolor = myhvec4(texture2D(Texture_Color, TexCoord));\n"
-"      colorscale *= texturecolor.a;\n"
-"      myhvec3 color = myhvec3(texturecolor);\n"
-"#ifdef USECOLORMAPPING\n"
-"      color += myhvec3(texture2D(Texture_Pants, TexCoord)) * Color_Pants + myhvec3(texture2D(Texture_Shirt, TexCoord)) * Color_Shirt;\n"
-"#endif\n"
-"      color *= (AmbientScale + DiffuseScale * max(dot(surfacenormal, diffusenormal), 0.0));\n"
-"#ifdef USESPECULAR\n"
-"      myhvec3 specularnormal = myhvec3(normalize(diffusenormal + myhvec3(normalize(EyeVector))));\n"
-"      color += myhvec3(texture2D(Texture_Gloss, TexCoord)) * SpecularScale * pow(max(dot(surfacenormal, specularnormal), 0.0), SpecularPower);\n"
-"#endif\n"
-"\n"
-"#ifdef USECUBEFILTER\n"
-"      // apply light cubemap filter\n"
-"      color *= myhvec3(textureCube(Texture_Cube, CubeVector));\n"
-"#endif\n"
-"\n"
-"      // calculate fragment color (apply light color and attenuation/fog scaling)\n"
-"      gl_FragColor = myhvec4(color * LightColor * colorscale, 1);\n"
-"}\n"
-;
-
-int R_Shadow_CompileShaderPermutation(int permutation)
-{
-       char *vertstring, *fragstring;
-       int vertstrings_count;
-       int fragstrings_count;
-       const char *vertstrings_list[SHADERPERMUTATION_COUNT+1];
-       const char *fragstrings_list[SHADERPERMUTATION_COUNT+1];
-       char permutationname[256];
-       if (r_shadow_program_compiledlight[permutation])
-               return r_shadow_program_light[permutation];
-       r_shadow_program_compiledlight[permutation] = true;
-       vertstring = (char *)FS_LoadFile("glsl/light.vert", tempmempool, false, NULL);
-       fragstring = (char *)FS_LoadFile("glsl/light.frag", tempmempool, false, NULL);
-       vertstrings_count = 0;
-       fragstrings_count = 0;
-       permutationname[0] = 0;
-       if (permutation & SHADERPERMUTATION_COLORMAPPING)
-       {
-               vertstrings_list[vertstrings_count++] = "#define USECOLORMAPPING\n";
-               fragstrings_list[fragstrings_count++] = "#define USECOLORMAPPING\n";
-               strlcat(permutationname, " colormapping", sizeof(permutationname));
-       }
-       if (permutation & SHADERPERMUTATION_SPECULAR)
-       {
-               vertstrings_list[vertstrings_count++] = "#define USESPECULAR\n";
-               fragstrings_list[fragstrings_count++] = "#define USESPECULAR\n";
-               strlcat(permutationname, " specular", sizeof(permutationname));
-       }
-       if (permutation & SHADERPERMUTATION_FOG)
-       {
-               vertstrings_list[vertstrings_count++] = "#define USEFOG\n";
-               fragstrings_list[fragstrings_count++] = "#define USEFOG\n";
-               strlcat(permutationname, " fog", sizeof(permutationname));
-       }
-       if (permutation & SHADERPERMUTATION_CUBEFILTER)
-       {
-               vertstrings_list[vertstrings_count++] = "#define USECUBEFILTER\n";
-               fragstrings_list[fragstrings_count++] = "#define USECUBEFILTER\n";
-               strlcat(permutationname, " cubefilter", sizeof(permutationname));
-       }
-       if (permutation & SHADERPERMUTATION_OFFSETMAPPING)
-       {
-               vertstrings_list[vertstrings_count++] = "#define USEOFFSETMAPPING\n";
-               fragstrings_list[fragstrings_count++] = "#define USEOFFSETMAPPING\n";
-               strlcat(permutationname, " offsetmapping", sizeof(permutationname));
-       }
-       if (permutation & SHADERPERMUTATION_SURFACENORMALIZE)
-       {
-               vertstrings_list[vertstrings_count++] = "#define SURFACENORMALIZE\n";
-               fragstrings_list[fragstrings_count++] = "#define SURFACENORMALIZE\n";
-               strlcat(permutationname, " surfacenormalize", sizeof(permutationname));
-       }
-       if (permutation & SHADERPERMUTATION_GEFORCEFX)
-       {
-               vertstrings_list[vertstrings_count++] = "#define GEFORCEFX\n";
-               fragstrings_list[fragstrings_count++] = "#define GEFORCEFX\n";
-               strlcat(permutationname, " halffloat", sizeof(permutationname));
-       }
-       vertstrings_list[vertstrings_count++] = vertstring ? vertstring : builtinshader_light_vert;
-       fragstrings_list[fragstrings_count++] = fragstring ? fragstring : builtinshader_light_frag;
-       r_shadow_program_light[permutation] = GL_Backend_CompileProgram(vertstrings_count, vertstrings_list, fragstrings_count, fragstrings_list);
-       if (r_shadow_program_light[permutation])
-       {
-               qglUseProgramObjectARB(r_shadow_program_light[permutation]);
-               qglUniform1iARB(qglGetUniformLocationARB(r_shadow_program_light[permutation], "Texture_Normal"), 0);CHECKGLERROR
-               qglUniform1iARB(qglGetUniformLocationARB(r_shadow_program_light[permutation], "Texture_Color"), 1);CHECKGLERROR
-               if (permutation & SHADERPERMUTATION_SPECULAR)
-               {
-                       qglUniform1iARB(qglGetUniformLocationARB(r_shadow_program_light[permutation], "Texture_Gloss"), 2);CHECKGLERROR
-               }
-               if (permutation & SHADERPERMUTATION_CUBEFILTER)
-               {
-                       qglUniform1iARB(qglGetUniformLocationARB(r_shadow_program_light[permutation], "Texture_Cube"), 3);CHECKGLERROR
-               }
-               if (permutation & SHADERPERMUTATION_FOG)
-               {
-                       qglUniform1iARB(qglGetUniformLocationARB(r_shadow_program_light[permutation], "Texture_FogMask"), 4);CHECKGLERROR
-               }
-               qglUniform1iARB(qglGetUniformLocationARB(r_shadow_program_light[permutation], "Texture_Pants"), 5);CHECKGLERROR
-               qglUniform1iARB(qglGetUniformLocationARB(r_shadow_program_light[permutation], "Texture_Shirt"), 6);CHECKGLERROR
-               qglUseProgramObjectARB(0);
-       }
-       else
-               Con_Printf("permutation%s failed for shader %s, some features may not work properly!\n", permutationname, "glsl/light");
-       if (fragstring)
-               Mem_Free(fragstring);
-       if (vertstring)
-               Mem_Free(vertstring);
-       return r_shadow_program_light[permutation];
-}
-
 void r_shadow_start(void)
 {
-       int i;
-       // use half float math where available (speed gain on NVIDIA GFFX and GF6)
-       if (gl_support_half_float)
-               Cvar_SetValue("r_shadow_glsl_usehalffloat", 1);
        // allocate vertex processing arrays
        numcubemaps = 0;
        r_shadow_attenuation2dtexture = NULL;
@@ -577,25 +284,11 @@ void r_shadow_start(void)
        r_shadow_buffer_numsurfacepvsbytes = 0;
        r_shadow_buffer_surfacepvs = NULL;
        r_shadow_buffer_surfacelist = NULL;
-       for (i = 0;i < SHADERPERMUTATION_COUNT;i++)
-       {
-               r_shadow_program_compiledlight[i] = false;
-               r_shadow_program_light[i] = 0;
-       }
 }
 
 void r_shadow_shutdown(void)
 {
-       int i;
        R_Shadow_UncompileWorldLights();
-       for (i = 0;i < SHADERPERMUTATION_COUNT;i++)
-       {
-               if (r_shadow_program_light[i])
-               {
-                       GL_Backend_FreeProgram(r_shadow_program_light[i]);
-                       r_shadow_program_light[i] = 0;
-               }
-       }
        numcubemaps = 0;
        r_shadow_attenuation2dtexture = NULL;
        r_shadow_attenuation3dtexture = NULL;
@@ -667,12 +360,6 @@ void R_Shadow_Help_f(void)
 "r_shadow_realtime_world_shadows : cast shadows from world lights\n"
 "r_shadow_realtime_world_compile : compile surface/visibility information\n"
 "r_shadow_realtime_world_compileshadow : compile shadow geometry\n"
-"r_shadow_glsl : use OpenGL Shading Language for lighting\n"
-"r_shadow_glsl_offsetmapping : enables Offset Mapping bumpmap enhancement\n"
-"r_shadow_glsl_offsetmapping_scale : controls depth of Offset Mapping\n"
-"r_shadow_glsl_offsetmapping_bias : should be negative half of scale\n"
-"r_shadow_glsl_usehalffloat : use lower quality lighting\n"
-"r_shadow_glsl_surfacenormalize : makes bumpmapping slightly higher quality\n"
 "r_shadow_scissor : use scissor optimization\n"
 "r_shadow_shadow_polygonfactor : nudge shadow volumes closer/further\n"
 "r_shadow_shadow_polygonoffset : nudge shadow volumes closer/further\n"
@@ -710,12 +397,6 @@ void R_Shadow_Init(void)
        Cvar_RegisterVariable(&r_shadow_shadow_polygonfactor);
        Cvar_RegisterVariable(&r_shadow_shadow_polygonoffset);
        Cvar_RegisterVariable(&r_shadow_texture3d);
-       Cvar_RegisterVariable(&r_shadow_glsl);
-       Cvar_RegisterVariable(&r_shadow_glsl_offsetmapping);
-       Cvar_RegisterVariable(&r_shadow_glsl_offsetmapping_scale);
-       Cvar_RegisterVariable(&r_shadow_glsl_offsetmapping_bias);
-       Cvar_RegisterVariable(&r_shadow_glsl_usehalffloat);
-       Cvar_RegisterVariable(&r_shadow_glsl_surfacenormalize);
        Cvar_RegisterVariable(&gl_ext_stenciltwoside);
        if (gamemode == GAME_TENEBRAE)
        {
@@ -1114,9 +795,6 @@ matrix4x4_t r_shadow_entitytoattenuationxyz;
 // this transforms only the Z to S, and T is always 0.5
 matrix4x4_t r_shadow_entitytoattenuationz;
 
-static int r_shadow_lightpermutation;
-static int r_shadow_lightprog;
-
 void R_Shadow_RenderMode_Begin(void)
 {
        rmeshstate_t m;
@@ -1146,7 +824,7 @@ void R_Shadow_RenderMode_Begin(void)
        else
                r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_STENCIL;
 
-       if (r_shadow_glsl.integer && gl_support_fragment_shader)
+       if (r_glsl.integer && gl_support_fragment_shader)
                r_shadow_lightingrendermode = R_SHADOW_RENDERMODE_LIGHT_GLSL;
        else if (gl_dot3arb && gl_texturecubemap && r_textureunits.integer >= 2 && gl_combine.integer && gl_stencil)
                r_shadow_lightingrendermode = R_SHADOW_RENDERMODE_LIGHT_DOT3;
@@ -1615,85 +1293,7 @@ static void R_Shadow_RenderSurfacesLighting_Light_GLSL(const entity_render_t *en
 {
        // ARB2 GLSL shader path (GFFX5200, Radeon 9500)
        int surfacelistindex;
-       // 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
-       r_shadow_lightpermutation = 0;
-       if (fogenabled)
-               r_shadow_lightpermutation |= SHADERPERMUTATION_FOG;
-       if ((dopants || doshirt))
-               r_shadow_lightpermutation |= SHADERPERMUTATION_COLORMAPPING;
-       if (specularscale > 0)
-               r_shadow_lightpermutation |= SHADERPERMUTATION_SPECULAR;
-       if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
-               r_shadow_lightpermutation |= SHADERPERMUTATION_CUBEFILTER;
-       if (r_shadow_glsl_offsetmapping.integer)
-               r_shadow_lightpermutation |= SHADERPERMUTATION_OFFSETMAPPING;
-       if (r_shadow_glsl_surfacenormalize.integer)
-               r_shadow_lightpermutation |= SHADERPERMUTATION_SURFACENORMALIZE;
-       if (r_shadow_glsl_usehalffloat.integer)
-               r_shadow_lightpermutation |= SHADERPERMUTATION_GEFORCEFX;
-       r_shadow_lightprog = r_shadow_program_light[r_shadow_lightpermutation];
-       if (!r_shadow_lightprog)
-       {
-               if (!r_shadow_program_compiledlight[r_shadow_lightpermutation])
-                       r_shadow_lightprog = R_Shadow_CompileShaderPermutation(r_shadow_lightpermutation);
-               if (!r_shadow_lightprog)
-               {
-                       // remove features until we find a valid permutation
-                       int i;
-                       for (i = SHADERPERMUTATION_COUNT-1;;i>>=1)
-                       {
-                               // reduce i more quickly whenever it would not remove any bits
-                               if (r_shadow_lightpermutation < i)
-                                       continue;
-                               r_shadow_lightpermutation &= i;
-                               if (!r_shadow_program_compiledlight[r_shadow_lightpermutation])
-                                       R_Shadow_CompileShaderPermutation(r_shadow_lightpermutation);
-                               r_shadow_lightprog = r_shadow_program_light[r_shadow_lightpermutation];
-                               if (r_shadow_lightprog)
-                                       break;
-                               if (!i)
-                                       return; // utterly failed
-                       }
-               }
-       }
-       qglUseProgramObjectARB(r_shadow_lightprog);CHECKGLERROR
-       R_Mesh_TexMatrix(0, &texture->currenttexmatrix);
-       R_Mesh_TexMatrix(3, &r_shadow_entitytolight);
-       R_Mesh_TexBind(0, R_GetTexture(normalmaptexture));
-       R_Mesh_TexBind(1, R_GetTexture(basetexture));
-       qglUniform3fARB(qglGetUniformLocationARB(r_shadow_lightprog, "LightPosition"), r_shadow_entitylightorigin[0], r_shadow_entitylightorigin[1], r_shadow_entitylightorigin[2]);CHECKGLERROR
-       if (r_shadow_lightpermutation & (SHADERPERMUTATION_SPECULAR | SHADERPERMUTATION_FOG | SHADERPERMUTATION_OFFSETMAPPING))
-       {
-               qglUniform3fARB(qglGetUniformLocationARB(r_shadow_lightprog, "EyePosition"), r_shadow_entityeyeorigin[0], r_shadow_entityeyeorigin[1], r_shadow_entityeyeorigin[2]);CHECKGLERROR
-       }
-       qglUniform3fARB(qglGetUniformLocationARB(r_shadow_lightprog, "LightColor"), lightcolorbase[0], lightcolorbase[1], lightcolorbase[2]);CHECKGLERROR
-       if (r_shadow_lightpermutation & SHADERPERMUTATION_COLORMAPPING)
-       {
-               R_Mesh_TexBind(5, R_GetTexture(pantstexture));
-               R_Mesh_TexBind(6, R_GetTexture(shirttexture));
-               qglUniform3fARB(qglGetUniformLocationARB(r_shadow_lightprog, "Color_Pants"), ent->colormap_pantscolor[0], ent->colormap_pantscolor[1], ent->colormap_pantscolor[2]);CHECKGLERROR
-               qglUniform3fARB(qglGetUniformLocationARB(r_shadow_lightprog, "Color_Shirt"), ent->colormap_shirtcolor[0], ent->colormap_shirtcolor[1], ent->colormap_shirtcolor[2]);CHECKGLERROR
-       }
-       if (r_shadow_lightpermutation & SHADERPERMUTATION_FOG)
-       {
-               qglUniform1fARB(qglGetUniformLocationARB(r_shadow_lightprog, "FogRangeRecip"), fograngerecip);CHECKGLERROR
-       }
-       qglUniform1fARB(qglGetUniformLocationARB(r_shadow_lightprog, "AmbientScale"), r_shadow_rtlight->ambientscale);CHECKGLERROR
-       qglUniform1fARB(qglGetUniformLocationARB(r_shadow_lightprog, "DiffuseScale"), r_shadow_rtlight->diffusescale);CHECKGLERROR
-       if (r_shadow_lightpermutation & SHADERPERMUTATION_SPECULAR)
-       {
-               R_Mesh_TexBind(2, R_GetTexture(glosstexture));
-               qglUniform1fARB(qglGetUniformLocationARB(r_shadow_lightprog, "SpecularPower"), 8);CHECKGLERROR
-               qglUniform1fARB(qglGetUniformLocationARB(r_shadow_lightprog, "SpecularScale"), specularscale);CHECKGLERROR
-       }
-       if (r_shadow_lightpermutation & SHADERPERMUTATION_OFFSETMAPPING)
-       {
-               qglUniform1fARB(qglGetUniformLocationARB(r_shadow_lightprog, "OffsetMapping_Scale"), r_shadow_glsl_offsetmapping_scale.value);CHECKGLERROR
-               qglUniform1fARB(qglGetUniformLocationARB(r_shadow_lightprog, "OffsetMapping_Bias"), r_shadow_glsl_offsetmapping_bias.value);CHECKGLERROR
-       }
+       R_SetupSurfaceShader(ent, texture, lightcolorbase, lightcolorpants, lightcolorshirt, basetexture, pantstexture, shirttexture, normalmaptexture, glosstexture, specularscale, dopants, doshirt);
        for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++)
        {
                const msurface_t *surface = surfacelist[surfacelistindex];
index 6a9b3f7..bc0dde0 100644 (file)
--- a/render.h
+++ b/render.h
@@ -221,6 +221,13 @@ extern cvar_t r_render;
 extern cvar_t r_waterwarp;
 
 extern cvar_t r_textureunits;
+extern cvar_t r_glsl;
+extern cvar_t r_glsl_offsetmapping;
+extern cvar_t r_glsl_offsetmapping_scale;
+extern cvar_t r_glsl_offsetmapping_bias;
+extern cvar_t r_glsl_usehalffloat;
+extern cvar_t r_glsl_surfacenormalize;
+
 extern cvar_t gl_polyblend;
 extern cvar_t gl_dither;
 
@@ -258,5 +265,55 @@ void R_UpdateAllTextureInfo(entity_render_t *ent);
 void R_QueueTextureSurfaceList(entity_render_t *ent, struct texture_s *texture, int texturenumsurfaces, const struct msurface_s **texturesurfacelist, const vec3_t modelorg);
 void R_DrawSurfaces(entity_render_t *ent, qboolean skysurfaces);
 
+#define SHADERPERMUTATION_DELUXEMAPPING (1<<0) // (lightmap) use directional pixel shading
+#define SHADERPERMUTATION_LIGHTSOURCE (1<<1) // (lightsource) indicates this is lightsource rendering mode
+#define SHADERPERMUTATION_FOG (1<<2) // tint the color by fog color or black if using additive blend mode
+#define SHADERPERMUTATION_COLORMAPPING (1<<3) // indicates this is a colormapped skin
+#define SHADERPERMUTATION_SPECULAR (1<<4) // (lightsource or deluxemapping) render specular effects
+#define SHADERPERMUTATION_CUBEFILTER (1<<5) // (lightsource) use cubemap light filter
+#define SHADERPERMUTATION_OFFSETMAPPING (1<<6) // adjust texcoords to roughly simulate a displacement mapped surface
+#define SHADERPERMUTATION_SURFACENORMALIZE (1<<7) // (lightsource or deluxemapping) improved bumpmapping
+#define SHADERPERMUTATION_GEFORCEFX (1<<8) // use half vector types if available (NVIDIA specific)
+#define SHADERPERMUTATION_COUNT (1<<9) // how many permutations are possible
+
+typedef struct r_glsl_permutation_s
+{
+       // indicates if we have tried compiling this permutation already
+       qboolean compiled;
+       // 0 if compilation failed
+       int program;
+       int loc_Texture_Normal;
+       int loc_Texture_Color;
+       int loc_Texture_Gloss;
+       int loc_Texture_Cube;
+       int loc_Texture_FogMask;
+       int loc_Texture_Pants;
+       int loc_Texture_Shirt;
+       int loc_Texture_Lightmap;
+       int loc_Texture_Deluxemap;
+       int loc_FogColor;
+       int loc_LightPosition;
+       int loc_EyePosition;
+       int loc_LightColor;
+       int loc_Color_Pants;
+       int loc_Color_Shirt;
+       int loc_FogRangeRecip;
+       int loc_AmbientScale;
+       int loc_DiffuseScale;
+       int loc_SpecularScale;
+       int loc_SpecularPower;
+       int loc_OffsetMapping_Scale;
+       int loc_OffsetMapping_Bias;
+}
+r_glsl_permutation_t;
+
+// information about each possible shader permutation
+extern r_glsl_permutation_t r_glsl_permutations[SHADERPERMUTATION_COUNT];
+// currently selected permutation
+r_glsl_permutation_t *r_glsl_permutation;
+
+void R_GLSL_CompilePermutation(int permutation);
+void R_SetupSurfaceShader(const entity_render_t *ent, const texture_t *texture, 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);
+
 #endif