static const char *builtinshaderstring =
"// ambient+diffuse+specular+normalmap+attenuation+cubemap+fog shader\n"
"// written by Forest 'LordHavoc' Hale\n"
+"\n"
+"// enable various extensions depending on permutation:\n"
+"\n"
"#ifdef USESHADOWMAPRECT\n"
"# extension GL_ARB_texture_rectangle : enable\n"
"#endif\n"
"# extension GL_EXT_gpu_shader4 : enable\n"
"#endif\n"
"\n"
+"#ifdef USESHADOWSAMPLER\n"
+"# extension GL_ARB_shadow : enable\n"
+"#endif\n"
+"\n"
"// common definitions between vertex shader and fragment shader:\n"
"\n"
"//#ifdef __GLSL_CG_DATA_TYPES\n"
extern qboolean r_shadow_usingshadowmap2d;
extern float r_shadow_shadowmap_texturescale[4];
extern float r_shadow_shadowmap_parameters[4];
-extern int r_shadow_shadowmapvsdct;
-extern int r_shadow_shadowmapfilter;
+extern qboolean r_shadow_shadowmapvsdct;
+extern qboolean r_shadow_shadowmapsampler;
+extern int r_shadow_shadowmappcf;
void R_SetupSurfaceShader(const vec3_t lightcolorbase, qboolean modellighting, float ambientscale, float diffusescale, float specularscale, rsurfacepass_t rsurfacepass)
{
// select a permutation of the lighting shader appropriate to this
else if(r_shadow_shadowmapvsdct)
permutation |= SHADERPERMUTATION_SHADOWMAPVSDCT;
- switch (r_shadow_shadowmapfilter)
- {
- case 1:
+ if (r_shadow_shadowmapsampler)
permutation |= SHADERPERMUTATION_SHADOWSAMPLER;
- break;
- case 2:
- permutation |= SHADERPERMUTATION_SHADOWSAMPLER | SHADERPERMUTATION_SHADOWMAPPCF;
- break;
- case 3:
- permutation |= SHADERPERMUTATION_SHADOWMAPPCF;
- break;
- case 4:
+ if (r_shadow_shadowmappcf > 1)
permutation |= SHADERPERMUTATION_SHADOWMAPPCF2;
- break;
- }
+ else if (r_shadow_shadowmappcf)
+ permutation |= SHADERPERMUTATION_SHADOWMAPPCF;
}
}
else if (rsurface.texture->currentmaterialflags & MATERIALFLAG_FULLBRIGHT)
GLuint r_shadow_fbocubeside[R_SHADOW_SHADOWMAP_NUMCUBEMAPS][6];
GLuint r_shadow_fbo2d;
int r_shadow_shadowmode;
-int r_shadow_shadowmapvsdct;
+int r_shadow_shadowmapfilterquality;
+int r_shadow_shadowmaptexturetype;
int r_shadow_shadowmapmaxsize;
-int r_shadow_shadowmapfilter;
+qboolean r_shadow_shadowmapvsdct;
+qboolean r_shadow_shadowmapsampler;
+int r_shadow_shadowmappcf;
int r_shadow_shadowmapborder;
int r_shadow_lightscissor[4];
cvar_t r_shadow_realtime_world_compileportalculling = {0, "r_shadow_realtime_world_compileportalculling", "1", "enables portal-based culling optimization during compilation"};
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_shadowmapping = {CVAR_SAVE, "r_shadow_shadowmapping", "0", "enables use of shadowmapping (depth texture sampling) instead of stencil shadow volumes, requires gl_fbo 1"};
-cvar_t r_shadow_shadowmapping_filterquality = {CVAR_SAVE, "r_shadow_shadowmapping_filterquality", "0", "shadowmap filter modes: 0 = no filtering, 1 = bilinear, 2 = bilinear 2x2 blur (fast), 3 = 3x3 blur (moderate), 4 = 4x4 blur (slow)"};
+cvar_t r_shadow_shadowmapping_texturetype = {CVAR_SAVE, "r_shadow_shadowmapping_texturetype", "0", "shadowmap texture types: 0 = auto-select, 1 = 2D, 2 = rectangle, 3 = cubemap"};
+cvar_t r_shadow_shadowmapping_filterquality = {CVAR_SAVE, "r_shadow_shadowmapping_filterquality", "-1", "shadowmap filter modes: -1 = auto-select, 0 = no filtering, 1 = bilinear, 2 = bilinear 2x2 blur (fast), 3 = 3x3 blur (moderate), 4 = 4x4 blur (slow)"};
cvar_t r_shadow_shadowmapping_vsdct = {CVAR_SAVE, "r_shadow_shadowmapping_vsdct", "1", "enables use of virtual shadow depth cube texture"};
cvar_t r_shadow_shadowmapping_minsize = {CVAR_SAVE, "r_shadow_shadowmapping_minsize", "32", "shadowmap size limit"};
cvar_t r_shadow_shadowmapping_maxsize = {CVAR_SAVE, "r_shadow_shadowmapping_maxsize", "512", "shadowmap size limit"};
cachepic_t *r_editlights_sprcubemapnoshadowlight;
cachepic_t *r_editlights_sprselection;
-void R_Shadow_FreeShadowMaps(void)
+void R_Shadow_SetShadowMode(void)
{
- int i;
-
r_shadow_shadowmapmaxsize = bound(1, r_shadow_shadowmapping_maxsize.integer, 2048);
- r_shadow_shadowmode = r_shadow_shadowmapping.integer;
- r_shadow_shadowmapvsdct = r_shadow_shadowmapping_vsdct.integer;
- r_shadow_shadowmapfilter = r_shadow_shadowmapping_filterquality.integer;
+ r_shadow_shadowmapvsdct = r_shadow_shadowmapping_vsdct.integer != 0;
+ r_shadow_shadowmapfilterquality = r_shadow_shadowmapping_filterquality.integer;
+ r_shadow_shadowmaptexturetype = r_shadow_shadowmapping_texturetype.integer;
r_shadow_shadowmapborder = bound(0, r_shadow_shadowmapping_bordersize.integer, 16);
r_shadow_shadowmaplod = -1;
+ r_shadow_shadowmapsampler = false;
+ r_shadow_shadowmappcf = 0;
+ r_shadow_shadowmode = 0;
+ if(r_shadow_shadowmapping.integer)
+ {
+ if(r_shadow_shadowmapfilterquality < 0)
+ {
+ if(strstr(gl_vendor, "NVIDIA"))
+ {
+ r_shadow_shadowmapsampler = gl_support_arb_shadow;
+ r_shadow_shadowmappcf = 1;
+ }
+ else if(gl_support_amd_texture_texture4 || gl_support_arb_texture_gather)
+ r_shadow_shadowmappcf = 1;
+ else if(strstr(gl_vendor, "ATI"))
+ r_shadow_shadowmappcf = 1;
+ else
+ r_shadow_shadowmapsampler = gl_support_arb_shadow;
+ }
+ else
+ {
+ switch (r_shadow_shadowmapfilterquality)
+ {
+ case 1:
+ r_shadow_shadowmapsampler = gl_support_arb_shadow;
+ break;
+ case 2:
+ r_shadow_shadowmapsampler = gl_support_arb_shadow;
+ r_shadow_shadowmappcf = 1;
+ break;
+ case 3:
+ r_shadow_shadowmappcf = 1;
+ break;
+ case 4:
+ r_shadow_shadowmappcf = 2;
+ break;
+ }
+ }
+ r_shadow_shadowmode = r_shadow_shadowmaptexturetype;
+ if(r_shadow_shadowmode <= 0)
+ {
+ if(!gl_texturerectangle || gl_support_arb_texture_non_power_of_two)
+ r_shadow_shadowmode = 1;
+ else if((gl_support_amd_texture_texture4 || gl_support_arb_texture_gather) && r_shadow_shadowmappcf && !r_shadow_shadowmapsampler)
+ r_shadow_shadowmode = 1;
+ else
+ r_shadow_shadowmode = 2;
+ }
+ }
+}
+
+void R_Shadow_FreeShadowMaps(void)
+{
+ int i;
+
+ R_Shadow_SetShadowMode();
- CHECKGLERROR
if (r_shadow_fborectangle)
qglDeleteFramebuffersEXT(1, &r_shadow_fborectangle);
r_shadow_fborectangle = 0;
r_shadow_shadowmapmaxsize = 0;
r_shadow_shadowmapsize = 0;
r_shadow_shadowmaplod = 0;
- r_shadow_shadowmapvsdct = 0;
- r_shadow_shadowmapfilter = 0;
+ r_shadow_shadowmapfilterquality = 0;
+ r_shadow_shadowmaptexturetype = 0;
+ r_shadow_shadowmapvsdct = false;
+ r_shadow_shadowmapsampler = false;
+ r_shadow_shadowmappcf = 0;
r_shadow_fborectangle = 0;
r_shadow_fbo2d = 0;
memset(r_shadow_fbocubeside, 0, sizeof(r_shadow_fbocubeside));
Cvar_RegisterVariable(&r_shadow_scissor);
Cvar_RegisterVariable(&r_shadow_shadowmapping);
Cvar_RegisterVariable(&r_shadow_shadowmapping_vsdct);
+ Cvar_RegisterVariable(&r_shadow_shadowmapping_texturetype);
Cvar_RegisterVariable(&r_shadow_shadowmapping_filterquality);
Cvar_RegisterVariable(&r_shadow_shadowmapping_maxsize);
Cvar_RegisterVariable(&r_shadow_shadowmapping_minsize);
if (!r_shadow_shadowmap2dtexture)
{
#if 1
- r_shadow_shadowmap2dtexture = R_LoadTextureShadowMap2D(r_shadow_texturepool, "shadowmap", maxsize*2, maxsize*4, r_shadow_shadowmapfilter == 1 || r_shadow_shadowmapfilter == 2);
+ int w = maxsize*2, h = gl_support_arb_texture_non_power_of_two ? maxsize*3 : maxsize*4;
+ r_shadow_shadowmap2dtexture = R_LoadTextureShadowMap2D(r_shadow_texturepool, "shadowmap", w, h, r_shadow_shadowmapsampler);
qglGenFramebuffersEXT(1, &r_shadow_fbo2d);CHECKGLERROR
qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, r_shadow_fbo2d);CHECKGLERROR
qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, R_GetTexture(r_shadow_shadowmap2dtexture), 0);CHECKGLERROR
R_Viewport_InitRectSideView(&viewport, &rsurface.rtlight->matrix_lighttoworld, side, size, r_shadow_shadowmapping_bordersize.integer, nearclip, farclip, NULL);
r_shadow_shadowmap_texturescale[0] = 1.0f / R_TextureWidth(r_shadow_shadowmap2dtexture);
r_shadow_shadowmap_texturescale[1] = 1.0f / R_TextureHeight(r_shadow_shadowmap2dtexture);
- r_shadow_shadowmap_parameters[0] = r_shadow_shadowmapvsdct ? size / (float)maxsize : 0.5f * (size - r_shadow_shadowmapborder);
- r_shadow_shadowmap_parameters[1] = r_shadow_shadowmapvsdct ? 0.75f * size / (float)maxsize : size;
+ r_shadow_shadowmap_parameters[0] = r_shadow_shadowmapvsdct ? 2*size * r_shadow_shadowmap_texturescale[0] : 0.5f * (size - r_shadow_shadowmapborder);
+ r_shadow_shadowmap_parameters[1] = r_shadow_shadowmapvsdct ? 3*size * r_shadow_shadowmap_texturescale[1] : size;
r_shadow_rendermode = R_SHADOW_RENDERMODE_SHADOWMAP2D;
}
else if (r_shadow_shadowmode == 2)
if (!r_shadow_shadowmaprectangletexture)
{
#if 1
- r_shadow_shadowmaprectangletexture = R_LoadTextureShadowMapRectangle(r_shadow_texturepool, "shadowmap", maxsize*2, maxsize*3, r_shadow_shadowmapfilter == 1 || r_shadow_shadowmapfilter == 2);
+ r_shadow_shadowmaprectangletexture = R_LoadTextureShadowMapRectangle(r_shadow_texturepool, "shadowmap", maxsize*2, maxsize*3, r_shadow_shadowmapsampler);
qglGenFramebuffersEXT(1, &r_shadow_fborectangle);CHECKGLERROR
qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, r_shadow_fborectangle);CHECKGLERROR
qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_RECTANGLE_ARB, R_GetTexture(r_shadow_shadowmaprectangletexture), 0);CHECKGLERROR
if (!r_shadow_shadowmapcubetexture[r_shadow_shadowmaplod])
{
#if 1
- r_shadow_shadowmapcubetexture[r_shadow_shadowmaplod] = R_LoadTextureShadowMapCube(r_shadow_texturepool, "shadowmapcube", bound(1, maxsize >> r_shadow_shadowmaplod, 2048), r_shadow_shadowmapfilter == 1 || r_shadow_shadowmapfilter == 2);
+ r_shadow_shadowmapcubetexture[r_shadow_shadowmaplod] = R_LoadTextureShadowMapCube(r_shadow_texturepool, "shadowmapcube", bound(1, maxsize >> r_shadow_shadowmaplod, 2048), r_shadow_shadowmapsampler);
qglGenFramebuffersEXT(6, r_shadow_fbocubeside[r_shadow_shadowmaplod]);CHECKGLERROR
for (i = 0;i < 6;i++)
{
dlight_t *light;
size_t range;
- if (r_shadow_shadowmapmaxsize != bound(1, r_shadow_shadowmapping_maxsize.integer, 2048) || r_shadow_shadowmode != r_shadow_shadowmapping.integer || r_shadow_shadowmapvsdct != r_shadow_shadowmapping_vsdct.integer || r_shadow_shadowmapfilter != r_shadow_shadowmapping_filterquality.integer || r_shadow_shadowmapborder != bound(0, r_shadow_shadowmapping_bordersize.integer, 16))
+ if (r_shadow_shadowmapmaxsize != bound(1, r_shadow_shadowmapping_maxsize.integer, 2048) ||
+ (r_shadow_shadowmode != 0) != (r_shadow_shadowmapping.integer != 0) ||
+ r_shadow_shadowmapvsdct != (r_shadow_shadowmapping_vsdct.integer != 0) ||
+ r_shadow_shadowmaptexturetype != r_shadow_shadowmapping_texturetype.integer ||
+ r_shadow_shadowmapfilterquality != r_shadow_shadowmapping_filterquality.integer ||
+ r_shadow_shadowmapborder != bound(0, r_shadow_shadowmapping_bordersize.integer, 16))
R_Shadow_FreeShadowMaps();
if (r_editlights.integer)
int gl_dot3arb = false;
// GL_ARB_depth_texture
int gl_depthtexture = false;
+// GL_ARB_shadow
+int gl_support_arb_shadow = false;
// GL_SGIS_texture_edge_clamp
int gl_support_clamptoedge = false;
// GL_EXT_texture_filter_anisotropic
int gl_support_texture_compression = false;
//GL_ARB_occlusion_query
int gl_support_arb_occlusion_query = false;
+//GL_AMD_texture_texture4
+int gl_support_amd_texture_texture4 = false;
+//GL_ARB_texture_gather
+int gl_support_arb_texture_gather = false;
// LordHavoc: if window is hidden, don't update screen
qboolean vid_hidden = true;
gl_support_arb_texture_non_power_of_two = false;
gl_dot3arb = false;
gl_depthtexture = false;
+ gl_support_arb_shadow = false;
gl_support_clamptoedge = false;
gl_support_anisotropy = false;
gl_max_anisotropy = 1;
gl_support_ext_framebuffer_object = false;
gl_support_texture_compression = false;
gl_support_arb_occlusion_query = false;
+ gl_support_amd_texture_texture4 = false;
+ gl_support_arb_texture_gather = false;
if (!GL_CheckExtension("1.1", opengl110funcs, NULL, false))
Sys_Error("OpenGL 1.1.0 functions not found");
// COMMANDLINEOPTION: GL: -norectangle disables GL_ARB_texture_rectangle (required for bumpmapping)
if ((gl_texturerectangle = GL_CheckExtension("GL_ARB_texture_rectangle", NULL, "-norectangle", false)))
qglGetIntegerv(GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB, &gl_max_rectangle_texture_size);
+// COMMANDLINEOPTION: GL: -nodepthtexture disables use of GL_ARB_depth_texture (required for shadowmapping)
gl_depthtexture = GL_CheckExtension("GL_ARB_depth_texture", NULL, "-nodepthtexture", false);
+// COMMANDLINEOPTION: GL: -noshadow disables use of GL_ARB_shadow (required for hardware shadowmap filtering)
+ gl_support_arb_shadow = GL_CheckExtension("GL_ARB_shadow", NULL, "-noshadow", false);
+
// COMMANDLINEOPTION: GL: -notexturecompression disables GL_ARB_texture_compression (which saves video memory if it is supported, but can also degrade image quality, see gl_texturecompression cvar documentation for more information)
gl_support_texture_compression = GL_CheckExtension("GL_ARB_texture_compression", texturecompressionfuncs, "-notexturecompression", false);
// COMMANDLINEOPTION: GL: -nocva disables GL_EXT_compiled_vertex_array (renders faster)
// COMMANDLINEOPTION: GL: -noocclusionquery disables GL_ARB_occlusion_query (which allows coronas to fade according to visibility, and potentially used for rendering optimizations)
gl_support_arb_occlusion_query = GL_CheckExtension("GL_ARB_occlusion_query", occlusionqueryfuncs, "-noocclusionquery", false);
+
+// COMMANDLINEOPTION: GL: -notexture4 disables GL_AMD_texture_texture4 (which provides fetch4 sampling)
+ gl_support_amd_texture_texture4 = GL_CheckExtension("GL_AMD_texture_texture4", NULL, "-notexture4", false);
+// COMMANDLINEOPTION: GL: -notexturegather disables GL_ARB_texture_gather (which provides fetch4 sampling)
+ gl_support_arb_texture_gather = GL_CheckExtension("GL_ARB_texture_gather", NULL, "-notexturegather", false);
}
void Force_CenterView_f (void)