Page 1 of 1

Extending the number of dynamic lights

Posted: Mon Aug 22, 2011 3:38 pm
by mh
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).

Code: Select all

#define MAX_DLIGHTS 128
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:

Code: Select all

int			dlightbits[(MAX_DLIGHTS + 31) >> 5];
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:

Code: Select all

void R_PushDlights (struct mnode_s *headnode);
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):

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);
	}
}
And the new R_MarkLights (also gl_light.c):

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]);
}
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:

Code: Select all

R_PushDlights (cl.worldmodel->nodes);
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:

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);
		}
	}
To this not-so-mess:

Code: Select all

	if (clmodel->firstmodelsurface != 0) R_PushDlights (clmodel->nodes + clmodel->hulls[0].firstclipnode);
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:

Code: Select all

if (!(surf->dlightbits & (1 << lnum))) continue;
To this:

Code: Select all

if (!(surf->dlightbits[lnum >> 5] & (1 << (lnum & 31)))) continue;
And that's it. Have fun.

Posted: Tue Aug 23, 2011 2:25 am
by qbism
This tute could be subtitled "cleaning up a lot of dlight garbage". R_MarkLights is greatly cleaned up. Silly r_dlightframecount goes away. Thank you!

In SW engine, R_PushDlights call would go in R_RenderView under R_SetupFrame.

BTW, R_PushDlights declaration could go to r_local.h.

Re: Extending the number of dynamic lights

Posted: Tue Aug 23, 2011 1:20 pm
by leileilol
mh wrote: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).
Could've been cutting room floor for bitflags for unreal-esque dynamic light effects such as this:

Image


Hopefully this dlight cleanup code helps my shitty shadow implementation lol

Posted: Wed Aug 24, 2011 3:26 am
by qbism
That shimmer would be cool around water/ lava.

Darklights still bleed through like regular dlights unless expensive vis checks... checks that slow platforms can't cash.