Models (XNA Game Studio 4.0 Programming)

Earlier in this topic, you were likened to a director, using your camera to bring your world to life. Just like the directors in movies who often use models to be the stars in their films, you will do the same thing!

What Is a Model?

A model is essentially a collection of geometry that is rendered together to form an object in the world.You’ve used them throughout this topic already, but let’s take more time now to go through the Model class itself, which has quite a bit of data.You remember from last topic when you were rendering things with vertex buffers and index buffers? Models use these objects, too, and are a great way to hold geometry.

If you use a Digital Content Creation (DCC) package, such as SoftImage Mod Tool (see Figure 5.9), to create your objects, when you export those creations to a file (such as an .fbx file or an .x file), you can add these to your content projects to use the Model content importer.These are loaded into the Model class.

A DCC package creating a model

Figure 5.9 A DCC package creating a model


Models

The first method you can look at on the Model class is the Draw method, because you’ve already used it in this topic. This method (as the name implies) draws the model using the supplied transform with the provided view and projection matrices and with the defaults for everything else. It is a quick and easy way to show something onscreen as you’ve seen so far.

A Tag property can also be used to store any type of data. Many content importers also include extra information in this object for the game to use. Let’s take a look at the Meshes property next.

Meshes

The Meshes property returns a collection of ModelMesh objects. In the box you’ve been using up until now in this topic, each model has only had one mesh inside of it. Although that is the simplest form of a model, models can be extremely complex. Imagine a model that represents a city. The city would have roads, buildings, and signs. Each of these objects can be represented by a different model mesh, whereas the combined set of meshes is the full model.

Each mesh also has a Draw method that can be used to render the mesh separately, but notice that this method has no parameters.This does the rendering of the mesh, but it does so with the current settings of the effects rather than setting the world, view, and projection matrices that the model’s Draw method does.This is because there is extra information inherent to each mesh that might change where it is in the world; we discuss this later in this topic.

Another piece of information found on the mesh is the BoundingSphere property, which describes a sphere that encompasses the entire mesh.The bounding sphere object has a few fields and methods that are used for a variety of reasons.The Center and Radius fields are used to describe the sphere, whereas there are a few helper methods such as Contains and Intersects used to detect objects in the sphere.You can also Transform the sphere using that helper method.

Each mesh can also have a Name (that can be set by your code or set during content importing and loading) and Tag similarly to the model itself. Aside from the bones (which are discussed in a moment), the last two properties on the mesh are Effects and MeshParts, which are interrelated.The Effects collection is the collection of effects that is associated with each mesh part and controls how the mesh parts are rendered. The number of effects and mesh parts is identical, and you can update or modify the effects for any of the mesh parts you’d like.

Note

Effects are discussed in depth in Chapers 6 and 8, "Built-In Shader Effects," and "Introduction to Custom Effects."

Each mesh part is the portion of the model that is actually rendered. Notice that one of the properties it has is the Effect that this part uses. Changing this property also affects the Effects property on the mesh at the same index this mesh part exists at.The rest of the properties contain the information about the geometry and gives you the information you need to draw the mesh part with the DrawIndexedPrimitives method.You can see the IndexBuffer and VertexBuffer properties that hold the index and vertex data and the PrimitiveCount to dictate how many triangles to draw.When drawing data using this information, you can always use a primitive type PrimitiveType.TriangleList.

A few other pieces of information on the mesh part can be used when setting the vertex buffer and index buffer to the device, such as NumVertices (which is the number of vertices in the buffer), VertexOffset (which is the number of vertices to skip before getting to the vertices required for this mesh part), and Startlndex (which is the number of indices to skip before getting to the first index required by this mesh part). Like the Model and ModelMesh before it, this also includes a Tag property.

Normally each mesh part is separated from the other portions of the mesh based on the material it uses to render itself.You see the term material used often in DCC packages and throughout literature on this subject. For the purposes of this topic, you can consider the material to be the combination of the Effect used to render the geometry and the textures required to do so.

