Page 1 of 3

Dynamic lights on moving brush models

Posted: Sun Mar 04, 2012 8:46 pm
by mh
Here we're going to fix dynamic lights so that they will correctly apply to moving brush models. This can be seen all over Quake, but a good example is the first secret in e1m4. After the shaft doors open and you kill the wizard, give yourself the rocket launcher and shoot it up at the platform. It's lit properly. Now shoot the two switches to lower the platform and jump to the bottom of the shaft. Shoot a few weapons to get the muzzleflash, and the platform isn't lit. That's because dynamic lights take no account of differences in entity positions; they're always at the origin so far as lights are concerned.

DISCLAIMER

This isn't the best way to fix it. I'm well aware of that. I am however showing this way so that it can be used as a baseline for doing it right. Also, it involves much fewer code modifications so it's easier to demonstrate what's needed without needing an overhaul of other parts of the engine first. It's expected that any serious implementation will do it better (e.g. by generalizing R_PushDlights and by properly calculating the inverse matrix in software).

So, add this to your dlight_t struct:

Code: Select all

vec3_t	transformed;
Then find your R_DrawBrushModel and replace the relevant part of the code with this:

Code: Select all

	// calculate dynamic lighting for bmodel if it's not an instanced model
	if (clmodel->firstmodelsurface != 0 && !gl_flashblend.value)
	{
		float matrix[16];

		// get the inverse transform for moving the light back to the same space as the model
		glPushMatrix ();
		glLoadIdentity ();
		glRotatef (-e->angles[2], 1, 0, 0);
		glRotatef (-e->angles[0], 0, 1, 0);
		glRotatef (-e->angles[1], 0, 0, 1);
		glTranslatef (-e->origin[0], -e->origin[1], -e->origin[2]);
		glGetFloatv (GL_MODELVIEW_MATRIX, matrix);
		glPopMatrix ();

		for (k = 0; k < MAX_DLIGHTS; k++)
		{
			if ((cl_dlights[k].die < cl.time) || (!cl_dlights[k].radius))
				continue;

			// move the light back to the same space as the model
			cl_dlights[k].transformed[0] = cl_dlights[k].origin[0] * matrix[0] + cl_dlights[k].origin[1] * matrix[4] + cl_dlights[k].origin[2] * matrix[8] + matrix[12];
			cl_dlights[k].transformed[1] = cl_dlights[k].origin[0] * matrix[1] + cl_dlights[k].origin[1] * matrix[5] + cl_dlights[k].origin[2] * matrix[9] + matrix[13];
			cl_dlights[k].transformed[2] = cl_dlights[k].origin[0] * matrix[2] + cl_dlights[k].origin[1] * matrix[6] + cl_dlights[k].origin[2] * matrix[10] + matrix[14];

			// light the model
			R_MarkLights (&cl_dlights[k], 1 << k, clmodel->nodes + clmodel->hulls[0].firstclipnode);
		}
	}
In R_PushDlights add this before the call to R_MarkLights:

Code: Select all

VectorCopy (l->origin, l->transformed);
And the rest is easy - just replace the use of "origin" in R_MarkLights and R_AddDynamicLights with "transformed", thus:

Code: Select all

dist = DotProduct (light->transformed, splitplane->normal) - splitplane->dist;

Code: Select all

		dist = DotProduct (cl_dlights[lnum].transformed, surf->plane->normal) -
				surf->plane->dist;

Code: Select all

			impact[i] = cl_dlights[lnum].transformed[i] -
					surf->plane->normal[i]*dist;

Re: Dynamic lights on moving brush models

Posted: Mon Mar 05, 2012 2:49 am
by leileilol
COOL!!!!! I'll try to try this in software.
a good place to test this is dm2's moving platform


In software, the R_DrawBrushModel equivelant is R_DrawBEntitiesOnList in r_main.c
i'm only a bit lost on the matrix translation

Re: Dynamic lights on moving brush models

Posted: Mon Mar 05, 2012 8:59 am
by mh
I'm not entirely certain I've got it right myself (in particular the inverse part) - the code was originally written for an engine where nice helper functions to do this kind of thing were available.

It's enough to just move each light by the entities origin though; something like this:

Code: Select all

			cl_dlights[k].transformed[0] = cl_dlights[k].origin[0] - e->origin[0];
			cl_dlights[k].transformed[1] = cl_dlights[k].origin[1] - e->origin[1];
			cl_dlights[k].transformed[2] = cl_dlights[k].origin[2] - e->origin[2];
Since the original Quake didn't have rotating bmodels this will be fine.

Re: Dynamic lights on moving brush models

Posted: Mon Mar 05, 2012 12:25 pm
by toneddu2000
SuperCool! So, if I understood correctly, if I use Ode, I could make a swinging lamp which castes real time shadows(like far cry did in a map)?

Re: Dynamic lights on moving brush models

Posted: Mon Mar 05, 2012 1:05 pm
by leileilol
Weird artifact
Image

R_DrawBEntitiesOnList

