Wrapping beams across surfaces?

Discuss programming in the QuakeC language.
Post Reply
mankrip
Posts: 924
Joined: Fri Jul 04, 2008 3:02 am

Wrapping beams across surfaces?

Post by mankrip »

I've made the lightning gun's beam slide across walls instead of just ricocheting, but there are two problems.

First, it doesn't work in all walls, e.g. this one in the start map:
Image
If I use a regular ricochet algorithm in this wall, it won't go to the side, and when I try to make it slide, it always bounces back to the player's origin. It's extremely weird how it doesn't work the same way on all walls. There are several walls and ceilings that have this same problem in the start map.

Second, it slides across the surface, but I realized this is not exactly what I wanted, because the beam goes to the side when the wall isn't perfectly orthogonal to the player's angles. What I really want is to make the beam "wrap" (I'm not sure whether this is the best word to describe this) across the wall, following this red line I've drawn using GIMP:
Image

This is my code:

Code: Select all

void() W_FireLightning =
{
	local	float		cells;
	local	vector	org;

	if (self.ammo_cells < 1)
	{
		self.weapon = W_BestWeapon ();
		W_SetCurrentAmmo ();
		return;
	}

// explode if under water
	if (self.waterlevel > 1)
	{
		cells = self.ammo_cells;
		self.ammo_cells = 0;
		W_SetCurrentAmmo ();
		T_RadiusDamage (self, self, 35*cells, world);
		return;
	}

	if (self.t_width < time)
	{
		sound (self, CHAN_WEAPON, "weapons/lhit.wav", 1, ATTN_NORM);
		self.t_width = time + 0.6;
	}
	self.punchangle_x = -2;

	self.currentammo = self.ammo_cells = self.ammo_cells - 1;

	org = self.origin + '0 0 16';
	
	traceline (org, org + v_forward*600, TRUE, self);
	
	WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
	WriteByte (MSG_BROADCAST, TE_LIGHTNING2);
	WriteEntity (MSG_BROADCAST, self);
	WriteCoord (MSG_BROADCAST, org_x);
	WriteCoord (MSG_BROADCAST, org_y);
	WriteCoord (MSG_BROADCAST, org_z);
	WriteCoord (MSG_BROADCAST, trace_endpos_x);
	WriteCoord (MSG_BROADCAST, trace_endpos_y);
	WriteCoord (MSG_BROADCAST, trace_endpos_z);

	if (trace_fraction < 1)
	{
		vector olddir, newdir, oldpos;
		entity e;
		e = spawn ();
		oldpos = trace_endpos;
		olddir = v_forward * 600;

		newdir_x = olddir_x * (-1 * trace_plane_normal_x + 1);
		newdir_y = olddir_y * (-1 * trace_plane_normal_y + 1);
		newdir_z = olddir_z * (-1 * trace_plane_normal_z + 1);
	//	newdir = normalize (newdir);

		traceline (oldpos, oldpos + newdir /* * 600 * (1 - trace_fraction) */, TRUE, self);

		WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
		WriteByte (MSG_BROADCAST, TE_LIGHTNING2);
		WriteEntity (MSG_BROADCAST, e);
		WriteCoord (MSG_BROADCAST, oldpos_x);
		WriteCoord (MSG_BROADCAST, oldpos_y);
		WriteCoord (MSG_BROADCAST, oldpos_z);
		WriteCoord (MSG_BROADCAST, trace_endpos_x);
		WriteCoord (MSG_BROADCAST, trace_endpos_y);
		WriteCoord (MSG_BROADCAST, trace_endpos_z);
		remove (e);
	}

	LightningDamage (self.origin, trace_endpos + v_forward*4, self, 30);
};
It's been a couple weeks since I've worked on this, so I'm not sure if it's exactly the same code I've used on those screenshots. I'm learning this through trial and error, so I've rewritten this code many times.
Ph'nglui mglw'nafh mankrip Hell's end wgah'nagl fhtagn.
==-=-=-=-=-=-=-=-=-=-==
Dev blog / Twitter / YouTube
taniwha
Posts: 401
Joined: Thu Jan 14, 2010 7:11 am
Contact:

Re: Wrapping beams across surfaces?

Post by taniwha »

Lets say the direction of the beam is V, the plane normal is N, then the travel along the plane (V') is given by: V' = V - V.N N

In quakec:

Code: Select all

   newvel = oldvel - (oldvel * planenormal) * planenormal;
Leave others their otherness.
http://quakeforge.net/
mankrip
Posts: 924
Joined: Fri Jul 04, 2008 3:02 am

Re: Wrapping beams across surfaces?

Post by mankrip »

This solved the first problem, but I don't want it to simply "travel along the plane", because it insists in going to the sides when the plane isn't perfectly orthogonal to the player's view.

See the second screenshot. The lightning beam is sliding along the wall, but it's not following the player's aim. It's going to the side. I want it to follow the direction of the red line, which follows the player's aim.

This is the current code:

Code: Select all

void() W_FireLightning =
{

[...]

	if (trace_fraction < 1)
	{
		vector olddir, newdir, oldpos;
		entity e;
		e = spawn ();
		oldpos = trace_endpos;
		olddir = v_forward * 600 * (1 - trace_fraction);

		newdir = olddir - (olddir * trace_plane_normal) * trace_plane_normal;

		traceline (oldpos, oldpos + newdir, TRUE, self);

		WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
		WriteByte (MSG_BROADCAST, TE_LIGHTNING2);
		WriteEntity (MSG_BROADCAST, e);
		WriteCoord (MSG_BROADCAST, oldpos_x);
		WriteCoord (MSG_BROADCAST, oldpos_y);
		WriteCoord (MSG_BROADCAST, oldpos_z);
		WriteCoord (MSG_BROADCAST, trace_endpos_x);
		WriteCoord (MSG_BROADCAST, trace_endpos_y);
		WriteCoord (MSG_BROADCAST, trace_endpos_z);
		remove (e);
	}

	LightningDamage (self.origin, trace_endpos + v_forward*4, self, 30);
};
Ph'nglui mglw'nafh mankrip Hell's end wgah'nagl fhtagn.
==-=-=-=-=-=-=-=-=-=-==
Dev blog / Twitter / YouTube
taniwha
Posts: 401
Joined: Thu Jan 14, 2010 7:11 am
Contact:

Re: Wrapping beams across surfaces?

Post by taniwha »

What you want actually doesn't make physical sense. The code I gave you causes the wall to rob the beam of exactly the velocity component going directly into the wall (if you were to aim perfectly perpendicular to the wall, the beam would just stop dead). The reason the beam goes off to the side when aiming up but at an angle to the wall is because that is the direction of the player's aim relative to the wall.

It might be better to think of the player's aim forming a cone around the line between the player and the wall such that the line is perpendicular to the wall (the wall might not actually exist at the point the line hits the wall. ie, you might have to imagine the wall being extended far enough). The player's aim then has two components: one going towards (perpendicular to) the wall, and one going along (parallel to) the wall (radiating out from the above mentioned intersection point). Hitting the wall removes the perpendicular component. The only way for the beam to go straight up when hitting the wall when the player is aiming a little to the left or right is if there is something else there to stop the beam going to the side. There is nothing there to do so. If you want to use friction with the wall, then the beam would just stop dead as friction works in all directions (parallel to the wall).

Try it with a garden-hose on the ground (or a wall if you really want, but then you've got gravity getting in the way).
Leave others their otherness.
http://quakeforge.net/
qbism
Posts: 1236
Joined: Thu Nov 04, 2004 5:51 am
Contact:

Re: Wrapping beams across surfaces?

Post by qbism »

The wall face itself is not the correct plane. The plane for this effect intersects the wall but the horizontal axis is perpendicular to the player.
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Re: Wrapping beams across surfaces?

Post by Spike »

so for a vertical wall, you want it to always go upwards?
ceriux
Posts: 2230
Joined: Sat Sep 06, 2008 3:30 pm
Location: Indiana, USA

Re: Wrapping beams across surfaces?

Post by ceriux »

i think what he wants to make it travel the direction the player is facing reguardless of which angle the wall is facing. so if hes aiming straight the beam goes straight, id assume that if you were aiming lower the beam would travel downward in the direction and if you were aiming higher it would travel upwards. if he was aiming side ways at the wall the beam would travel across the wall horizontally .
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Re: Wrapping beams across surfaces?

Post by Spike »

assuming he's looking forward, the view is above the point of origin of the lightning beam.
this means that the beam always connects from the bottom of the screen towards the center (perspective and all that). which means that the beam needs to always go vertically upwards, assuming the wall is vertical.
even if the player is aiming downwards.

I really have no idea of the maths for that.
taniwha
Posts: 401
Joined: Thu Jan 14, 2010 7:11 am
Contact:

Re: Wrapping beams across surfaces?

Post by taniwha »

What I want to know is what's supposed to happen on a non-vertical surface? (eg, floor, ramp etc) At least the code I gave gives consistent (and physically accurate) results for all surface directions.
Leave others their otherness.
http://quakeforge.net/
mankrip
Posts: 924
Joined: Fri Jul 04, 2008 3:02 am

Re: Wrapping beams across surfaces?

Post by mankrip »

qbism wrote:The wall face itself is not the correct plane. The plane for this effect intersects the wall but the horizontal axis is perpendicular to the player.
Exactly. The plane normals must be transformed for it to work. I just don't know how.
taniwha wrote:What you want actually doesn't make physical sense.
It does. Here's a more practical example: Instead of a beam, think about objects. If you drop a ball on an inclined floor, it will follow the direction of the floor, as you described. However, if you drop this, no matter the angle of the floor, it will follow its original direction. So, I only want the height of the trajectory to be adjusted... However, this "height" must be actually relative to the player's angles, not to the world.

It seems that the core of the problem is that the ricochet and the sliding formulas does not take the player's original angles in consideration at all. In that second pic of the first post, the trajectory of the lightning bolt would be correct (in terms of what I'm trying to achieve), if the roll angle of the player was turned into that direction (and I'm talking about the player's position and angles, not the camera's position and angles, so the view offset doesn't matter here). The sliding/ricochet formulas completely ignores the player's angles.

There must be a way to rotate the trace_plane_normal according to the player's angles.
Spike wrote:so for a vertical wall, you want it to always go upwards?
Only if the player is looking up; if he's looking down, it must go downwards. And if neither, it should stop.
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: Wrapping beams across surfaces?

Post by qbism »

Might be worth trying- subtract the perpendicular vector of olddir from trace_plane_normal. The perp. vector is like the axle of your saw blade. Negate the component of direction change occurring against the side of the blade.
something like

Code: Select all

...
oldpos = trace_endpos;
olddir = v_forward * 600 * (1 - trace_fraction);
oldir_perp = [calculate perpendicular vector of old direction.  Some qc function for this?]
impact_plane = normalize (trace_plane_normal - olddir_perp);

newdir = olddir - (olddir * impact_plane) * impact_plane;

traceline (oldpos, oldpos + newdir, TRUE, self);
...
mankrip
Posts: 924
Joined: Fri Jul 04, 2008 3:02 am

Re: Wrapping beams across surfaces?

Post by mankrip »

I kept hammering my head at the code, and came up with this:

Code: Select all

	if (trace_fraction < 1)
	{
		vector olddir, newdir, tempdir, oldpos, playernormal, tempnormal;
		entity e;
		e = spawn ();
		oldpos = trace_endpos;
		playernormal = normalize (self.angles);
		bprint ("\n");
		bprint ("self.angles "); bprint (vtos (self.angles)); bprint ("\n");
		bprint ("playernormal "); bprint (vtos (playernormal)); bprint ("\n");
		tempdir = olddir = v_forward * 600;// * (1 - trace_fraction);

		/*
		if (fabs (trace_plane_normal_x) == 1)
		{
			tempnormal_x = 1;//playernormal_x;
			if (playernormal_y < 0)
				tempnormal_y = playernormal_z;
			else
				tempnormal_y = playernormal_z * -1;
			tempnormal_z = 1;//playernormal_y;
			tempdir_x = tempdir_x * fabs (tempnormal_x);
			tempdir_y = tempdir_y * 0;//tempnormal_y;
			tempdir_z = tempdir_z * fabs (tempnormal_z);
		}
		if (fabs (trace_plane_normal_y) == 1)
		{
			tempnormal_x = 0;//playernormal_z;
			tempnormal_y = 1;//playernormal_x;
			tempnormal_z = 1;//playernormal_y;
			tempdir_x = tempdir_x * tempnormal_x;
			tempdir_y = tempdir_y * fabs (tempnormal_y);
			tempdir_z = tempdir_z * fabs (tempnormal_z);
		}
		if (fabs (trace_plane_normal_z) == 1)
		{
			tempnormal_x = 1;//fabs (playernormal_x);
			tempnormal_y = 1;//tempnormal_x + fabs (playernormal_y);
		//	tempnormal_x = tempnormal_y;
		//	bprint ("tempnormal_x "); bprint (ftos (tempnormal_x)); bprint ("\n");
			tempnormal_z = 0;//playernormal_z - tempnormal_x;
			tempdir_x = tempdir_x * tempnormal_x;
			tempdir_y = tempdir_y * tempnormal_y;
			tempdir_z = tempdir_z * tempnormal_z;
		}
		// */
		// /*
		tempnormal_x = fabs (trace_plane_normal_x);
		tempnormal_y = (1 - fabs (trace_plane_normal_x));
		tempnormal_z = fabs (trace_plane_normal_x);

		if (trace_plane_normal_x && trace_plane_normal_y && !trace_plane_normal_z)
		{	// hack for vertical walls in non-square angles
			tempnormal_x = tempnormal_x * (1 - fabs (trace_plane_normal_y));
			tempnormal_y = tempnormal_y * fabs (trace_plane_normal_y);
			tempnormal_z = tempnormal_z * fabs (trace_plane_normal_y);
	
			tempnormal_x = tempnormal_x * fabs (trace_plane_normal_z);
			tempnormal_y = tempnormal_y * fabs (trace_plane_normal_z);
			tempnormal_z = tempnormal_z * (1 - fabs (trace_plane_normal_z));
		}
		else
		{
			tempnormal_x = tempnormal_x + (1 - fabs (trace_plane_normal_y));
			tempnormal_y = tempnormal_y + fabs (trace_plane_normal_y);
			tempnormal_z = tempnormal_z + fabs (trace_plane_normal_y);
	
			tempnormal_x = tempnormal_x + fabs (trace_plane_normal_z);
			tempnormal_y = tempnormal_y + fabs (trace_plane_normal_z);
			tempnormal_z = tempnormal_z + (1 - fabs (trace_plane_normal_z));
		}
		// */
		/*
		if (( trace_plane_normal_x &&  trace_plane_normal_y && !trace_plane_normal_z)
		||	(!trace_plane_normal_x && !trace_plane_normal_y &&  trace_plane_normal_z))
		{
			tempnormal_x = fabs (trace_plane_normal_x);
			tempnormal_x = tempnormal_x - fabs (trace_plane_normal_y);
	//		tempnormal_x = tempnormal_x + fabs (trace_plane_normal_z);
			tempnormal_x = fabs (trace_plane_normal_z) - tempnormal_x;
	
			tempnormal_y = fabs (trace_plane_normal_y);
	//		tempnormal_y = tempnormal_y + fabs (trace_plane_normal_z);
			tempnormal_y = tempnormal_y - fabs (trace_plane_normal_x);
			tempnormal_y = fabs (trace_plane_normal_z) - tempnormal_y;
	
			tempnormal_z = fabs (trace_plane_normal_x);
			tempnormal_z = tempnormal_z + fabs (trace_plane_normal_y);
	//		tempnormal_z = tempnormal_z - fabs (trace_plane_normal_z);
			tempnormal_z = tempnormal_z - fabs (trace_plane_normal_z);
		}
		else
		{
			tempnormal_x = fabs (trace_plane_normal_x);
			tempnormal_y = (1 - fabs (trace_plane_normal_x));
			tempnormal_z = fabs (trace_plane_normal_x);

			tempnormal_x = tempnormal_x + (1 - fabs (trace_plane_normal_y));
			tempnormal_y = tempnormal_y + fabs (trace_plane_normal_y);
			tempnormal_z = tempnormal_z + fabs (trace_plane_normal_y);
	
			tempnormal_x = tempnormal_x + fabs (trace_plane_normal_z);
			tempnormal_y = tempnormal_y + fabs (trace_plane_normal_z);
			tempnormal_z = tempnormal_z + (1 - fabs (trace_plane_normal_z));
		}
		// */

		tempnormal = normalize (tempnormal);

		tempdir_x = tempdir_x * tempnormal_x;
		tempdir_y = tempdir_y * tempnormal_y;
		tempdir_z = tempdir_z * tempnormal_z;
		
		bprint ("trace_plane_normal "); bprint (vtos (trace_plane_normal)); bprint ("\n");
		
	//	tempdir_x = tempdir_y = 0;
	//	trace_plane_normal_x = 0;
	//	trace_plane_normal_y = 0;
	//	trace_plane_normal_z = 0;
	//	newdir = olddir - (olddir * trace_plane_normal) * trace_plane_normal;
	//	trace_plane_normal_x = playernormal_x * trace_plane_normal_x;
	//	trace_plane_normal_y = playernormal_y * trace_plane_normal_y;
	//	trace_plane_normal_z = playernormal_z * trace_plane_normal_z;
		newdir = tempdir - (tempdir * trace_plane_normal) * trace_plane_normal;
		newdir = normalize (newdir) * 600;
	//	newdir_x = olddir_x - newdir_x;
	//	newdir_y = olddir_y - newdir_y;
		/*
		newdir_x = olddir_x * ((-1 * (trace_plane_normal_x)) + 1);
		newdir_y = olddir_y * ((-1 * (trace_plane_normal_y)) + 1);
		newdir_z = olddir_z * ((-1 * (trace_plane_normal_z)) + 1);
		*/

	//	traceline (oldpos, oldpos + newdir, TRUE, self);
		trace_endpos = oldpos + newdir;

		WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
		WriteByte (MSG_BROADCAST, TE_LIGHTNING2);
		WriteEntity (MSG_BROADCAST, e);
		WriteCoord (MSG_BROADCAST, oldpos_x);
		WriteCoord (MSG_BROADCAST, oldpos_y);
		WriteCoord (MSG_BROADCAST, oldpos_z);
		WriteCoord (MSG_BROADCAST, trace_endpos_x);
		WriteCoord (MSG_BROADCAST, trace_endpos_y);
		WriteCoord (MSG_BROADCAST, trace_endpos_z);
		remove (e);
	}
It works on all vertical walls at any angle (thanks to that little "special case" hack; without it, it would only work on vertical walls at square angles), and on all horizontal floors. It doesn't work on slopes, and I haven't tried to make it work with rolling angles (self.angles_z) yet because by default Quake has no controls for that.

I've kept a bunch of other experiments commented out in this code, so you can see what I've already tried. I'm starting to think about experimenting with the v_forward, v_right and v_up vectors instead of normalizing self.angles.
Ph'nglui mglw'nafh mankrip Hell's end wgah'nagl fhtagn.
==-=-=-=-=-=-=-=-=-=-==
Dev blog / Twitter / YouTube
gnounc
Posts: 428
Joined: Mon Apr 06, 2009 6:26 am

Re: Wrapping beams across surfaces?

Post by gnounc »

post us a youtube video when you get what you wanted, for those of us who couldnt figure out what it was.
taniwha
Posts: 401
Joined: Thu Jan 14, 2010 7:11 am
Contact:

Re: Wrapping beams across surfaces?

Post by taniwha »

A particle-beam is not a circular-saw, but whatever. Now you have to explain why the "beam" would go vertically and not some other arbitrary direction :)
Leave others their otherness.
http://quakeforge.net/
mankrip
Posts: 924
Joined: Fri Jul 04, 2008 3:02 am

Re: Wrapping beams across surfaces?

Post by mankrip »

taniwha wrote:Now you have to explain why the "beam" would go vertically and not some other arbitrary direction :)
Because it's the direction I need. I'm going to use this in my current project, but there's still a lot to do before I can reveal anything.

If I ever figure out how to make this algorithm follow the roll angle (and fix the slopes bug), any arbitrary direction will be possible.
Ph'nglui mglw'nafh mankrip Hell's end wgah'nagl fhtagn.
==-=-=-=-=-=-=-=-=-=-==
Dev blog / Twitter / YouTube
Post Reply