rendering lighting everywhere but shadow.
In our case we use a biased stencil clear of 128 to avoid requiring the
-stencil wrap extension (but probably should support it).
+stencil wrap extension (but probably should support it), and to address
+Creative's patent on this sort of technology we also draw the frontfaces
+first, and backfaces second (decrement, increment).
+
+Patent warning:
+This algorithm may be covered by Creative's patent (US Patent #6384822)
+on Carmack's Reverse paper (which I have not read), however that patent
+seems to be about drawing a stencil shadow from a model in an otherwise
+unshadowed scene, where as realtime lighting technology draws light where
+shadows do not lie.
cvar_t r_shadow_scissor = {0, "r_shadow_scissor", "1"};
cvar_t r_shadow_bumpscale_bumpmap = {0, "r_shadow_bumpscale_bumpmap", "4"};
cvar_t r_shadow_bumpscale_basetexture = {0, "r_shadow_bumpscale_basetexture", "0"};
-cvar_t r_shadow_polygonoffset = {0, "r_shadow_polygonoffset", "0"};
+cvar_t r_shadow_polygonfactor = {0, "r_shadow_polygonfactor", "0"};
+cvar_t r_shadow_polygonoffset = {0, "r_shadow_polygonoffset", "1"};
cvar_t r_shadow_portallight = {0, "r_shadow_portallight", "1"};
cvar_t r_shadow_projectdistance = {0, "r_shadow_projectdistance", "10000"};
cvar_t r_shadow_texture3d = {0, "r_shadow_texture3d", "1"};
"r_shadow_scissor : use scissor optimization\n"
"r_shadow_bumpscale_bumpmap : depth scale for bumpmap conversion\n"
"r_shadow_bumpscale_basetexture : base texture as bumpmap with this scale\n"
+"r_shadow_polygonfactor : nudge shadow volumes closer/further\n"
"r_shadow_polygonoffset : nudge shadow volumes closer/further\n"
"r_shadow_portallight : use portal visibility for static light precomputation\n"
"r_shadow_projectdistance : shadow volume projection distance\n"
Cvar_RegisterVariable(&r_shadow_scissor);
Cvar_RegisterVariable(&r_shadow_bumpscale_bumpmap);
Cvar_RegisterVariable(&r_shadow_bumpscale_basetexture);
+ Cvar_RegisterVariable(&r_shadow_polygonfactor);
Cvar_RegisterVariable(&r_shadow_polygonoffset);
Cvar_RegisterVariable(&r_shadow_portallight);
Cvar_RegisterVariable(&r_shadow_projectdistance);
GL_VertexPointer(varray_vertex3f2);
if (r_shadowstage == SHADOWSTAGE_STENCIL)
{
- // increment stencil if backface is behind depthbuffer
- qglCullFace(GL_BACK); // quake is backwards, this culls front faces
- qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
- R_Mesh_Draw(outverts, tris, shadowelements);
- c_rt_shadowmeshes++;
- c_rt_shadowtris += numtris;
// decrement stencil if frontface is behind depthbuffer
qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
+ R_Mesh_Draw(outverts, tris, shadowelements);
+ c_rt_shadowmeshes++;
+ c_rt_shadowtris += numtris;
+ // increment stencil if backface is behind depthbuffer
+ qglCullFace(GL_BACK); // quake is backwards, this culls front faces
+ qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
}
R_Mesh_Draw(outverts, tris, shadowelements);
c_rt_shadowmeshes++;
shadowmesh_t *mesh;
if (r_shadowstage == SHADOWSTAGE_STENCIL)
{
- // increment stencil if backface is behind depthbuffer
- qglCullFace(GL_BACK); // quake is backwards, this culls front faces
- qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
+ // decrement stencil if frontface is behind depthbuffer
+ qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
+ qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
for (mesh = firstmesh;mesh;mesh = mesh->next)
{
GL_VertexPointer(mesh->vertex3f);
c_rtcached_shadowmeshes++;
c_rtcached_shadowtris += mesh->numtriangles;
}
- // decrement stencil if frontface is behind depthbuffer
- qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
- qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
+ // increment stencil if backface is behind depthbuffer
+ qglCullFace(GL_BACK); // quake is backwards, this culls front faces
+ qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
}
for (mesh = firstmesh;mesh;mesh = mesh->next)
{
GL_DepthTest(true);
R_Mesh_State_Texture(&m);
GL_Color(0, 0, 0, 1);
+ qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
qglDisable(GL_SCISSOR_TEST);
r_shadowstage = SHADOWSTAGE_NONE;
GL_BlendFunc(GL_ONE, GL_ZERO);
GL_DepthMask(false);
GL_DepthTest(true);
- if (r_shadow_polygonoffset.value != 0)
- {
- qglPolygonOffset(1.0f, r_shadow_polygonoffset.value);
- qglEnable(GL_POLYGON_OFFSET_FILL);
- }
- else
- qglDisable(GL_POLYGON_OFFSET_FILL);
+ qglPolygonOffset(r_shadow_polygonfactor.value, r_shadow_polygonoffset.value);
+ //if (r_shadow_polygonoffset.value != 0)
+ //{
+ // qglPolygonOffset(r_shadow_polygonfactor.value, r_shadow_polygonoffset.value);
+ // qglEnable(GL_POLYGON_OFFSET_FILL);
+ //}
+ //else
+ // qglDisable(GL_POLYGON_OFFSET_FILL);
qglDepthFunc(GL_LESS);
+ qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
qglEnable(GL_STENCIL_TEST);
qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
qglStencilFunc(GL_ALWAYS, 128, 0xFF);
GL_BlendFunc(GL_ONE, GL_ONE);
GL_DepthMask(false);
GL_DepthTest(true);
- qglDisable(GL_POLYGON_OFFSET_FILL);
+ qglPolygonOffset(0, 0);
+ //qglDisable(GL_POLYGON_OFFSET_FILL);
GL_Color(1, 1, 1, 1);
qglColorMask(1, 1, 1, 1);
qglDepthFunc(GL_EQUAL);
+ qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
qglDisable(GL_STENCIL_TEST);
qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
qglStencilFunc(GL_EQUAL, 128, 0xFF);
GL_BlendFunc(GL_ONE, GL_ONE);
GL_DepthMask(false);
GL_DepthTest(true);
- qglDisable(GL_POLYGON_OFFSET_FILL);
+ qglPolygonOffset(0, 0);
+ //qglDisable(GL_POLYGON_OFFSET_FILL);
GL_Color(1, 1, 1, 1);
qglColorMask(1, 1, 1, 1);
qglDepthFunc(GL_EQUAL);
+ qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
qglEnable(GL_STENCIL_TEST);
qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
// only draw light where this geometry was already rendered AND the
GL_BlendFunc(GL_ONE, GL_ZERO);
GL_DepthMask(true);
GL_DepthTest(true);
- qglDisable(GL_POLYGON_OFFSET_FILL);
+ qglPolygonOffset(0, 0);
+ //qglDisable(GL_POLYGON_OFFSET_FILL);
GL_Color(1, 1, 1, 1);
qglColorMask(1, 1, 1, 1);
qglDisable(GL_SCISSOR_TEST);
qglDepthFunc(GL_LEQUAL);
+ qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
qglDisable(GL_STENCIL_TEST);
qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
qglStencilFunc(GL_ALWAYS, 128, 0xFF);
{
int entnum, style, islight;
char key[256], value[1024];
- float origin[3], radius, color[3], light, scale, originhack[3], overridecolor[3];
+ float origin[3], radius, color[3], light, fadescale, lightscale, originhack[3], overridecolor[3];
const char *data;
if (cl.worldmodel == NULL)
originhack[0] = originhack[1] = originhack[2] = 0;
color[0] = color[1] = color[2] = 1;
overridecolor[0] = overridecolor[1] = overridecolor[2] = 1;
- scale = 1;
+ fadescale = 1;
+ lightscale = 1;
style = 0;
islight = false;
while (1)
else if (!strcmp("color", key))
sscanf(value, "%f %f %f", &color[0], &color[1], &color[2]);
else if (!strcmp("wait", key))
- scale = atof(value);
+ fadescale = atof(value);
else if (!strcmp("classname", key))
{
if (!strncmp(value, "light", 5))
}
else if (!strcmp("style", key))
style = atoi(value);
+ else if (cl.worldmodel->type == mod_brushq3)
+ {
+ if (!strcmp("scale", key))
+ lightscale = atof(value);
+ if (!strcmp("fade", key))
+ fadescale = atof(value);
+ }
}
if (light <= 0 && islight)
light = 300;
- radius = min(light * r_editlights_quakelightsizescale.value / scale, 1048576);
+ if (lightscale <= 0)
+ lightscale = 1;
+ if (fadescale <= 0)
+ fadescale = 1;
+ radius = min(light * r_editlights_quakelightsizescale.value * lightscale / fadescale, 1048576);
light = sqrt(bound(0, light, 1048576)) * (1.0f / 16.0f);
if (color[0] == 1 && color[1] == 1 && color[2] == 1)
VectorCopy(overridecolor, color);