Aligning corpses with surfaces

Discuss programming in the QuakeC language.
Cobalt
Posts: 445
Joined: Wed Jun 10, 2009 2:58 am
Location: New England, USA
Contact:

Aligning corpses with surfaces

Post by Cobalt »

My fellow coder Primallove shared with me some code that aligns corpses with the floors pretty well.

Code: Select all

void() AlignwithSurface =
{ 

traceline (self.origin, self.origin - '0 0 128', TRUE, self);

if(trace_fraction != 1)
{
local vector align;
align = trace_plane_normal;
makevectors(self.v_angle);
self.angles = vectoangles( normalize(v_forward - ( (v_forward * align) / (v_up * align)) * v_up) );
}
	
};
Seems to be working ok except the _y angles seem to be succeptable to alteration at times, resulting in the corpse's angles_y flipping away from its original values. Any recommendaitons on a fix?
mankrip
Posts: 924
Joined: Fri Jul 04, 2008 3:02 am

Re: Aligning corpses with surfaces

Post by mankrip »

Try replacing

Code: Select all

 makevectors(self.v_angle); 
... with

Code: Select all

self.angles_x = self.angles_x * -1;
makevectors (self.angles); 
My QC skills are rusted, but I don't see why .v_angle was used in there.
Ph'nglui mglw'nafh mankrip Hell's end wgah'nagl fhtagn.
==-=-=-=-=-=-=-=-=-=-==
Dev blog / Twitter / YouTube
Cobalt
Posts: 445
Joined: Wed Jun 10, 2009 2:58 am
Location: New England, USA
Contact:

Re: Aligning corpses with surfaces

Post by Cobalt »

Thanks will try it and see.

v_angle is a field only present in actual client entities. Its almost the same as .angles, except the _x (pitch) element of the vector is trimmed down in the extreme max and min so when the mouse or whatever key is used to pitch your view reaches the limit where it would invert the model and look incorrect is met, its basicly clamped. Somewhere near a 45 degree limit both ways is what it comes to. Anyone else here that can define it better , please do so.

So other ents like monsters for example have no .v_angle field, and if you call a makevectors on it, It seems with my experiments at least, the field is null and wont yield a value if I recall.


mankrip wrote:Try replacing

Code: Select all

 makevectors(self.v_angle); 
... with

Code: Select all

self.angles_x = self.angles_x * -1;
makevectors (self.angles); 
My QC skills are rusted, but I don't see why .v_angle was used in there.
Cobalt
Posts: 445
Joined: Wed Jun 10, 2009 2:58 am
Location: New England, USA
Contact:

Re: Aligning corpses with surfaces

Post by Cobalt »

Also while Im at it, saw this new routine that I think Gnouc made in his clean src repo:

Code: Select all

void (vector ang) makevectors2 = 
{
	ang_x = ang_x * -1;
	makevectors(ang);
};

Basicly its doing what you stated above, however its come to my attention that altering any specific arguement thats carried over to another function is actually "destructive" to the arguement being carried. So here ' ang ' appears to be a harmless float in this makevectors2 funct, while the code contained therein is altering 'ang' for the purposes of the calc. Regretably, as soon as this calc is done, if the 'ang' field is actually ' .v_angle ' or another .vector on a specific ent, its now been changed during that calc. So the better way is to localy float ' vec ' , then recalc using the harmless local float. I recently started noticing this alot lately with my coding practices, and I have to change them. Spike had already mentioned this in another thread.
gnounc
Posts: 428
Joined: Mon Apr 06, 2009 6:26 am

Re: Aligning corpses with surfaces

Post by gnounc »

that little snippit was suggested by spoike. i threw it in for completeness.
mankrip
Posts: 924
Joined: Fri Jul 04, 2008 3:02 am

Re: Aligning corpses with surfaces

Post by mankrip »

The pich angle (_x) is inverted in the .v_angle field, which is why it must be multiplied by -1 when using .angles instead of .v_angle and vice versa. At least, that's what happens with the view angles in the engine code.

All QC fields are always present in all entities, so .v_angle is also present in all entities. But the engine only sets its value on client entities, because client entities are the only ones that have a viewport. On all other entities, its value is initialized as '0 0 0' and the engine never changes it by itself, so you could use it for other things.

Oh, I guess I know the problem. You're not using that code on the monsters, you're only using it on the player(s). The thing is, the player can still look around while he's dead, which means that his .v_angle can change after he's been killed.
Ph'nglui mglw'nafh mankrip Hell's end wgah'nagl fhtagn.
==-=-=-=-=-=-=-=-=-=-==
Dev blog / Twitter / YouTube
Cobalt
Posts: 445
Joined: Wed Jun 10, 2009 2:58 am
Location: New England, USA
Contact:

Re: Aligning corpses with surfaces

Post by Cobalt »

Yea I like it. I havent really tested my findings with a printout yet to see the other field change from the calc, but I have made other similar functions that
are ' destructive ' to the carried field where the carried field is altered in any way in the function its carried to. It might be better to make a new float there, and do the math with the local float so its guaranteed not to be harmful.
gnounc wrote:that little snippit was suggested by spoike. i threw it in for completeness.
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Re: Aligning corpses with surfaces

