Dynamic lights on moving brush models

Post tutorials on how to do certain tasks within game or engine code here.
mh
Posts: 2292
Joined: Sat Jan 12, 2008 1:38 am

Dynamic lights on moving brush models

Post 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;
We had the power, we had the space, we had a sense of time and place
We knew the words, we knew the score, we knew what we were fighting for
leileilol
Posts: 2783
Joined: Fri Oct 15, 2004 3:23 am

Re: Dynamic lights on moving brush models

Post 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
i should not be here
mh
Posts: 2292
Joined: Sat Jan 12, 2008 1:38 am

Re: Dynamic lights on moving brush models

Post 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.
We had the power, we had the space, we had a sense of time and place
We knew the words, we knew the score, we knew what we were fighting for
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: Dynamic lights on moving brush models

Post 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)?
Meadow Fun!! - my first commercial game, made with FTEQW game engine
leileilol
Posts: 2783
Joined: Fri Oct 15, 2004 3:23 am

Re: Dynamic lights on moving brush models

Post 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);
					}
				}
i should not be here
Baker
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Re: Dynamic lights on moving brush models

Post 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).
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
mh
Posts: 2292
Joined: Sat Jan 12, 2008 1:38 am

Re: Dynamic lights on moving brush models

Post 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.
We had the power, we had the space, we had a sense of time and place
We knew the words, we knew the score, we knew what we were fighting for
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: Dynamic lights on moving brush models

Post 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
Meadow Fun!! - my first commercial game, made with FTEQW game engine
Chip
Posts: 575
Joined: Wed Jan 21, 2009 9:12 am
Location: Dublin, Ireland
Contact:

Re: Dynamic lights on moving brush models

Post 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.
QuakeWiki
getButterfly - WordPress Support Services
Roo Holidays

Fear not the dark, but what the dark hides.
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: Dynamic lights on moving brush models

Post 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
Meadow Fun!! - my first commercial game, made with FTEQW game engine
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Re: Dynamic lights on moving brush models

Post 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.
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: Dynamic lights on moving brush models

Post by toneddu2000 »

Thanks Spike! I'll try it and post results in another thread not to spoil this one! :)
Meadow Fun!! - my first commercial game, made with FTEQW game engine
mankrip
Posts: 924
Joined: Fri Jul 04, 2008 3:02 am

Re: Dynamic lights on moving brush models

Post 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.
Ph'nglui mglw'nafh mankrip Hell's end wgah'nagl fhtagn.
==-=-=-=-=-=-=-=-=-=-==
Dev blog / Twitter / YouTube
qbism
Posts: 1236
Joined: Thu Nov 04, 2004 5:51 am
Contact:

Re: Dynamic lights on moving brush models

Post 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
leileilol
Posts: 2783
Joined: Fri Oct 15, 2004 3:23 am

Re: Dynamic lights on moving brush models

Post 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
i should not be here
Post Reply