Code: Select all

		// calculate dynamic lighting for bmodel if it's not an
			// instanced model
				if (clmodel->firstmodelsurface != 0)
				{
					for (k=0 ; k<MAX_DLIGHTS ; k++)
					{
						



						if ((cl_dlights[k].die < cl.time) ||
							(!cl_dlights[k].radius))
						{
							continue;
						}
						// MH dlight fix
						// move the light back to the same space as the model
						
					    cl_dlights[k].transformed[0] = cl_dlights[k].origin[0] - r_entorigin[0];
					    cl_dlights[k].transformed[1] = cl_dlights[k].origin[1] - r_entorigin[1];
					    cl_dlights[k].transformed[2] = cl_dlights[k].origin[2] - r_entorigin[2];

						R_MarkLights (&cl_dlights[k], 1<<k,
							clmodel->nodes + clmodel->hulls[0].firstclipnode);
					}
					for (k=0 ; k<MAX_SHADOWS ; k++)
					{
						if ((cl_shadows[k].die < cl.time) ||
							(!cl_shadows[k].radius))
						{
							continue;
						}

						R_MarkShadows (&cl_shadows[k], 1<<k,
							clmodel->nodes + clmodel->hulls[0].firstclipnode);
					}
				}

Re: Dynamic lights on moving brush models

Posted: Mon Mar 05, 2012 5:09 pm
by Baker
toneddu2000 wrote:SuperCool! So, if I understood correctly, if I use Ode, I could make a swinging lamp which castes real time shadows(like far cry did in a map)?
No this is making original Quake's existing feature work right. In MH's first post he provides an example.

Examples of dynamic light in Quake (WinQuake and GL) is the illumination of firing a rocket and as the rocket moves the area under the rocket is lit up on the surface of the floor. On moving brush models --- a brush model is a door ... essentially anything that is part of a map --- Quake doesn't treat them properly (in a great many ways, really).

Re: Dynamic lights on moving brush models

Posted: Mon Mar 05, 2012 5:52 pm
by mh
I've been looking over the software Quake source and it seems that further work is needed for this.

As I understand it, the basic loop goes something like this:

- Identify which entities get drawn
- Mark lights in the world
- Mark lights for entities
- Build surface caches for the world
- Build surface caches for entities
- Draw the lot

The problem here is that the "transformed" member will go out of step - it's necessary to retransform again during the surface cache building stages, for both entities and for the world.

Re: Dynamic lights on moving brush models

Posted: Tue Mar 06, 2012 8:44 am
by toneddu2000
Baker wrote:No this is making original Quake's existing feature work right. In MH's first post he provides an example.
Ah,ok, thanks. Sorry for my unmotivated enthusiasm! :)
Anyway, is it so difficult to "link" dinamyc lights (at least in DP) with models in map?So for example, use a func_train with a light with dynamic shadows that when it moves along a path it casts dynamic shadows?...it would be cool

Re: Dynamic lights on moving brush models

Posted: Tue Mar 06, 2012 1:16 pm
by Chip
toneddu2000 wrote:
Baker wrote:No this is making original Quake's existing feature work right. In MH's first post he provides an example.
Ah,ok, thanks. Sorry for my unmotivated enthusiasm! :)
Anyway, is it so difficult to "link" dinamyc lights (at least in DP) with models in map?So for example, use a func_train with a light with dynamic shadows that when it moves along a path it casts dynamic shadows?...it would be cool
Did you try r_shadows 1, or r_shadows 2?

Also, r_shadow_shadowmapping (1 or 2) depending on your Darkplaces build.

Re: Dynamic lights on moving brush models

Posted: Tue Mar 06, 2012 3:59 pm
by toneddu2000
Thanks Chip for the hint but I didn't want to cast fake shadows, I want to parent a real light to a model, so wherever the model goes the shadow will cast realtime shadows

Re: Dynamic lights on moving brush models

Posted: Tue Mar 06, 2012 4:41 pm
by Spike
there's some tenebrae extension that allows you to attach proper realtime lights to entities, but you might need stuff like MOVETYPE_FOLLOW if its a bsp entity.

Re: Dynamic lights on moving brush models

Posted: Thu Mar 08, 2012 8:47 am
by toneddu2000
Thanks Spike! I'll try it and post results in another thread not to spoil this one! :)

Re: Dynamic lights on moving brush models

Posted: Sat Apr 21, 2012 5:28 pm
by mankrip
Thanks for bringing this up mh. Now I understand why Quake never had big moving trains, or really complex sets of moving BSP entities.

I just figured out how to implement this in Makaqu, and here's a copy-paste tutorial.

Just open r_light.c, copy/overwrite the four lines featuring the "dynamic lights on moving brush models fix" string below,

Code: Select all