Post by Spike »

Code: Select all

void() aligntoground =
{
    traceline (self.origin, self.origin - '0 0 128', TRUE, self);
    if(trace_fraction != 1)
    {
        vector ang = self.angles;
        ang_x *= -1;
        makevectors(ang);
        vector coplanar = v_forward-(v_forward*trace_plane_normal)*trace_plane_normal;
        self.angles = vectoangles(coplanar, trace_plane_normal);
    }
};
just a quick hack, untested.
traces down to find the ground plane.
figures out the entity's current forward vector (as currently displayed, player or monster).
clips that forward vector to follow the ground.
converts that vector back to an angle, using the ground plane to determin the roll angle.
requires DP_QC_VECTOANGLES_WITH_ROLL.
unlike the previous version, it should orient correctly when facing along a slope (instead of only up/down the slope or whatever that maths was meant to do).
mankrip
Posts: 924
Joined: Fri Jul 04, 2008 3:02 am

Re: Aligning corpses with surfaces

Post by mankrip »

Spike wrote:requires DP_QC_VECTOANGLES_WITH_ROLL.
unlike the previous version, it should orient correctly when facing along a slope
:shock:
Ph'nglui mglw'nafh mankrip Hell's end wgah'nagl fhtagn.
==-=-=-=-=-=-=-=-=-=-==
Dev blog / Twitter / YouTube
Cobalt
Posts: 445
Joined: Wed Jun 10, 2009 2:58 am
Location: New England, USA
Contact:

Re: Aligning corpses with surfaces

Post by Cobalt »

Yea, fancy stuff :)

Its called vectoangles2 in DP. Heres what I wound up writing to accomplish Spikes code:

Code: Select all


void() AlignwithSurface =
{ 

traceline (self.origin, self.origin - '0 0 128', TRUE, self);
    if(trace_fraction != 1)
    {
        local vector ang, coplanar,tpn;
	 
          ang = self.angles;
	 
	 tpn = trace_plane_normal;
	 ang = self.angles;
	 // tpn_y = ang_y;
        ang_x = ang_x * -1;
        makevectors(ang);
        coplanar = v_forward-(v_forward*tpnl)*tpn;
        self.angles = vectoangles2(coplanar, tpn);
    }

	
};

So far it seems to be matching the angles very well, see screenshot

Image



Stil the issue Im seeing is the corpse's _y element being passed seems to be the trace planes value and not the _y element of its original angles. I tried to hack
into the _y and keep it in the new code, but it still seems to be picking it up from trace plane normal. So when the deathframes are done and its not facing the _y of the normal, it flips it immediately. Not happening in this screenshot because if you notice the 4 inclines to the center are 90 degrees from each other.


mankrip wrote:
Spike wrote:requires DP_QC_VECTOANGLES_WITH_ROLL.
unlike the previous version, it should orient correctly when facing along a slope
:shock:
mankrip
Posts: 924
Joined: Fri Jul 04, 2008 3:02 am

Re: Aligning corpses with surfaces

Post by mankrip »

The tpnl in your code seems to be a typo.
Ph'nglui mglw'nafh mankrip Hell's end wgah'nagl fhtagn.
==-=-=-=-=-=-=-=-=-=-==
Dev blog / Twitter / YouTube
mankrip
Posts: 924
Joined: Fri Jul 04, 2008 3:02 am

Re: Aligning corpses with surfaces

Post by mankrip »

Now I'm sure of what's going on.

The problem is that the X, Y and Z angles in the QC code are a single transformation. To do it correctly, the model's vertices would have to be transformed twice: First by translating them around the rotation's angles, and then by translating their positions from a plane to another.

However, Quake doesn't support that. So, when you align the model to the plane, you lose its actual orientation's angles.

Maybe a way to hack around this is to use two entities for the corpses.
Don't set any model on the first entity, and set the model on the second entity instead.
Also, set the second entity to MOVETYPE_FOLLOW, and set the first entity as its owner.
Now you should be able to do the plane calculations on the second entity, the rotation calculations on the first entity, and the engine should do the transformations correctly... depending on how the MOVETYPE_FOLLOW extension was implemented.
Ph'nglui mglw'nafh mankrip Hell's end wgah'nagl fhtagn.
==-=-=-=-=-=-=-=-=-=-==
Dev blog / Twitter / YouTube
Cobalt
Posts: 445
Joined: Wed Jun 10, 2009 2:58 am
Location: New England, USA
Contact:

Re: Aligning corpses with surfaces

Post by Cobalt »

Yea, sorry didnt notice that, should be tpn.

Other than it altering the _y element of the calc, works pretty well as is. I was trying to hijack the _y as a fix by keeping it as the original _y of the ent, but it failed so I commented it out. Saw some interesting outcomes with the code as is during tests last night...one was that it matched the angle at the very top of the slope, and wound up looking kinda funky. Didnt take a screenshot of it, but this one was another case where it worked well.

Image


