Extending the number of dynamic lights
Posted: Mon Aug 22, 2011 3:38 pm
Stock ID Quake caps the max number of dynamic lights to 32, but let's face it, lots of dynamic lights make the world a much more interesting place. So let's increase it.
Unfortunately we can't just bump the value of MAX_DLIGHTS because which dynamic lights hit a surface is stored in a 32-bit integer, so we need something more.
First thing, let's find the #define of MAX_DLIGHTS and move it to the top of quakedef.h - we do this because we're going to be referencing it in a number of header files so it's handier to have it here. While we're at it, let's also increase the number; I picked 128 for this example but you can pick something else if you want (I recommend a multiple of 32).
Now find the dlightbits member of the msurface_t struct; for GLQuake it should be in gl_model.h (this also works with software Quake although some of the names are different, and should be even easily adaptable to Quake II), and change it to:
I do it this way so that if I ever change the value of MAX_DLIGHTS I don't need to go hunting through multiple locations for anything that depends on it.
There's also a dlightbits member of entity_t (in render.h), but that's not used in either GLQuake or software Quake. Delete it if you want, it might save you some confusion later on (and save you 4 bytes of memory per entity if you're into that kind of thing).
OK, so we've changed how we store dynamic lights, now we need to change how we mark them. Find the definition of R_PushDlights in render.h and change it to:
This is just a convenience so that we can use the same function for brush models as we use for the world.
Next, here's the new R_PushDlights (gl_light.c):
And the new R_MarkLights (also gl_light.c):
Now, this R_PushDlights function is called from a pretty stupid place - view.c - so let's move it to R_DrawWorld in gl_rsurf.c instead; pop it just before the call to R_RecursiveWorldNode like so:
You'll note that this has enabled us to get rid of r_dlightframecount and use r_framecount instead, which is more nice cleaning up.
Moving on to brush models, look for R_DrawBrushModel (also in gl_rsurf.c) and change this mess:
To this not-so-mess:
More cleaning up here.
So, all that's left is to change how we actually update the lightmaps for each surface, so still in gl_rsurf.c look for R_AddDynamicLights and change this:
To this:
And that's it. Have fun.
Unfortunately we can't just bump the value of MAX_DLIGHTS because which dynamic lights hit a surface is stored in a 32-bit integer, so we need something more.
First thing, let's find the #define of MAX_DLIGHTS and move it to the top of quakedef.h - we do this because we're going to be referencing it in a number of header files so it's handier to have it here. While we're at it, let's also increase the number; I picked 128 for this example but you can pick something else if you want (I recommend a multiple of 32).
Code: Select all
#define MAX_DLIGHTS 128Code: Select all
int dlightbits[(MAX_DLIGHTS + 31) >> 5];There's also a dlightbits member of entity_t (in render.h), but that's not used in either GLQuake or software Quake. Delete it if you want, it might save you some confusion later on (and save you 4 bytes of memory per entity if you're into that kind of thing).
OK, so we've changed how we store dynamic lights, now we need to change how we mark them. Find the definition of R_PushDlights in render.h and change it to:
Code: Select all
void R_PushDlights (struct mnode_s *headnode);Next, here's the new R_PushDlights (gl_light.c):
Code: Select all
void R_PushDlights (mnode_t *headnode)
{
int i;
dlight_t *l = cl_dlights;
for (i = 0; i < MAX_DLIGHTS; i++, l++)
{
if (l->die < cl.time || (l->radius <= 0))
continue;
R_MarkLights (l, i, headnode);
}
}Code: Select all
void R_MarkLights (dlight_t *light, int num, mnode_t *node)
{
mplane_t *splitplane;
float dist;
msurface_t *surf;
int i;
if (node->contents < 0)
return;
splitplane = node->plane;
dist = DotProduct (light->origin, splitplane->normal) - splitplane->dist;
if (dist > light->radius)
{
R_MarkLights (light, num, node->children[0]);
return;
}
if (dist < -light->radius)
{
R_MarkLights (light, num, node->children[1]);
return;
}
// mark the polygons
surf = cl.worldmodel->surfaces + node->firstsurface;
for (i = 0; i < node->numsurfaces; i++, surf++)
{
if (surf->dlightframe != r_framecount)
{
memset (surf->dlightbits, 0, sizeof (surf->dlightbits));
surf->dlightframe = r_framecount;
}
surf->dlightbits[num >> 5] |= 1 << (num & 31);
}
R_MarkLights (light, num, node->children[0]);
R_MarkLights (light, num, node->children[1]);
}Code: Select all
R_PushDlights (cl.worldmodel->nodes);Moving on to brush models, look for R_DrawBrushModel (also in gl_rsurf.c) and change this mess:
Code: Select all
if (clmodel->firstmodelsurface != 0)
{
for (k = 0; k < MAX_DLIGHTS; k++)
{
if ((cl_dlights[k].die < cl.time) || (!cl_dlights[k].radius))
continue;
R_MarkLights (&cl_dlights[k], 1 << k, clmodel->nodes + clmodel->hulls[0].firstclipnode);
}
}Code: Select all
if (clmodel->firstmodelsurface != 0) R_PushDlights (clmodel->nodes + clmodel->hulls[0].firstclipnode);So, all that's left is to change how we actually update the lightmaps for each surface, so still in gl_rsurf.c look for R_AddDynamicLights and change this:
Code: Select all
if (!(surf->dlightbits & (1 << lnum))) continue;Code: Select all
if (!(surf->dlightbits[lnum >> 5] & (1 << (lnum & 31)))) continue;