Click here to Skip to main content
15,860,972 members
Articles / Multimedia / DirectX
Article

XNA: A Beginner's Introduction

Rate me:
Please Sign up or sign in to vote.
4.91/5 (36 votes)
17 Jul 2007CPOL9 min read 206.9K   1.9K   129   36
Getting started with XNA, Blender, and 3D modeling
Screenshot - img7.png

Introduction

This is an introductory article for XNA development, discussing tools and the minimum amount of code to start using the XNA Game Studio with simple models created in Blender.

Tools

To write applications (such as games) using the XNA Game Studio, you must download and install a variety of tools. Since Microsoft changes its website frequently, these links are not guaranteed to work. If you don't get to the download pages, use Google to search for the new links using the appropriate keywords.

This article is written using:

  • Visual Studio C# 2005 Express Edition with SP1
  • XNA Game Studio Express 1.0 Refresh
  • XNA Framework 1.0 Refresh
  • Blender 2.44
  • Python 2.51

Visual Studio Express

XNA development, as of the writing of this article, can only be done using Visual Studio Express.

  • Download Visual Studio C# 2005 Express Edition here
  • Download SP1 for VS Expression Edition here (scroll down a bit)

XNA

XNA development requires both the Game Studio Express (GSE) and the XNA Framework:

  • Download XNA Game Studio Express here
  • Download XNA Framework here

Modeling Tool

If you're going to be doing anything "serious" with XNA, you will quickly discover that you need a modeling tool to create your 3D models. I recommend Blender (which is free) or AutoDesk Maya, which is $2000. Guess which tool I'll be mentioning in this and other articles?

  • Download Blender here
  • Drool over Maya using the Maya Personal Learning Edition here

The Maya PLE cannot be used for creating models for XNA (as far as I know) because it cannot export to the format that XNA requires.

Plug-Ins

You will probably want to take advantage of plug-ins and scripts in Blender, which use Python.

  • Download Python here

Blender automatically detects the Python installation, so no further work on your part is necessary.

Tutorials

Tutorials on XNA and Blender are quite abundant and the Microsoft and Blender sites respectively and on numerous third party sites. Simply Google and you will find many resources. I started by going through the Microsoft tutorial on displaying a 3D model. Be aware of the aspect ratio bug which I describe here. It's not fatal, but it will lead to stretched objects if you use the Microsoft tutorial as a basis for working with models created in Blender.

Creating a Windows XNA Project

Launch Visual Studio C# 2005 Express Edition.

Creating the Windows Game Project

Create a Windows Game project by selecting the Windows Game project icon:

Image 2

Enter a name for the project and click on OK.

Create a Model Folder

Models (spaceships, people, objects, etc.) typically live in a Content\Models folder. Right click on the project and add the Content folder and Models sub-folder, so your project tree looks like this:

Image 3

If you build and run this project, you will get a window with a blue field.

Create a Model in Blender

Launch Blender. Blender starts with a cube as an initial model, although since you're looking at the cube from the top, it looks like a square:

Image 4

To see that it's actually a cube, click on the numpad 0 (with numlock on) and you will see:

Image 5

Export the Model

XNA cannot load Blender files, so rather, you must export your Blender model to an FBX format. You can read a little bit about FBX here. Basically it's an open standard, platform independent 3D file format created by AutoDesk. You can also export to DirectX (.x) format, however this brings up a complicated options dialog, whereas the FBX export does not.

The Blender UI will be a bit weird to Windows users, so here's the walkthrough:

Image 6
  • On the menu bar at the top, click on File (do not try to use the keyboard with the typical Alt-F keystroke combo!)
  • Move the mouse down to Export
  • Move the mouse over to Autodesk FBX
  • A dialog will appear with two textboxes, the first showing the path and the second for entering the filename. Click on the second edit box and type in "cube" (without the quotes).
  • Click on the Export FBX button or press the Enter key twice.

Import the Model into XNA

The model is now ready for importing into XNA:

  • Go back to the XNA Game Studio and right click on the Models folder and select "Add / Existing Item...".
  • In the file types, select "Content Pipeline Files"
  • Navigate to the Blender folder and select the cube.fbx file.

Add the Code to Render the Model

Rendering the model involves:

  • Defining where the model is
  • Defining where the camera is
  • Defining the camera orientation
  • Loading the model into the content pipeline
  • Drawing the model

All of this work will be done in the game1.cs file and the Game1 class.

Defining Where the Model Is

For simplicity, the model is placed at world coordinates (0, 0, 0). Create a field for the model position:

C#
protected Vector3 modelPosition = Vector3.Zero;

Defining Where the Camera Is

