Let me first explain the problem. Let's say you are writing a game where you control an aeroplane flying through 3D space. You want to implement the following controls: Pad Up (Joystick forward) to pitch your plane downwards, Pad Down (Joystick back) pitches up, Pad Left (Joystick Left) rolls the plane to the left and Pad Right (Joystick Right) rolls to the right.
The 3D object you are using has the Z axis running along the fuselage of the plane, the X axis runs across the wings and the Y axis points upwards.
Now you go to implement the control system in your game using the Matrix Rotation functions to rotate your model about the world. Most programs keep track of the angle their object is pointing, and use RotMatrix() to produce a Local to World matrix for your object. While this method may work for a car on a racetrack, it simply won't work for realistic control of a plane due to the "Gimbal Lock" problem.
To solve this, you need to keep track of a set of axis for your plane that are continuously updated as the plane rotates. These axis ALWAYS reference the plane as described above (i.e. X runs along the wings, Z runs through the fuselage and Y is up) regardless of it's orientation in 3D space. This way you can easily pitch the aircraft by simply rotating about the planes X axis. But to do this, you need to know how to rotate around an arbitrary axis, as the Matrix Rotation functions supplied in the Net Yaroze library don't support this.
First off, you need to store your axis. These must be unit vectors, and point in the direction required. To create these vectors for the plane model, use the following code fragment.
axisx.vx = ONE; axisx.vy = 0; axisx.vz = 0; axisy.vx = 0; axisy.vy = ONE; axisy.vz = 0; axisz.vx = 0; axisz.vy = 0; axisz.vz = ONE;
When you want to rotate the plane, you have to do it by each axis in turn. If the plane pitches, rotate around axisx. If the plane rolls, rotate around axisy. In each instance, you need to update your axis vectors so that they always remain aligned with the plane.
To rotate about an arbitrary axis, you need the following matrix. The angle to rotate is a, and the axis components are vx, vy and vz.
| vx.vx+(1-vx.vx)cos(a) vx.vy(1-cos(a))-vz.sin(a) vx.vz(1-cos(a))+vy.sin(a) | | vx.vy(1-cos(a))+vz.sin(a) vy.vy+(1-vy.vy)cos(a) vy.vz(1-cos(a))-vx.sin(a) | | vx.vz(1-cos(a))-vy.sin(a) vy.vz(1-cos(a))+vx.sin(a) vz.vz+(1-vz.vz)cos(a) |
You can quite easily speed up creation of this matrix by pre-calculating certain parts (e.g (1-cos(a)) etc..).
If you want to rotate around axisx, then you need to update axisy and axisz from this matrix. If you store your axis in a VECTOR, you can just use ApplyMatrixLV().
Creating a Local to World matrix.
Now you are using your own axis for the model, you can't use RotMatrix() to create the coord member of your object's GsCOORDINATE2 structure. However, you can just use the values directly from your axis vectors. Assuming tmp is a MATRIX, use the following.
tmp.m[0][0] = axisx.vx; tmp.m[1][0] = axisx.vy; tmp.m[2][0] = axisx.vz; tmp.m[0][1] = axisy.vx; tmp.m[1][1] = axisy.vy; tmp.m[2][1] = axisy.vz; tmp.m[0][2] = axisz.vx; tmp.m[1][2] = axisz.vy; tmp.m[2][2] = axisz.vz;
Don't forget to set tmp.t as normal, and your object is now ready for display.
It's not over yet!
There is one final problem to overcome. If you run this, your object will quickly become distorted and unrecognisable. This is because your axis vectors are slowly deterioating due to inaccuracies in using fixed point numbers. To solve this, you need to 'repair' your axis vectors after each set of rotations.
First off, Normalize your Z vector (axisz).
Now create a new X vector from the cross product of the Y and Z vectors. (axisy x axisz).
Finally create a new Y vector from the cross product of the Z and X vectors (axisz x axisx).
This should keep your axis in tip top condition.
To summarise, here's what happens when the player presses Left on the pad (plane rolls left).
1) Create a matrix to rotate around the axisz (around the planes fuselage).
2) Apply this matrix to axisx and axisy.
3) Repair all three axis vectors.
4) Use your axis vectors to create the local to world matrix.