Rotation

Started by
7 comments, last by beebs1 12 years, 5 months ago
Hiya,

I posted about this before and thought I'd solved it - I'm close, but it seems I still don't have it working correctly.

I'm trying to make a spaceship turn towards the player. To do this, I find the desired direction vector from the two positions.

I first tried flattening this vector onto the z/y and z/x planes, and using the dot product to find the angles seperately. This works, but as the ship lines up the angle between direction and intended direction gets very small, and the dot product starts producing NaN...

I've read that I can use atan2 to find the required yaw/pitch angles as well as the turn direction, but I can't get it to work.


float pitchAngle = atan2f(myDirection.y - goalDirection.y, myDirection.z - goalDirection.z);


With the ship at (0, 80, -400) moving in (0, 0, 1) towards the player at the origin, the pitch angle should be 78.7 degrees. However, it comes out as 84 degrees.

If I use positions instead of directions instead, i.e.


float pitchAngle = atan2f(myPosition.y - goalPosition.y, myPosition.z - goalPosition.z);


I get the right angle in some cases, depending on which way the ship is moving with respect to the player.

Does anyone know the correct calculations for yaw and pitch?

Thanks :)
Advertisement


I first tried flattening this vector onto the z/y and z/x planes, and using the dot product to find the angles seperately. This works, but as the ship lines up the angle between direction and intended direction gets very small, and the dot product starts producing NaN...



This seems like a straightforward, intuitive solution for you - have you considered letting the ship "snap" to the desired angle once the difference between the actual and desired angles is very small?

Hiya,

I posted about this before and thought I'd solved it - I'm close, but it seems I still don't have it working correctly.

I'm trying to make a spaceship turn towards the player. To do this, I find the desired direction vector from the two positions.

I first tried flattening this vector onto the z/y and z/x planes, and using the dot product to find the angles seperately. This works, but as the ship lines up the angle between direction and intended direction gets very small, and the dot product starts producing NaN...

I've read that I can use atan2 to find the required yaw/pitch angles as well as the turn direction, but I can't get it to work.


float pitchAngle = atan2f(myDirection.y - goalDirection.y, myDirection.z - goalDirection.z);


With the ship at (0, 80, -400) moving in (0, 0, 1) towards the player at the origin, the pitch angle should be 78.7 degrees. However, it comes out as 84 degrees.

If I use positions instead of directions instead, i.e.


float pitchAngle = atan2f(myPosition.y - goalPosition.y, myPosition.z - goalPosition.z);


I get the right angle in some cases, depending on which way the ship is moving with respect to the player.

Does anyone know the correct calculations for yaw and pitch?

Thanks :)


I'm thinking this could have something to do with local vs global coordinates, but would have to see more code. Specifically how do you calculate myDirection and goalDirection and are they in the same space?

As your second equation doesn't seem to have any thing to do with direction, I wonder how the relative direction of travel is effecting anything?
Thanks for your replies!

I've gone back to the 2D vector method to track down exactly where the NaN is coming from - in th following code, the dot product is returning 1.0000001. It might be a precision error.


float pitchAngle = acosf(zyGoalDirection.dot(zyDirection));


I'll try clamping the value passed to acos() or snapping the ship as Kobo suggests.

Burnt_Fyr - As an example, the players mouse movements are translated into an angle to yaw and pitch each frame. I build a matrix to rotate around the y-axis, and then yaw the local x/z axes by that amount. I then build another matrix to rotate the local y/z axes around the local x-axis for pitch. Finally I just 're-orthogonal-ize' the local axes and save the local-Z as my direction.

Thanks again :)
A common method of building a coordinate frame for an object that looks at the given direction is to generate a "lookat" matrix. For example, see float3x3::LookAt in MathGeoLib. (click on the link in the Syntax line for the implementation). This is a mechanism that utilizes vector arithmetic and avoids trigonometric functions.
This should do:
float pitchAngle = acosf(min(1.0f, zyGoalDirection.dot(zyDirection)));
It now works perfectly when the ship is heading towards the player.


float cosTheta = zyGoalDirection.dot(zyMyDirection);

clampMinMax(cosTheta, -1.0f, 1.0f);

float pitchAngle = acosf(cosTheta);

/* If I'm above, I need to pitch down. */
if(myPosition.y > goalPosition.y)
pitchAngle *= -1.0f;


I can't quite get it to work when the ship is heading away from the player.

