SHADERSTATICPARM_SHADOWSAMPLER = 10, ///< sampler
SHADERSTATICPARM_CELSHADING = 11, ///< celshading (alternative diffuse and specular math)
SHADERSTATICPARM_CELOUTLINES = 12, ///< celoutline (depth buffer analysis to produce outlines)
+ SHADERSTATICPARM_FXAA = 13 ///< fast approximate anti aliasing
};
-#define SHADERSTATICPARMS_COUNT 13
+#define SHADERSTATICPARMS_COUNT 14
static const char *shaderstaticparmstrings_list[SHADERSTATICPARMS_COUNT];
static int shaderstaticparms_count = 0;
if (r_glsl_postprocess_uservec4_enable.integer)
R_COMPILESHADER_STATICPARM_ENABLE(SHADERSTATICPARM_POSTPROCESS_USERVEC4);
}
+ if (r_fxaa.integer)
+ R_COMPILESHADER_STATICPARM_ENABLE(SHADERSTATICPARM_FXAA);
if (r_glsl_offsetmapping_lod.integer && r_glsl_offsetmapping_lod_distance.integer > 0)
R_COMPILESHADER_STATICPARM_ENABLE(SHADERSTATICPARM_OFFSETMAPPING_USELOD);
R_COMPILESHADER_STATICPARM_EMIT(SHADERSTATICPARM_SHADOWSAMPLER, "USESHADOWSAMPLER");
R_COMPILESHADER_STATICPARM_EMIT(SHADERSTATICPARM_CELSHADING, "USECELSHADING");
R_COMPILESHADER_STATICPARM_EMIT(SHADERSTATICPARM_CELOUTLINES, "USECELOUTLINES");
+ R_COMPILESHADER_STATICPARM_EMIT(SHADERSTATICPARM_FXAA, "USEFXAA");
}
/// information about each possible shader permutation
// look up all the uniform variable names we care about, so we don't
// have to look them up every time we set them
+#if 0
+ // debugging aid
+ {
+ GLint activeuniformindex = 0;
+ GLint numactiveuniforms = 0;
+ char uniformname[128];
+ GLsizei uniformnamelength = 0;
+ GLint uniformsize = 0;
+ GLenum uniformtype = 0;
+ memset(uniformname, 0, sizeof(uniformname));
+ qglGetProgramiv(p->program, GL_ACTIVE_UNIFORMS, &numactiveuniforms);
+ Con_Printf("Shader has %i uniforms\n", numactiveuniforms);
+ for (activeuniformindex = 0;activeuniformindex < numactiveuniforms;activeuniformindex++)
+ {
+ qglGetActiveUniform(p->program, activeuniformindex, sizeof(uniformname) - 1, &uniformnamelength, &uniformsize, &uniformtype, uniformname);
+ Con_Printf("Uniform %i name \"%s\" size %i type %i\n", (int)activeuniformindex, uniformname, (int)uniformsize, (int)uniformtype);
+ }
+ }
+#endif
+
p->loc_Texture_First = qglGetUniformLocation(p->program, "Texture_First");
p->loc_Texture_Second = qglGetUniformLocation(p->program, "Texture_Second");
p->loc_Texture_GammaRamps = qglGetUniformLocation(p->program, "Texture_GammaRamps");
if (p->loc_Texture_ReflectCube >= 0) {p->tex_Texture_ReflectCube = sampler;qglUniform1i(p->loc_Texture_ReflectCube , sampler);sampler++;}
if (p->loc_Texture_BounceGrid >= 0) {p->tex_Texture_BounceGrid = sampler;qglUniform1i(p->loc_Texture_BounceGrid , sampler);sampler++;}
// get the uniform block indices so we can bind them
+#ifndef USE_GLES2 /* FIXME: GLES3 only */
if (vid.support.arb_uniform_buffer_object)
p->ubiloc_Skeletal_Transform12_UniformBlock = qglGetUniformBlockIndex(p->program, "Skeletal_Transform12_UniformBlock");
else
+#endif
p->ubiloc_Skeletal_Transform12_UniformBlock = -1;
// clear the uniform block bindings
p->ubibind_Skeletal_Transform12_UniformBlock = -1;
// bind the uniform blocks in use
ubibind = 0;
+#ifndef USE_GLES2 /* FIXME: GLES3 only */
if (p->ubiloc_Skeletal_Transform12_UniformBlock >= 0) {p->ubibind_Skeletal_Transform12_UniformBlock = ubibind;qglUniformBlockBinding(p->program, p->ubiloc_Skeletal_Transform12_UniformBlock, ubibind);ubibind++;}
+#endif
// we're done compiling and setting up the shader, at least until it is used
CHECKGLERROR
Con_DPrintf("^5GLSL shader %s compiled (%i textures).\n", permutationname, sampler);
if (!r_glsl_permutation->program)
{
if (!r_glsl_permutation->compiled)
+ {
+ Con_DPrintf("Compiling shader mode %u permutation %u\n", mode, permutation);
R_GLSL_CompilePermutation(perm, mode, permutation);
+ }
if (!r_glsl_permutation->program)
{
// remove features until we find a valid permutation
case RENDERPATH_GL20:
case RENDERPATH_GLES2:
R_SetupShader_SetPermutationGLSL(SHADERMODE_DEPTH_OR_SHADOW, permutation);
+#ifndef USE_GLES2 /* FIXME: GLES3 only */
if (r_glsl_permutation->ubiloc_Skeletal_Transform12_UniformBlock >= 0 && rsurface.batchskeletaltransform3x4buffer) qglBindBufferRange(GL_UNIFORM_BUFFER, r_glsl_permutation->ubibind_Skeletal_Transform12_UniformBlock, rsurface.batchskeletaltransform3x4buffer->bufferobject, rsurface.batchskeletaltransform3x4offset, rsurface.batchskeletaltransform3x4size);
+#endif
break;
case RENDERPATH_GL13:
case RENDERPATH_GLES1:
if (rsurface.batchskeletaltransform3x4buffer)
permutation |= SHADERPERMUTATION_SKELETAL;
R_SetupShader_SetPermutationGLSL(mode, permutation);
+#ifndef USE_GLES2 /* FIXME: GLES3 only */
if (r_glsl_permutation->ubiloc_Skeletal_Transform12_UniformBlock >= 0 && rsurface.batchskeletaltransform3x4buffer) qglBindBufferRange(GL_UNIFORM_BUFFER, r_glsl_permutation->ubibind_Skeletal_Transform12_UniformBlock, rsurface.batchskeletaltransform3x4buffer->bufferobject, rsurface.batchskeletaltransform3x4offset, rsurface.batchskeletaltransform3x4size);
+#endif
if (r_glsl_permutation->loc_ModelToReflectCube >= 0) {Matrix4x4_ToArrayFloatGL(&rsurface.matrix, m16f);qglUniformMatrix4fv(r_glsl_permutation->loc_ModelToReflectCube, 1, false, m16f);}
if (mode == SHADERMODE_LIGHTSOURCE)
{
r_loadnormalmap = true;
r_loadgloss = true;
r_loadfog = false;
+#ifdef GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT
if (vid.support.arb_uniform_buffer_object)
qglGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &r_uniformbufferalignment);
- break;
+#endif
+ break;
case RENDERPATH_GL13:
case RENDERPATH_GLES1:
Cvar_SetValueQuick(&r_textureunits, vid.texunits);
r_texture_numcubemaps = 0;
r_refdef.fogmasktable_density = 0;
+
+#ifdef __ANDROID__
+ // For Steelstorm Android
+ // FIXME CACHE the program and reload
+ // FIXME see possible combinations for SS:BR android
+ Con_DPrintf("Compiling most used shaders for SS:BR android... START\n");
+ R_SetupShader_SetPermutationGLSL(0, 12);
+ R_SetupShader_SetPermutationGLSL(0, 13);
+ R_SetupShader_SetPermutationGLSL(0, 8388621);
+ R_SetupShader_SetPermutationGLSL(3, 0);
+ R_SetupShader_SetPermutationGLSL(3, 2048);
+ R_SetupShader_SetPermutationGLSL(5, 0);
+ R_SetupShader_SetPermutationGLSL(5, 2);
+ R_SetupShader_SetPermutationGLSL(5, 2048);
+ R_SetupShader_SetPermutationGLSL(5, 8388608);
+ R_SetupShader_SetPermutationGLSL(11, 1);
+ R_SetupShader_SetPermutationGLSL(11, 2049);
+ R_SetupShader_SetPermutationGLSL(11, 8193);
+ R_SetupShader_SetPermutationGLSL(11, 10241);
+ Con_DPrintf("Compiling most used shaders for SS:BR android... END\n");
+#endif
}
static void gl_main_shutdown(void)
case RENDERPATH_GL20:
case RENDERPATH_GLES1:
case RENDERPATH_GLES2:
-#ifdef GL_SAMPLES_PASSED_ARB
+#if defined(GL_SAMPLES_PASSED_ARB) && !defined(USE_GLES2)
if (r_maxqueries)
qglDeleteQueriesARB(r_maxqueries, r_queries);
#endif
Cvar_RegisterVariable(&r_batch_dynamicbuffer);
if (gamemode == GAME_NEHAHRA || gamemode == GAME_TENEBRAE)
Cvar_SetValue("r_fullbrights", 0);
+#ifdef DP_MOBILETOUCH
+ // GLES devices have terrible depth precision in general, so...
+ Cvar_SetValueQuick(&r_nearclip, 4);
+ Cvar_SetValueQuick(&r_farclip_base, 4096);
+ Cvar_SetValueQuick(&r_farclip_world, 0);
+ Cvar_SetValueQuick(&r_useinfinitefarclip, 0);
+#endif
R_RegisterModule("GL_Main", gl_main_start, gl_main_shutdown, gl_main_newmap, NULL, NULL);
}
VID_CheckExtensions();
// LordHavoc: report supported extensions
+#ifdef CONFIG_MENU
Con_DPrintf("\nQuakeC extensions for server and client: %s\nQuakeC extensions for menu: %s\n", vm_sv_extensions, vm_m_extensions );
+#else
+ Con_DPrintf("\nQuakeC extensions for server and client: %s\n", vm_sv_extensions );
+#endif
// clear to black (loading plaque will be seen over this)
GL_Clear(GL_COLOR_BUFFER_BIT, NULL, 1.0f, 128);
case RENDERPATH_GL11:
case RENDERPATH_GL13:
case RENDERPATH_GLES1:
+#ifndef USE_GLES2
qglLoadMatrixf(gl_modelview16f);CHECKGLERROR
+#endif
break;
case RENDERPATH_SOFT:
DPSOFTRAST_UniformMatrix4fv(DPSOFTRAST_UNIFORM_ModelViewProjectionMatrixM1, 1, false, gl_modelviewprojection16f);
static void R_tcMod_ApplyToMatrix(matrix4x4_t *texmatrix, q3shaderinfo_layer_tcmod_t *tcmod, int currentmaterialflags)
{
int w, h, idx;
- double f;
- double offsetd[2];
+ float shadertime;
+ float f;
+ float offsetd[2];
float tcmat[12];
matrix4x4_t matrix, temp;
+ // if shadertime exceeds about 9 hours (32768 seconds), just wrap it,
+ // it's better to have one huge fixup every 9 hours than gradual
+ // degradation over time which looks consistently bad after many hours.
+ //
+ // tcmod scroll in particular suffers from this degradation which can't be
+ // effectively worked around even with floor() tricks because we don't
+ // know if tcmod scroll is the last tcmod being applied, and for clampmap
+ // a workaround involving floor() would be incorrect anyway...
+ shadertime = rsurface.shadertime;
+ if (shadertime >= 32768.0f)
+ shadertime -= floor(rsurface.shadertime * (1.0f / 32768.0f)) * 32768.0f;
switch(tcmod->tcmod)
{
case Q3TCMOD_COUNT:
Matrix4x4_CreateTranslate(&matrix, 0, 0, 0);
break;
case Q3TCMOD_ROTATE:
- f = tcmod->parms[0] * rsurface.shadertime;
Matrix4x4_CreateTranslate(&matrix, 0.5, 0.5, 0);
- Matrix4x4_ConcatRotate(&matrix, (f / 360 - floor(f / 360)) * 360, 0, 0, 1);
+ Matrix4x4_ConcatRotate(&matrix, tcmod->parms[0] * rsurface.shadertime, 0, 0, 1);
Matrix4x4_ConcatTranslate(&matrix, -0.5, -0.5, 0);
break;
case Q3TCMOD_SCALE:
Matrix4x4_CreateScale3(&matrix, tcmod->parms[0], tcmod->parms[1], 1);
break;
case Q3TCMOD_SCROLL:
- // extra care is needed because of precision breakdown with large values of time
+ // this particular tcmod is a "bug for bug" compatible one with regards to
+ // Quake3, the wrapping is unnecessary with our shadetime fix but quake3
+ // specifically did the wrapping and so we must mimic that...
offsetd[0] = tcmod->parms[0] * rsurface.shadertime;
offsetd[1] = tcmod->parms[1] * rsurface.shadertime;
Matrix4x4_CreateTranslate(&matrix, offsetd[0] - floor(offsetd[0]), offsetd[1] - floor(offsetd[1]), 0);
}
}
- // generate texcoords based on the chosen texcoord source
- switch(rsurface.texture->tcgen.tcgen)
+ if (rsurface.batchtexcoordtexture2f)
{
- default:
- case Q3TCGEN_TEXTURE:
- break;
- case Q3TCGEN_LIGHTMAP:
-// rsurface.batchtexcoordtexture2f = R_FrameData_Alloc(batchnumvertices * sizeof(float[2]));
-// rsurface.batchtexcoordtexture2f_vertexbuffer = NULL;
-// rsurface.batchtexcoordtexture2f_bufferoffset = 0;
- if (rsurface.batchtexcoordlightmap2f)
- memcpy(rsurface.batchtexcoordlightmap2f, rsurface.batchtexcoordtexture2f, batchnumvertices * sizeof(float[2]));
- break;
- case Q3TCGEN_VECTOR:
-// rsurface.batchtexcoordtexture2f = R_FrameData_Alloc(batchnumvertices * sizeof(float[2]));
-// rsurface.batchtexcoordtexture2f_vertexbuffer = NULL;
-// rsurface.batchtexcoordtexture2f_bufferoffset = 0;
- for (j = 0;j < batchnumvertices;j++)
- {
- rsurface.batchtexcoordtexture2f[j*2+0] = DotProduct(rsurface.batchvertex3f + 3*j, rsurface.texture->tcgen.parms);
- rsurface.batchtexcoordtexture2f[j*2+1] = DotProduct(rsurface.batchvertex3f + 3*j, rsurface.texture->tcgen.parms + 3);
- }
- break;
- case Q3TCGEN_ENVIRONMENT:
- // make environment reflections using a spheremap
- rsurface.batchtexcoordtexture2f = (float *)R_FrameData_Alloc(batchnumvertices * sizeof(float[2]));
- rsurface.batchtexcoordtexture2f_vertexbuffer = NULL;
- rsurface.batchtexcoordtexture2f_bufferoffset = 0;
- for (j = 0;j < batchnumvertices;j++)
+ // generate texcoords based on the chosen texcoord source
+ switch(rsurface.texture->tcgen.tcgen)
{
- // identical to Q3A's method, but executed in worldspace so
- // carried models can be shiny too
+ default:
+ case Q3TCGEN_TEXTURE:
+ break;
+ case Q3TCGEN_LIGHTMAP:
+ // rsurface.batchtexcoordtexture2f = R_FrameData_Alloc(batchnumvertices * sizeof(float[2]));
+ // rsurface.batchtexcoordtexture2f_vertexbuffer = NULL;
+ // rsurface.batchtexcoordtexture2f_bufferoffset = 0;
+ if (rsurface.batchtexcoordlightmap2f)
+ memcpy(rsurface.batchtexcoordtexture2f, rsurface.batchtexcoordlightmap2f, batchnumvertices * sizeof(float[2]));
+ break;
+ case Q3TCGEN_VECTOR:
+ // rsurface.batchtexcoordtexture2f = R_FrameData_Alloc(batchnumvertices * sizeof(float[2]));
+ // rsurface.batchtexcoordtexture2f_vertexbuffer = NULL;
+ // rsurface.batchtexcoordtexture2f_bufferoffset = 0;
+ for (j = 0;j < batchnumvertices;j++)
+ {
+ rsurface.batchtexcoordtexture2f[j*2+0] = DotProduct(rsurface.batchvertex3f + 3*j, rsurface.texture->tcgen.parms);
+ rsurface.batchtexcoordtexture2f[j*2+1] = DotProduct(rsurface.batchvertex3f + 3*j, rsurface.texture->tcgen.parms + 3);
+ }
+ break;
+ case Q3TCGEN_ENVIRONMENT:
+ // make environment reflections using a spheremap
+ rsurface.batchtexcoordtexture2f = (float *)R_FrameData_Alloc(batchnumvertices * sizeof(float[2]));
+ rsurface.batchtexcoordtexture2f_vertexbuffer = NULL;
+ rsurface.batchtexcoordtexture2f_bufferoffset = 0;
+ for (j = 0;j < batchnumvertices;j++)
+ {
+ // identical to Q3A's method, but executed in worldspace so
+ // carried models can be shiny too
- float viewer[3], d, reflected[3], worldreflected[3];
+ float viewer[3], d, reflected[3], worldreflected[3];
- VectorSubtract(rsurface.localvieworigin, rsurface.batchvertex3f + 3*j, viewer);
- // VectorNormalize(viewer);
+ VectorSubtract(rsurface.localvieworigin, rsurface.batchvertex3f + 3*j, viewer);
+ // VectorNormalize(viewer);
- d = DotProduct(rsurface.batchnormal3f + 3*j, viewer);
+ d = DotProduct(rsurface.batchnormal3f + 3*j, viewer);
- reflected[0] = rsurface.batchnormal3f[j*3+0]*2*d - viewer[0];
- reflected[1] = rsurface.batchnormal3f[j*3+1]*2*d - viewer[1];
- reflected[2] = rsurface.batchnormal3f[j*3+2]*2*d - viewer[2];
- // note: this is proportinal to viewer, so we can normalize later
+ reflected[0] = rsurface.batchnormal3f[j*3+0]*2*d - viewer[0];
+ reflected[1] = rsurface.batchnormal3f[j*3+1]*2*d - viewer[1];
+ reflected[2] = rsurface.batchnormal3f[j*3+2]*2*d - viewer[2];
+ // note: this is proportinal to viewer, so we can normalize later
- Matrix4x4_Transform3x3(&rsurface.matrix, reflected, worldreflected);
- VectorNormalize(worldreflected);
+ Matrix4x4_Transform3x3(&rsurface.matrix, reflected, worldreflected);
+ VectorNormalize(worldreflected);
- // note: this sphere map only uses world x and z!
- // so positive and negative y will LOOK THE SAME.
- rsurface.batchtexcoordtexture2f[j*2+0] = 0.5 + 0.5 * worldreflected[1];
- rsurface.batchtexcoordtexture2f[j*2+1] = 0.5 - 0.5 * worldreflected[2];
+ // note: this sphere map only uses world x and z!
+ // so positive and negative y will LOOK THE SAME.
+ rsurface.batchtexcoordtexture2f[j*2+0] = 0.5 + 0.5 * worldreflected[1];
+ rsurface.batchtexcoordtexture2f[j*2+1] = 0.5 - 0.5 * worldreflected[2];
+ }
+ break;
}
- break;
- }
- // the only tcmod that needs software vertex processing is turbulent, so
- // check for it here and apply the changes if needed
- // and we only support that as the first one
- // (handling a mixture of turbulent and other tcmods would be problematic
- // without punting it entirely to a software path)
- if (rsurface.texture->tcmods[0].tcmod == Q3TCMOD_TURBULENT)
- {
- amplitude = rsurface.texture->tcmods[0].parms[1];
- animpos = rsurface.texture->tcmods[0].parms[2] + rsurface.shadertime * rsurface.texture->tcmods[0].parms[3];
-// rsurface.batchtexcoordtexture2f = R_FrameData_Alloc(batchnumvertices * sizeof(float[2]));
-// rsurface.batchtexcoordtexture2f_vertexbuffer = NULL;
-// rsurface.batchtexcoordtexture2f_bufferoffset = 0;
- for (j = 0;j < batchnumvertices;j++)
- {
- rsurface.batchtexcoordtexture2f[j*2+0] += amplitude * sin(((rsurface.batchvertex3f[j*3+0] + rsurface.batchvertex3f[j*3+2]) * 1.0 / 1024.0f + animpos) * M_PI * 2);
- rsurface.batchtexcoordtexture2f[j*2+1] += amplitude * sin(((rsurface.batchvertex3f[j*3+1] ) * 1.0 / 1024.0f + animpos) * M_PI * 2);
+ // the only tcmod that needs software vertex processing is turbulent, so
+ // check for it here and apply the changes if needed
+ // and we only support that as the first one
+ // (handling a mixture of turbulent and other tcmods would be problematic
+ // without punting it entirely to a software path)
+ if (rsurface.texture->tcmods[0].tcmod == Q3TCMOD_TURBULENT)
+ {
+ amplitude = rsurface.texture->tcmods[0].parms[1];
+ animpos = rsurface.texture->tcmods[0].parms[2] + rsurface.shadertime * rsurface.texture->tcmods[0].parms[3];
+ // rsurface.batchtexcoordtexture2f = R_FrameData_Alloc(batchnumvertices * sizeof(float[2]));
+ // rsurface.batchtexcoordtexture2f_vertexbuffer = NULL;
+ // rsurface.batchtexcoordtexture2f_bufferoffset = 0;
+ for (j = 0;j < batchnumvertices;j++)
+ {
+ rsurface.batchtexcoordtexture2f[j*2+0] += amplitude * sin(((rsurface.batchvertex3f[j*3+0] + rsurface.batchvertex3f[j*3+2]) * 1.0 / 1024.0f + animpos) * M_PI * 2);
+ rsurface.batchtexcoordtexture2f[j*2+1] += amplitude * sin(((rsurface.batchvertex3f[j*3+1] ) * 1.0 / 1024.0f + animpos) * M_PI * 2);
+ }
}
}
static void R_DrawDebugModel(void)
{
entity_render_t *ent = rsurface.entity;
- int i, j, k, l, flagsmask;
+ int i, j, flagsmask;
const msurface_t *surface;
dp_model_t *model = ent->model;
- vec3_t v;
if (!sv.active && !cls.demoplayback && ent != r_refdef.scene.worldentity)
return;
if (r_shownormals.value != 0 && qglBegin)
{
+ int l, k;
+ vec3_t v;
if (r_showdisabledepthtest.integer)
{
GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);