GL_Backend_ResetState();
}
-unsigned int GL_Backend_CompileProgram(int vertexstrings_count, const char **vertexstrings_list, int fragmentstrings_count, const char **fragmentstrings_list)
+qboolean GL_Backend_CompileShader(int programobject, GLenum shadertypeenum, const char *shadertype, int numstrings, const char **strings)
{
- GLint vertexshadercompiled, fragmentshadercompiled, programlinked;
- GLuint vertexshaderobject, fragmentshaderobject, programobject = 0;
+ int shaderobject;
+ int shadercompiled;
char compilelog[MAX_INPUTLINE];
+ shaderobject = qglCreateShaderObjectARB(shadertypeenum);CHECKGLERROR
+ if (!shaderobject)
+ return false;
+ qglShaderSourceARB(shaderobject, numstrings, strings, NULL);CHECKGLERROR
+ qglCompileShaderARB(shaderobject);CHECKGLERROR
+ qglGetObjectParameterivARB(shaderobject, GL_OBJECT_COMPILE_STATUS_ARB, &shadercompiled);CHECKGLERROR
+ qglGetInfoLogARB(shaderobject, sizeof(compilelog), NULL, compilelog);CHECKGLERROR
+ if (compilelog[0])
+ Con_DPrintf("%s shader compile log:\n%s\n", shadertype, compilelog);
+ if (!shadercompiled)
+ {
+ qglDeleteObjectARB(shaderobject);CHECKGLERROR
+ return false;
+ }
+ qglAttachObjectARB(programobject, shaderobject);CHECKGLERROR
+ qglDeleteObjectARB(shaderobject);CHECKGLERROR
+ return true;
+}
+
+unsigned int GL_Backend_CompileProgram(int vertexstrings_count, const char **vertexstrings_list, int geometrystrings_count, const char **geometrystrings_list, int fragmentstrings_count, const char **fragmentstrings_list)
+{
+ GLint programlinked;
+ GLuint programobject = 0;
+ char linklog[MAX_INPUTLINE];
CHECKGLERROR
programobject = qglCreateProgramObjectARB();CHECKGLERROR
if (!programobject)
return 0;
- if (developer.integer >= 100)
- {
- int i;
- Con_Printf("Compiling shader:\n");
- if (vertexstrings_count)
- {
- Con_Printf("------ VERTEX SHADER ------\n");
- for (i = 0;i < vertexstrings_count;i++)
- Con_Print(vertexstrings_list[i]);
- Con_Print("\n");
- }
- if (fragmentstrings_count)
- {
- Con_Printf("------ FRAGMENT SHADER ------\n");
- for (i = 0;i < fragmentstrings_count;i++)
- Con_Print(fragmentstrings_list[i]);
- Con_Print("\n");
- }
- }
+ if (vertexstrings_count && !GL_Backend_CompileShader(programobject, GL_VERTEX_SHADER_ARB, "vertex", vertexstrings_count, vertexstrings_list))
+ goto cleanup;
- if (vertexstrings_count)
- {
- vertexshaderobject = qglCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);CHECKGLERROR
- if (!vertexshaderobject)
- {
- qglDeleteObjectARB(programobject);
- CHECKGLERROR
- return 0;
- }
- qglShaderSourceARB(vertexshaderobject, vertexstrings_count, vertexstrings_list, NULL);CHECKGLERROR
- qglCompileShaderARB(vertexshaderobject);CHECKGLERROR
- qglGetObjectParameterivARB(vertexshaderobject, GL_OBJECT_COMPILE_STATUS_ARB, &vertexshadercompiled);CHECKGLERROR
- qglGetInfoLogARB(vertexshaderobject, sizeof(compilelog), NULL, compilelog);CHECKGLERROR
- if (compilelog[0])
- Con_DPrintf("vertex shader compile log:\n%s\n", compilelog);
- if (!vertexshadercompiled)
- {
- qglDeleteObjectARB(programobject);CHECKGLERROR
- qglDeleteObjectARB(vertexshaderobject);CHECKGLERROR
- return 0;
- }
- qglAttachObjectARB(programobject, vertexshaderobject);CHECKGLERROR
- qglDeleteObjectARB(vertexshaderobject);CHECKGLERROR
- }
+#ifdef GL_GEOMETRY_SHADER_ARB
+ if (geometrystrings_count && !GL_Backend_CompileShader(programobject, GL_GEOMETRY_SHADER_ARB, "geometry", geometrystrings_count, geometrystrings_list))
+ goto cleanup;
+#endif
- if (fragmentstrings_count)
- {
- fragmentshaderobject = qglCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);CHECKGLERROR
- if (!fragmentshaderobject)
- {
- qglDeleteObjectARB(programobject);CHECKGLERROR
- return 0;
- }
- qglShaderSourceARB(fragmentshaderobject, fragmentstrings_count, fragmentstrings_list, NULL);CHECKGLERROR
- qglCompileShaderARB(fragmentshaderobject);CHECKGLERROR
- qglGetObjectParameterivARB(fragmentshaderobject, GL_OBJECT_COMPILE_STATUS_ARB, &fragmentshadercompiled);CHECKGLERROR
- qglGetInfoLogARB(fragmentshaderobject, sizeof(compilelog), NULL, compilelog);CHECKGLERROR
- if (compilelog[0])
- Con_DPrintf("fragment shader compile log:\n%s\n", compilelog);
- if (!fragmentshadercompiled)
- {
- qglDeleteObjectARB(programobject);CHECKGLERROR
- qglDeleteObjectARB(fragmentshaderobject);CHECKGLERROR
- return 0;
- }
- qglAttachObjectARB(programobject, fragmentshaderobject);CHECKGLERROR
- qglDeleteObjectARB(fragmentshaderobject);CHECKGLERROR
- }
+ if (fragmentstrings_count && !GL_Backend_CompileShader(programobject, GL_FRAGMENT_SHADER_ARB, "fragment", fragmentstrings_count, fragmentstrings_list))
+ goto cleanup;
qglLinkProgramARB(programobject);CHECKGLERROR
qglGetObjectParameterivARB(programobject, GL_OBJECT_LINK_STATUS_ARB, &programlinked);CHECKGLERROR
- qglGetInfoLogARB(programobject, sizeof(compilelog), NULL, compilelog);CHECKGLERROR
- if (compilelog[0])
+ qglGetInfoLogARB(programobject, sizeof(linklog), NULL, linklog);CHECKGLERROR
+ if (linklog[0])
{
- Con_DPrintf("program link log:\n%s\n", compilelog);
+ Con_DPrintf("program link log:\n%s\n", linklog);
// software vertex shader is ok but software fragment shader is WAY
// too slow, fail program if so.
// NOTE: this string might be ATI specific, but that's ok because the
// ATI R300 chip (Radeon 9500-9800/X300) is the most likely to use a
// software fragment shader due to low instruction and dependent
// texture limits.
- if (strstr(compilelog, "fragment shader will run in software"))
+ if (strstr(linklog, "fragment shader will run in software"))
programlinked = false;
}
if (!programlinked)
- {
- qglDeleteObjectARB(programobject);CHECKGLERROR
- return 0;
- }
- CHECKGLERROR
+ goto cleanup;
return programobject;
+cleanup:
+ qglDeleteObjectARB(programobject);CHECKGLERROR
+ return 0;
}
void GL_Backend_FreeProgram(unsigned int prog)
{NULL, NULL}
};
-void R_GLSL_CompilePermutation(int permutation)
+void R_GLSL_CompilePermutation(const char *filename, int permutation)
{
int i;
- r_glsl_permutation_t *p = r_glsl_permutations + permutation;
+ qboolean shaderfound;
+ r_glsl_permutation_t *p = r_glsl_permutations + (permutation & SHADERPERMUTATION_COUNTMASK);
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];
char permutationname[256];
if (p->compiled)
return;
p->compiled = true;
+ p->program = 0;
vertstrings_list[0] = "#define VERTEX_SHADER\n";
+ geomstrings_list[0] = "#define GEOMETRY_SHADER\n";
fragstrings_list[0] = "#define FRAGMENT_SHADER\n";
vertstrings_count = 1;
+ geomstrings_count = 1;
fragstrings_count = 1;
permutationname[0] = 0;
for (i = 0;permutationinfo[i][0];i++)
if (permutation & (1<<i))
{
vertstrings_list[vertstrings_count++] = permutationinfo[i][0];
+ geomstrings_list[geomstrings_count++] = permutationinfo[i][0];
fragstrings_list[fragstrings_count++] = permutationinfo[i][0];
strlcat(permutationname, permutationinfo[i][1], sizeof(permutationname));
}
{
// keep line numbers correct
vertstrings_list[vertstrings_count++] = "\n";
+ geomstrings_list[geomstrings_count++] = "\n";
fragstrings_list[fragstrings_count++] = "\n";
}
}
- shaderstring = (char *)FS_LoadFile("glsl/default.glsl", r_main_mempool, false, NULL);
+ shaderstring = (char *)FS_LoadFile(filename, r_main_mempool, false, NULL);
+ shaderfound = false;
if (shaderstring)
{
- Con_DPrintf("GLSL shader text loaded from disk\n");
+ Con_DPrintf("GLSL shader text for \"%s\" loaded from disk\n", filename);
vertstrings_list[vertstrings_count++] = shaderstring;
+ geomstrings_list[geomstrings_count++] = shaderstring;
fragstrings_list[fragstrings_count++] = shaderstring;
+ shaderfound = true;
}
- else
+ else if (!strcmp(filename, "glsl/default.glsl"))
{
+ Con_DPrintf("GLSL shader text for \"%s\" loaded from engine\n", filename);
vertstrings_list[vertstrings_count++] = builtinshaderstring;
+ geomstrings_list[geomstrings_count++] = builtinshaderstring;
fragstrings_list[fragstrings_count++] = builtinshaderstring;
- }
- p->program = GL_Backend_CompileProgram(vertstrings_count, vertstrings_list, fragstrings_count, fragstrings_list);
+ shaderfound = true;
+ }
+ // clear any lists that are not needed by this shader
+ if (!(permutation & SHADERPERMUTATION_USES_VERTEXSHADER))
+ vertstrings_count = 0;
+ if (!(permutation & SHADERPERMUTATION_USES_GEOMETRYSHADER))
+ geomstrings_count = 0;
+ if (!(permutation & SHADERPERMUTATION_USES_FRAGMENTSHADER))
+ fragstrings_count = 0;
+ // compile the shader program
+ if (shaderfound && vertstrings_count + geomstrings_count + fragstrings_count)
+ p->program = GL_Backend_CompileProgram(vertstrings_count, vertstrings_list, geomstrings_count, geomstrings_list, fragstrings_count, fragstrings_list);
if (p->program)
{
CHECKGLERROR
qglUseProgramObjectARB(p->program);CHECKGLERROR
+ // look up all the uniform variable names we care about, so we don't
+ // have to look them up every time we set them
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_DiffuseColor = qglGetUniformLocationARB(p->program, "DiffuseColor");
p->loc_SpecularColor = qglGetUniformLocationARB(p->program, "SpecularColor");
p->loc_LightDir = qglGetUniformLocationARB(p->program, "LightDir");
+ // initialize the samplers to refer to the texture units we use
if (p->loc_Texture_Normal >= 0) qglUniform1iARB(p->loc_Texture_Normal, 0);
if (p->loc_Texture_Color >= 0) qglUniform1iARB(p->loc_Texture_Color, 1);
if (p->loc_Texture_Gloss >= 0) qglUniform1iARB(p->loc_Texture_Gloss, 2);
// 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;
r_glsl_permutation = NULL;
+ // TODO: implement geometry-shader based shadow volumes someday
if (r_shadow_rtlight)
{
- permutation |= SHADERPERMUTATION_MODE_LIGHTSOURCE;
+ // 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 (specularscale > 0)
+ permutation |= SHADERPERMUTATION_SPECULAR;
+ if (r_refdef.fogenabled)
+ permutation |= SHADERPERMUTATION_FOG;
+ if (rsurface_texture->colormapping)
+ permutation |= SHADERPERMUTATION_COLORMAPPING;
+ if (r_glsl_offsetmapping.integer)
+ {
+ permutation |= SHADERPERMUTATION_OFFSETMAPPING;
+ if (r_glsl_offsetmapping_reliefmapping.integer)
+ permutation |= SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING;
+ }
}
- else
+ else if (rsurface_texture->currentmaterialflags & MATERIALFLAG_FULLBRIGHT)
{
- if (!(rsurface_texture->currentmaterialflags & MATERIALFLAG_FULLBRIGHT))
+ // bright unshaded geometry
+ shaderfilename = "glsl/default.glsl";
+ permutation = SHADERPERMUTATION_USES_VERTEXSHADER | SHADERPERMUTATION_USES_FRAGMENTSHADER;
+ if (rsurface_texture->currentskinframe->glow)
+ permutation |= SHADERPERMUTATION_GLOW;
+ if (r_refdef.fogenabled)
+ permutation |= SHADERPERMUTATION_FOG;
+ if (rsurface_texture->colormapping)
+ permutation |= SHADERPERMUTATION_COLORMAPPING;
+ if (r_glsl_offsetmapping.integer)
{
- if (modellighting)
- permutation |= SHADERPERMUTATION_MODE_LIGHTDIRECTION;
- else if (r_glsl_deluxemapping.integer >= 1 && rsurface_lightmaptexture)
- {
- if (r_refdef.worldmodel && r_refdef.worldmodel->brushq3.deluxemapping)
- {
- if (r_refdef.worldmodel->brushq3.deluxemapping_modelspace)
- permutation |= SHADERPERMUTATION_MODE_LIGHTDIRECTIONMAP_MODELSPACE;
- else
- permutation |= SHADERPERMUTATION_MODE_LIGHTDIRECTIONMAP_TANGENTSPACE;
- }
- else if (r_glsl_deluxemapping.integer >= 2) // fake mode
- permutation |= SHADERPERMUTATION_MODE_LIGHTDIRECTIONMAP_TANGENTSPACE;
- }
+ permutation |= SHADERPERMUTATION_OFFSETMAPPING;
+ if (r_glsl_offsetmapping_reliefmapping.integer)
+ permutation |= SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING;
}
+ }
+ else if (modellighting)
+ {
+ // directional model lighting
+ shaderfilename = "glsl/default.glsl";
+ permutation = SHADERPERMUTATION_USES_VERTEXSHADER | SHADERPERMUTATION_USES_FRAGMENTSHADER;
+ permutation |= SHADERPERMUTATION_MODE_LIGHTDIRECTION;
if (rsurface_texture->currentskinframe->glow)
permutation |= SHADERPERMUTATION_GLOW;
+ if (specularscale > 0)
+ permutation |= SHADERPERMUTATION_SPECULAR;
+ if (r_refdef.fogenabled)
+ permutation |= SHADERPERMUTATION_FOG;
+ if (rsurface_texture->colormapping)
+ permutation |= SHADERPERMUTATION_COLORMAPPING;
+ if (r_glsl_offsetmapping.integer)
+ {
+ permutation |= SHADERPERMUTATION_OFFSETMAPPING;
+ if (r_glsl_offsetmapping_reliefmapping.integer)
+ permutation |= SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING;
+ }
}
- if (specularscale > 0)
- permutation |= SHADERPERMUTATION_SPECULAR;
- if (r_refdef.fogenabled)
- permutation |= SHADERPERMUTATION_FOG;
- if (rsurface_texture->colormapping)
- permutation |= SHADERPERMUTATION_COLORMAPPING;
- if (r_glsl_offsetmapping.integer)
+ else
{
- permutation |= SHADERPERMUTATION_OFFSETMAPPING;
- if (r_glsl_offsetmapping_reliefmapping.integer)
- permutation |= SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING;
+ // lightmapped wall
+ shaderfilename = "glsl/default.glsl";
+ permutation = SHADERPERMUTATION_USES_VERTEXSHADER | SHADERPERMUTATION_USES_FRAGMENTSHADER;
+ if (r_glsl_deluxemapping.integer >= 1 && rsurface_lightmaptexture && r_refdef.worldmodel && r_refdef.worldmodel->brushq3.deluxemapping)
+ {
+ // deluxemapping (light direction texture)
+ if (rsurface_lightmaptexture && r_refdef.worldmodel && r_refdef.worldmodel->brushq3.deluxemapping && r_refdef.worldmodel->brushq3.deluxemapping_modelspace)
+ permutation |= SHADERPERMUTATION_MODE_LIGHTDIRECTIONMAP_MODELSPACE;
+ else
+ permutation |= SHADERPERMUTATION_MODE_LIGHTDIRECTIONMAP_TANGENTSPACE;
+ if (specularscale > 0)
+ permutation |= SHADERPERMUTATION_SPECULAR;
+ }
+ else if (r_glsl_deluxemapping.integer >= 2)
+ {
+ // fake deluxemapping (uniform light direction in tangentspace)
+ permutation |= SHADERPERMUTATION_MODE_LIGHTDIRECTIONMAP_TANGENTSPACE;
+ if (specularscale > 0)
+ permutation |= SHADERPERMUTATION_SPECULAR;
+ }
+ else
+ {
+ // ordinary lightmapping
+ permutation |= 0;
+ }
+ if (rsurface_texture->currentskinframe->glow)
+ permutation |= SHADERPERMUTATION_GLOW;
+ if (r_refdef.fogenabled)
+ permutation |= SHADERPERMUTATION_FOG;
+ if (rsurface_texture->colormapping)
+ permutation |= SHADERPERMUTATION_COLORMAPPING;
+ if (r_glsl_offsetmapping.integer)
+ {
+ permutation |= SHADERPERMUTATION_OFFSETMAPPING;
+ if (r_glsl_offsetmapping_reliefmapping.integer)
+ permutation |= SHADERPERMUTATION_OFFSETMAPPING_RELIEFMAPPING;
+ }
}
- if (!r_glsl_permutations[permutation].program)
+ if (!r_glsl_permutations[permutation & SHADERPERMUTATION_COUNTMASK].program)
{
- if (!r_glsl_permutations[permutation].compiled)
- R_GLSL_CompilePermutation(permutation);
- if (!r_glsl_permutations[permutation].program)
+ if (!r_glsl_permutations[permutation & SHADERPERMUTATION_COUNTMASK].compiled)
+ R_GLSL_CompilePermutation(shaderfilename, permutation);
+ if (!r_glsl_permutations[permutation & SHADERPERMUTATION_COUNTMASK].program)
{
// remove features until we find a valid permutation
int i;
if (permutation < i)
continue;
permutation &= i;
- if (!r_glsl_permutations[permutation].compiled)
- R_GLSL_CompilePermutation(permutation);
- if (r_glsl_permutations[permutation].program)
+ if (!r_glsl_permutations[permutation & SHADERPERMUTATION_COUNTMASK].compiled)
+ R_GLSL_CompilePermutation(shaderfilename, permutation);
+ if (r_glsl_permutations[permutation & SHADERPERMUTATION_COUNTMASK].program)
break;
if (!i)
return 0; // utterly failed
}
}
}
- r_glsl_permutation = r_glsl_permutations + permutation;
+ r_glsl_permutation = r_glsl_permutations + (permutation & SHADERPERMUTATION_COUNTMASK);
CHECKGLERROR
qglUseProgramObjectARB(r_glsl_permutation->program);CHECKGLERROR
R_Mesh_TexMatrix(0, &rsurface_texture->currenttexmatrix);
void R_SwitchSurfaceShader(int permutation)
{
- if (r_glsl_permutation != r_glsl_permutations + permutation)
+ if (r_glsl_permutation != r_glsl_permutations + (permutation & SHADERPERMUTATION_COUNTMASK))
{
- r_glsl_permutation = r_glsl_permutations + permutation;
+ r_glsl_permutation = r_glsl_permutations + (permutation & SHADERPERMUTATION_COUNTMASK);
CHECKGLERROR
qglUseProgramObjectARB(r_glsl_permutation->program);
CHECKGLERROR