For example, in the imaginary city model, you can also imagine that the road portions of the model are all one mesh. If you had three different kinds of roads, such as a paved street, a gravel road, and a dirt road, you would potentially use different textures and possibly even different effects for those, so your roads mesh would have three different mesh parts: one for the paved street, one for the gravel roads, and a final one for the dirt roads.

See Figure 5.10 for an example of how models, meshes, and mesh parts are all interrelated.

 Models, meshes, and mesh parts

Figure 5.10 Models, meshes, and mesh parts

Throughout the discussions of models, you didn’t about one particular type of object, so let’s take a look at it now.

Bones

The ModelMesh has a property ParentBone that you skipped, and the model itself had quite a few methods and properties that deal with bones.What exactly are bones (aside from things like a femur or a song by Alice in Chains)? At a high level, bones are what connect each mesh to every other mesh. See Figure 5.11 for an example of a simple model with three meshes.

 A model with three meshes

Figure 5.11 A model with three meshes

Let’s assume you want to build a model of a person, and what is in Figure 5.11 is what you have so far.You have the sphere representing the head, a cylinder representing the neck, and a cube representing the torso. Each of these body parts can be considered a mesh in the larger full model, and bones are what tie these together. Each bone tells you the relationship between itself, its parent, and its children.

One of the meshes in the model is the root bone.This is the mesh that is the root of the other meshes and is accessed via the Root property on the model, which returns a ModelBone object.These objects have a few different properties to help explain the relationship of that particular bone to the others in the model. First, it has the Children property that is a collection of ModelBone objects that define the children of this bone. You can also get the Parent (again, as a ModelBone) of this bone. Like the mesh, you can also get the Name of this bone, which can be set during content import and creation.

You also see the Index property, which is the index of this bone in the model’s Bones collection (which is the entire collection of bones for the model). Finally, you see the Transform property, which is the transform of this bone in relation to its parent. Let’s look at an example.

In the model in Figure 5.11, you can imagine that the torso would be the root bone with a single child being the cylinder forming the neck, which in turn, has a single child as the sphere forming the head. If the model had arms and the torso was the root, you could extrapolate out that it would have multiple children for the neck, arms, and legs.

Now, assume that the torso has a transformation of Matrix.Identity. It has no scale or rotation and it is located at the origin (0,0,0). However, the neck is approximately three units above that, so the Transform property of the neck bone is Matrix.CreateTranslation(0,3,0) .You can see that the head is even higher, yet approximately three units above its parent, the neck, so its Transform property would be Matrix.CreateTranslation(0,3,0), even though it is approximately six units above the torso! If your head was slightly turned, you could instead have its Transform property be Matrix.CreateRotationX( MathHelper.PiOver4) * Matrix.CreateTranslation (0,3,0). The transform is always based on the parent bone and not the model itself. This is especially useful for animated characters because like in this example, you can easily have your head turn side to side by simply changing the rotation transform on a single bone, rather than repositioning the entire set of geometry.

There are also three methods on the Model class you can use to get or set these trans-forms.You can use the CopyBoneTransformsTo method to create a copy of the transforms each bone has into a new array of matrices (for example, what you might want to pass into an effect).You can also use the similar (but opposite order) method of CopyBoneTransformsFrom to update all the bones with these new transforms.There is also another helper method called CopyAbsoluteBoneTransformsTo, which like the nonabsolute one, copies all the transforms into an array of matrices.The difference is this one combines each child’s transform with all ofits parent transforms.

What this means, in the previous example, by using CopyBoneTransformsTo you would have an array of three matrices: the first member having Matrix.Identity and the second two members having Matrix.CreateTranslation(0,3,0). However, if you use CopyAbsoluteBoneTransformsTo, you would have the same array of three matrices with the first two members remaining the same, but the third member would instead be Matrix.CreateTranslation(0,6,0) because it combines the transform with its parents transforms.

Rendering Models