void R_MarkLights (dlight_t *light, int bit, mnode_t *node)
{
	mplane_t	*splitplane;
	float		dist;
	msurface_t	*surf;
	int			i;
	vec3_t		origin_for_ent; // mankrip - dynamic lights on moving brush models fix

	if (node->contents < 0)
		return;

	splitplane = node->plane;
	VectorSubtract (light->origin, currententity->origin, origin_for_ent); // mankrip - dynamic lights on moving brush models fix
	dist = DotProduct (origin_for_ent, splitplane->normal) - splitplane->dist; // mankrip - dynamic lights on moving brush models fix - edited

	if (dist > light->radius)
	{
		R_MarkLights (light, bit, node->children[0]);
		return;
	}
	if (dist < -light->radius)
	{
		R_MarkLights (light, bit, 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_dlightframecount)
		{
			surf->dlightbits = 0;
			surf->dlightframe = r_dlightframecount;
		}
		surf->dlightbits |= bit;
	}

	R_MarkLights (light, bit, node->children[0]);
	R_MarkLights (light, bit, node->children[1]);
}


/*
=============
R_PushDlights
=============
*/
void R_PushDlights (void)
{
	int		i;
	dlight_t	*l;

	r_dlightframecount = r_framecount + 1;	// because the count hasn't
											//  advanced yet for this frame
	l = cl_dlights;

	currententity = &cl_entities[0]; // mankrip - dynamic lights on moving brush models fix
	for (i=0 ; i<MAX_DLIGHTS ; i++, l++)
	{
		if (l->die < cl.time || !l->radius)
			continue;
		R_MarkLights ( l, 1<<i, cl.worldmodel->nodes );
	}
}
... and do the same for the corresponding four lines in r_surf.c:

Code: Select all

void R_AddDynamicLights (void)
{
	msurface_t *surf;
	int			lnum;
	int			sd, td;
	float		dist, rad, minlight;
	vec3_t		impact, local;
	vec3_t		origin_for_ent; // mankrip - dynamic lights on moving brush models fix
	int			s, t;
	int			i;
	int			smax, tmax;
	mtexinfo_t	*tex;

	surf = r_drawsurf.surf;
	smax = (surf->extents[0]>>4)+1;
	tmax = (surf->extents[1]>>4)+1;
	tex = surf->texinfo;

	for (lnum=0 ; lnum<MAX_DLIGHTS ; lnum++)
	{
		if ( !(surf->dlightbits & (1<<lnum) ) )
			continue;		// not lit by this light

		rad = cl_dlights[lnum].radius;
		VectorSubtract (cl_dlights[lnum].origin, currententity->origin, origin_for_ent); // mankrip - dynamic lights on moving brush models fix
		dist = DotProduct (origin_for_ent, surf->plane->normal) - // mankrip - dynamic lights on moving brush models fix - edited
				surf->plane->dist;
		rad -= fabs(dist);
		minlight = cl_dlights[lnum].minlight;
		if (rad < minlight)
			continue;
		minlight = rad - minlight;

		for (i=0 ; i<3 ; i++)
		{
			impact[i] = origin_for_ent[i] - // mankrip - dynamic lights on moving brush models fix - edited
					surf->plane->normal[i]*dist;
		}

		local[0] = DotProduct (impact, tex->vecs[0]) + tex->vecs[0][3];
		local[1] = DotProduct (impact, tex->vecs[1]) + tex->vecs[1][3];

		local[0] -= surf->texturemins[0];
		local[1] -= surf->texturemins[1];

		for (t = 0 ; t<tmax ; t++)
		{
			td = local[1] - t*16;
			if (td < 0)
				td = -td;
			for (s=0 ; s<smax ; s++)
			{
				sd = local[0] - s*16;
				if (sd < 0)
					sd = -sd;
				if (sd > td)
					dist = sd + (td>>1);
				else
					dist = td + (sd>>1);
				if (dist < minlight)
//#ifdef QUAKE2
				{
					unsigned temp;
					temp = (rad - dist)*256;
					i = t*smax + s;
					if (!cl_dlights[lnum].dark)
						blocklights[i] += temp;
					else
					{
						if (blocklights[i] > temp)
							blocklights[i] -= temp;
						else
							blocklights[i] = 0;
					}
				}
//#else
//					blocklights[t*smax + s] += (rad - dist)*256;
//#endif
			}
		}
	}
}
mh wrote:As I understand it, the basic loop goes something like this:

- Identify which entities get drawn
[...]
Actually, the main problem with the BSP renderer, at least in software, is that it does not care much about entities, and do much of the work by blindly navigating through the BSP tree. As you can see, I had to set currententity in R_PushDlights because it wasn't set prior to that.

Re: Dynamic lights on moving brush models

Posted: Sun Apr 22, 2012 12:00 am
by qbism
mankrip wrote:I just figured out how to implement this in Makaqu, and here's a copy-paste tutorial.
Quick copy-paste test on E1M4 works great! This can be combined with mh's "Extending the number of dynamic lights" tute. http://forums.inside3d.com/viewtopic.php?t=4257

Re: Dynamic lights on moving brush models

Posted: Sun Apr 22, 2012 7:39 am
by leileilol
I just applied it to Engoo's shadows!


It's now in svn but i believe I hit a wall lighting bug. I tried to apply this onto the faster lordhavoc marklights function, a video