Im also wondering on maybe applying force using something like Gyro to slowly glide the image to the bottom of the slope. The tricky thing is the size of the ent while its frame is one of the laying flat cases, its hard or maybe impossible to setsice accurately for every case....so I guess the only way would be some traceline calcs from the top of the ent plus maybe its f_forward, then straignt down, then check the trace plane_x value for a change, then remove the force from the ent. Stairs would be another similar case.




mankrip wrote:The tpnl in your code seems to be a typo.
Cobalt
Posts: 445
Joined: Wed Jun 10, 2009 2:58 am
Location: New England, USA
Contact:

Re: Aligning corpses with surfaces

Post by Cobalt »

I think you nailed it. I was gonna experiment more but some other experiments with setsize I have done showed me basicly what you say....the traceplane angles seem to be limited to 90 degree shifts only...nothing in between. The angles vs BBox thing again.

Actually I was pondering creating a _follow type ent for the corpses , so yea I think thats a better idea.

mankrip wrote:Now I'm sure of what's going on.

The problem is that the X, Y and Z angles in the QC code are a single transformation. To do it correctly, the model's vertices would have to be transformed twice: First by translating them around the rotation's angles, and then by translating their positions from a plane to another.

However, Quake doesn't support that. So, when you align the model to the plane, you lose its actual orientation's angles.

Maybe a way to hack around this is to use two entities for the corpses.
Don't set any model on the first entity, and set the model on the second entity instead.
Also, set the second entity to MOVETYPE_FOLLOW, and set the first entity as its owner.
Now you should be able to do the plane calculations on the second entity, the rotation calculations on the first entity, and the engine should do the transformations correctly... depending on how the MOVETYPE_FOLLOW extension was implemented.
unmaker
Posts: 1
Joined: Sat May 04, 2013 1:30 pm

Re: Aligning corpses with surfaces

Post by unmaker »

Hi there. Also first post.

I thought I'd share my implementation that I came up with for my little QC sandbox.
Uses DP inverse trig builtins. It's being called by clients and monsters on every server frame, corpses in their last animation frame, also by grenades once they stop bouncing. Originally it also changed the roll angle, but I can't recall why I took it out.

Code: Select all

void(entity ent) slope_angles =
{
	local vector org, org2, ang, pitchdir, rolldir, norm;
	local float pitch_sin, roll_sin, pitch, roll;

// ent is an observer or not initialized yet
	if (ent.movetype == MOVETYPE_NOCLIP)
		return;

	if (!ent.solid)
		return;

//	if (ent.size == VEC_0)	// not initialized
//	if (ent.origin == VEC_0)
//		return;

// hack to avoid traceline runaways for ents placed upside down 
	if (ent.angles_x > 90 || ent.angles_x < -90)
		return;

	if (ent.angles_z > 90 || ent.angles_z < -90)
		return;

// trace line downwards and try to hit a ground plane
	org = ent.origin;

	if (ent.flags & FL_MONSTER)
	if (!checkclient())
		return;

	org2 = org + '0 0 -16';	// use small dist to account for thin walkways and such 
	traceline(org, org2, MOVE_NOMONSTERS, ent);

// if unsuccessful, then increment and trace again
	while ((trace_fraction == 1) && (org_z - org2_z >= ent.mins_z))	
	{
		org2 = org2 + '0 0 -8';
		traceline(org, org2, MOVE_NOMONSTERS, ent);

		if (org2_z <= -1024)	// prevent runaways
			break;
	}

	norm = trace_plane_normal;

	if (checkbottom(ent) &&		// on ground
	    (trace_fraction != 1) && 	// touching the ground
	    (norm_x || norm_y))		// ground plane is a slope
	{
		ang = ent.angles;
	
		if (ent.flags & FL_CORPSE)
		{
			ang_x = 0;
			ang_z = 0;
		}
		else if (ent.flags & FL_CLIENT)
			ang_z = 0;	// no roll, for client rollangle
	
		makevectors(ang);
	
		pitchdir = vreverse(v_forward);
		rolldir = v_right;
	
		pitch_sin = pitchdir_x*norm_x + pitchdir_y*norm_y + pitchdir_z*norm_z;
		pitch = RAD2DEG*asin(pitch_sin);
		roll_sin = rolldir_x*norm_x + rolldir_y*norm_y + rolldir_z*norm_z;
		roll = RAD2DEG*asin(roll_sin);
	
		if (ent.flags & FL_CORPSE)
		{
			ent.angles_x = pitch;
			ent.angles_z = roll;
			return;
		}
	
		if (ent.flags & FL_MONSTER)
		{
			if (ent.enemy == world)	// currently not eyeing somebody
			{
				ent.idealpitch = pitch/3;
				changepitch(ent);
			}
//			ent.idealroll = roll/3;
//			changeroll(self);
			return;
		}
	
		if (ent.flags & FL_CLIENT)
		{
			ent.angles_x = ent.angles_x + pitch/3;
			ent.angles_z = ent.angles_z + roll/3;
		}
	}
	else if (ent.flags & FL_MONSTER)	// not on a slope, so adjust back to normal
	{
		if (ent.enemy == world)
		{	
			ent.idealpitch = 0;
			changepitch(ent);
		}
//		ent.idealroll = 0;
//		changeroll(self);
	}
};
Post Reply