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
The LookAt function in OpenGL creates a view matrix that transforms vertices from world space to camera space. It takes three vectors as arguments that together describe the position and orientation of a camera.
The lookat matrix is a matrix that positions / rotates something to point to (look at) a point in space, from another point in space.
Creates a rotation with the specified forward and upwards directions.
Both of current answers have various problems for edge cases. The accepted answer is not correct for other reasons as well including the fact that it sets w=pi for one of the cases and also it doesn't do proper norms. After looking around quite a bit and testing several cases, I also found out that you need front and up vector to do this computation. So without further ado below is the code I'm using:
Quaternion lookAt(const Vector3f& sourcePoint, const Vector3f& destPoint, const Vector3f& front, const Vector3f& up)
{
Vector3f toVector = (destPoint - sourcePoint).normalized();
//compute rotation axis
Vector3f rotAxis = front.cross(toVector).normalized();
if (rotAxis.squaredNorm() == 0)
rotAxis = up;
//find the angle around rotation axis
float dot = VectorMath::front().dot(toVector);
float ang = std::acosf(dot);
//convert axis angle to quaternion
return Eigen::AngleAxisf(rotAxis, ang);
}
Bove uses popular Eigen library. If you don't want to use that then you might need following replacement for Eigen::AngleAxisf
:
//Angle-Axis to Quaternion
Quaternionr angleAxisf(const Vector3r& axis, float angle) {
auto s = std::sinf(angle / 2);
auto u = axis.normalized();
return Quaternionr(std::cosf(angle / 2), u.x() * s, u.y() * s, u.z() * s);
}
Note that special cases for dot product 0 or 1 or -1 gets automatically handled because normalized() returns 0 for the zero vector in Eigen library.
On the side note, for all your conversions worries, this is a great document to go to.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With