# Forum

## Aligning corpses with surfaces

Discuss programming in the QuakeC language.

### Aligning corpses with surfaces

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?

Cobalt

Posts: 445
Joined: Wed Jun 10, 2009 2:58 am
Location: New England, USA

### Re: Aligning corpses with surfaces

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

mankrip

Posts: 915
Joined: Fri Jul 04, 2008 3:02 am

### Re: Aligning corpses with surfaces

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

### Re: Aligning corpses with surfaces

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.

Cobalt

Posts: 445
Joined: Wed Jun 10, 2009 2:58 am
Location: New England, USA

### Re: Aligning corpses with surfaces

that little snippit was suggested by spoike. i threw it in for completeness.

gnounc

Posts: 424
Joined: Mon Apr 06, 2009 6:26 am

### Re: Aligning corpses with surfaces

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

mankrip

Posts: 915
Joined: Fri Jul 04, 2008 3:02 am

### Re: Aligning corpses with surfaces

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.

Cobalt

Posts: 445
Joined: Wed Jun 10, 2009 2:58 am
Location: New England, USA

### Re: Aligning corpses with surfaces

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

Posts: 2895
Joined: Fri Nov 05, 2004 3:12 am
Location: UK

### Re: Aligning corpses with surfaces

Spike wrote:requires DP_QC_VECTOANGLES_WITH_ROLL.
unlike the previous version, it should orient correctly when facing along a slope

Ph'nglui mglw'nafh mankrip Hell's end wgah'nagl fhtagn.
==-=-=-=-=-=-=-=-=-=-==

mankrip

Posts: 915
Joined: Fri Jul 04, 2008 3:02 am

### Re: Aligning corpses with surfaces

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

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

Cobalt

Posts: 445
Joined: Wed Jun 10, 2009 2:58 am
Location: New England, USA

### Re: Aligning corpses with surfaces

The tpnl in your code seems to be a typo.
Ph'nglui mglw'nafh mankrip Hell's end wgah'nagl fhtagn.
==-=-=-=-=-=-=-=-=-=-==

mankrip

Posts: 915
Joined: Fri Jul 04, 2008 3:02 am

### Re: Aligning corpses with surfaces

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

mankrip

Posts: 915
Joined: Fri Jul 04, 2008 3:02 am

### Re: Aligning corpses with surfaces

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.

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

### Re: Aligning corpses with surfaces

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.

Cobalt

Posts: 445
Joined: Wed Jun 10, 2009 2:58 am
Location: New England, USA

### Re: Aligning corpses with surfaces

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);   }};`
unmaker

Posts: 1
Joined: Sat May 04, 2013 1:30 pm

Next

### Who is online

Users browsing this forum: No registered users and 1 guest