Search code examples
cmathopenglquaternions

Quaternion "lookAt" function


Im struggling with the following problem. Im working with bone animation and I want (ie) the head of the player to follow an another object in space. My up axis is +Z my forward axis is +Y, and the magnitude of the quaternion is in W. I tried to use the mesa code for gluLookAt and use the 3x3 matrix to transform to a quaternion but it doesn't work as expected so I go in another direction...

So far I got the following code that is "almost" working at least the head of the player is rotating (however the X angle seems to affect the Y rotation axis) in the good direction but its looking straight up instead on following an object on the floor at about 65 degree:

qt LookRotation( v3 lookAt, v3 upDirection )
{
qt t;

v3 forward = lookAt;
v3 up = upDirection;

OrthoNormalize( &forward, &up );

v3 right = v3_cross( up, forward );

mat3 m = mat3_make( right.x, up.x, forward.x,
                    right.y, up.y, forward.y,
                    right.z, up.z, forward.z );

t.w = sqrtf( 1.0f +
             m.r[ 0 ].x +
             m.r[ 1 ].y +
             m.r[ 2 ].z ) * 0.5f;

float w4_recip = 1.0f / ( 4.0f * t.w );

t.x = ( m.r[ 2 ].y - m.r[ 1 ].z ) * w4_recip;

t.y = ( m.r[ 0 ].z - m.r[ 2 ].x ) * w4_recip;

t.z = ( m.r[ 1 ].x - m.r[ 0 ].y ) * w4_recip;

t = qt_normalize( t );

return t;
}

... ... ...

v3 v = v3_sub( vec4_to_v3( transform.world.r[ 3 ] /* The object XYZ location in the world */),
           skeleton->final_pose.location[ i ] /* i = The head joint location */ );

v = v3_normalize( v );

qt q = LookRotation( v,
        v3_make( 0.0f, 0.0f, 1.0f ) );

Can someone help me figuring out this problem... Im kinda new with quaternions and don't really know where I could have messed up. After quite some research basically what I want to do is something like the Unity API: http://docs.unity3d.com/Documentation/ScriptReference/Quaternion.LookRotation.html


Solution

  • I think this function will do what you need:

    /// <summary>
    /// Evaluates a rotation needed to be applied to an object positioned at sourcePoint to face destPoint
    /// </summary>
    /// <param name="sourcePoint">Coordinates of source point</param>
    /// <param name="destPoint">Coordinates of destionation point</param>
    /// <returns></returns>
    public static Quaternion LookAt(Vector3 sourcePoint, Vector3 destPoint)
    {
        Vector3 forwardVector = Vector3.Normalize(destPoint - sourcePoint);
    
        float dot = Vector3.Dot(Vector3.forward, forwardVector);
    
        if (Math.Abs(dot - (-1.0f)) < 0.000001f)
        {
            return new Quaternion(Vector3.up.x, Vector3.up.y, Vector3.up.z, 3.1415926535897932f);
        }
        if (Math.Abs(dot - (1.0f)) < 0.000001f)
        {
            return Quaternion.identity;
        }
    
        float rotAngle = (float)Math.Acos(dot);
        Vector3 rotAxis = Vector3.Cross(Vector3.forward, forwardVector);
        rotAxis = Vector3.Normalize(rotAxis);
        return CreateFromAxisAngle(rotAxis, rotAngle);
    }
    
    // just in case you need that function also
    public static Quaternion CreateFromAxisAngle(Vector3 axis, float angle)
    {
        float halfAngle = angle * .5f;
        float s = (float)System.Math.Sin(halfAngle);
        Quaternion q;
        q.x = axis.x * s;
        q.y = axis.y * s;
        q.z = axis.z * s;
        q.w = (float)System.Math.Cos(halfAngle);
        return q;
    }
    

    This code comes from here: https://gamedev.stackexchange.com/questions/15070/orienting-a-model-to-face-a-target I just slightly modified it to fit my case, which was implementation of the transform.LookAt without using Unity3D.