tags:

views:

2363

answers:

4

I have a 3d object that I wish to be able to rotate around in 3d. The easiest way is to directly translate X and Y mouse motion to rotation about the Y and X axes, but if there is some rotation along both axes, the way the model rotates becomes highly counterintuitive (i.e. if you flip the object 180 degrees about one axis, your motion along the other axis is reversed).

I could simply do the above method, but instead of storing the amount to rotate about the two axes, I could store the full rotation matrix and just further rotate it along the same axes for each mouse drag, but I'm concerned that that would quickly have precision issues.

+1  A: 

Create an accumulator matrix and initialize it with the identity.

Each frame, apply that to your modelview/world matrix state before drawing the object.

Upon mouse motion, construct a rotation matrix about the X axis with some sensitivity_constant * delta_x. Construct another rotation matrix about the Y axis for the other component. Multiply one, then the other onto the accumulator.

The accumulator will change as you move the mouse. When drawing, it will orient the object as you expect.

Also, the person talking about quaternions is right; this will look good only for small incremental changes. If you drag it quickly on a diagonal, it won't rotate quite the way you expect.

Kevin Conner
I overestimated the effect of floating point imprecision. Simply accumulating the rotations works fine.I'll look into quaternions, but since I'm only rotating about 2 axes, everything looks fine.
+3  A: 

You can deal with loss of precision by renormalising your rotation matrix so each of the 3 rows are perpendicular again. Or you can regenerate the rotation matrix you are about to modify based on existing information about the object, and this takes away the need for renormalisation.

Alternatively you can use quaternions, which is an alternative to Euler angles for dealing with rotations.

I learned much of this in my early days from this faq, which deals with this problem (though for another application) in Euler's are Evil.

freespace
A: 

It is probably most intuitive to rotate the object around the axis perpendicular to the current drag direction, either incrementally with each mouse motion, or relative to the drag start position. The two options give slightly different user interactions, which each have their pluses and minuses.

There is a relatively straightforward way to convert an angle and a 3d vector representing the axis being rotated around into a rotation matrix.

You are right in that updating a raw rotation matrix through incremental rotations will result in the matrix no longer being a pure rotation matrix. That is because a 3x3 rotation matrix has three times as much data than needed to represent a rotation.

A more compact way to represent rotations is with Euler Angles, having a minimal 3 value vector. You could take the current rotation as an Euler angle vector, convert it to a matrix, apply the rotation (incremental or otherwise), and convert the matrix back to an Euler angle vector. That last step would naturally eliminate any non-rotational component to your matrix, so that you once again end up with a pure rotational matrix for the next state.

Euler angles are conceptually nice, however it is a lot of work to do the back and forth conversions.

A more practical choice is Quaternions (also), which are four element vectors. The four elements specify rotation and uniform scale, and it happens that if you go in and normalize the vector to unit length, you will get a scale factor of 1.0. It turns out that an angle-axis value can also be converted to a quaternion value very easily by

q.x = sin(0.5*angle) * axis.x;
q.y = sin(0.5*angle) * axis.y;
q.z = sin(0.5*angle) * axis.z;
q.w = cos(0.5*angle);

You can then take the quaternion product (which uses only simple multiplication and addition) of the current rotation quaternion and incremental rotation quaternion to get a new quaternion which represents performing both rotations. At that point you can normalize the length to ensure a pure rotation, but otherwise continue iteratively combining rotations.

Converting the quaternion to a rotation matrix is very straightforward (uses only multiplication and addition) when you want to display the model in its rotated state using traditional graphics API's.

Lloyd
+2  A: 

In my Computer Graphics course, we were given the following code which allowed us not to reinvent the wheel.

trackball.h

trackball.c

Joseph