Enough of all this text—‘let’s actually use the model class to draw some stuff onscreen! Create a new Windows Game project and add any model you’d like from the downloadable examples. Now, get ready to do some rendering.You need to add a variable for your model, such as:

tmpD-121_thumb

 

tmpD-122_thumb

Naturally, you need to replace the YourModel with whatever model you picked. Although you haven’t gotten to the effects yet, the next section of code enables default lights for all portions of the model that have it.

Note

By default, models loaded through the content pipeline have default effects set, depending on the materials set in the DCC package used to create it.

Next, because you’ve picked any random model you wanted, and models can be of a wide variety of sizes, you need to add a helper method to get the size of the largest mesh in the model, so you can scale it correctly. In a real game, you never have to do this trickery because you control the scale and size of the models, but here, it’s a quick operation to have a good guess. Add this private function to your game class:

tmpD-123_thumb

This goes through each mesh and finds the one with the largest bounding radius and returns the largest radius it finds. Now you can add another helper method to draw the model, which you’ update a few times over the next few pages to see the various ways of rendering the model. The first one is the easiest:

tmpD-124_thumb

The only thing interesting you use for the model is the world matrix.You create a scale that makes the model approximately 1.0f units.This enables you to use a static camera regardless of model size and still see it in its entirety.

Finally, you need to do the actual rendering. Replace your Draw method with the following:

tmpD-125_thumb

Notice here that you’re positioning the camera slightly offset from the model and four units away.This is possible only because of the scaling done in the DrawModel call; otherwise, you have to base your camera size on the size of the model. Nothing here is exactly new, though, and running the application shows your model rendered on screen.

Next, let’s add the new method DrawModelViaMeshes to use the meshes instead of the single Draw method on model. Add the following method and update your DrawModel call to use this one instead:

tmpD-126_thumb

This also needs a helper method to get the parent bone transform, so add this, too:

tmpD-127_thumb

So, what is going on here? You set the initial world matrix you want to use as the basis for everything, much like you did in the previous one-line call, but after that, things seem to get much more complicated! In reality, this is straightforward. For every mesh in your model, you go through the effects and set the matrices each needs.The view and projection matrices are the same for every effect (because you don’t want the camera moving around based on which portion of the model you’ render); however, the world matrix is vastly different.

Each mesh sets its world matrix to the parent’s world matrix combined with the constant world matrix for the model. Notice that the helper method to get the parent’s world matrix is recursive. Because each bone’s transform is relative to its parent’s transform, to get the full transform for any bone in the model, you need to combine its transform with all of its parents, which is what the helper method does. It stops when it gets to the root bone because it has no parent and simply returns the root bone’s transform.

Note

In a real game, you do not want to use this recursive function every single frame as you did here to get the bone transforms. You would instead cache them (and use perhaps the CopyAbsoluteBoneTransformsTo helper method), but it was done this way to illustrate the concept.

If you want even more control, however, you can render everything in the model exclusively through the device methods without ever calling one of the helper Draw methods.Add yet another helper method to draw the model via the vertex data itself and update your DrawModel call to use this one instead:

tmpD-128_thumb

 

 

 

tmpD-129_thumb

Notice the similarities between this one and the last one.You still need to enumerate through each of the meshes; however, instead of calling the Draw method on the meshes, enumerate through each of the mesh parts.You set the world, view, and projection matrices as you did before, although the actual rendering is done much differently.You learn in subsequent topics that effects can have multiple passes (where you render the same thing multiple times with different effects), and this code handles the situation by enumerating through the effects before drawing.

You can see that the data needed to do the rendering is on the ModelMeshPart class. You need to set the vertex and index buffers to the device, which are on the part, and then the data needed to make the DrawlndexedPrimitives call are also there. By default, models loaded via the built-in importers from the content pipeline require the primitive type to be TriangleList, although creating your own importers can change this if you want to change it.

Summary

In this topic, you learned about the camera and the view and projection matrices.You were introduced to the model class and learned how to render objects in the world with it.

In the next topic, you look at the effects runtime and the built-in effects that are part of the framework.

Next post:

Previous post: