In the previous article, we discussed the polling of individual gyroscope axis values for driving the orientation of an object model in virtual 3D space. As a review, the basic structure for developing a sensor-based UI for 3D navigation is entirely driven by the Core Motion library in iOS, for which the CMMotionManager class is instantiated as a direct interface to the onboard accelerometer and gyroscope hardware in iPhone, iTouch and iPad devices (gyroscope available as of iPhone 4, iTouch 4G and iPad 1G). Sample code to initialize the CMMotionManager is provided below:

(CMTestViewController.h, in the private interface variable declaration area)
CMMotionManager *motionManager;
(CMTestViewController.m, in viewDidLoad or similar setup)
motionManager = [[CMMotionManager alloc] init];
motionManager.accelerometerUpdateInterval = 0.01;
motionManager.deviceMotionUpdateInterval = 0.01;
[motionManager startDeviceMotionUpdates];

At this point, the motionManager variable can be polled at any point in your code to retrieve a CMDeviceMotion structure, describing the physical motion of the device in discrete intervals defined by the update interval property described above. The accelerometerUpdateInterval and deviceMotionUpdateInterval represent the interval (in seconds) for providing accelerometer and device-motion updates to the block handler, respectively. The six degrees of physical motion can be described by accelerometer values for each independent axis (X, Y, Z) as well as the overall attitude of the device, derived from the embedded “attitude” class variable that can be retrieved through:

motionManager.deviceMotion.attitude

This variable contains properties such as “.yaw”, “.pitch” and “.roll”, which we previously used to describe rotation of the device along its Z, X and Y axes respectively. In addition, “attitude” provides a property named “rotationMatrix” that provides a full 3×3 matrix describing the Euler rotations that are required to translate a rigid body in 3D Euclidian space. The Euler angles represent a series of aggregate rotations, each around a single axis, that define the orientation of a point of reference in a three dimensional coordinate system. Therefore, the rotation matrix derived from the gyroscope attitude can be described as the product of three elemental rotation matrices, each representing a single axis. As mentioned in the previous post, the order in which these rotations are applied can significantly affect the overall translation of a point in 3D space, consisting of yaw, pitch and roll.

In addition, these operations result in the introduction of Gimbal lock, or the loss of one degree of freedom when two out of the three rotation axes in 3D Euclidian space are oriented in parallel and the system becomes locked to rotation in 2D space. We observed this phenomenon as either the gyroscope X or Y axis was rotated in parallel with the Z axis, effectively locking rotation of the remaining axis. In order to counteract this effect, a static fourth axis is added by embedding the attitude.rotationMatrix in a 4×4 Matrix with an identity vector for the fourth dimension. Note that a model view matrix is defined for the object model in 3D space and subsequently multiplied by the rotation matrix to complete the transformation. The model view matrix is distinct from the projection matrix, which defines the camera angle, aspect ratio and near/far values for Z offset.

Here is an example of how this might be arranged in the GLKit library, exclusive to iOS 5.0:

CMDeviceMotion *deviceMotion = motionManager.deviceMotion;
CMAttitude *attitude = deviceMotion.attitude;
rotation = attitude.rotationMatrix;
GLKMatrix4 rotationMatrix = GLKMatrix4Make(rotation.m11, rotation.m21, rotation.m31, 0, rotation.m12, rotation.m22, rotation.m32, 0, rotation.m13, rotation.m23, rotation.m33, 0, 0, 0, 0, 1);
modelViewMatrix = GLKMatrix4Multiply(modelViewMatrix, rotationMatrix);

From this point, the modelViewMatrix can be scaled, rotated by a gravity correction factor or applied directly to the transform property of either an object model or skybox effect, inherent to the GLKit API that was included in the iOS 5.0 release to simplify OpenGL ES by providing a usable template and higher level function calls for common 3D modeling elements. By replacing the aggregate yaw, pitch and roll rotation operations with a full rotation matrix accounting for the Euler angles, we have resolved any further ambiguity in model view orientation as well as the incidence of Gimbal lock when taking into account the azimuth in 3D space.