Normal to factor - Sloped movement

Started by
9 comments, last by JohnBolton 18 years, 7 months ago
I want to convert a dot product into a factor to be used with animation. My characters have animations for movement up/down 45 degree slopes. So I need to calculate a percentage of the slope animation to blend with the standard movement animation. For example, if the slope is 22.5 degrees, the factor is 0.5. The animation result would be standard * 0.5 + up_slope * 0.5. A slope of -9.0 degrees would be standard * 0.8 + down_slope * 0.2. I have everything working, but I'm thinking the factor is coming out too strong for the slope - it looks like they are trying to walk more steeply than the slope. Here's the routine I'm using:

// These are set elsewhere
VEC left_foot_ground_normal, right_foot_ground_normal;

// Combine the normals
VEC dir = left_foot_ground_normal + right_foot_ground_normal;
dir.Normalize();

// Check for lack of slope
if( dir.y == 1.0f ) // edited - not: if( dir.Normal.y == 1.0f )!
  return;

// CharacterMoveAngle is the XZ angle he is moving in the world
// So I use that to rotate the slope to be relative to his direction
dir.RotateY( CharacterMoveAngle );

// dir.x is irrelevant - animations don't account for side-to-side sloping.
// only the direction the character is moving in can be sloped. So I throw
// that out and renormalize. I think this may be my problem area. Will the
// slope relative to the character's movement direction be the same angle
// after removing x? Or will it be amplified?
dir.x = 0.0f;
dir.Normalize();

// This may also be a problem area. Not sure if I'm calculating it correctly
FLOAT slope_angle;
if( dir.y >= 1.0f )
   slope_angle = 0.0f;
else if( dir.y <= -1.0f )
   slope_angle = -PI;
else if( dir.z > 0.0f )
   slope_angle = -acosf( dir.y );
else
   slope_angle = acosf( dir.y );

// Convert to factor
FLOAT slope_factor = slope_angle / ( PI / 4 );

Please mention anything that doesn't make sense or isn't clear and I'll fully explain. Any suggestions are appreciated. Y is up, by the way, and z (after rotating to be relative) is forward. [Edited by - Kest on September 6, 2005 4:14:06 PM]
Advertisement
I must say I don't fully understand the meaning of the variables, so let me just make some remarks and see if I struck gold somewhere. If not, feel free to ignore everything I say below :D

Simply throwing X out and renormalizing seems wrong. What you want to do is move to a 2D situation, by projecting the vector on it's own plane (the plane your character is walking along), not on the X=0 plane. Imagine your character walking along the Z=0 axis: if you look at things from the X=0 plane you wouldn't see anything in 2D.

Also, I don't really get the acos. As you have the "opposite" and "adjacent" sides of a triangle, the atan operation is what comes most naturally. Then again, my trigonometry is not what it should be, so maybe I just don't get what you are trying to do.

I'd try something like this (pseudo-code):

   x = Sqrt(dir.X^2 + dir.Y^2); // 2D information about the horizontal part   z = dir.Z; // the vertical part   angle = atan(z/x); // the angle   ...convert to slope factor...
Ack! Sorry, I slipped a few typos in that. There is no dir.Normal. Just dir.

Y and Z are basically the 2D plane. Just look at Z as though it were X. If I were looking at from a side perspective, I would ignore Z instead of X.

So you're saying to not do anything to the vector? Just use the two normal attributes? Take for example, sticking to an XY setup, the normalized vector XYZ( 0.140, 0.700, 0.700 )

Using x and y from this gives me 0.140, 0.700.
But removing z and renormalizing gives me 0.196, 0.981.

Would both of these return the same angle with atan? The division is actually pretty close, but slightly off.

edit:
Quote:
x = Sqrt(dir.X^2 + dir.Y^2); // 2D information about the horizontal part
z = dir.Z; // the vertical part
angle = atan(z/x); // the angle
...convert to slope factor...


It looks like you're using all three coordinates in this. I'm not sure I understand why. Y is vertical, X is left and right, and Z is forward and back. Left and right would not influence the sloping, only forward/back and vertical. But I might just be totally missing what is happening..?

edit2: This image might help. If the character is moving in this direction relative to a slope, there is no effect applied:
Free Image Hosting at www.ImageShack.us

But if he is moving this direction, sloping is applied:
Free Image Hosting at www.ImageShack.us
Is "rel_dir.y" correct? You don't use it anywhere else. Other than that your logic seems right.

It won't fix your problem but I would change this:
    dir.x = 0.0f;    dir.Normalize();    FLOAT slope_angle;    if( dir.y >= 1.0f )       slope_angle = 0.0f;    else if( dir.y <= -1.0f )       slope_angle = -PI;    else if( dir.z > 0.0f )       slope_angle = -acosf( rel_dir.y );    else       slope_angle = acosf( rel_dir.y );    FLOAT slope_factor = slope_angle / ( PI / 4 ); 
to this:
    FLOAT slope_angle = atan2( dir.z, dir.y );    FLOAT slope_factor = slope_angle / ( PI / 4 ); 
Not only is it much simpler, but it also avoids the problem of the parameter to acos() being outside the range [-1,1] (which you forgot to check).



Edit: So here is the problem. When you blend the animation, you aren't blending angles -- you are blending positions. Therefore, your interpolation factor should be a ratio of position rather than angle. In that case:
    FLOAT slope_factor = dir.z / dir.y; 


