If you're digging through xcube looking for good rotation handling, I've achieved sane rotations during object manipulation (in GLUT which I hate, although I admire than it seems to be using the spaceball through Xinput correctly). Hopefully this'll be of use to someone.
1) A handler accepts the xrf, yrf, and zrf ("f" just means "float" here) rotations from the spaceball (glut has a callback for spaceball events)
2) I convert it into a quaternion with code resembling:
Code: Select all
typedef struct { float w, x, y, z; } Quaternionf_t;
Quaternionf_t axis6_input_rotation;
QuaternionFromAngles( & axis6_input_rotation, xrf, yrf, zrf);
That function's basically:
Code: Select all
void QuaternionFromAngles(Quaternionf_t *trg, float xr, float yr, float zr)
/*:note: pitch = xr, yaw = yr, roll = zr; angles are in radians */
{
float s1 = sin(zr/2.0);
float s2 = sin(yr/2.0);
float s3 = sin(xr/2.0);
float s2_x_s1 = s2 * s1;
float c1 = cos(zr/2.0);
float c2 = cos(yr/2.0);
float c3 = cos(xr/2.0);
float c2_x_c1 = c2 * c1;
trg->w = c3 * c2_x_c1 + s3 * s2_x_s1;
trg->x = s3 * c2_x_c1 - c3 * s2_x_s1;
trg->y = c3 * s2 * c1 + s3 * c2 * s1;
trg->z = c3 * c2 * s1 - s3 * s2 * c1;
QuaternionNormalize(trg);
}
3) Then I compose this newly input rotational delta just obtained into the retained rotation:
Code: Select all
QuaternionMultiply(&rotation, &axis6_input_rotation, &rotation);
That function looks like:
Code: Select all
void QuaternionMultiply(Quaternionf_t *trg, Quaternionf_t *a, Quaternionf_t *b)
/*:note: trg may be either of a, b, or some other target area */
{
float x = a->w * b->x + a->x * b->w + a->y * b->z - a->z * b->y;
float y = a->w * b->y + a->y * b->w + a->z * b->x - a->x * b->z;
float z = a->w * b->z + a->z * b->w + a->x * b->y - a->y * b->x;
float w = a->w * b->w - a->x * b->x - a->y * b->y - a->z * b->z;
trg->w = w;
trg->x = x;
trg->y = y;
trg->z = z;
}
4) I eventually apply the quaternion notation during OpenGL rendering with this, which converts the quaternion into a rotation (angle) around an arbitrarily-oriented line instead of one of the X, Y, or Z axes:
Code: Select all
XYZf_t axis;
float angle = QuaternionGetAxisAngle(&rotation, &axis);
glRotatef(Rad2Deg(angle), axis.x, axis.y, axis.z);
That function being:
Code: Select all
float QuaternionGetAxisAngle(Quaternionf_t *q, XYZf_t *axis)
{
float scale = sqrt(q->x * q->x + q->y * q->y + q->z * q->z);
float angle = acos(q->w); // (radians), will be zeroed or doubled shortly
if(NearZerof(scale)) {
angle = 0;
axis->x = axis->y = 0;
axis->z = 1;
} else {
angle *= 2.0;
axis->x = q->x / scale;
axis->y = q->y / scale;
axis->z = q->z / scale;
QuaternionNormalize(q);
}
return angle;
}
---
With all that, my rotations end up consistently representing the object being rotated with utter disregard to its own perspective, but rather twist it with the viewer's concept of the object's up, left, and right.