Wrapping beams across surfaces?
Moderator: InsideQC Admins
15 posts
• Page 1 of 1
Wrapping beams across surfaces?
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:

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:

This is my code:
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.
First, it doesn't work in all walls, e.g. this one in the start map:

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:

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.
-

mankrip - Posts: 915
- Joined: Fri Jul 04, 2008 3:02 am
Re: Wrapping beams across surfaces?
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:
In quakec:
- Code: Select all
newvel = oldvel - (oldvel * planenormal) * planenormal;
Leave others their otherness.
http://quakeforge.net/
http://quakeforge.net/
- taniwha
- Posts: 399
- Joined: Thu Jan 14, 2010 7:11 am
Re: Wrapping beams across surfaces?
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:
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);
};
-

mankrip - Posts: 915
- Joined: Fri Jul 04, 2008 3:02 am
Re: Wrapping beams across surfaces?
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).
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/
http://quakeforge.net/
- taniwha
- Posts: 399
- Joined: Thu Jan 14, 2010 7:11 am
Re: Wrapping beams across surfaces?
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.
-
qbism - Posts: 1236
- Joined: Thu Nov 04, 2004 5:51 am
Re: Wrapping beams across surfaces?
so for a vertical wall, you want it to always go upwards?
- Spike
- Posts: 2892
- Joined: Fri Nov 05, 2004 3:12 am
- Location: UK
Re: Wrapping beams across surfaces?
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 .
-

ceriux - Posts: 2223
- Joined: Sat Sep 06, 2008 3:30 pm
- Location: Indiana, USA
Re: Wrapping beams across surfaces?
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.
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.
- Spike
- Posts: 2892
- Joined: Fri Nov 05, 2004 3:12 am
- Location: UK
Re: Wrapping beams across surfaces?
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/
http://quakeforge.net/
- taniwha
- Posts: 399
- Joined: Thu Jan 14, 2010 7:11 am
Re: Wrapping beams across surfaces?
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.
-

mankrip - Posts: 915
- Joined: Fri Jul 04, 2008 3:02 am
Re: Wrapping beams across surfaces?
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
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);
...
-
qbism - Posts: 1236
- Joined: Thu Nov 04, 2004 5:51 am
Re: Wrapping beams across surfaces?
I kept hammering my head at the code, and came up with this:
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.
- 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.
-

mankrip - Posts: 915
- Joined: Fri Jul 04, 2008 3:02 am
Re: Wrapping beams across surfaces?
post us a youtube video when you get what you wanted, for those of us who couldnt figure out what it was.
-

gnounc - Posts: 424
- Joined: Mon Apr 06, 2009 6:26 am
Re: Wrapping beams across surfaces?
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/
http://quakeforge.net/
- taniwha
- Posts: 399
- Joined: Thu Jan 14, 2010 7:11 am
Re: Wrapping beams across surfaces?
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.
-

mankrip - Posts: 915
- Joined: Fri Jul 04, 2008 3:02 am
15 posts
• Page 1 of 1
Who is online
Users browsing this forum: No registered users and 1 guest