Search code examples
xnarotationalignmentpointquaternions

Quaternions, rotate a model and align with a direction


Suppose you have quaternion that describes the rotation of a 3D Model.

What I want to do is, given an Object (with rotationQuaternion, side vector...), I want to align it to a target point.

For a spaceship, I want the cockpit to point to a target.

Here is some code I have ... It's not doing what I want and I don't know why...

        if (_target._ray.Position != _obj._ray.Position)
        {
            Vector3 vec = Vector3.Normalize(_target._ray.Position - _obj._ray.Position);
            float angle = (float)Math.Acos(Vector3.Dot(vec, _obj._ray.Direction));
            Vector3 cross = Vector3.Cross(vec, _obj._ray.Direction);

            if (cross == Vector3.Zero)
                cross = _obj._side;

            _obj._rotationQuaternion *= Quaternion.CreateFromAxisAngle(cross,angle);
        }
        // Updates direction, up, side vectors and model Matrix
        _obj.UpdateMatrix();

after some time the rotationQuaternion is filled with almost Zero at X,Y,Z and W

Any help? Thanks ;-)


Solution

  • Your code's a bit funky.

    if (_target._ray.Position != _obj._ray.Position)
    {
    

    This may or may not be correct. Clearly, you've overridden the equals comparator. The correct thing be be doing here would be to ensure that the dot-product between the two (unit-length) rays is close to 1. If the rays have the same origin, then presumably have equal 'positions' means they're the same.

      Vector3 vec = Vector3.Normalize(_target._ray.Position - _obj._ray.Position);
    

    This seems particularly wrong. Unless the minus operator has been overridden in a strange way, subtracting this way doesn't make sense.

    Here's pseudocode for what I recommend:

    normalize3(targetRay);
    normalize3(objectRay);
    angleDif = acos(dotProduct(targetRay,objectRay));
    if (angleDif!=0) {
      orthoRay = crossProduct(objectRay,targetRay);
      normalize3(orthoRay);
      deltaQ = quaternionFromAxisAngle(orthoRay,angleDif);
      rotationQuaternion = deltaQ*rotationQuaternion;
      normalize4(rotationQuaternion);
    }
    

    Two things to note here:

    1. Quaternions are not commutative. I've assumed that your quaternions are rotating column vectors; so I put deltaQ on the left. It's not clear what your *= operator is doing.
    2. It's important to regularly normalize your quaternions after multiplication. Otherwise small errors accumulate and they drift away from unit length causing all manner of grief.