q3rally/engine/code/renderergl2/tr_glsl.c
Zack Middleton 04ea0f76f8 ioquake3 resync to commit c1ab47a7 from 60dfabbe
Use nanosleep(2) instead of usleep(3)
Makefile: fix compilation on FreeBSD
Update macOS version for GitHub CI
OpenGL2: Add OpenGL ES 2.0+ support
OpenGL2: Add run-time support for unsigned short indexes
OpenGL2: Use CPU vertex animation if too few vertex attributes
Merge pull request #664 from zturtleman/opengles2
OpenGL2: Fix issues running under WebGL
OpenGL2: Fallback to OpenGL ES if OpenGL fails
Add minimal emscripten support
Fix emscripten build directions
Use BASEGAME for emscripten assets
Fix using emscripten 3.1.27+
Allow building for emscripten with "emmake make"
Add -ffast-math for emscripten to match other platforms
Add .vscode and baseq3 to gitignore
Rebuild every target if Makefile changes
ioquake3.html replaces Emscripten-generated HTML shell
Get rid of "Nothing to be done for _.zip" message
Fix build when specifying PLATFORM=emscripten manually instead of using emmake
Silence compiler warnings
Support debug Emscripten build
Speed up GitHub Actions with -j
Add GitHub Actions workflow for web/Emscripten
Add Emscripten to README
Add DEPEND_MAKEFILE to disable rebuild on Makefile edit
Add BUILD_RENDERER_OPENGL1 to disable opengl1
Clean up emscripten in Makefile
Make emscripten --preload-file opt-in
Allow web client to use unzipped QVMs
Customize the web client HTML file
Add support for overriding basegame to web client
Add support for mods to web client
Rename client-config.json based on CLIENTBIN
2024-06-13 15:44:18 -05:00

1655 lines
45 KiB
C

