I have two objects (target and player), both have Position (Vector3) and Rotation (Quaternion). I want the target to rotate and be facing right at the player. The target, when it shoots something should shoot right at the player.
I've seen plenty of examples of slerping to the player, but I don't want incremental rotation, well, I suppose that would be ok as long as I can make the slerp be 100%, and as long as it actually worked.
FYI - Im able to use the position and rotation to do plenty of other things and it all works great except this last piece I cant figure out.
Code samples run in the Target's class, Position = the targets position, Avatar = the player.
EDIT
Im now using Maik's c# code he has provided and it works great!
Answer
There are more than one ways to do it. You can calculate the absolute orientation or the rotation relative to your avatar, that means your new orientation = avatarOrientation * q. Here is the latter one:
Calculate the rotation axis by taking the cross product of your avatar's unit forward vector and the unit vector from avatar to target, the new forward vector:
vector newForwardUnit = vector::normalize(target - avatarPosition);
vector rotAxis = vector::cross(avatarForwardUnit, newForwardUnit);Calculate the rotation angle using the dot-product
float rotAngle = acos(vector::dot(avatarForwardUnit, newForwardUnit));
Create the quaternion using rotAxis and rotAngle and multiply it with avatar's current orientation
quaternion q(rotAxis, rotAngle);
quaternion newRot = avatarRot * q;
If you need help finding the avatar's current forward vector, the input for 1. just shoot :)
EDIT: Calculating the absolute orientation is actually a bit easier, use the forward vector of the identity-matrix instead of avatars forward vector as input for 1) and 2). And don't multiply it in 3), instead use it directly as the new orientation: newRot = q
Important to note: The solution has 2 anomalies caused by the nature of the cross-product:
If the forward vectors are equal. Solution here is simply return the identity quaternion
If the vectors point exactly in the opposite direction. The solution here is to create the quaternion by using avatars up axis as rotation axis and the angle 180.0 degrees.
Here is the implementation in C++ that takes care of those edge cases. Converting it to C# should be easy.
// returns a quaternion that rotates vector a to vector b
quaternion get_rotation(const vector &a, const vector &b, const vector &up)
{
ASSERT_VECTOR_NORMALIZED(a);
ASSERT_VECTOR_NORMALIZED(b);
float dot = vector::dot(a, b);
// test for dot -1
if(nearly_equal_eps_f(dot, -1.0f, 0.000001f))
{
// vector a and b point exactly in the opposite direction,
// so it is a 180 degrees turn around the up-axis
return quaternion(up, gdeg2rad(180.0f));
}
// test for dot 1
else if(nearly_equal_eps_f(dot, 1.0f, 0.000001f))
{
// vector a and b point exactly in the same direction
// so we return the identity quaternion
return quaternion(0.0f, 0.0f, 0.0f, 1.0f);
}
float rotAngle = acos(dot);
vector rotAxis = vector::cross(a, b);
rotAxis = vector::normalize(rotAxis);
return quaternion(rotAxis, rotAngle);
}
EDIT: Corrected version of Marc's XNA code
// the new forward vector, so the avatar faces the target
Vector3 newForward = Vector3.Normalize(Position - GameState.Avatar.Position);
// calc the rotation so the avatar faces the target
Rotation = Helpers.GetRotation(Vector3.Forward, newForward, Vector3.Up);
Cannon.Shoot(Position, Rotation, this);
public static Quaternion GetRotation(Vector3 source, Vector3 dest, Vector3 up)
{
float dot = Vector3.Dot(source, dest);
if (Math.Abs(dot - (-1.0f)) < 0.000001f)
{
// vector a and b point exactly in the opposite direction,
// so it is a 180 degrees turn around the up-axis
return new Quaternion(up, MathHelper.ToRadians(180.0f));
}
if (Math.Abs(dot - (1.0f)) < 0.000001f)
{
// vector a and b point exactly in the same direction
// so we return the identity quaternion
return Quaternion.Identity;
}
float rotAngle = (float)Math.Acos(dot);
Vector3 rotAxis = Vector3.Cross(source, dest);
rotAxis = Vector3.Normalize(rotAxis);
return Quaternion.CreateFromAxisAngle(rotAxis, rotAngle);
}
No comments:
Post a Comment