[Edited by - JohnBolton on September 6, 2005 4:57:29 PM]
John BoltonLocomotive Games (THQ)Current Project: Destroy All Humans (Wii). IN STORES NOW!
Quote:Original post by JohnBolton
Is "rel_dir.y" correct? You don't use it anywhere else. Other than that your logic seems right.

I'm really sorry about that. That's what I get for copying and pasting.

Quote:FLOAT slope_angle = atan2( dir.z, dir.y );
Not only is it much simpler, but it also avoids the problem of the parameter to acos() being outside the range [-1,1] (which you forgot to check).

Well, without the typo, I am actually checking for the parameter being out of range. The code I'm really using has most of these routines wrapped into engine-level functions. I kind of pulled all of the code out of those functions to make sure everything was at hand to be understood. I would imagine that atan2 is most likely doing about the same thing? I'm just not very familiar with trig functions.
Quote:Original post by JohnBolton
Edit: So here is the problem. When you blend the animation, you aren't blending angles -- you are blending positions. Therefore, your interpolation factor should be a ratio of position rather than angle. In that case:
    FLOAT slope_factor = dir.z / dir.y; 

Eh? Positions of what? You're losing me :)

The goal is to extract the relative angle from the ground normalized vector and then convert that angle into a -1 to 1 factor. -1 representing -45 degrees, and 1 representing +45 degrees.

edit: You probably thought I was using the foot height of each foot to calculate the slope? I'm sorry I didn't make that more clear. I'm just getting the polygon normal under each foot, adding those together, then renormalizing. Basically I want to make sure both feet contribute to the result, in case they are standing on different angles.
Quote:Original post by Kest
Eh? Positions of what? You're losing me :)

The goal is to extract the relative angle from the ground normalized vector and then convert that angle into a -1 to 1 factor. -1 representing -45 degrees, and 1 representing +45 degrees.

If your goal is to interpolate angles, then your code looks right (though I would use atan2() instead). However, I don't think you should interpolate the angles, because blending doesn't rotate the character. Instead of blending between -45 degrees and 0 degrees and +45 degrees, you should blend between a slope of -1 and a slope of 0 and a slope of +1.
John BoltonLocomotive Games (THQ)Current Project: Destroy All Humans (Wii). IN STORES NOW!
Quote:Original post by JohnBolton
However, I don't think you should interpolate the angles, because blending doesn't rotate the character.

I don't quite follow this. Blending doesn't rotate the character? You mean I shouldn't interpolate the angles because the result is not used to rotate? I'm not rotating the character, but I still need an angle - or at least a method to convert from ground normal to factor.

Quote:Instead of blending between -45 degrees and 0 degrees and +45 degrees, you should blend between a slope of -1 and a slope of 0 and a slope of +1.

I'm still not understanding. I'm not blending degrees at all. I just convert to angles to make sure the factor is linear. I'm pretty sure dot products are not linear. How can I determine if the slope is -1 or 0 or 1 without calculating the angle first?

Let me just explain the whole system to make sure I haven't blotched something:
1) Get the ground normal of both feet
2) Combine those two normals into ground_normal
3) Rotate ground_normal to be relative to the character's movement direction (put into movement space)
4) Convert the Y-Z ratio into a -1 to 1 blend factor
5) Use that factor to blend standard-walking anim with slope walking anim

In regard to blending, are we now talking about #2, #4, or #5?

The idea of using both feet is just a cosmetic improvement. I could simply use one foot, or just the terrain normal directly under his position coordinates (no foot at all). Combing the two feet normals gives the best result. If the left foot is hovering over flat ground, and right foot is hovering over a 45 degree slope, he will (or should) animate as though he is walking up a 22.5 degree slope.

I really appreciate the help. I'm sorry it's taking me so long to catch on.
Quote:Original post by Kest
...
4) Convert the Y-Z ratio into a -1 to 1 blend factor
...

In regard to blending, are we now talking about #2, #4, or #5?

I'm talking about #4. You are converting the direction of the normal to an angle and then comparing that angle to 45 degrees to get a blend factor. That's what I meant when I said you are blending angles. For example, you want a slope whose angle is 22.5 degrees to give you a blending factor of 0.5.

Let me try to show you the problem with that approach. Let's consider an animation frame where the foot is supposed to touch the ground. Suppose in the level animation, the location of the foot is at [1, 0] and in the sloped animation, the location of the foot is at [1, 1]. For an angle of 22.5 degrees, you have a blend factor of 0.5, which means that the location of the foot is at [1, 0.5]. However, if the angle of ground is 22.5 degrees, the height of the ground at x = 1 is tan( 22.5 ), which is 0.414, not 0.5. The foot is at [1.0, 0.5] and the ground is at [1.0, 0.414].

To fix this, throw away all the angle stuff and just use the slope itself as the blend factor.
John BoltonLocomotive Games (THQ)Current Project: Destroy All Humans (Wii). IN STORES NOW!
Quote:Original post by JohnBolton
To fix this, throw away all the angle stuff and just use the slope itself as the blend factor.

But there is nothing representing slope. Only a ground normal. Are you saying to just use a dot product? Or just use the ground normal?

Again, just to make sure I'm making sense, the animation for sloping is walking up a 45 degree slope. So you're saying that such an animation, factored to 0.5, will not walk up a 22.5 degree slope accurately? Or that the calculation I'm performing to get the factor will give me 0.5 for something other than a 22.5 degree slope? The angle comes out as 22.5. I divide that by PI / 4. What could be wrong?

This topic is closed to new replies.

Advertisement