So I’ve been trying to figure this pretty simple idea out. Basically what I have is a freelook camera. When I am freelooking, My controller rotates, but the actor stays at the same rotation (thus turning the camera, not the actor). The next step to this is for animation purposes, I need to know how much the camera rotated. In my mind, the simple way to do this is like this…

Convert the camera orientation to a normalized look vector. If you’re only interested in one axis, such as yaw, zero out the other components.

Convert the character orientation to a normalized vector.

Get the dot product of the two vectors.

Get an inverse cosine of the dot product. This is the theta value between the two vectors.

Keep in mind that if the vectors are the same or 180 degrees apart, the dot product is going to fail.

Here’s my implementation in C# which I used for my own game engine:

/// <summary>
/// Gives you the COUNTER-CLOCKWISE angle, starting from the base vector and ending at the end vector.
/// Note: The parameter order matters.
/// </summary>
/// <param name="baseVector">The starting unit vector</param>
/// <param name="endVector">the end unit vector</param>
/// <returns>A radian angle between the vectors</returns>
public static Angle FindAngleBetween(Vector3 baseVector, Vector3 endVector)
{
if (baseVector == Vector3.Zero || endVector == Vector3.Zero)
{
throw new Exception("Both the base vector and end vector need to have a non-zero vector length.");
}
//The angle between two vectors can either be between 0->180 or 180->360.
//the resulting angle is going to be dependent on the calling order. We will
//always wind counter-clockwise from vector A until we get to vector B.
Vector3 A = baseVector;
Vector3 B = endVector;
A.Normalize();
B.Normalize();
//check the two known edge cases for cross product
if (A == -B)
return new Angle(MathHelper.Pi);
else if (A == B)
return new Angle(0);
//I need to be viewing the angles so that A->B is a counter-clockwise rotation.
Angle Atheta;
if (A.Y < 0)
{ //bottom quadrants
float Adot0 = MathHelper.Clamp(Vector3.Dot(A, Vector3.Left), -1.0f, 1.0f);
Atheta = new Angle(System.Math.Acos(Adot0) + MathHelper.Pi);
}
else
{ //top quadrants
float Adot0 = MathHelper.Clamp(Vector3.Dot(A, -Vector3.Left), -1.0f, 1.0f);
Atheta = new Angle(System.Math.Acos(Adot0));
}
Angle Btheta;
if (B.Y < 0)
{ //bottom quadrants
float Bdot0 = MathHelper.Clamp(Vector3.Dot(B, Vector3.Left), -1.0f, 1.0f);
Btheta = new Angle(System.Math.Acos(Bdot0) + MathHelper.Pi);
}
else
{ //top quadrants
float Bdot0 = MathHelper.Clamp(Vector3.Dot(B, -Vector3.Left), -1.0f, 1.0f);
Btheta = new Angle(System.Math.Acos(Bdot0));
}
return System.Math.Acos(MathHelper.Clamp(Vector2.Dot(baseAngle.Vector2, endAngle.Vector2), -1, 1));
}