/*
===========================================================================
Copyright (C) 2006-2009 Robert Beckebans <trebor_7@users.sourceforge.net>
This file is part of XreaL source code.
XreaL source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
XreaL source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with XreaL source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
// tr_glsl.c
#include "tr_local.h"
#include "tr_dsa.h"
extern const char *fallbackShader_bokeh_vp;
extern const char *fallbackShader_bokeh_fp;
extern const char *fallbackShader_calclevels4x_vp;
extern const char *fallbackShader_calclevels4x_fp;
extern const char *fallbackShader_depthblur_vp;
extern const char *fallbackShader_depthblur_fp;
extern const char *fallbackShader_dlight_vp;
extern const char *fallbackShader_dlight_fp;
extern const char *fallbackShader_down4x_vp;
extern const char *fallbackShader_down4x_fp;
extern const char *fallbackShader_fogpass_vp;
extern const char *fallbackShader_fogpass_fp;
extern const char *fallbackShader_generic_vp;
extern const char *fallbackShader_generic_fp;
extern const char *fallbackShader_lightall_vp;
extern const char *fallbackShader_lightall_fp;
extern const char *fallbackShader_pshadow_vp;
extern const char *fallbackShader_pshadow_fp;
extern const char *fallbackShader_shadowfill_vp;
extern const char *fallbackShader_shadowfill_fp;
extern const char *fallbackShader_shadowmask_vp;
extern const char *fallbackShader_shadowmask_fp;
extern const char *fallbackShader_ssao_vp;
extern const char *fallbackShader_ssao_fp;
extern const char *fallbackShader_texturecolor_vp;
extern const char *fallbackShader_texturecolor_fp;
extern const char *fallbackShader_tonemap_vp;
extern const char *fallbackShader_tonemap_fp;
typedef struct uniformInfo_s
{
char *name;
int type;
}
uniformInfo_t;
// These must be in the same order as in uniform_t in tr_local.h.
static uniformInfo_t uniformsInfo[] =
{
{ "u_DiffuseMap", GLSL_INT },
{ "u_LightMap", GLSL_INT },
{ "u_NormalMap", GLSL_INT },
{ "u_DeluxeMap", GLSL_INT },
{ "u_SpecularMap", GLSL_INT },
{ "u_TextureMap", GLSL_INT },
{ "u_LevelsMap", GLSL_INT },
{ "u_CubeMap", GLSL_INT },
{ "u_ScreenImageMap", GLSL_INT },
{ "u_ScreenDepthMap", GLSL_INT },
{ "u_ShadowMap", GLSL_INT },
{ "u_ShadowMap2", GLSL_INT },
{ "u_ShadowMap3", GLSL_INT },
{ "u_ShadowMap4", GLSL_INT },
{ "u_ShadowMvp", GLSL_MAT16 },
{ "u_ShadowMvp2", GLSL_MAT16 },
{ "u_ShadowMvp3", GLSL_MAT16 },
{ "u_ShadowMvp4", GLSL_MAT16 },
{ "u_EnableTextures", GLSL_VEC4 },
{ "u_DiffuseTexMatrix0", GLSL_VEC4 },
{ "u_DiffuseTexMatrix1", GLSL_VEC4 },
{ "u_DiffuseTexMatrix2", GLSL_VEC4 },
{ "u_DiffuseTexMatrix3", GLSL_VEC4 },
{ "u_DiffuseTexMatrix4", GLSL_VEC4 },
{ "u_DiffuseTexMatrix5", GLSL_VEC4 },
{ "u_DiffuseTexMatrix6", GLSL_VEC4 },
{ "u_DiffuseTexMatrix7", GLSL_VEC4 },
{ "u_TCGen0", GLSL_INT },
{ "u_TCGen0Vector0", GLSL_VEC3 },
{ "u_TCGen0Vector1", GLSL_VEC3 },
{ "u_DeformGen", GLSL_INT },
{ "u_DeformParams", GLSL_FLOAT5 },
{ "u_ColorGen", GLSL_INT },
{ "u_AlphaGen", GLSL_INT },
{ "u_Color", GLSL_VEC4 },
{ "u_BaseColor", GLSL_VEC4 },
{ "u_VertColor", GLSL_VEC4 },
{ "u_DlightInfo", GLSL_VEC4 },
{ "u_LightForward", GLSL_VEC3 },
{ "u_LightUp", GLSL_VEC3 },
{ "u_LightRight", GLSL_VEC3 },
{ "u_LightOrigin", GLSL_VEC4 },
{ "u_ModelLightDir", GLSL_VEC3 },
{ "u_LightRadius", GLSL_FLOAT },
{ "u_AmbientLight", GLSL_VEC3 },
{ "u_DirectedLight", GLSL_VEC3 },
{ "u_PortalRange", GLSL_FLOAT },
{ "u_FogDistance", GLSL_VEC4 },
{ "u_FogDepth", GLSL_VEC4 },
{ "u_FogEyeT", GLSL_FLOAT },
{ "u_FogColorMask", GLSL_VEC4 },
{ "u_ModelMatrix", GLSL_MAT16 },
{ "u_ModelViewProjectionMatrix", GLSL_MAT16 },
{ "u_Time", GLSL_FLOAT },
{ "u_VertexLerp" , GLSL_FLOAT },
{ "u_NormalScale", GLSL_VEC4 },
{ "u_SpecularScale", GLSL_VEC4 },
{ "u_ViewInfo", GLSL_VEC4 },
{ "u_ViewOrigin", GLSL_VEC3 },
{ "u_LocalViewOrigin", GLSL_VEC3 },
{ "u_ViewForward", GLSL_VEC3 },
{ "u_ViewLeft", GLSL_VEC3 },
{ "u_ViewUp", GLSL_VEC3 },
{ "u_InvTexRes", GLSL_VEC2 },
{ "u_AutoExposureMinMax", GLSL_VEC2 },
{ "u_ToneMinAvgMaxLinear", GLSL_VEC3 },
{ "u_PrimaryLightOrigin", GLSL_VEC4 },
{ "u_PrimaryLightColor", GLSL_VEC3 },
{ "u_PrimaryLightAmbient", GLSL_VEC3 },
{ "u_PrimaryLightRadius", GLSL_FLOAT },
{ "u_CubeMapInfo", GLSL_VEC4 },
{ "u_AlphaTest", GLSL_INT },
{ "u_BoneMatrix", GLSL_MAT16_BONEMATRIX },
};
typedef enum
{
GLSL_PRINTLOG_PROGRAM_INFO,
GLSL_PRINTLOG_SHADER_INFO,
GLSL_PRINTLOG_SHADER_SOURCE
}
glslPrintLog_t;
static void GLSL_PrintLog(GLuint programOrShader, glslPrintLog_t type, qboolean developerOnly)
{
char *msg;
static char msgPart[1024];
int maxLength = 0;
int i;
int printLevel = developerOnly ? PRINT_DEVELOPER : PRINT_ALL;
switch (type)
{
case GLSL_PRINTLOG_PROGRAM_INFO:
ri.Printf(printLevel, "Program info log:\n");
qglGetProgramiv(programOrShader, GL_INFO_LOG_LENGTH, &maxLength);
break;
case GLSL_PRINTLOG_SHADER_INFO:
ri.Printf(printLevel, "Shader info log:\n");
qglGetShaderiv(programOrShader, GL_INFO_LOG_LENGTH, &maxLength);
break;
case GLSL_PRINTLOG_SHADER_SOURCE:
ri.Printf(printLevel, "Shader source:\n");
qglGetShaderiv(programOrShader, GL_SHADER_SOURCE_LENGTH, &maxLength);
break;
}
if (maxLength <= 0)
{
ri.Printf(printLevel, "None.\n");
return;
}
if (maxLength < 1023)
msg = msgPart;
else
msg = ri.Malloc(maxLength);
switch (type)
{
case GLSL_PRINTLOG_PROGRAM_INFO:
qglGetProgramInfoLog(programOrShader, maxLength, &maxLength, msg);
break;
case GLSL_PRINTLOG_SHADER_INFO:
qglGetShaderInfoLog(programOrShader, maxLength, &maxLength, msg);
break;
case GLSL_PRINTLOG_SHADER_SOURCE:
qglGetShaderSource(programOrShader, maxLength, &maxLength, msg);
break;
}
if (maxLength < 1023)
{
msgPart[maxLength + 1] = '\0';
ri.Printf(printLevel, "%s\n", msgPart);
}
else
{
for(i = 0; i < maxLength; i += 1023)
{
Q_strncpyz(msgPart, msg + i, sizeof(msgPart));
ri.Printf(printLevel, "%s", msgPart);
}
ri.Printf(printLevel, "\n");
ri.Free(msg);
}
}
static void GLSL_GetShaderHeader( GLenum shaderType, const GLchar *extra, char *dest, int size )
{
float fbufWidthScale, fbufHeightScale;
dest[0] = '\0';
// HACK: abuse the GLSL preprocessor to turn GLSL 1.20 shaders into 1.30 ones
if(glRefConfig.glslMajorVersion > 1 || (glRefConfig.glslMajorVersion == 1 && glRefConfig.glslMinorVersion >= 30))
{
if (qglesMajorVersion >= 3 && glRefConfig.glslMajorVersion >= 3)
Q_strcat(dest, size, "#version 300 es\n");
else if (glRefConfig.glslMajorVersion > 1 || (glRefConfig.glslMajorVersion == 1 && glRefConfig.glslMinorVersion >= 50))
Q_strcat(dest, size, "#version 150\n");
else
Q_strcat(dest, size, "#version 130\n");
// `extra' may contain #extension which must be directly after #version
if (extra)
{
Q_strcat(dest, size, extra);
}
if (qglesMajorVersion >= 2)
{
Q_strcat(dest, size, "precision mediump float;\n");
Q_strcat(dest, size, "precision mediump sampler2DShadow;\n");
}
if(shaderType == GL_VERTEX_SHADER)
{
Q_strcat(dest, size, "#define attribute in\n");
Q_strcat(dest, size, "#define varying out\n");
}
else
{
Q_strcat(dest, size, "#define varying in\n");
Q_strcat(dest, size, "out vec4 out_Color;\n");
Q_strcat(dest, size, "#define gl_FragColor out_Color\n");
Q_strcat(dest, size, "#define texture2D texture\n");
Q_strcat(dest, size, "#define textureCubeLod textureLod\n");
Q_strcat(dest, size, "#define shadow2D texture\n");
}
}
else
{
if (qglesMajorVersion >= 2)
{
Q_strcat(dest, size, "#version 100\n");
if (extra)
{
Q_strcat(dest, size, extra);
}
Q_strcat(dest, size, "precision mediump float;\n");
if (glRefConfig.shadowSamplers)
{
Q_strcat(dest, size, "precision mediump sampler2DShadow;\n");
Q_strcat(dest, size, "#define shadow2D(a,b) shadow2DEXT(a,b)\n");
}
}
else
{
Q_strcat(dest, size, "#version 120\n");
if (extra)
{
Q_strcat(dest, size, extra);
}
Q_strcat(dest, size, "#define shadow2D(a,b) shadow2D(a,b).r\n");
}
}
// HACK: add some macros to avoid extra uniforms and save speed and code maintenance
//Q_strcat(dest, size,
// va("#ifndef r_SpecularExponent\n#define r_SpecularExponent %f\n#endif\n", r_specularExponent->value));
//Q_strcat(dest, size,
// va("#ifndef r_SpecularScale\n#define r_SpecularScale %f\n#endif\n", r_specularScale->value));
//Q_strcat(dest, size,
// va("#ifndef r_NormalScale\n#define r_NormalScale %f\n#endif\n", r_normalScale->value));
Q_strcat(dest, size, "#ifndef M_PI\n#define M_PI 3.14159265358979323846\n#endif\n");
//Q_strcat(dest, size, va("#ifndef MAX_SHADOWMAPS\n#define MAX_SHADOWMAPS %i\n#endif\n", MAX_SHADOWMAPS));
Q_strcat(dest, size,
va("#ifndef deformGen_t\n"
"#define deformGen_t\n"
"#define DGEN_WAVE_SIN %i\n"
"#define DGEN_WAVE_SQUARE %i\n"
"#define DGEN_WAVE_TRIANGLE %i\n"
"#define DGEN_WAVE_SAWTOOTH %i\n"
"#define DGEN_WAVE_INVERSE_SAWTOOTH %i\n"
"#define DGEN_BULGE %i\n"
"#define DGEN_MOVE %i\n"
"#endif\n",
DGEN_WAVE_SIN,
DGEN_WAVE_SQUARE,
DGEN_WAVE_TRIANGLE,
DGEN_WAVE_SAWTOOTH,
DGEN_WAVE_INVERSE_SAWTOOTH,
DGEN_BULGE,
DGEN_MOVE));
Q_strcat(dest, size,
va("#ifndef tcGen_t\n"
"#define tcGen_t\n"
"#define TCGEN_LIGHTMAP %i\n"
"#define TCGEN_TEXTURE %i\n"
"#define TCGEN_ENVIRONMENT_MAPPED %i\n"
"#define TCGEN_FOG %i\n"
"#define TCGEN_VECTOR %i\n"
"#endif\n",
TCGEN_LIGHTMAP,
TCGEN_TEXTURE,
TCGEN_ENVIRONMENT_MAPPED,
TCGEN_FOG,
TCGEN_VECTOR));
Q_strcat(dest, size,
va("#ifndef colorGen_t\n"
"#define colorGen_t\n"
"#define CGEN_LIGHTING_DIFFUSE %i\n"
"#endif\n",
CGEN_LIGHTING_DIFFUSE));
Q_strcat(dest, size,
va("#ifndef alphaGen_t\n"
"#define alphaGen_t\n"
"#define AGEN_LIGHTING_SPECULAR %i\n"
"#define AGEN_PORTAL %i\n"
"#endif\n",
AGEN_LIGHTING_SPECULAR,
AGEN_PORTAL));
fbufWidthScale = 1.0f / ((float)glConfig.vidWidth);
fbufHeightScale = 1.0f / ((float)glConfig.vidHeight);
Q_strcat(dest, size,
va("#ifndef r_FBufScale\n#define r_FBufScale vec2(%f, %f)\n#endif\n", fbufWidthScale, fbufHeightScale));
if (r_pbr->integer)
Q_strcat(dest, size, "#define USE_PBR\n");
if (r_cubeMapping->integer)
{
int cubeMipSize = r_cubemapSize->integer;
int numRoughnessMips = 0;
while (cubeMipSize)
{
cubeMipSize >>= 1;
numRoughnessMips++;
}
numRoughnessMips = MAX(1, numRoughnessMips - 2);
Q_strcat(dest, size, va("#define ROUGHNESS_MIPS float(%d)\n", numRoughnessMips));
}
// OK we added a lot of stuff but if we do something bad in the GLSL shaders then we want the proper line
// so we have to reset the line counting
Q_strcat(dest, size, "#line 0\n");
}
static int GLSL_CompileGPUShader(GLuint program, GLuint *prevShader, const GLchar *buffer, int size, GLenum shaderType)
{
GLint compiled;
GLuint shader;
shader = qglCreateShader(shaderType);
qglShaderSource(shader, 1, (const GLchar **)&buffer, &size);
// compile shader
qglCompileShader(shader);
// check if shader compiled
qglGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
if(!compiled)
{
GLSL_PrintLog(shader, GLSL_PRINTLOG_SHADER_SOURCE, qfalse);
GLSL_PrintLog(shader, GLSL_PRINTLOG_SHADER_INFO, qfalse);
ri.Error(ERR_DROP, "Couldn't compile shader");
return 0;
}
if (*prevShader)
{
qglDetachShader(program, *prevShader);
qglDeleteShader(*prevShader);
}
// attach shader to program
qglAttachShader(program, shader);
*prevShader = shader;
return 1;
}
static int GLSL_LoadGPUShaderText(const char *name, const char *fallback,
GLenum shaderType, char *dest, int destSize)
{
char filename[MAX_QPATH];
GLchar *buffer = NULL;
const GLchar *shaderText = NULL;
int size;
int result;
if(shaderType == GL_VERTEX_SHADER)
{
Com_sprintf(filename, sizeof(filename), "glsl/%s_vp.glsl", name);
}
else
{
Com_sprintf(filename, sizeof(filename), "glsl/%s_fp.glsl", name);
}
if ( r_externalGLSL->integer ) {
size = ri.FS_ReadFile(filename, (void **)&buffer);
} else {
size = 0;
buffer = NULL;
}
if(!buffer)
{
if (fallback)
{
ri.Printf(PRINT_DEVELOPER, "...loading built-in '%s'\n", filename);
shaderText = fallback;
size = strlen(shaderText);
}
else
{
ri.Printf(PRINT_DEVELOPER, "couldn't load '%s'\n", filename);
return 0;
}
}
else
{
ri.Printf(PRINT_DEVELOPER, "...loading '%s'\n", filename);
shaderText = buffer;
}
if (size > destSize)
{
result = 0;
}
else
{
Q_strncpyz(dest, shaderText, size + 1);
result = 1;
}
if (buffer)
{
ri.FS_FreeFile(buffer);
}
return result;
}
static void GLSL_LinkProgram(GLuint program)
{
GLint linked;
qglLinkProgram(program);
qglGetProgramiv(program, GL_LINK_STATUS, &linked);
if(!linked)
{
GLSL_PrintLog(program, GLSL_PRINTLOG_PROGRAM_INFO, qfalse);
ri.Error(ERR_DROP, "shaders failed to link");
}
}
static void GLSL_ShowProgramUniforms(GLuint program)
{
int i, count, size;
GLenum type;
char uniformName[1000];
// query the number of active uniforms
qglGetProgramiv(program, GL_ACTIVE_UNIFORMS, &count);
// Loop over each of the active uniforms, and set their value
for(i = 0; i < count; i++)
{
qglGetActiveUniform(program, i, sizeof(uniformName), NULL, &size, &type, uniformName);
ri.Printf(PRINT_DEVELOPER, "active uniform: '%s'\n", uniformName);
}
}
static int GLSL_InitGPUShader2(shaderProgram_t * program, const char *name, int attribs, const char *vpCode, const char *fpCode)
{
ri.Printf(PRINT_DEVELOPER, "------- GPU shader -------\n");
if(strlen(name) >= MAX_QPATH)
{
ri.Error(ERR_DROP, "GLSL_InitGPUShader2: \"%s\" is too long", name);
}
Q_strncpyz(program->name, name, sizeof(program->name));
program->program = qglCreateProgram();
program->attribs = attribs;
if (!(GLSL_CompileGPUShader(program->program, &program->vertexShader, vpCode, strlen(vpCode), GL_VERTEX_SHADER)))
{
ri.Printf(PRINT_ALL, "GLSL_InitGPUShader2: Unable to load \"%s\" as GL_VERTEX_SHADER\n", name);
qglDeleteProgram(program->program);
return 0;
}
if(fpCode)
{
if(!(GLSL_CompileGPUShader(program->program, &program->fragmentShader, fpCode, strlen(fpCode), GL_FRAGMENT_SHADER)))
{
ri.Printf(PRINT_ALL, "GLSL_InitGPUShader2: Unable to load \"%s\" as GL_FRAGMENT_SHADER\n", name);
qglDeleteProgram(program->program);
return 0;
}
}
if(attribs & ATTR_POSITION)
qglBindAttribLocation(program->program, ATTR_INDEX_POSITION, "attr_Position");
if(attribs & ATTR_TEXCOORD)
qglBindAttribLocation(program->program, ATTR_INDEX_TEXCOORD, "attr_TexCoord0");
if(attribs & ATTR_LIGHTCOORD)
qglBindAttribLocation(program->program, ATTR_INDEX_LIGHTCOORD, "attr_TexCoord1");
// if(attribs & ATTR_TEXCOORD2)
// qglBindAttribLocation(program->program, ATTR_INDEX_TEXCOORD2, "attr_TexCoord2");
// if(attribs & ATTR_TEXCOORD3)
// qglBindAttribLocation(program->program, ATTR_INDEX_TEXCOORD3, "attr_TexCoord3");
if(attribs & ATTR_TANGENT)
qglBindAttribLocation(program->program, ATTR_INDEX_TANGENT, "attr_Tangent");
if(attribs & ATTR_NORMAL)
qglBindAttribLocation(program->program, ATTR_INDEX_NORMAL, "attr_Normal");
if(attribs & ATTR_COLOR)
qglBindAttribLocation(program->program, ATTR_INDEX_COLOR, "attr_Color");
if(attribs & ATTR_PAINTCOLOR)
qglBindAttribLocation(program->program, ATTR_INDEX_PAINTCOLOR, "attr_PaintColor");
if(attribs & ATTR_LIGHTDIRECTION)
qglBindAttribLocation(program->program, ATTR_INDEX_LIGHTDIRECTION, "attr_LightDirection");
if(attribs & ATTR_BONE_INDEXES)
qglBindAttribLocation(program->program, ATTR_INDEX_BONE_INDEXES, "attr_BoneIndexes");
if(attribs & ATTR_BONE_WEIGHTS)
qglBindAttribLocation(program->program, ATTR_INDEX_BONE_WEIGHTS, "attr_BoneWeights");
if(attribs & ATTR_POSITION2)
qglBindAttribLocation(program->program, ATTR_INDEX_POSITION2, "attr_Position2");
if(attribs & ATTR_NORMAL2)
qglBindAttribLocation(program->program, ATTR_INDEX_NORMAL2, "attr_Normal2");
if(attribs & ATTR_TANGENT2)
qglBindAttribLocation(program->program, ATTR_INDEX_TANGENT2, "attr_Tangent2");
GLSL_LinkProgram(program->program);
return 1;
}
static int GLSL_InitGPUShader(shaderProgram_t * program, const char *name,
int attribs, qboolean fragmentShader, const GLchar *extra, qboolean addHeader,
const char *fallback_vp, const char *fallback_fp)
{
char vpCode[32000];
char fpCode[32000];
char *postHeader;
int size;
int result;
size = sizeof(vpCode);
if (addHeader)
{
GLSL_GetShaderHeader(GL_VERTEX_SHADER, extra, vpCode, size);
postHeader = &vpCode[strlen(vpCode)];
size -= strlen(vpCode);
}
else
{
postHeader = &vpCode[0];
}
if (!GLSL_LoadGPUShaderText(name, fallback_vp, GL_VERTEX_SHADER, postHeader, size))
{
return 0;
}
if (fragmentShader)
{
size = sizeof(fpCode);
if (addHeader)
{
GLSL_GetShaderHeader(GL_FRAGMENT_SHADER, extra, fpCode, size);
postHeader = &fpCode[strlen(fpCode)];
size -= strlen(fpCode);
}
else
{
postHeader = &fpCode[0];
}
if (!GLSL_LoadGPUShaderText(name, fallback_fp, GL_FRAGMENT_SHADER, postHeader, size))
{
return 0;
}
}
result = GLSL_InitGPUShader2(program, name, attribs, vpCode, fragmentShader ? fpCode : NULL);
return result;
}
void GLSL_InitUniforms(shaderProgram_t *program)
{
int i, size;
GLint *uniforms = program->uniforms;
size = 0;
for (i = 0; i < UNIFORM_COUNT; i++)
{
uniforms[i] = qglGetUniformLocation(program->program, uniformsInfo[i].name);
if (uniforms[i] == -1)
continue;
program->uniformBufferOffsets[i] = size;
switch(uniformsInfo[i].type)
{
case GLSL_INT:
size += sizeof(GLint);
break;
case GLSL_FLOAT:
size += sizeof(GLfloat);
break;
case GLSL_FLOAT5:
size += sizeof(vec_t) * 5;
break;
case GLSL_VEC2:
size += sizeof(vec_t) * 2;
break;
case GLSL_VEC3:
size += sizeof(vec_t) * 3;
break;
case GLSL_VEC4:
size += sizeof(vec_t) * 4;
break;
case GLSL_MAT16:
size += sizeof(vec_t) * 16;
break;
case GLSL_MAT16_BONEMATRIX:
size += sizeof(vec_t) * 16 * glRefConfig.glslMaxAnimatedBones;
break;
default:
break;
}
}
program->uniformBuffer = ri.Malloc(size);
}
void GLSL_FinishGPUShader(shaderProgram_t *program)
{
GLSL_ShowProgramUniforms(program->program);
GL_CheckErrors();
}
void GLSL_SetUniformInt(shaderProgram_t *program, int uniformNum, GLint value)
{
GLint *uniforms = program->uniforms;
GLint *compare = (GLint *)(program->uniformBuffer + program->uniformBufferOffsets[uniformNum]);
if (uniforms[uniformNum] == -1)
return;
if (uniformsInfo[uniformNum].type != GLSL_INT)
{
ri.Printf( PRINT_WARNING, "GLSL_SetUniformInt: wrong type for uniform %i in program %s\n", uniformNum, program->name);
return;
}
if (value == *compare)
{
return;
}
*compare = value;
qglProgramUniform1iEXT(program->program, uniforms[uniformNum], value);
}
void GLSL_SetUniformFloat(shaderProgram_t *program, int uniformNum, GLfloat value)
{
GLint *uniforms = program->uniforms;
GLfloat *compare = (GLfloat *)(program->uniformBuffer + program->uniformBufferOffsets[uniformNum]);
if (uniforms[uniformNum] == -1)
return;
if (uniformsInfo[uniformNum].type != GLSL_FLOAT)
{
ri.Printf( PRINT_WARNING, "GLSL_SetUniformFloat: wrong type for uniform %i in program %s\n", uniformNum, program->name);
return;
}
if (value == *compare)
{
return;
}
*compare = value;
qglProgramUniform1fEXT(program->program, uniforms[uniformNum], value);
}
void GLSL_SetUniformVec2(shaderProgram_t *program, int uniformNum, const vec2_t v)
{
GLint *uniforms = program->uniforms;
vec_t *compare = (float *)(program->uniformBuffer + program->uniformBufferOffsets[uniformNum]);
if (uniforms[uniformNum] == -1)
return;
if (uniformsInfo[uniformNum].type != GLSL_VEC2)
{
ri.Printf( PRINT_WARNING, "GLSL_SetUniformVec2: wrong type for uniform %i in program %s\n", uniformNum, program->name);
return;
}
if (v[0] == compare[0] && v[1] == compare[1])
{
return;
}
compare[0] = v[0];
compare[1] = v[1];
qglProgramUniform2fEXT(program->program, uniforms[uniformNum], v[0], v[1]);
}
void GLSL_SetUniformVec3(shaderProgram_t *program, int uniformNum, const vec3_t v)
{
GLint *uniforms = program->uniforms;
vec_t *compare = (float *)(program->uniformBuffer + program->uniformBufferOffsets[uniformNum]);
if (uniforms[uniformNum] == -1)
return;
if (uniformsInfo[uniformNum].type != GLSL_VEC3)
{
ri.Printf( PRINT_WARNING, "GLSL_SetUniformVec3: wrong type for uniform %i in program %s\n", uniformNum, program->name);
return;
}
if (VectorCompare(v, compare))
{
return;
}
VectorCopy(v, compare);
qglProgramUniform3fEXT(program->program, uniforms[uniformNum], v[0], v[1], v[2]);
}
void GLSL_SetUniformVec4(shaderProgram_t *program, int uniformNum, const vec4_t v)
{
GLint *uniforms = program->uniforms;
vec_t *compare = (float *)(program->uniformBuffer + program->uniformBufferOffsets[uniformNum]);
if (uniforms[uniformNum] == -1)
return;
if (uniformsInfo[uniformNum].type != GLSL_VEC4)
{
ri.Printf( PRINT_WARNING, "GLSL_SetUniformVec4: wrong type for uniform %i in program %s\n", uniformNum, program->name);
return;
}
if (VectorCompare4(v, compare))
{
return;
}
VectorCopy4(v, compare);
qglProgramUniform4fEXT(program->program, uniforms[uniformNum], v[0], v[1], v[2], v[3]);
}
void GLSL_SetUniformFloat5(shaderProgram_t *program, int uniformNum, const vec5_t v)
{
GLint *uniforms = program->uniforms;
vec_t *compare = (float *)(program->uniformBuffer + program->uniformBufferOffsets[uniformNum]);
if (uniforms[uniformNum] == -1)
return;
if (uniformsInfo[uniformNum].type != GLSL_FLOAT5)
{
ri.Printf( PRINT_WARNING, "GLSL_SetUniformFloat5: wrong type for uniform %i in program %s\n", uniformNum, program->name);
return;
}
if (VectorCompare5(v, compare))
{
return;
}
VectorCopy5(v, compare);
qglProgramUniform1fvEXT(program->program, uniforms[uniformNum], 5, v);
}
void GLSL_SetUniformMat4(shaderProgram_t *program, int uniformNum, const mat4_t matrix)
{
GLint *uniforms = program->uniforms;
vec_t *compare = (float *)(program->uniformBuffer + program->uniformBufferOffsets[uniformNum]);
if (uniforms[uniformNum] == -1)
return;
if (uniformsInfo[uniformNum].type != GLSL_MAT16)
{
ri.Printf( PRINT_WARNING, "GLSL_SetUniformMat4: wrong type for uniform %i in program %s\n", uniformNum, program->name);
return;
}
if (Mat4Compare(matrix, compare))
{
return;
}
Mat4Copy(matrix, compare);
qglProgramUniformMatrix4fvEXT(program->program, uniforms[uniformNum], 1, GL_FALSE, matrix);
}
void GLSL_SetUniformMat4BoneMatrix(shaderProgram_t *program, int uniformNum, /*const*/ mat4_t *matrix, int numMatricies)
{
GLint *uniforms = program->uniforms;
vec_t *compare = (float *)(program->uniformBuffer + program->uniformBufferOffsets[uniformNum]);
if (uniforms[uniformNum] == -1) {
return;
}
if (uniformsInfo[uniformNum].type != GLSL_MAT16_BONEMATRIX)
{
ri.Printf( PRINT_WARNING, "GLSL_SetUniformMat4BoneMatrix: wrong type for uniform %i in program %s\n", uniformNum, program->name);
return;
}
if (numMatricies > glRefConfig.glslMaxAnimatedBones)
{
ri.Printf( PRINT_WARNING, "GLSL_SetUniformMat4BoneMatrix: too many matricies (%d/%d) for uniform %i in program %s\n",
numMatricies, glRefConfig.glslMaxAnimatedBones, uniformNum, program->name);
return;
}
if (!memcmp(matrix, compare, numMatricies * sizeof(mat4_t)))
{
return;
}
Com_Memcpy(compare, matrix, numMatricies * sizeof(mat4_t));
qglProgramUniformMatrix4fvEXT(program->program, uniforms[uniformNum], numMatricies, GL_FALSE, &matrix[0][0]);
}
void GLSL_DeleteGPUShader(shaderProgram_t *program)
{
if(program->program)
{
if (program->vertexShader)
{
qglDetachShader(program->program, program->vertexShader);
qglDeleteShader(program->vertexShader);
}
if (program->fragmentShader)
{
qglDetachShader(program->program, program->fragmentShader);
qglDeleteShader(program->fragmentShader);
}
qglDeleteProgram(program->program);
if (program->uniformBuffer)
{
ri.Free(program->uniformBuffer);
}
Com_Memset(program, 0, sizeof(*program));
}
}
void GLSL_InitGPUShaders(void)
{
int startTime, endTime;
int i;
char extradefines[1024];
int attribs;
int numGenShaders = 0, numLightShaders = 0, numEtcShaders = 0;
ri.Printf(PRINT_ALL, "------- GLSL_InitGPUShaders -------\n");
R_IssuePendingRenderCommands();
startTime = ri.Milliseconds();
// OpenGL ES may not have enough attributes to fit ones used for vertex animation
if ( glRefConfig.maxVertexAttribs > ATTR_INDEX_NORMAL2 ) {
ri.Printf(PRINT_ALL, "Using GPU vertex animation\n");
glRefConfig.gpuVertexAnimation = qtrue;
} else {
ri.Printf(PRINT_ALL, "Using CPU vertex animation\n");
glRefConfig.gpuVertexAnimation = qfalse;
}
for (i = 0; i < GENERICDEF_COUNT; i++)
{
if ((i & GENERICDEF_USE_VERTEX_ANIMATION) && (i & GENERICDEF_USE_BONE_ANIMATION))
continue;
if ((i & GENERICDEF_USE_BONE_ANIMATION) && !glRefConfig.glslMaxAnimatedBones)
continue;
attribs = ATTR_POSITION | ATTR_TEXCOORD | ATTR_LIGHTCOORD | ATTR_NORMAL | ATTR_COLOR;
extradefines[0] = '\0';
if (i & GENERICDEF_USE_DEFORM_VERTEXES)
Q_strcat(extradefines, 1024, "#define USE_DEFORM_VERTEXES\n");
if (i & GENERICDEF_USE_TCGEN_AND_TCMOD)
{
Q_strcat(extradefines, 1024, "#define USE_TCGEN\n");
Q_strcat(extradefines, 1024, "#define USE_TCMOD\n");
}
if (i & GENERICDEF_USE_VERTEX_ANIMATION)
{
if (!glRefConfig.gpuVertexAnimation)
continue;
Q_strcat(extradefines, 1024, "#define USE_VERTEX_ANIMATION\n");
attribs |= ATTR_POSITION2 | ATTR_NORMAL2;
}
else if (i & GENERICDEF_USE_BONE_ANIMATION)
{
Q_strcat(extradefines, 1024, va("#define USE_BONE_ANIMATION\n#define MAX_GLSL_BONES %d\n", glRefConfig.glslMaxAnimatedBones));
attribs |= ATTR_BONE_INDEXES | ATTR_BONE_WEIGHTS;
}
if (i & GENERICDEF_USE_FOG)
Q_strcat(extradefines, 1024, "#define USE_FOG\n");
if (i & GENERICDEF_USE_RGBAGEN)
Q_strcat(extradefines, 1024, "#define USE_RGBAGEN\n");
if (!GLSL_InitGPUShader(&tr.genericShader[i], "generic", attribs, qtrue, extradefines, qtrue, fallbackShader_generic_vp, fallbackShader_generic_fp))
{
ri.Error(ERR_FATAL, "Could not load generic shader!");
}
GLSL_InitUniforms(&tr.genericShader[i]);
GLSL_SetUniformInt(&tr.genericShader[i], UNIFORM_DIFFUSEMAP, TB_DIFFUSEMAP);
GLSL_SetUniformInt(&tr.genericShader[i], UNIFORM_LIGHTMAP, TB_LIGHTMAP);
GLSL_FinishGPUShader(&tr.genericShader[i]);
numGenShaders++;
}
attribs = ATTR_POSITION | ATTR_TEXCOORD;
if (!GLSL_InitGPUShader(&tr.textureColorShader, "texturecolor", attribs, qtrue, extradefines, qtrue, fallbackShader_texturecolor_vp, fallbackShader_texturecolor_fp))
{
ri.Error(ERR_FATAL, "Could not load texturecolor shader!");
}
GLSL_InitUniforms(&tr.textureColorShader);
GLSL_SetUniformInt(&tr.textureColorShader, UNIFORM_TEXTUREMAP, TB_DIFFUSEMAP);
GLSL_FinishGPUShader(&tr.textureColorShader);
numEtcShaders++;
for (i = 0; i < FOGDEF_COUNT; i++)
{
if ((i & FOGDEF_USE_VERTEX_ANIMATION) && (i & FOGDEF_USE_BONE_ANIMATION))
continue;
if ((i & FOGDEF_USE_VERTEX_ANIMATION) && !glRefConfig.gpuVertexAnimation)
continue;
if ((i & FOGDEF_USE_BONE_ANIMATION) && !glRefConfig.glslMaxAnimatedBones)
continue;
attribs = ATTR_POSITION | ATTR_NORMAL | ATTR_TEXCOORD;
extradefines[0] = '\0';
if (i & FOGDEF_USE_DEFORM_VERTEXES)
Q_strcat(extradefines, 1024, "#define USE_DEFORM_VERTEXES\n");
if (i & FOGDEF_USE_VERTEX_ANIMATION)
{
Q_strcat(extradefines, 1024, "#define USE_VERTEX_ANIMATION\n");
attribs |= ATTR_POSITION2 | ATTR_NORMAL2;
}
else if (i & FOGDEF_USE_BONE_ANIMATION)
{
Q_strcat(extradefines, 1024, va("#define USE_BONE_ANIMATION\n#define MAX_GLSL_BONES %d\n", glRefConfig.glslMaxAnimatedBones));
attribs |= ATTR_BONE_INDEXES | ATTR_BONE_WEIGHTS;
}
if (!GLSL_InitGPUShader(&tr.fogShader[i], "fogpass", attribs, qtrue, extradefines, qtrue, fallbackShader_fogpass_vp, fallbackShader_fogpass_fp))
{
ri.Error(ERR_FATAL, "Could not load fogpass shader!");
}
GLSL_InitUniforms(&tr.fogShader[i]);
GLSL_FinishGPUShader(&tr.fogShader[i]);
numEtcShaders++;
}
for (i = 0; i < DLIGHTDEF_COUNT; i++)
{
attribs = ATTR_POSITION | ATTR_NORMAL | ATTR_TEXCOORD;
extradefines[0] = '\0';
if (i & DLIGHTDEF_USE_DEFORM_VERTEXES)
{
Q_strcat(extradefines, 1024, "#define USE_DEFORM_VERTEXES\n");
}
if (!GLSL_InitGPUShader(&tr.dlightShader[i], "dlight", attribs, qtrue, extradefines, qtrue, fallbackShader_dlight_vp, fallbackShader_dlight_fp))
{
ri.Error(ERR_FATAL, "Could not load dlight shader!");
}
GLSL_InitUniforms(&tr.dlightShader[i]);
GLSL_SetUniformInt(&tr.dlightShader[i], UNIFORM_DIFFUSEMAP, TB_DIFFUSEMAP);
GLSL_FinishGPUShader(&tr.dlightShader[i]);
numEtcShaders++;
}
for (i = 0; i < LIGHTDEF_COUNT; i++)
{
int lightType = i & LIGHTDEF_LIGHTTYPE_MASK;
qboolean fastLight = !(r_normalMapping->integer || r_specularMapping->integer);
// skip impossible combos
if ((i & LIGHTDEF_USE_PARALLAXMAP) && !r_parallaxMapping->integer)
continue;
if ((i & LIGHTDEF_USE_SHADOWMAP) && (!lightType || !r_sunlightMode->integer))
continue;
if ((i & LIGHTDEF_ENTITY_VERTEX_ANIMATION) && (i & LIGHTDEF_ENTITY_BONE_ANIMATION))
continue;
if ((i & LIGHTDEF_ENTITY_BONE_ANIMATION) && !glRefConfig.glslMaxAnimatedBones)
continue;
attribs = ATTR_POSITION | ATTR_TEXCOORD | ATTR_COLOR | ATTR_NORMAL;
extradefines[0] = '\0';
if (r_dlightMode->integer >= 2)
Q_strcat(extradefines, 1024, "#define USE_SHADOWMAP\n");
if (glRefConfig.swizzleNormalmap)
Q_strcat(extradefines, 1024, "#define SWIZZLE_NORMALMAP\n");
if (lightType)
{
Q_strcat(extradefines, 1024, "#define USE_LIGHT\n");
if (fastLight)
Q_strcat(extradefines, 1024, "#define USE_FAST_LIGHT\n");
switch (lightType)
{
case LIGHTDEF_USE_LIGHTMAP:
Q_strcat(extradefines, 1024, "#define USE_LIGHTMAP\n");
if (r_deluxeMapping->integer && !fastLight)
Q_strcat(extradefines, 1024, "#define USE_DELUXEMAP\n");
attribs |= ATTR_LIGHTCOORD | ATTR_LIGHTDIRECTION;
break;
case LIGHTDEF_USE_LIGHT_VECTOR:
Q_strcat(extradefines, 1024, "#define USE_LIGHT_VECTOR\n");
break;
case LIGHTDEF_USE_LIGHT_VERTEX:
Q_strcat(extradefines, 1024, "#define USE_LIGHT_VERTEX\n");
attribs |= ATTR_LIGHTDIRECTION;
break;
default:
break;
}
if (r_normalMapping->integer)
{
Q_strcat(extradefines, 1024, "#define USE_NORMALMAP\n");
attribs |= ATTR_TANGENT;
if ((i & LIGHTDEF_USE_PARALLAXMAP) && !(i & LIGHTDEF_ENTITY_VERTEX_ANIMATION) && !(i & LIGHTDEF_ENTITY_BONE_ANIMATION) && r_parallaxMapping->integer)
{
Q_strcat(extradefines, 1024, "#define USE_PARALLAXMAP\n");
if (r_parallaxMapping->integer > 1)
Q_strcat(extradefines, 1024, "#define USE_RELIEFMAP\n");
if (r_parallaxMapShadows->integer)
Q_strcat(extradefines, 1024, "#define USE_PARALLAXMAP_SHADOWS\n");
Q_strcat(extradefines, 1024, va("#define r_parallaxMapOffset %f\n", r_parallaxMapOffset->value));
}
}
if (r_specularMapping->integer)
Q_strcat(extradefines, 1024, "#define USE_SPECULARMAP\n");
if (r_cubeMapping->integer)
{
Q_strcat(extradefines, 1024, "#define USE_CUBEMAP\n");
if (r_cubeMapping->integer == 2)
Q_strcat(extradefines, 1024, "#define USE_BOX_CUBEMAP_PARALLAX\n");
}
else if (r_deluxeSpecular->value > 0.000001f)
{
Q_strcat(extradefines, 1024, va("#define r_deluxeSpecular %f\n", r_deluxeSpecular->value));
}
switch (r_glossType->integer)
{
case 0:
default:
Q_strcat(extradefines, 1024, "#define GLOSS_IS_GLOSS\n");
break;
case 1:
Q_strcat(extradefines, 1024, "#define GLOSS_IS_SMOOTHNESS\n");
break;
case 2:
Q_strcat(extradefines, 1024, "#define GLOSS_IS_ROUGHNESS\n");
break;
case 3:
Q_strcat(extradefines, 1024, "#define GLOSS_IS_SHININESS\n");
break;
}
}
if (i & LIGHTDEF_USE_SHADOWMAP)
{
Q_strcat(extradefines, 1024, "#define USE_SHADOWMAP\n");
if (r_sunlightMode->integer == 1)
Q_strcat(extradefines, 1024, "#define SHADOWMAP_MODULATE\n");
else if (r_sunlightMode->integer == 2)
Q_strcat(extradefines, 1024, "#define USE_PRIMARY_LIGHT\n");
}
if (i & LIGHTDEF_USE_TCGEN_AND_TCMOD)
{
Q_strcat(extradefines, 1024, "#define USE_TCGEN\n");
Q_strcat(extradefines, 1024, "#define USE_TCMOD\n");
}
if (i & LIGHTDEF_ENTITY_VERTEX_ANIMATION)
{
Q_strcat(extradefines, 1024, "#define USE_MODELMATRIX\n");
if (glRefConfig.gpuVertexAnimation)
{
Q_strcat(extradefines, 1024, "#define USE_VERTEX_ANIMATION\n");
attribs |= ATTR_POSITION2 | ATTR_NORMAL2;
if (r_normalMapping->integer)
{
attribs |= ATTR_TANGENT2;
}
}
}
else if (i & LIGHTDEF_ENTITY_BONE_ANIMATION)
{
Q_strcat(extradefines, 1024, "#define USE_MODELMATRIX\n");
Q_strcat(extradefines, 1024, va("#define USE_BONE_ANIMATION\n#define MAX_GLSL_BONES %d\n", glRefConfig.glslMaxAnimatedBones));
attribs |= ATTR_BONE_INDEXES | ATTR_BONE_WEIGHTS;
}
if (!GLSL_InitGPUShader(&tr.lightallShader[i], "lightall", attribs, qtrue, extradefines, qtrue, fallbackShader_lightall_vp, fallbackShader_lightall_fp))
{
ri.Error(ERR_FATAL, "Could not load lightall shader!");
}
GLSL_InitUniforms(&tr.lightallShader[i]);
GLSL_SetUniformInt(&tr.lightallShader[i], UNIFORM_DIFFUSEMAP, TB_DIFFUSEMAP);
GLSL_SetUniformInt(&tr.lightallShader[i], UNIFORM_LIGHTMAP, TB_LIGHTMAP);
GLSL_SetUniformInt(&tr.lightallShader[i], UNIFORM_NORMALMAP, TB_NORMALMAP);
GLSL_SetUniformInt(&tr.lightallShader[i], UNIFORM_DELUXEMAP, TB_DELUXEMAP);
GLSL_SetUniformInt(&tr.lightallShader[i], UNIFORM_SPECULARMAP, TB_SPECULARMAP);
GLSL_SetUniformInt(&tr.lightallShader[i], UNIFORM_SHADOWMAP, TB_SHADOWMAP);
GLSL_SetUniformInt(&tr.lightallShader[i], UNIFORM_CUBEMAP, TB_CUBEMAP);
GLSL_FinishGPUShader(&tr.lightallShader[i]);
numLightShaders++;
}
for (i = 0; i < SHADOWMAPDEF_COUNT; i++)
{
if ((i & SHADOWMAPDEF_USE_VERTEX_ANIMATION) && (i & SHADOWMAPDEF_USE_BONE_ANIMATION))
continue;
if ((i & SHADOWMAPDEF_USE_VERTEX_ANIMATION) && !glRefConfig.gpuVertexAnimation)
continue;
if ((i & SHADOWMAPDEF_USE_BONE_ANIMATION) && !glRefConfig.glslMaxAnimatedBones)
continue;
attribs = ATTR_POSITION | ATTR_NORMAL | ATTR_TEXCOORD;
extradefines[0] = '\0';
if (i & SHADOWMAPDEF_USE_VERTEX_ANIMATION)
{
Q_strcat(extradefines, 1024, "#define USE_VERTEX_ANIMATION\n");
attribs |= ATTR_POSITION2 | ATTR_NORMAL2;
}
if (i & SHADOWMAPDEF_USE_BONE_ANIMATION)
{
Q_strcat(extradefines, 1024, va("#define USE_BONE_ANIMATION\n#define MAX_GLSL_BONES %d\n", glRefConfig.glslMaxAnimatedBones));
attribs |= ATTR_BONE_INDEXES | ATTR_BONE_WEIGHTS;
}
if (!GLSL_InitGPUShader(&tr.shadowmapShader[i], "shadowfill", attribs, qtrue, extradefines, qtrue, fallbackShader_shadowfill_vp, fallbackShader_shadowfill_fp))
{
ri.Error(ERR_FATAL, "Could not load shadowfill shader!");
}
GLSL_InitUniforms(&tr.shadowmapShader[i]);
GLSL_FinishGPUShader(&tr.shadowmapShader[i]);
numEtcShaders++;
}
attribs = ATTR_POSITION | ATTR_NORMAL;
extradefines[0] = '\0';
Q_strcat(extradefines, 1024, "#define USE_PCF\n#define USE_DISCARD\n");
if (!GLSL_InitGPUShader(&tr.pshadowShader, "pshadow", attribs, qtrue, extradefines, qtrue, fallbackShader_pshadow_vp, fallbackShader_pshadow_fp))
{
ri.Error(ERR_FATAL, "Could not load pshadow shader!");
}
GLSL_InitUniforms(&tr.pshadowShader);
GLSL_SetUniformInt(&tr.pshadowShader, UNIFORM_SHADOWMAP, TB_DIFFUSEMAP);
GLSL_FinishGPUShader(&tr.pshadowShader);
numEtcShaders++;
attribs = ATTR_POSITION | ATTR_TEXCOORD;
extradefines[0] = '\0';
if (!GLSL_InitGPUShader(&tr.down4xShader, "down4x", attribs, qtrue, extradefines, qtrue, fallbackShader_down4x_vp, fallbackShader_down4x_fp))
{
ri.Error(ERR_FATAL, "Could not load down4x shader!");
}
GLSL_InitUniforms(&tr.down4xShader);
GLSL_SetUniformInt(&tr.down4xShader, UNIFORM_TEXTUREMAP, TB_DIFFUSEMAP);
GLSL_FinishGPUShader(&tr.down4xShader);
numEtcShaders++;
attribs = ATTR_POSITION | ATTR_TEXCOORD;
extradefines[0] = '\0';
if (!GLSL_InitGPUShader(&tr.bokehShader, "bokeh", attribs, qtrue, extradefines, qtrue, fallbackShader_bokeh_vp, fallbackShader_bokeh_fp))
{
ri.Error(ERR_FATAL, "Could not load bokeh shader!");
}
GLSL_InitUniforms(&tr.bokehShader);
GLSL_SetUniformInt(&tr.bokehShader, UNIFORM_TEXTUREMAP, TB_DIFFUSEMAP);
GLSL_FinishGPUShader(&tr.bokehShader);
numEtcShaders++;
attribs = ATTR_POSITION | ATTR_TEXCOORD;
extradefines[0] = '\0';
if (!GLSL_InitGPUShader(&tr.tonemapShader, "tonemap", attribs, qtrue, extradefines, qtrue, fallbackShader_tonemap_vp, fallbackShader_tonemap_fp))
{
ri.Error(ERR_FATAL, "Could not load tonemap shader!");
}
GLSL_InitUniforms(&tr.tonemapShader);
GLSL_SetUniformInt(&tr.tonemapShader, UNIFORM_TEXTUREMAP, TB_COLORMAP);
GLSL_SetUniformInt(&tr.tonemapShader, UNIFORM_LEVELSMAP, TB_LEVELSMAP);
GLSL_FinishGPUShader(&tr.tonemapShader);
numEtcShaders++;
for (i = 0; i < 2; i++)
{
attribs = ATTR_POSITION | ATTR_TEXCOORD;
extradefines[0] = '\0';
if (!i)
Q_strcat(extradefines, 1024, "#define FIRST_PASS\n");
if (!GLSL_InitGPUShader(&tr.calclevels4xShader[i], "calclevels4x", attribs, qtrue, extradefines, qtrue, fallbackShader_calclevels4x_vp, fallbackShader_calclevels4x_fp))
{
ri.Error(ERR_FATAL, "Could not load calclevels4x shader!");
}
GLSL_InitUniforms(&tr.calclevels4xShader[i]);
GLSL_SetUniformInt(&tr.calclevels4xShader[i], UNIFORM_TEXTUREMAP, TB_DIFFUSEMAP);
GLSL_FinishGPUShader(&tr.calclevels4xShader[i]);
numEtcShaders++;
}
// GLSL 1.10+ or GL_EXT_shadow_samplers extension are required for sampler2DShadow type
if (glRefConfig.glslMajorVersion > 1 || (glRefConfig.glslMajorVersion == 1 && glRefConfig.glslMinorVersion >= 10)
|| glRefConfig.shadowSamplers)
{
attribs = ATTR_POSITION | ATTR_TEXCOORD;
extradefines[0] = '\0';
if (qglesMajorVersion < 3 && glRefConfig.shadowSamplers)
{
Q_strcat(extradefines, 1024, "#extension GL_EXT_shadow_samplers : enable\n");
}
if (r_shadowFilter->integer >= 1)
Q_strcat(extradefines, 1024, "#define USE_SHADOW_FILTER\n");
if (r_shadowFilter->integer >= 2)
Q_strcat(extradefines, 1024, "#define USE_SHADOW_FILTER2\n");
if (r_shadowCascadeZFar->integer != 0)
Q_strcat(extradefines, 1024, "#define USE_SHADOW_CASCADE\n");
Q_strcat(extradefines, 1024, va("#define r_shadowMapSize %f\n", r_shadowMapSize->value));
Q_strcat(extradefines, 1024, va("#define r_shadowCascadeZFar %f\n", r_shadowCascadeZFar->value));
if (!GLSL_InitGPUShader(&tr.shadowmaskShader, "shadowmask", attribs, qtrue, extradefines, qtrue, fallbackShader_shadowmask_vp, fallbackShader_shadowmask_fp))
{
ri.Error(ERR_FATAL, "Could not load shadowmask shader!");
}
GLSL_InitUniforms(&tr.shadowmaskShader);
GLSL_SetUniformInt(&tr.shadowmaskShader, UNIFORM_SCREENDEPTHMAP, TB_COLORMAP);
GLSL_SetUniformInt(&tr.shadowmaskShader, UNIFORM_SHADOWMAP, TB_SHADOWMAP);
GLSL_SetUniformInt(&tr.shadowmaskShader, UNIFORM_SHADOWMAP2, TB_SHADOWMAP2);
GLSL_SetUniformInt(&tr.shadowmaskShader, UNIFORM_SHADOWMAP3, TB_SHADOWMAP3);
GLSL_SetUniformInt(&tr.shadowmaskShader, UNIFORM_SHADOWMAP4, TB_SHADOWMAP4);
GLSL_FinishGPUShader(&tr.shadowmaskShader);
numEtcShaders++;
}
// GLSL 1.10+ or GL_OES_standard_derivatives extension are required for dFdx() and dFdy() GLSL functions
if (glRefConfig.glslMajorVersion > 1 || (glRefConfig.glslMajorVersion == 1 && glRefConfig.glslMinorVersion >= 10)
|| glRefConfig.standardDerivatives)
{
attribs = ATTR_POSITION | ATTR_TEXCOORD;
extradefines[0] = '\0';
if (qglesMajorVersion < 3 && glRefConfig.standardDerivatives)
{
Q_strcat(extradefines, 1024, "#extension GL_OES_standard_derivatives : enable\n");
}
if (!GLSL_InitGPUShader(&tr.ssaoShader, "ssao", attribs, qtrue, extradefines, qtrue, fallbackShader_ssao_vp, fallbackShader_ssao_fp))
{
ri.Error(ERR_FATAL, "Could not load ssao shader!");
}
GLSL_InitUniforms(&tr.ssaoShader);
GLSL_SetUniformInt(&tr.ssaoShader, UNIFORM_SCREENDEPTHMAP, TB_COLORMAP);
GLSL_FinishGPUShader(&tr.ssaoShader);
numEtcShaders++;
for (i = 0; i < 4; i++)
{
attribs = ATTR_POSITION | ATTR_TEXCOORD;
extradefines[0] = '\0';
if (qglesMajorVersion < 3 && glRefConfig.standardDerivatives)
{
Q_strcat(extradefines, 1024, "#extension GL_OES_standard_derivatives : enable\n");
}
if (i & 1)
Q_strcat(extradefines, 1024, "#define USE_VERTICAL_BLUR\n");
else
Q_strcat(extradefines, 1024, "#define USE_HORIZONTAL_BLUR\n");
if (!(i & 2))
Q_strcat(extradefines, 1024, "#define USE_DEPTH\n");
if (!GLSL_InitGPUShader(&tr.depthBlurShader[i], "depthBlur", attribs, qtrue, extradefines, qtrue, fallbackShader_depthblur_vp, fallbackShader_depthblur_fp))
{
ri.Error(ERR_FATAL, "Could not load depthBlur shader!");
}
GLSL_InitUniforms(&tr.depthBlurShader[i]);
GLSL_SetUniformInt(&tr.depthBlurShader[i], UNIFORM_SCREENIMAGEMAP, TB_COLORMAP);
GLSL_SetUniformInt(&tr.depthBlurShader[i], UNIFORM_SCREENDEPTHMAP, TB_LIGHTMAP);
GLSL_FinishGPUShader(&tr.depthBlurShader[i]);
numEtcShaders++;
}
}
#if 0
attribs = ATTR_POSITION | ATTR_TEXCOORD;
extradefines[0] = '\0';
if (!GLSL_InitGPUShader(&tr.testcubeShader, "testcube", attribs, qtrue, extradefines, qtrue, NULL, NULL))
{
ri.Error(ERR_FATAL, "Could not load testcube shader!");
}
GLSL_InitUniforms(&tr.testcubeShader);
GLSL_SetUniformInt(&tr.testcubeShader, UNIFORM_TEXTUREMAP, TB_COLORMAP);
GLSL_FinishGPUShader(&tr.testcubeShader);
numEtcShaders++;
#endif
endTime = ri.Milliseconds();
ri.Printf(PRINT_ALL, "loaded %i GLSL shaders (%i gen %i light %i etc) in %5.2f seconds\n",
numGenShaders + numLightShaders + numEtcShaders, numGenShaders, numLightShaders,
numEtcShaders, (endTime - startTime) / 1000.0);
}
void GLSL_ShutdownGPUShaders(void)
{
int i;
ri.Printf(PRINT_ALL, "------- GLSL_ShutdownGPUShaders -------\n");
for (i = 0; i < ATTR_INDEX_COUNT && i < glRefConfig.maxVertexAttribs; i++)
qglDisableVertexAttribArray(i);
GL_BindNullProgram();
for ( i = 0; i < GENERICDEF_COUNT; i++)
GLSL_DeleteGPUShader(&tr.genericShader[i]);
GLSL_DeleteGPUShader(&tr.textureColorShader);
for ( i = 0; i < FOGDEF_COUNT; i++)
GLSL_DeleteGPUShader(&tr.fogShader[i]);
for ( i = 0; i < DLIGHTDEF_COUNT; i++)
GLSL_DeleteGPUShader(&tr.dlightShader[i]);
for ( i = 0; i < LIGHTDEF_COUNT; i++)
GLSL_DeleteGPUShader(&tr.lightallShader[i]);
for ( i = 0; i < SHADOWMAPDEF_COUNT; i++)
GLSL_DeleteGPUShader(&tr.shadowmapShader[i]);
GLSL_DeleteGPUShader(&tr.pshadowShader);
GLSL_DeleteGPUShader(&tr.down4xShader);
GLSL_DeleteGPUShader(&tr.bokehShader);
GLSL_DeleteGPUShader(&tr.tonemapShader);
for ( i = 0; i < 2; i++)
GLSL_DeleteGPUShader(&tr.calclevels4xShader[i]);
GLSL_DeleteGPUShader(&tr.shadowmaskShader);
GLSL_DeleteGPUShader(&tr.ssaoShader);
for ( i = 0; i < 4; i++)
GLSL_DeleteGPUShader(&tr.depthBlurShader[i]);
}
void GLSL_BindProgram(shaderProgram_t * program)
{
GLuint programObject = program ? program->program : 0;
char *name = program ? program->name : "NULL";
if(r_logFile->integer)
{
// don't just call LogComment, or we will get a call to va() every frame!
GLimp_LogComment(va("--- GLSL_BindProgram( %s ) ---\n", name));
}
if (GL_UseProgram(programObject))
backEnd.pc.c_glslShaderBinds++;
}
shaderProgram_t *GLSL_GetGenericShaderProgram(int stage)
{
shaderStage_t *pStage = tess.xstages[stage];
int shaderAttribs = 0;
if (tess.fogNum && pStage->adjustColorsForFog)
{
shaderAttribs |= GENERICDEF_USE_FOG;
}
switch (pStage->rgbGen)
{
case CGEN_LIGHTING_DIFFUSE:
shaderAttribs |= GENERICDEF_USE_RGBAGEN;
break;
default:
break;
}
switch (pStage->alphaGen)
{
case AGEN_LIGHTING_SPECULAR:
case AGEN_PORTAL:
shaderAttribs |= GENERICDEF_USE_RGBAGEN;
break;
default:
break;
}
if (pStage->bundle[0].tcGen != TCGEN_TEXTURE)
{
shaderAttribs |= GENERICDEF_USE_TCGEN_AND_TCMOD;
}
if (tess.shader->numDeforms && !ShaderRequiresCPUDeforms(tess.shader))
{
shaderAttribs |= GENERICDEF_USE_DEFORM_VERTEXES;
}
if (glState.vertexAnimation)
{
shaderAttribs |= GENERICDEF_USE_VERTEX_ANIMATION;
}
else if (glState.boneAnimation)
{
shaderAttribs |= GENERICDEF_USE_BONE_ANIMATION;
}
if (pStage->bundle[0].numTexMods)
{
shaderAttribs |= GENERICDEF_USE_TCGEN_AND_TCMOD;
}
return &tr.genericShader[shaderAttribs];
}