That seems pretty simple, doesn't it? Unfortunately, nothing is ever as simple as it seems because outside of storing the device variable and setting a property, you haven't covered any of the other methods before. The animation root frame object is completely new. In the Direct3D API, the mesh hierarchies are commonly used to facilitate animation (and technically, that's what you're doing as well). The primary function of the mesh hierarchy is skeletal animation, but your tank doesn't have any bones. The only thing you care about is the frame hierarchy, which maintains the child/sibling relationship for the data in the frame. After that frame is loaded, you can use a method on the frame class to get the bounding sphere of the tank. Construction Cue
Loading the hierarchy from a file takes many of the same parameters that loading a mesh normally takes, but an entire new class is instantiated during the process, and you haven't even defined that class yet. You want to do that now, so add a new code file into your project. You can call it hierarchy.cs because that's what its job will be. You will find the implementation for this class in Listing 13.3. Listing 13.3. The Allocate Hierarchy Classpublic class TankAllocHierarchy : AllocateHierarchy { /// <summary> /// Create a new hierarchy frame /// </summary> public override Frame CreateFrame(string name) { // Create the new derived tank frame TankFrame frame = new TankFrame(); // Store the variables frame.Name = name; frame.TransformationMatrix = Matrix.Identity; frame.CombinedTransformationMatrix = Matrix.Identity; return frame; } /// <summary> /// Create a new mesh container for the tank hierarchy /// </summary> public override MeshContainer CreateMeshContainer(string name, MeshData meshData, ExtendedMaterial[] materials, EffectInstance[] effectInstances, GraphicsStream adjacency, SkinInformation skinInfo) { TankMeshContainer mesh = new TankMeshContainer(); // Store the variables we care about mesh.Name = name; mesh.MeshData = meshData; return mesh; } } As you see here, the TankAllocHierarchy class derives from the AllocateHierarchy abstract class, which requires you to implement a CreateFrame and CreateMeshContainer method. The frames hold the actual hierarchy data, but the mesh containers hold the vertex and adjacency data for each section of the hierarchy. In each of these methods, the objects are simply created, the name is set, and then they are returned. The name corresponds to the string constants you declared earlier this chapter (although you didn't cover all the names the tank files have). You might also notice in the frame creation sets some matrices as well. The matrices are needed because of the relationship between the full object (for example, the whole tank) and the partial object (for example, the gun barrel). The stored transformation matrix is the matrix that will transform a particular frame into the correct position and orientation relative to its parent. The combined transformation matrix is simply the combination of the frame matrix with those of its parents. It might benefit you to see the implementations of these two classes, which appear in Listing 13.4. Listing 13.4. The Frame and Mesh Container Classespublic class TankFrame : Frame { // Store the combined transformation matrix private Matrix combined = Matrix.Identity; public Matrix CombinedTransformationMatrix { get { return combined; } set { combined = value; } } } public class TankMeshContainer : MeshContainer { // We need no extra data here } You'll notice that you don't define the normal transformation matrix in the frame because that is stored in the base class. The mesh container class doesn't need any extra data, but because the mesh container base class is marked abstract, you must take the time to derive a new class from it. That's it for loading a frame hierarchy. Are you surprised how simple it was? Back in the tank's object constructor, when the LoadHierarchyFromFile method is called, the CreateMeshContainer and CreateFrame methods are called quite a few times while each submesh is loaded into the hierarchy. Next, you simply store the root frame, and you're now off and ready to start rendering a tank. You can see the draw method in Listing 13.5. Listing 13.5. Drawing a Tankpublic void Draw(GameEngine engine) { // Set the device's texture that will be used for rendering // the tank in either scenario engine.RenderingDevice.SetTexture(0, tankTexture); // Draw the mesh hierarchy DrawFrame(engine, engine.RenderingDevice, rootFrame); } |