Il existe plusieurs façons de procéder. Vous pouvez calculer l'orientation absolue ou la rotation par rapport à votre avatar, cela signifie que votre nouvelle orientation = avatarOrientation * q. Voici le dernier:
Calculez l'axe de rotation en prenant le produit croisé du vecteur avant unitaire de votre avatar et le vecteur unitaire d'avatar à la cible, le nouveau vecteur avant:
vector newForwardUnit = vector::normalize(target - avatarPosition);
vector rotAxis = vector::cross(avatarForwardUnit, newForwardUnit);
Calculer l'angle de rotation à l'aide du produit scalaire
float rotAngle = acos(vector::dot(avatarForwardUnit, newForwardUnit));
Créez le quaternion en utilisant rotAxis et rotAngle et multipliez-le avec l'orientation actuelle de l'avatar
quaternion q(rotAxis, rotAngle);
quaternion newRot = avatarRot * q;
Si vous avez besoin d'aide pour trouver le vecteur avant actuel de l'avatar, l'entrée pour 1. tirez juste :)
EDIT: Le calcul de l'orientation absolue est en fait un peu plus facile, utilisez le vecteur direct de la matrice d'identité au lieu du vecteur direct avatars comme entrée pour 1) et 2). Et ne le multipliez pas en 3), utilisez-le directement comme nouvelle orientation:newRot = q
Important à noter: La solution présente 2 anomalies dues à la nature du produit croisé:
Si les vecteurs directs sont égaux. La solution ici est simplement de retourner le quaternion d'identité
Si les vecteurs pointent exactement dans la direction opposée. La solution ici est de créer le quaternion en utilisant les avatars axe haut comme axe de rotation et l'angle 180,0 degrés.
Voici l'implémentation en C ++ qui prend en charge ces cas marginaux. Le convertir en C # devrait être facile.
// 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: Version corrigée du code 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 d'autres termes, vous définissez simplement la rotation sur la rotation B à long terme. Slerp sert uniquement à déterminer à quoi devrait ressembler la rotation (0% <x <100%) entre les deux.