Create a field for the camera position:

C#
protected Vector3 cameraPosition = new Vector3(0.0f, 0.0f, 10.0f);

How did I arrive at these values? The vector consists of the x, y, and z axis position of the model. X is left-right, Y is up-down, and Z is in-out. If you look at the screenshots of the cube above, you will notice a grid. Each grid line represents one unit, therefore the cube is a 2 units wide, 2 units long, and 2 units tall. The camera is position ten units away from the world center (0, 0, 0) which creates a reasonably sized rendering of the cube.

Loading the Model into the Content Pipeline

Create a field for your model:

C#
protected Model myModel;

In the LoadGraphicsContent method, load your cube model:

C#
protected override void LoadGraphicsContent(bool loadAllContent)
{
  if (loadAllContent)
  {
    myModel = content.Load<Model>("Content\\Models\\cube");
  }
}

Note that you do not specify the file extension. The XNA Game Studio will load the appropriate file, as the model is actually compiled to a different file extension from the FBX file that you added to the Content\Models folders.

While we're here, let's also get the aspect ratio of the viewport (our window) as this is necessary for rendering the model so that it looks like a cube. Add the field:

C#
protected float aspectRatio;

and, to the above method, calculate the aspect ratio:

C#
protected override void LoadGraphicsContent(bool loadAllContent)
{
  if (loadAllContent)
  {
    myModel = content.Load<Model>("Content\\Models\\cube1");
  }

  aspectRatio = ((float)graphics.GraphicsDevice.Viewport.Width) / 
     ((float)graphics.GraphicsDevice.Viewport.Height);
}

Drawing the Model

Modify the Draw method so that it iterates through the model meshes, translating and rotating them appropriately, etc.

C#
protected override void Draw(GameTime gameTime)
{
  graphics.GraphicsDevice.Clear(Color.CornflowerBlue);

  Matrix[] transforms = new Matrix[myModel.Bones.Count];
  myModel.CopyAbsoluteBoneTransformsTo(transforms);

  // Draw the model. A model can have multiple meshes, so loop.
  foreach (ModelMesh mesh in myModel.Meshes)
  {
    // This is where the mesh orientation is set, as well as our camera and
    // projection.
    foreach (BasicEffect effect in mesh.Effects)
    {
      effect.EnableDefaultLighting();
      effect.World = transforms[mesh.ParentBone.Index];
      effect.View = Matrix.CreateLookAt(cameraPosition, Vector3.Zero, 
         Vector3.Up);
      effect.Projection = Matrix.CreatePerspectiveFieldOfView(
         MathHelper.ToRadians(45.0f), aspectRatio, 1.0f, 10000.0f);
    }

    // Draw the mesh, using the effects set above.
    mesh.Draw();
  }

  base.Draw(gameTime);
}

When you run the program, you will get a cube rendered on the blue field:

Image 7

The following is a brief description of what is going on in the above method. Each of these subtopics deserves considerably more description that I have provided, but I feel that is outside the scope of this beginner article.

Bones

The code above is the basic rendering loop for all models in the game. XNA uses a skeletal animation technique which you can read about here.

<p<code>CopyAbsoluteBoneTransformsTo</code /> flattens the bone hierarchy and puts the bones into an array so that the parent bone matrix can be quickly acquired in this assignment: <p /> <pre lang="cs">effect.World = transforms[mesh.ParentBone.Index];</pre> <p>Alternatively, you could write this as:</p> <pre lang="cs">effect.World = mesh.ParentBone.Transform;</pre> <p>However, since each mesh in the child needs to access its parent bone transform (so that the mesh builds on the transform of the parent bone) indexing is ultimately faster than asking the parent bone to recalculate its transform every time.</p> <p>How many bones do you think this model has? If you answer 1, because you think there's just one model, then you would be partly correct. However, there is always a root that defines the topmost parent of the bone tree:</p> <img height="254" hspace="0" src="/KB/game/xna1/img8.png" width="262" border="0" /> <h4>Model Meshes</h4> <p>A model consists of meshes (read about meshes in the Blender documentation). How many meshes do you think the cube has? The expected answer would be 6--one mesh for each face of the cube. However, the cube model only has one mesh! What is confusing is actually the local variable named <code>mesh, which instead should be called modelMesh, as there is a single mesh to represent the entire model. So one has to be clear about the difference between XNA ModelMesh instances and Blender meshes.

The XNA Mesh for the cube:

Image 8

consists of 1 ModelMeshPart object which has 8 vertices, which is the number of vertices in the cube (the above model has 51 vertices).

As a second example, an object like this:

Image 9

