Quaternions define a subset of a hypercomplex number system. Quaternions are defined by (i2 = j2 = k2 = ijk = -1). jME makes use of Quaternions because they allow for compact representations of rotations, or correspondingly, orientations, in 3D space. With only four float values, we can represent an object’s orientation, where a rotation matrix would require nine. They also require fewer arithmetic operations for concatenation.
Additional benefits of the Quaternion is reducing the chance of Gimbal Lock and allowing for easily interpolation between two rotations (spherical linear interpolation or slerp).
While Quaternions are quite difficult to fully understand, there are an exceeding number of convenience methods to allow you to use them without having to understand the math behind it. Basically, these methods involve nothing more than setting the Quaternion’s x,y,z,w values using other means of representing rotations. The Quaternion is then contained in Spatial as its local rotation component.
Quaternion q has the form
q = <_w,x,y,z_> = w + xi + yj + zk
or alternatively, it can be written as:
q = s + v, where s represents the scalar part corresponding to the w-component of q, and v represents the vector part of the (x, y, z) components of q.
Multiplication of Quaternions uses the distributive law and adheres to the following rules with multiplying the imaginary components (i, j, k):
i2 = j2 = k2 = -1+
ij = -ji = k+
jk = -kj = i+
ki = -ik = j
However, Quaternion multiplication is not commutative, so we have to pay attention to order.
q1q2 = s1s2 - v1 dot v2 + s1v2 + s2v1 + v1 X v2
Quaternions also have conjugates where the conjugate of q is (s - v)
These basic operations allow us to convert various rotation representations to Quaternions.
You might wish to represent your rotations as Angle Axis pairs. That is, you define a axis of rotation and the angle with which to rotate about this axis. Quaternion defines a method
fromAngleNormalAxis) to create a Quaternion from this pair. This is actually used quite a bit in jME demos to continually rotate objects. You can also obtain a Angle Axis rotation from an existing Quaternion using
You can also represent a rotation by defining three angles. The angles represent the rotation about the individual axes. Passing in a three-element array of floats defines the angles where the first element is X, second Y and third is Z. The method provided by Quaternion is
fromAngles and can also fill an array using
If you have three axes that define your rotation, where the axes define the left axis, up axis and directional axis respectively) you can make use of
fromAxes to generate the Quaternion. It should be noted that this will generate a new Matrix object that is then garbage collected, thus, this method should not be used if it will be called many times. Again,
toAxes will populate a Vector3f array.
Commonly you might find yourself with a Matrix defining a rotation. In fact, it’s very common to contain a rotation in a Matrix, create a Quaternion, rotate the Quaternion, and then get the Matrix back. Quaternion contains a
fromRotationMatrix method that will create the appropriate Quaternion based on the given Matrix. The
toRotationMatrix will populate a given Matrix.
Matrix3f mat = new Matrix3f(); mat.setColumn(0, new Vector3f(1,0,0)); mat.setColumn(1, new Vector3f(0,-1,0)); mat.setColumn(2, new Vector3f(0,0,1)); s.getLocalRotation().fromRotationMatrix(mat);
As you can see there are many ways to build a Quaternion. This allows you to work with rotations in a way that is conceptually easier to picture, but still build Quaternions for internal representation.
One of the biggest advantages to using Quaternions is allowing interpolation between two rotations. That is, if you have an initial Quaternion representing the original orientation of an object, and you have a final Quaternion representing the orientation you want the object to face, you can do this very smoothly with slerp. Simply supply the time, where time is [0, 1] and 0 is the initial rotation and 1 is the final rotation.
Quaternion q1; Quaternion q2; //the rotation half-way between these two Quaternion q3 = q1.slerp(q2, 0.5f);
See 3D-Rotations for troubleshooting and extra information on using quaternions.