6 min read

Drawing 3D Models

Introduction

In previous tutorials, we’ve talked about where to get 3D models from, or how to make your own. In this tutorial, we will take a look at how to use a 3D model in your game. This tutorial will just cover the basics, but in future tutorials we will look at a few more advanced features of drawing in 3D and working with models.

Loading the Model

The first thing we need to do is acquire a 3D model to place in our game. There’s a good chance that you’ve already got something that you want to put in your game, but if not, you can go over to my 3D Model Library and grab one. For this tutorial, I will use the SimpleShip model there, but you are more than welcome to use one of your own.

Take your model, and all associated textures, and add them to the content of your project. See the tutorial on managing content if you don’t remember how to do this. Now, we want our game to manage the model, but we don’t want it to manage the texture, other than when it loads the model, so we will want to exclude any textures by right-clicking on the texture in the Solution Explorer and choosing Exclude From Project. When we do this, the content pipeline will no longer process the file, but it will still be in the location that the model will look for it.

The next step is to actually load the model into our game. This is pretty easy to do. We will first create a variable to store the model. XNA has a type already created specifically for this called the Model class. So go to the beginning of your class where your other instance variables are (with the template, you should already have one called graphics and one called spriteBatch), and add the following line of code:

private Model model;

Now we just need to load our model into this object. Go down to the LoadContent() method, and add the following line of code (replace “Ship” with the name of your model if you are using a different model):

model = Content.Load<Model>("Ship");

Your model will now be ready for use!

Drawing the Model

We are now ready to draw our model. It is at this point that we will use the stuff we talked about in the previous tutorial, which discussed [matrices]https://rbwhitaker.com/tutorials/xna/3d/basic-matrices/). If you haven’t been through that tutorial, I would recommend going back to them now, but I’ve made the tutorial in such a way that if you want to continue without it, you can still do that. In order to draw, we will need to define the three standard matrices. So go back up to your instance variables section and add the following code:

private Matrix world = Matrix.CreateTranslation(new Vector3(0, 0, 0));
private Matrix view = Matrix.CreateLookAt(new Vector3(0, 0, 10), new Vector3(0, 0, 0), Vector3.UnitY);
private Matrix projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45), 800f / 480f, 0.1f, 100f);

With these created, we have everything we need to draw. To make things easy for us, let’s go down just below the Draw() method and create a new method, that will draw a single model, with given matrices. We can do this by adding the following code to our program:

private void DrawModel(Model model, Matrix world, Matrix view, Matrix projection)
{
    foreach (ModelMesh mesh in model.Meshes)
    {
        foreach (BasicEffect effect in mesh.Effects)
        {
            effect.World = world;
            effect.View = view;
            effect.Projection = projection;
        }

        mesh.Draw();
    }
}

This code shouldn’t be too difficult to understand. All it does is goes through each of the model’s meshes (a model can be made up of multiple objects, which are called meshes), and it goes through each of the effects and sets the effect’s matrices to be the matrices that we want. We will talk about effects a lot more later. For now, it is enough for you to know that an effect simply determines how a model is to be displayed on the screen. Finally, we tell each mesh of the model to be drawn, and we’re done.

All we need to do now is call this method with the appropriate values, and we will see our model appear on the screen. So add the following line of code to your Draw() method:

DrawModel(model, world, view, projection);

You should now be able to run your game, and see the model being drawn, as shown below:

Screenshot 1

You might be thinking that it doesn’t look too good right now and that it doesn’t do anything but sit there, which is true. We will make big improvements in both of these areas in the next few tutorials.

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace Using3DModels
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        /// <summary>
        /// Stores the model that we are going to draw.
        /// </summary>
        private Model model;

        /// <summary>
        /// Stores the world matrix for the model, which transforms the
        /// model to be in the correct position, scale, and rotation
        /// in the game world.
        /// </summary>
        private Matrix world = Matrix.CreateTranslation(new Vector3(0, 0, 0));

        /// <summary>
        /// Stores the view matrix for the model, which gets the model
        /// in the right place, relative to the camera.
        /// </summary>
        private Matrix view = Matrix.CreateLookAt(new Vector3(0, 0, 10), new Vector3(0, 0, 0), Vector3.UnitY);

        /// <summary>
        /// Stores the projection matrix, which gets the model projected
        /// onto the screen in the correct way.  Essentially, this defines the
        /// properties of the camera you are using.
        /// </summary>
        private Matrix projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45), 800f / 480f, 0.1f, 100f);


        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            model = Content.Load<Model>("Ship");
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            DrawModel(model, world, view, projection);

            base.Draw(gameTime);
        }

        /// <summary>
        /// Does the work of drawing a model, given specific world, view, and projection
        /// matrices.
        /// </summary>
        /// <param name="model">The model to draw</param>
        /// <param name="world">The transformation matrix to get the model in the right place in the world.</param>
        /// <param name="view">The transformation matrix to get the model in the right place, relative to the camera.</param>
        /// <param name="projection">The transformation matrix to project the model's points onto the screen correctly.</param>
        private void DrawModel(Model model, Matrix world, Matrix view, Matrix projection)
        {
            foreach (ModelMesh mesh in model.Meshes)
            {
                foreach (BasicEffect effect in mesh.Effects)
                {
                    effect.World = world;
                    effect.View = view;
                    effect.Projection = projection;
                }

                mesh.Draw();
            }
        }
    }
}
Download the complete project