Quaternion over-rotation problem?

Started by
14 comments, last by RandomAxess 19 years, 10 months ago
ok, i''ve re-tried what you have suggested Annon. I''m still having the exact same problem tho, I rotate in a diagonal up-right or diagonal down-right when i hit up or down (respectively). The right and left still work fine, however.

This is my code.
iUp = initial Up vector
iDir = initial "foreward" vector.

direction and up are the vectors used by glLookAt.

Rotate(TVec3& rot_axis, float angle)
{
cout << rot_axis << endl;
unitize(rot_axis);

//update the current quaternion
Quaternion axis;
axis = axis_to_quat(angle, rot_axis);
unitize_quat(axis);
current = multiply_quat(axis, current);

direction = rotate_vector(iDir, current);
up = rotate_vector(iUp, current);

}

axis_to_quat is the following:

Quaternion axis_to_quat(float angle, Vec3f euler)
{
//first, convert to radians.
angle = angle * (M_PI / 180.0);

//second, declare a resulting quaternion
Quaternion result;

//thrid, do the appropriate math to generate the quaternion
result.a = cos(angle / 2.0);
result.v[0] = euler[0] * sin(angle / 2.0);
result.v[1] = euler[1] * sin(angle / 2.0);
result.v[2] = euler[2] * sin(angle / 2.0);

return result;
}

multiply_quat:

Quaternion multiply_quat(Quaternion& q1, Quaternion& q2)
{
//create our new quaternion
Quaternion q3;
//set the a value
//note that this onLY results if it is a UNIT
//quaternion

q3.v[0] = (q1.a * q2.v[0]) + (q1.v[0] * q2.a) + (q1.v[1] * q2.v[2]) - (q1.v[2] * q1.v[1]);
q3.v[1] = (q1.a * q2.v[1]) - (q1.v[0] * q2.v[2]) + (q1.v[1] * q2.a) + (q1.v[2] * q2.v[0]);
q3.v[2] = (q1.a * q2.v[2]) + (q1.v[0] * q2.v[1]) - (q1.v[1] * q2.v[0]) + (q1.v[2] * q2.a);
q3.a = (q1.a * q2.a) - (q1.v[0] * q2.v[0]) - (q1.v[1] * q2.v[1]) - (q1.v[2] * q2.v[2]);

//now, return that quaternion
return q3;
}

and finally rotate_vector:
Vec3f rotate_vector(Vec3f& vector, Quaternion& quat)
{
//create a temporary vector-based quaternion
Quaternion v;
v.a = 0.0;
v.v = vector;
//now, do this process: quat * v * quat''
v = multiply_quat(multiply_quat(quat, v), conjugate(quat));

//return the v value
return v.v;
}

in case you are wondering, the TVec3 is basically the equivalent to Vec3f, it casts into Vec3f and does everything the same, it is used by the utility kit i learnt graphics on (namely MxGUI which uses FLTK).

Advertisement
Unless I missed it, you''re not showing us where you get the appropriate axes for yaw, pitch and roll. Perhaps you can show us the code where you read the keyboard and call the rotate function.
the axis i''m rotating about is gotten from a key press. So lets say i look left:

if(key == ''a'') //rotate left
{
cam.Rotate(cam.up, 5.0);
canvas->redraw(); //redraw the canvas
}

i''m not including the keypress function because its really not that important.
What the above does is if i click ''a'', then i''m rotating 5 degrees about the camera''s up vector. The redraw just updates the window.

If i wanted to look up, i rotate around the cross product of up and foreward vector:

else if(key == ''w'') //rotate up
{
cam.Rotate(cross(cam.direction, cam.up), 5.0);
canvas->redraw(); //redraw the canvas
}

where cam.direction is the camera''s foreward direction vector.
Hmm. The only thing I can think of is that as you are rotating your direction and up vectors incrementally, they could be drifting away from the ''true'' forward and up vectors as defined by your quaternion, which could produce unpredictable results. But I''m not sure how this would cause the exact symptom you''re describing.

Stupid question, but are you sure the ''rotate right'' code block isn''t getting triggered also when you look up and down?

When you rotate up, do you rotate at a 45 degree angle, i.e. do you rotate right at the same rate you rotate up?

In any case, you might look into how to extract a matrix from a quaternion. This way, you could get your forward, up and side vectors directly from your quaternion each frame, and they would be guaranteed to be accurate. I''m not sure if this would fix your problem, but it might be a good idea.
well, i commented out the direction and up updates one at a time just to see what each on does. When i only update the direction vector, it moves up and to the right. When i only update the up vector, it "spins" along the direction vector...

My thinking is there is something wrong with my math? But what i don''t understand is why it works fine when rotating around the up vector ( right and left initially set to (0,0,1)).
Ah, finally! a conclusion!
Thanks to everyone for their continued support in trying to solve the problems i was encountering.

As it turns out, i went back, re-examined the math and the code, and I used a multiplication function similar to namethatnobodyelsetook showed. I''ll be looking into setting it into a matrix as well, just incase it ever needs to be used in the future, but for now i''m quite happy.

As for the current working implementation, i get the axis i wish to rotate about and the angle. i then construct the quaternion out of it, take the vUP and vForeward vectors and do:
v'' = q * v * q''

it works perfectly, and having thouroughly tested against the values from doing a matrix-based rotation, it is an exact match.

Once again, thank you everyone for their help and keeping up with this thread.

This topic is closed to new replies.

Advertisement