Bullet stores both position and rotation in a
btTransform which is a 4x4 matrix under the hood. You can extract just the rotation part into a normalized
btQuaternion which is a 4D "vector". The components of a Quaternion are NOT the normalized_axis + angle, however with some practice you can "eyeball" a Quaternion to get an idea of what the axis and rotation are.
Quaternions are useful because it is relatively easy to do rotation math with them to obtain other rotations. Once you're done with rotation math you would take the
finalQuaternion and do something like this:
Code: Select all
transform.setRotation(finalQuaternion);
body->setTransform(transform);
There are many tutorials about Quaternions online, however I will offer a crash course on them here:
(1) Quaternions in general span all of 4D space, however when it comes to rotations we are only concerned with those Quaternions which live on the surface of the unit hypersphere: centered at the origin with radius = 1.
(2) The subset of Quaternions on the unit hypersphere form a "Group" (research "Group Theory" for more about what this means) under some operator we will call "multiplication". In other words, when you "multiply" two elements in the Group the result is another element of the Group.
(3) There is an isomorphism (sorta) between the Group of unit Quaternions and the Group of Rotations in 3D. In other words, the two Groups behave in the same way under the multiplication operator. This is why unitary Quaternions are so useful in 3D math.
(4) The "sorta" qualifier is there because really there is a 2-to-1 mapping from Quaternion space to Rotations. For each valid Rotation there are two Quaternions that represent it, and these Quaternions are at opposite ends of the hypersphere: Q and -Q. This fact matters sometimes when you're doing interpolation from one Quaternion to another, but usually you can forget about it.
(5) Internally the Quaternion has four elements:
<x,y,z,w>. In code: some Quaternion implementations might order them
<w,x,y,z> but the
btQuaternion ctor which takes four floats orders them
<x,y,z,w>. The
<x,y,z> components are called the "imaginary" part, similar to the "imaginary number '
i' you learned in high school) but in this case there are three distinct imaginary axes and they are often referred to as
<i,j,k>. The
<w> component is known as the "real part".
(6) The identity
btQuaternion (zero rotation) has the form:
<0,0,0,1>
(7) For a rotation of
A radians about a normalized axis
<X,Y,Z> the corresponding
btQuaternion has components:
<sX, sY, sZ, c> (and its negative as per item
(4)) where:
s = sin(A/2) and
c=cos(A/2). Knowing this, with some practice you can "eyeball" the components of a
btQuaternion to get an idea as to what sort of rotation it represents. For example:
<1,0,0,0> = pi radians about X-axis
<0,1,0,0> = pi radians about Y-axis
<sqrt(2)/2, 0, 0, sqrt(2)/2> = pi/2 radians about X-axis
There are some common "gotchas" when working with
btQuaternions in code. Here are some:
(8) Due to floating point error a
btQuaternion probably doesn't lie on the unit hypersphere, but it is close. Consequently the product of two
btQuaternions:
Q1*Q2 can drift even further from the sphere. Normally this isn't a problem, but if you ever find yourself recycling a
btQuaternion to compute its next value then it will eventually explode off the sphere... unless you constantly normalize. In other words:
Code: Select all
Q = Q * deltaQ; // BAD!
Q = (Q * deltaQ).normalize(); // GOOD!
If you're going to use the result then discard it, don't normalize, but if you're going to save the
btQuaternion for the next loop then normalize it every loop. Note, this is not a problem if you're "storing" the
btQuaternion in a
btTrasnform because normalization automatically happens when storing the rotation into the 4x4 matrix.
(9) Bullet does not provide a convenient operator between
btQuaternion and
btVector3. It is implemented but probably doesn't do what you think: it doesn't just rotate the vector. To rotate a
btVector3 in Bullet first store the rotation into a
btTransform and use that.
(10) Rotations do not commute, so order matters. When doing rotation math you need to figure out if it is
Q1*Q2 or
Q2*Q1. My advice for this is to always imagine the rotations operating from the LEFT on a hypothetical vector on the RIGHT. So: consider it like so:
Q1 * Q2 * v
The question becomes: Which rotation should operate first? That one always goes RIGHT-most, and subsequent rotations operate from RIGHT to LEFT order.