Hay más de una forma de hacerlo. Puede calcular la orientación absoluta o la rotación relativa a su avatar, eso significa que su nueva orientación = avatarOrientation * q. Aquí está el último:
Calcule el eje de rotación tomando el producto cruzado del vector de unidad de avance de su avatar y el vector de unidad de avatar a destino, el nuevo vector de avance:
vector newForwardUnit = vector::normalize(target - avatarPosition);
vector rotAxis = vector::cross(avatarForwardUnit, newForwardUnit);
Calcule el ángulo de rotación usando el producto punto
float rotAngle = acos(vector::dot(avatarForwardUnit, newForwardUnit));
Crea el cuaternión usando rotAxis y rotAngle y multiplícalo con la orientación actual del avatar
quaternion q(rotAxis, rotAngle);
quaternion newRot = avatarRot * q;
Si necesita ayuda para encontrar el vector de avance actual del avatar, la entrada para 1. simplemente dispare :)
EDITAR: Calcular la orientación absoluta es en realidad un poco más fácil, use el vector directo de la matriz de identidad en lugar del vector directo de avatares como entrada para 1) y 2). Y no lo multiplique en 3), en su lugar, úselo directamente como la nueva orientación:newRot = q
Importante tener en cuenta: la solución tiene 2 anomalías causadas por la naturaleza del producto cruzado:
Si los vectores directos son iguales. La solución aquí es simplemente devolver la identidad quaternion
Si los vectores apuntan exactamente en la dirección opuesta. La solución aquí es crear el cuaternión mediante el uso de avatares en el eje hacia arriba como eje de rotación y el ángulo de 180.0 grados.
Aquí está la implementación en C ++ que se ocupa de esos casos extremos. Convertirlo a C # debería ser fácil.
// 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);
}
EDITAR: versión corregida del código XNA de Marc
// 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);
}
0*(rotation A) + 1*(rotation B)
, en otras palabras, solo está configurando la rotación en la rotación B a lo largo. Slerp es solo para determinar cómo debería verse la rotación (0% <x <100%) del camino intermedio.