3D Rotations Using Quaternions
I hope someone can help me debug my code for performing rotations about three axiis using quaternions, and so avoiding gimbal-lock. I wrote the code after trawling the internet for information, but unfortunately the code I've written still suffers from the dreaded gimbal-lock! The information was mainly extracted from "www.gamasutra.com" and "www.flipcode.com/documents/matrfaq.html". I'm using the OpenGL API, and perform the rotation by calling glMultMatrixf(). The code is as follows:
/***************************************************************************************************************
FILE NAME: rotate.h
AUTHOR: Justin C. Nixon
CREATED/UPDATED: 24/06/2000 24/06/2000
NOTES:
***************************************************************************************************************/
#ifndef ROTATE_H
#define ROTATE_H
typedef float RADIANS;
typedef float DEGREES;
#ifndef PI
#define PI 3.14159265358979323846f
#endif
#define RADIANS_TO_DEGREES 360.0f/(2*PI)
struct axis_angle
{
RADIANS x, y, z;
};
struct quaternion
{
float w, x, y, z;
};
struct attitude
{
DEGREES roll, pitch, heading;
};
void normalise_quaternion(struct quaternion *); // Normalise a quaternion.
void multiply_quaternion(struct quaternion *result_p, struct quaternion *a_p, struct quaternion *b_p); // Multiply two quaternions.
void axis_angles_to_quaternion(struct quaternion *, struct axis_angle *); // Convert axis angles to a quaternion.
void quaternion_to_matrix(struct quaternion *, float *);
void quaternion_to_attitude(struct quaternion *, struct attitude *); // Convert a quaternion to familiar roll, pitch, and heading values.
#endif
void normalise_quaternion(struct quaternion *quaternion_p)
{
float magnitude;
magnitude=(quaternion_p->w*quaternion_p->w)+(quaternion_p->x*quaternion_p->x)+(quaternion_p->y*quaternion_p->y)+(quaternion_p->z*quaternion_p->z); // Find the magnitude.
// Divide by the magnitude to normalise.
quaternion_p->w=quaternion_p->w/magnitude;
quaternion_p->x=quaternion_p->x/magnitude;
quaternion_p->y=quaternion_p->y/magnitude;
quaternion_p->z=quaternion_p->z/magnitude;
}
void multiply_quaternion(struct quaternion *result_p, struct quaternion *quaternion1_p, struct quaternion *quaternion2_p)
{
float a, b, c, d, e, f, g, h;
a=(quaternion1_p->w+quaternion1_p->x)*(quaternion2_p->w+quaternion2_p->x);
b=(quaternion1_p->z-quaternion1_p->y)*(quaternion2_p->y-quaternion2_p->z);
c=(quaternion1_p->x-quaternion1_p->w)*(quaternion2_p->y+quaternion2_p->z);
d=(quaternion1_p->y+quaternion1_p->z)*(quaternion2_p->x-quaternion2_p->w);
e=(quaternion1_p->x+quaternion1_p->z)*(quaternion2_p->x+quaternion2_p->y);
f=(quaternion1_p->x-quaternion1_p->z)*(quaternion2_p->x-quaternion2_p->y);
g=(quaternion1_p->w+quaternion1_p->y)*(quaternion2_p->w-quaternion2_p->z);
h=(quaternion1_p->w-quaternion1_p->y)*(quaternion2_p->w+quaternion2_p->z);
result_p->w=b+((-e-f+g+h)/2);
result_p->x=a-((e+f+g+h)/2);
result_p->y=-c+((e-f+g-h)/2);
result_p->z=-d+((e-f-g+h)/2);
normalise_quaternion(result_p);
}
void axis_angles_to_quaternion(struct quaternion *quaternion_p, struct axis_angle *axis_angle_p)
{
struct quaternion quaternion_wxy, quaternion_wx, quaternion_wy, quaternion_wz;
quaternion_wx.w=(float)cos(axis_angle_p->x/2.0f);
quaternion_wx.x=(float)sin(axis_angle_p->x/2.0f);
quaternion_wx.y=0.0f;
quaternion_wx.z=0.0f;
normalise_quaternion(&quaternion_wx);
quaternion_wy.w=(float)cos(axis_angle_p->y/2.0f);
quaternion_wy.x=0.0f;
quaternion_wy.y=(float)sin(axis_angle_p->y/2.0f);
quaternion_wy.z=0.0f;
normalise_quaternion(&quaternion_wy);
quaternion_wz.w=(float)cos(axis_angle_p->z/2.0f);
quaternion_wz.x=0.0f;
quaternion_wz.y=0.0f;
quaternion_wz.z=(float)sin(axis_angle_p->z/2.0f);
normalise_quaternion(&quaternion_wz);
multiply_quaternion(&quaternion_wxy, &quaternion_wx, &quaternion_wy);
multiply_quaternion(quaternion_p, &quaternion_wxy, &quaternion_wz);
}
void quaternion_to_matrix(struct quaternion *quaternion_p, float *matrix_p)
{
float xx, xy, xz, xw, yy, yz, yw, zz, zw;
xx=quaternion_p->x*quaternion_p->x;
xy=quaternion_p->x*quaternion_p->y;
xz=quaternion_p->x*quaternion_p->z;
xw=quaternion_p->x*quaternion_p->w;
yy=quaternion_p->y*quaternion_p->y;
yz=quaternion_p->y*quaternion_p->z;
yw=quaternion_p->y*quaternion_p->w;
zz=quaternion_p->z*quaternion_p->z;
zw=quaternion_p->z*quaternion_p->w;
*matrix_p=1.0f-2.0f*(yy+zz); // Element 0
matrix_p++;
*matrix_p=2.0f*(xy-zw); // Element 1
matrix_p++;
*matrix_p=2.0f*(xz+yw); // Element 2
matrix_p++;
*matrix_p=0.0f; // Element 3
matrix_p++;
*matrix_p=2.0f*(xy+zw); // Element 4
matrix_p++;
*matrix_p=1.0f-2.0f*(xx+zz); // Element 5
matrix_p++;
*matrix_p=2.0f*(yz-xw); // Element 6
matrix_p++;
*matrix_p=0.0f; // Element 7
matrix_p++;
*matrix_p=2.0f*(xz-yw); // Element 8
matrix_p++;
*matrix_p=2.0f*(yz+xw); // Element 9
matrix_p++;
*matrix_p=1.0f-2.0f*(xx+yy); // Element 10
matrix_p++;
*matrix_p=0.0f; // Element 11
matrix_p++;
*matrix_p=0.0f; // Element 12
matrix_p++;
*matrix_p=0.0f; // Element 13
matrix_p++;
*matrix_p=0.0f; // Element 14
matrix_p++;
*matrix_p=1.0f; // Element 15
}
void quaternion_to_attitude(struct quaternion *quaternion_p, struct attitude *attitude_p)
{
float sine_angle, tx, ty, tz;
attitude_p->roll=(float)acos(quaternion_p->w)*2*RADIANS_TO_DEGREES; // Calculate roll.
sine_angle=(float)sqrt(1.0f-(quaternion_p->w*quaternion_p->w)); // Calculate sine angle.
if(fabs(sine_angle)<0.0005f) sine_angle=1.0f; // Adjust sine angle if required.
tx=quaternion_p->x/sine_angle;
ty=quaternion_p->y/sine_angle;
tz=quaternion_p->z/sine_angle;
attitude_p->pitch=(float)-asin(ty)*RADIANS_TO_DEGREES; // Calculate pitch.
if((tx*tx)+(tz*tz)<0.0005f)
{
attitude_p->heading=0.0f; // Set heading.
}
else
{
attitude_p->heading=(float)atan2(tx,tz)*RADIANS_TO_DEGREES; // Calculate heading.
}
if(attitude_p->heading<0.0f) attitude_p->heading+=360.0f; // Adjust heading if required.
}
Edited by - Justin Nixon on 7/2/00 12:06:33 PM
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement
Recommended Tutorials
Advertisement