Search code examples
cangle

Angle to Quaternion - Making an object facing another object


i have two Objects in a 3D World and want to make the one object facing the other object. I already calculated all the angles and stuff (pitch angle and yaw angle). The problem is i have no functions to set the yaw or pitch individually which means that i have to do it by a quaternion. As the only function i have is: SetEnetyQuaternion(float x, float y, float z, float w). This is my pseudocode i have yet:

float px, py, pz;
float tx, ty, tz;           
float distance;
GetEnetyCoordinates(ObjectMe, &px, &py, &pz);
GetEnetyCoordinates(TargetObject, &tx, &ty, &tz);

float yaw, pitch;
float deltaX, deltaY, deltaZ;

deltaX = tx - px;
deltaY = ty - py;
deltaZ = tz - pz;

float hyp = SQRT((deltaX*deltaX) + (deltaY*deltaY) + (deltaZ*deltaZ));

yaw = (ATAN2(deltaY, deltaX));
if(yaw < 0) { yaw += 360; }


pitch = ATAN2(-deltaZ, hyp);
if (pitch < 0) { pitch += 360; }

//here is the part where i need to do a calculation to convert the angles

SetEnetyQuaternion(ObjectMe, pitch, 0, yaw, 0);

What i tried yet was calculating the sinus from those angles devided with 2 but this didnt work - i think this is for euler angles or something like that but didnt help me. The roll(y axis) and the w argument can be left out i think as i dont want my object to have a roll. Thats why i put 0 in.

If anyone has any idea i would really appreciate help. Thank you in advance :)


Solution

  • Let's suppose that the quaternion you want describes the attitude of the player relative to some reference attitude. It is then essential to know what the reference attitude is.

    Moreover, you need to understand that an object's attitude comprises more than just its facing -- it also comprises the object's orientation around that facing. For example, imagine the player facing directly in the positive x direction of the position coordinate system. This affords many different attitudes, from the one where the player is standing straight up to ones where he is horizontal on either his left or right side, to one where he is standing on his head, and all those in between.

    Let's suppose that the appropriate reference attitude is the one facing parallel to the positive x direction, and with "up" parallel to the positive z direction (we'll call this "vertical"). Let's also suppose that among the attitudes in which the player is facing the target, you want the one having "up" most nearly vertical. We can imagine the wanted attitude change being performed in two steps: a rotation about the coordinate y axis followed by a rotation about the coordinate z axis. We can write a unit quaternion for each of these, and the desired quaternion for the overall rotation is the Hamilton product of these quaternions.

    The quaternion for a rotation of angle θ around the unit vector described by coordinates (x, y, z) is (cos θ/2, x sin θ/2, y sin θ/2, z sin θ/2). Consider then, the first quaternion you want, corresponding to the pitch. You have

    double semiRadius = sqrt(deltaX * deltaX + deltaY * deltaY);
    double cosPitch = semiRadius / hyp;
    double sinPitch = deltaZ / hyp;  // but note that we don't actually need this
    

    . But you need the sine and cosine of half that angle. The half-angle formulae come in handy here:

    double sinHalfPitch = sqrt((1 - cosPitch) / 2) * ((deltaZ < 0) ? -1 : 1);
    double cosHalfPitch = sqrt((1 + cosPitch) / 2);
    

    The cosine will always be nonnegative because the pitch angle must be in the first or fourth quadrant; the sine will be positive if the object is above the player, or negative if it is below. With all that being done, the first quaternion is

    (cosHalfPitch, 0, sinHalfPitch, 0)
    

    Similar analysis applies to the second quaternion. The cosine and sine of the full rotation angle are

    double cosYaw = deltaX / semiRadius;
    double sinYaw = deltaY / semiRadius;  // again, we don't actually need this
    

    We can again apply the half-angle formulae, but now we need to account for the full angle to be in any quadrant. The half angle, however, can be only in quadrant 1 or 2, so its sine is necessarily non-negative:

    double sinHalfYaw = sqrt((1 - cosYaw) / 2);
    double cosHalfYaw = sqrt((1 + cosYaw) / 2) * ((deltaY < 0) ? -1 : 1);
    

    That gives us an overall second quaternion of

    (cosHalfYaw, 0, 0, sinHalfYaw)
    

    The quaternion you want is the Hamilton product of these two, and you must take care to compute it with the correct operand order (qYaw * qPitch), because the Hamilton product is not commutative. All the zeroes in the two factors make the overall expression much simpler than it otherwise would be, however:

    (cosHalfYaw * cosHalfPitch,
    -sinHalfYaw * sinHalfPitch,
     cosHalfYaw * sinHalfPitch,
     sinHalfYaw * cosHalfPitch)
    

    At this point I remind you that we started with an assumption about the reference attitude for the quaternion system, and the this result depends on that choice. I also remind you that I made an assumption about the wanted attitude, and that also affects this result.

    Finally, I observe that this approach breaks down where the target object is very nearly directly above or directly below the player (corresponding to semiRadius taking a value very near zero) and where the player is very nearly on top of the target (corresponding to hyp taking a value very near zero). There is a non-zero chance of causing a division by zero if you use these formulae exactly as given, so you'll want to think about how to deal with that.)