Manipulating the Tanks


Remember earlier when you were looking at the new frame class? Two sets of matrices were being stored, the transformation matrix and the combined matrix. However, they were never actually set to anything, so how are they supposed to work? Well, the transformation matrices are actually set by the runtime, after the model is loaded. It knows, for example, that the tank's gun turret should sit just above the tank's body, and it sets the transformation matrix to make this happen. The combined transformation matrix, however, is something you've added that the runtime knows nothing about. Look at the Update method in Listing 13.8 and add it to your tank class.

Listing 13.8. Updating the Tank
 /// <summary> /// Update the tank /// </summary> public void Update(float time) {     // Update the matrices for this tank based on where the tank     // currently is in the world     UpdateFrameMatrices(rootFrame, Matrix.Translation(tankPosition),                         rotationMatrix);     // Now get the wheel frames and update them     for(int i = 0; i < WheelFrames.Length; i++)     {         TankFrame wheelFrame = Frame.Find(rootFrame, WheelFrames[i]) as TankFrame;         wheelFrame.CombinedTransformationMatrix = Matrix.RotationY(wheelAngle)             * wheelFrame.CombinedTransformationMatrix;     } } 

The goal of this method is to assign the combined matrix transformation for every frame in the hierarchy, and the UpdateFrameMatrices method that is called first is the major part of this. When you look at the implementation (Listing 13.9), you'll see it's structured similarly to the DrawFrame method you implemented earlier because it will walk the hierarchy to set the data.

Listing 13.9. Updating Frame Matrices
 private void UpdateFrameMatrices(TankFrame frame, Matrix parentMatrix,                                  Matrix rotationMatrix) {     // Check which frames we are updating     switch(frame.Name)     {         case GunBarrelFrame:         case GunCasingFrame:             Matrix barrelRotation = Matrix.RotationX(barrelAngle);             Matrix turretRotation = Matrix.RotationY(turretAngle);             Matrix combinedRotation = (barrelRotation * turretRotation)                 * rotationMatrix * parentMatrix;             frame.CombinedTransformationMatrix = frame.TransformationMatrix                 * combinedRotation  ;             // Calculate the direction the barrel is facing             if (frame.Name == GunBarrelFrame)             {                 gunDirection = new Vector3(combinedRotation.M31,                     combinedRotation.M32, combinedRotation.M33);                 gunDirection.Normalize();                 gunPosition = new Vector3(frame.CombinedTransformationMatrix.M41,                     frame.CombinedTransformationMatrix.M42 + 23.0f,                     frame.CombinedTransformationMatrix.M43);             }             break;         case GunTurretFrame:             Matrix gunTurretRotation = Matrix.RotationY(turretAngle);             frame.CombinedTransformationMatrix = frame.TransformationMatrix                 * gunTurretRotation * rotationMatrix * parentMatrix;             break;         default:             // by default, just multiply the transformation matrix             // by the parent matrix             frame.CombinedTransformationMatrix = frame.TransformationMatrix                 * rotationMatrix * parentMatrix;             break;     }     if (frame.FrameSibling != null)     {         UpdateFrameMatrices(frame.FrameSibling as TankFrame, parentMatrix,                             rotationMatrix);     }     if (frame.FrameFirstChild != null)     {         // No need to include the rotation in any children frames,         // just use identity         UpdateFrameMatrices(frame.FrameFirstChild as TankFrame,             frame.CombinedTransformationMatrix, Matrix.Identity);     } } 

Now there's a scary-looking method! First impressions are always deceiving, though, because when you break it down, it's pretty simple. First, you need to check which frame you are using, and if it's one you happen to care about, do some fancy math calculations. What are these calculations actually intending to accomplish? You want to look at the gun's barrel and casing. (The casing is what connects the barrel to the turret.) Because the barrel and casing sit on top of the turret, you want to combine these matrices. Notice that first you get a rotation matrix for the turret and the barrel, the turret rotates along the Y axis, and the barrel rotates along the X axis.

Remember from Chapter 10, "A Quick 3D-Math Primer," combining matrices has to happen in the correct order. The combined rotation matrix that we have for the barrel and casing is calculated by multiplying first the child matrix (barrel) by the parent matrix (turret). Then, that result is multiplied against the global rotation matrix (which is passed in to the function), and finally, that result is multiplied against the parent matrix (which is also passed in to the method).

Once that is calculated, you have a single matrix that represents the rotation of the gun barrel and the gun casing. To calculate the combined transformation matrix, you take the automatically generated transformation matrix and the combined rotation matrix and multiply them together (ensuring that the automatically generated one is used first). You'll notice the gun turret frame does an almost identical thing, except it doesn't calculate the parent matrix first. You'll also notice that the default case is simply multiplying the automatically generated transformation matrix by the rotation matrix and the parent matrix (which is the simplest case).

Did you notice the extra checks for the gun barrel's direction and position? Later, during the game development, you need to know where the gun barrel is so you know where to fire your weapon. One of the nice things about the matrices being used is you can use the members to calculate your rotation and your position quite easily. As you can see from the code, the first three items of the third column relate to the direction the object is facing. The first three items of the fourth row is the position of the object in world space. The direction vector is normalized to make calculations easier. These two variables are very important later.

After the combined transformation matrix is calculated for this frame, you need to finish walking the frame hierarchy to ensure that each frame gets updated. For siblings, you simply pass on the same two matrices as you received from the method (because the siblings should each have the same parent). However, for the child frames, you actually pass in the combined matrix of this frame as the parent matrix and the identity matrix as the rotation matrix. You don't want to pass in the global rotation matrix again, but it's already been transformed by the parent matrixand if you do include it again, you get your objects being rotated twice as quickly as they should be.

With that, depending on the position and orientation of your tank, and the values of the angle variables for the turret and gun barrel, when you render the tank now, you will see it being rendered with each object able to move independently (see Figure 13.2).

Figure 13.2. The tank with a rotated turret.




Beginning 3D Game Programming
Beginning 3D Game Programming
ISBN: 0672326612
EAN: 2147483647
Year: 2003
Pages: 191
Authors: Tom Miller

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net