For example, at the first frame the pitch angle should be -11.3 degrees. As the ship is heading away from the player, the pitchAngle comes out as -168.7 degrees - which is fair. So I've added the following code.


if(pitchAngle > 90.0f)
pitchAngle = 180.0f - pitchAngle;



This gives the correct pitch angle of -11.3 degrees.

But as the ship moves downwards (along the negative y-axis) the angle increases instead of decreases. By the time the ship has reached y = 1, it wants to pitch -45 degrees when it should be close to 0.

I must have something the wrong way around.. Can anyone spot it?

Cheers.

[Edit] Thanks Alvaro!

Does anyone know the correct calculations for yaw and pitch?


Yes, but I'm not going to tell you, because you're doing it wrong. If you ever ask that question again, it means you've f**ked up..... !!! ohmy.gif

[color="#1C2837"]I've read that I can use atan2 to find the required yaw/pitch angles as well as the turn direction, but I can't get it to work.[/quote]
[color="#1c2837"]
If you ever hear yourself saying the words yaw/pitch again, you're doing it wrong.....


[color="#1C2837"]I first tried flattening this vector onto the z/y and z/x planes, and using the dot product to find the angles seperately. This works, but as the ship lines up the angle between direction and intended direction gets very small, and the dot product starts producing NaN...


Uhm. You have a bug. It is (by definition) impossible for dot() to return NaN. So assuming your dot product code is correct, one can only assume that you've had a division by zero somewhere previously. Using a debugger to determine where the NaN came from would be useful....
[color="#1C2837"]I'm trying to make a spaceship turn towards the player. To do this, I find the desired direction vector from the two positions.[/quote]
[color="#1C2837"]
This bit is right ;)

If you simply want to set the orientation at this point. Use the goal dir as the Z axis of the matrix (which ever is your 'forward' direction). Cross product the goal dir with the up vector, and you have your resulting matrix (i.e. use a matrix::lookAt method).

If you want to smoothly rotate between the two, then it's a bit more involved....

[color="#1C2837"]
Vector3 myDirection;
Vector3 goalDirection;


[color="#1C2837"]To determine the angle between them, we use the dot product:

[color="#1C2837"]
// if the vectors aren't normalised, normalise them....
myDirection = normalise(myDirection);
goalDirection = normalise(goalDirection);
float angle = acosf( dot(myDirection, goalDirection) );

[color="#1C2837"]
But we also need the axis of rotation:

[color="#1C2837"]
Vector3 axis = cross(myDirection, goalDirection);

[color="#1c2837"]Voila! You now have an axis angle rotation that will tell you how to rotate between the 2 orientations.

Now you don't have to use that as an axis angle. If for example the local up axis of your ship is the Y axis, then simply create a Y rotation matrix of (angle/numSteps) and multiply by your space ship matrix (same applies to X and Z if they are your local up axes). You might want to clamp that angle to the max angular turning speed of your ship though.....


[color="#1c2837"]p.s. I'd actually recommend storing your rotations as Quaternions. They make this sort of thing trivial to achieve.
[color="#1C2837"]
If you simply want to set the orientation at this point. Use the goal dir as the Z axis of the matrix (which ever is your 'forward' direction). Cross product the goal dir with the up vector, and you have your resulting matrix (i.e. use a matrix::lookAt method).


Thanks. The problem is that ships have only 4DOF and a maximum angle which they are allowed to clime - so they aren't allowed to become inverted. I see that the cross product of the direction and goal direction will produce the right rotation axis, but if the ship is travelling away from the player then this axis will involve the ship inverting itself.

For example - if the player is at (0, 0, 0) and the ship is at (0, 5, 10) moving in (0, 0, 1). The ship will need to turn around the y-axis by 180 degrees and pitch down by 26.6 degrees to face the player. It's not allowed to be inverted, so it can't rotate around the cross product of (0, 0, 1) and (0, -5, -10).

So...


[color="#1C2837"]
Vector3 axis = cross(myDirection, goalDirection);

[color="#1c2837"]Voila! You now have an axis angle rotation that will tell you how to rotate between the 2 orientations.

Now you don't have to use that as an axis angle. If for example the local up axis of your ship is the Y axis, then simply create a Y rotation matrix of (angle/numSteps) and multiply by your space ship matrix (same applies to X and Z if they are your local up axes). You might want to clamp that angle to the max angular turning speed of your ship though.....


I'm not sure if I can use this?

Thanks.

This topic is closed to new replies.

Advertisement