PDA

View Full Version : Setting an orientation roll angle to zero


karligula
07-14-2008, 05:50 AM
Hello folks,

I'm using a quaternion to define an entity's orientation, but I want to keep it flat to the world, so I want to zero the roll. I've written this bit of code:

quaternion orientation;
entity->GetOrientation(&orientation);

// Get the x and z axes
vec3 xAxis;
vec3 zAxis;
mtx matrix;
quaternionToMatrix(&matrix,&orientation);
xAxis.x=matrix.f[0][0];
xAxis.y=matrix.f[0][1];
xAxis.z=matrix.f[0][2];
zAxis.x=matrix.f[2][0];
zAxis.y=matrix.f[2][1];
zAxis.z=matrix.f[2][2];

// Get the flat axis
vec3 flatAxis;
if((zAxis.y<0.99999f)&&(zAxis.y>-0.99999f))
{
// Create a flat axis from the cross product of the z axis and a vertical vector
vec3 vertical;
vertical.x=0.0f;
vertical.y=1.0f;
vertical.z=0.0f;
veccross(&flatAxis,&vertical,&zAxis);
vecnormalise(&flatAxis,&flatAxis);
}
else
{
// If the zaxis is pointing straight up or down, getting a flat axis with a cross product won't work
// so set a default flat axis along the x axis
flatAxis.x=1.0f;
flatAxis.y=0.0f;
flatAxis.z=0.0f;
}

// Get the current roll angle between the xaxis and the flat axis
float rollAngle=RAD2DEG(acos(vecdot(&xAxis,&flatAxis)));

// Get the rotation angle
float angle=-rollAngle;

// Get the rotation axis
vec3 axis;
axis.x=0.0f;
axis.y=0.0f;
axis.z=1.0f;

// Create a rotation quat from the rotation axis and angle, and apply it to the orientation
quaternion rotation;
axisAngleToQuaternion(&rotation,&axis,DEG2RAD(angle));
quaternionMul(&orientation,&orientation,&rotation);
quaternionNormalise(&orientation);

// Set the orientation
entity->SetOrientation(&orientation);

But for the life of me I CANNOT get it to work. Despite applying the opposite rotation, the orientation actually increases in the same direction, ie angle 0f 7 degrees becomes 14 then 28, etc. So the quat just seems to spin around its z axis and never goes flat.

All the functions I'm calling here are used many times elsewhere in the engine so I'm pretty sure they're working fine. I've checked and double checked and triple checked the order of the parameters I'm passing. Is there some subtely to quats that I'm not taking into account?

I'm getting ready to throw the PC out the window here...

.oisyn
07-14-2008, 06:09 AM
rollAngle is not a "signed" angle. For example, if you take the 'up' vector, and then rotate it either x or -x degrees along the 'forward' vector, and then you calculate the angles between your new and old 'up', it will be x no matter what direction you turned. So another rotation by -x along the same 'forward' vector doesn't bring you back to where you started if you rotated by -x in the first place.

Rather than defining axis as (0, 0, 1), you should take the unit vector of the crossproduct between xAxis and flatAxis (not necessarily in that order, figure that out first). This gives you the correct rotation axis in world space (e.g., it will be flipped if the rotation needs to be in the other direction). Because it is in world space rather than in local space, you need to reverse the order of quaternion multiplication as well.

karligula
07-14-2008, 08:20 AM
Thanks for replying oisyn, I knew I was doing something stupid but couldn't see it ;-)

But I can't do a cross product of the xaxis with the flat axis because the xaxis could be flat already, which would result in a zero length z axis. So instead I'm doing a dot product with the flat axis and y axis, which I can use to tell which direction from vertical it is.

Can't believe this has taken me so long... guess I'm having one of those useless days...

.oisyn
07-14-2008, 08:25 AM
Thanks for replying oisyn, I knew I was doing something stupid but couldn't see it ;-)

But I can't do a cross product of the xaxis with the flat axis because the xaxis could be flat already, which would result in a zero length z axis.
If it is, you won't need to do a rotation, now would you? ;)
angle would be 0 in that case, even if you stick to the dotproduct, and so the extra rotation by the quaternion would be a no-op anyway. But indeed, checking the direction of flataxis against y would work as well.