(in which the quad--square--meshes have been converted to triangles for one face and one of the triangles has been extruded) still is represented as only one model mesh in the XNA model.

Effects

This line of code:

C#
foreach (BasicEffect effect in mesh.Effects)

is an shorthand way of saying (again note we're dealing with the model mesh part):

C#
foreach (ModelMeshPart part in mesh.MeshParts)
{
  BasicEffect effect = (BasicEffect)part.Effect;
  ...

However the former is optimized to avoid redundant setup work if more than one ModelMeshPart shares the same effect (reference).

Effect Properties

Each model mesh is rendered based on:

  • its orientation
  • the orientation of the camera
  • the camera field of view

as expressed by these three statements:

C#
effect.World = transforms[mesh.ParentBone.Index];
effect.View = Matrix.CreateLookAt(cameraPosition, Vector3.Zero, Vector3.Up);
effect.Projection = Matrix.CreatePerspectiveFieldOfView(
     MathHelper.ToRadians(45.0f), aspectRatio, 1.0f, 10000.0f);
  • The World property establishes the world matrix for this model mesh (from the perspective of the model's world), which is typically a combination of the parent bone transforms and any local transforms applied just to this bone.
  • The View property establishes the orientation of the camera--its position, where it's looking, and its orientation.
  • The Projection property establishes how the camera projects its view onto the screen--the field of view, the aspect ratio of the viewport, and the boundaries (in distance) of what the camera can see: the near plane and the far plane.

Each model mesh requires the World property to be set to reflect any changes in the parent bone and any local changes. The View and Projection properties need to be set whenever the camera position and orientation changes. In this simple example, we could pre-calculate those values:

C#
effect.World = transforms[mesh.ParentBone.Index];
effect.View = lookAt;
effect.Projection = projection;

where lookAt and projection are Matrix objects initialized in the LoadGraphicsContent method:

C#
lookAt = Matrix.CreateLookAt(cameraPosition, Vector3.Zero, Vector3.Up);
projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45.0f),
    aspectRatio, 1.0f, 10000.0f);

Animating the Model

The above screenshot of the cube looks utterly boring and not like a cube at all. Getting an object to rotate on its own is one of the simplest forms of animation. The following code will rotate the cube around the X and Y axis, giving it some nice spin. First, add:

C#
protected float modelRotation = 0.0f;

to the Game1 class. This field is used for holding the rotational angle.

In the Update method, add the line to increment the model rotation based on the elapsed time between updates:

C#
protected override void Update(GameTime gameTime)
{
  // Allows the game to exit
  if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
    this.Exit();

  modelRotation += (float)gameTime.ElapsedGameTime.TotalMilliseconds * 
     MathHelper.ToRadians(0.1f);
  base.Update(gameTime);
}

In the Draw method, modify the World property assignment to rotate the model meshes:

C#
protected override void Draw(GameTime gameTime)
{
  graphics.GraphicsDevice.Clear(Color.CornflowerBlue);

  Matrix[] transforms = new Matrix[myModel.Bones.Count];
  myModel.CopyAbsoluteBoneTransformsTo(transforms);

  // Draw the model. A model can have multiple meshes, so loop.
  foreach (ModelMesh mesh in myModel.Meshes)
  {
    // This is where the mesh orientation is set, as well as our camera and 
    // projection.
    foreach (BasicEffect effect in mesh.Effects)
    {
      effect.EnableDefaultLighting();
      effect.World = transforms[mesh.ParentBone.Index] * 
          Matrix.CreateRotationX(modelRotation) * 
          Matrix.CreateRotationY(modelRotation);
      effect.View = Matrix.CreateLookAt(cameraPosition, Vector3.Zero, 
        Vector3.Up);
      effect.Projection = Matrix.CreatePerspectiveFieldOfView(
        MathHelper.ToRadians(45.0f), aspectRatio, 1.0f, 10000.0f);
    }

    // Draw the mesh, using the effects set above.
    mesh.Draw();
  }

  base.Draw(gameTime);
}

This creates a nice rotating cube:

Image 10

Conclusion

This article encapsulates the research and experimentation that I've done to get a basic understanding of XNA and how to use a tool such as Blender to start developing models that XNA can render. I've been blogging about my trials and tribulations here, and I will continue to do so as I make progress on this concept that I'm developing. I'm primarily interested in using XNA as a rendering space rather than game development, but I may just for fun diverge and put together a simple game that I wrote years ago in Windows 3.1.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect Interacx
United States United States
Blog: https://marcclifton.wordpress.com/
Home Page: http://www.marcclifton.com
Research: http://www.higherorderprogramming.com/
GitHub: https://github.com/cliftonm

All my life I have been passionate about architecture / software design, as this is the cornerstone to a maintainable and extensible application. As such, I have enjoyed exploring some crazy ideas and discovering that they are not so crazy after all. I also love writing about my ideas and seeing the community response. As a consultant, I've enjoyed working in a wide range of industries such as aerospace, boatyard management, remote sensing, emergency services / data management, and casino operations. I've done a variety of pro-bono work non-profit organizations related to nature conservancy, drug recovery and women's health.

Comments and Discussions

 
QuestionThanks Pin
haitrieu74910-Oct-12 23:03
haitrieu74910-Oct-12 23:03 
GeneralMy vote of 5 Pin
Peter Hawke27-Mar-12 12:21
Peter Hawke27-Mar-12 12:21 
QuestionThanks Pin
nspangen2-Feb-12 19:10
nspangen2-Feb-12 19:10 
QuestionCan i load a model using dialogbox Pin
bqhoang11-Jul-11 16:57
bqhoang11-Jul-11 16:57 
General'No suitable method found to override' error [modified] Pin
Noobie One17-Jun-11 10:41
Noobie One17-Jun-11 10:41 
GeneralRe: 'No suitable method found to override' error [modified] Pin
Peter Hawke27-Mar-12 12:20
Peter Hawke27-Mar-12 12:20 
Okay I see what you are doing wrong. You need to just put

C#
myModel = Content.Load<Model>("Content\\Models\\cube");
aspectRatio = ((float)graphics.GraphicsDevice.Viewport.Width) /
                ((float)graphics.GraphicsDevice.Viewport.Height);

that code into the LoadContent method. There really is no kneed to make a new method for that. Oh and one more thing just use
C#
myModel = Content.Load<Model>("Models\\cube");
instead. I used that and it worked for me just fine.
GeneralGood Introduction Pin
jeri_attrix12-Nov-10 0:08
jeri_attrix12-Nov-10 0:08 
GeneralGod job Pin
Miaw from Indonesia3-Sep-10 15:35
Miaw from Indonesia3-Sep-10 15:35 
GeneralMy vote of 5 Pin
Member 47289746-Aug-10 6:40
Member 47289746-Aug-10 6:40 
QuestionVery good Pin
Mohammad Dayyan19-Aug-08 15:12
Mohammad Dayyan19-Aug-08 15:12 
Generalawesome Pin
keisal21-Jul-08 10:03
keisal21-Jul-08 10:03 
GeneralRe: awesome Pin
Member 88425128-Sep-08 16:24
Member 88425128-Sep-08 16:24 
GeneralPointing newbies your way! Pin
Judah Gabriel Himango9-Aug-07 17:01
sponsorJudah Gabriel Himango9-Aug-07 17:01 
GeneralRe: Pointing newbies your way! Pin
Marc Clifton13-Aug-07 3:37
mvaMarc Clifton13-Aug-07 3:37 
GeneralRe: Pointing newbies your way! Pin
Judah Gabriel Himango13-Aug-07 4:06
sponsorJudah Gabriel Himango13-Aug-07 4:06 
GeneralThanks Marc Pin
Judah Gabriel Himango1-Aug-07 19:04
sponsorJudah Gabriel Himango1-Aug-07 19:04 
GeneralOther free modelers Pin
Arjan Schouten21-Jul-07 14:21
Arjan Schouten21-Jul-07 14:21 
GeneralRe: Other free modelers Pin
Marc Clifton21-Jul-07 14:56
mvaMarc Clifton21-Jul-07 14:56 
GeneralHie thee to thine childs's High School Pin
LimeyRedneck19-Jul-07 3:42
professionalLimeyRedneck19-Jul-07 3:42 
GeneralRe: Hie thee to thine childs's High School Pin
Marc Clifton19-Jul-07 3:58
mvaMarc Clifton19-Jul-07 3:58 
GeneralRe: Hie thee to thine childs's High School Pin
LimeyRedneck19-Jul-07 4:20
professionalLimeyRedneck19-Jul-07 4:20 
GeneralRhino3d and NURBS (and mesh) Pin
Sluggish18-Jul-07 7:47
Sluggish18-Jul-07 7:47 
GeneralRe: Rhino3d and NURBS (and mesh) Pin
Marc Clifton18-Jul-07 8:14
mvaMarc Clifton18-Jul-07 8:14 
GeneralCinema4D Pin
leppie18-Jul-07 4:54
leppie18-Jul-07 4:54 
GeneralRe: Cinema4D Pin
Pete O'Hanlon18-Jul-07 5:03
subeditorPete O'Hanlon18-Jul-07 5:03 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.