Week6 - Introduction to 3D programming
Week6.png

Start Project: Week6Start.zip

Finished Project: Week6Finish.zip

Notes

Much of this week's workshop covered general basics of 3D programming and nothing specific to XNA.

We started off going over the different matrices that we will be using to draw the scene. A good tutorial of this can be found here.

In the starting project in Game1.cs, declare 3 matrices right below skybox and cameraPosition:

Matrix world = Matrix.Identity;
Matrix view = Matrix.CreateLookAt(new Vector3(0, 1, 4), Vector3.Zero, Vector3.Up);
Matrix projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45), 800f / 600f, 0.01f, 100f);

In the matrix tutorial it explains what these 3 matrices are, so I won't do that here.

There is a line in Draw() that is commented out. If you look, you'll see that it relies on the view and projection matrices (that's why it was commented out). Go ahead and uncomment that line of code and see what it draws now. It should be a pristine orange lake.

Once we have that drawing it's time to draw the ship that will fly around here. A good tutorial for drawing models can be found here.

Declare a Model object right below the 3 matrices:

Model ship;

and then in the LoadContent() method at the bottom put:
ship = Content.Load<Model>("Models/viper");

Oh, by the way, the vast majority of the code here can be sucessfully compiled after every line of code. So after every line of code, type ctrl+shift+b to build the project and make sure there aren't any compile errors.

The last thing we want to do is draw the ship. In the draw method put the following lines of code (before or after the 3 lines of code used to draw the skybox):

foreach (ModelMesh mesh in ship.Meshes)
{
    foreach (BasicEffect effect in mesh.Effects)
    {
        effect.EnableDefaultLighting();
        effect.World = world;
        effect.View = view;
        effect.Projection = projection;
    }
 
    mesh.Draw();
}

There should be a ship that is sitting there in the middle of the screen now.

Again, the tutorial above goes over everything here so I won't take the time to explain it. If you haven't gone to either of the tutorials here, I would recommend it. The first one on matrices has a lot of information but just seeing what they do and getting that exposure is important. The second tutorial, drawing 3D models, is very short and easy to understand.

The next thing we will want to do is get the ship to actually move. We're not going to do anything fancy yet - this won't be as polished as Descent - but we will see that movement in 3D is very similar to movement in 2D. The tutorial for this part can be found here.

Right below the declaration of ship, declare a Vector3 called position:

Vector3 position;

This will be the position of the ship. Now let's give it some user input to change the position of the ship. Put the following code in the Update() method:
if (keyboardState.IsKeyDown(Keys.Left))
    position.X -= 0.02f;
if (keyboardState.IsKeyDown(Keys.Right))
    position.X += 0.02f;
if (keyboardState.IsKeyDown(Keys.Up))
    position.Y += 0.02f;
if (keyboardState.IsKeyDown(Keys.Down))
    position.Y -= 0.02f;
if (keyboardState.IsKeyDown(Keys.Add))
    position.Z -= 0.02f;
if (keyboardState.IsKeyDown(Keys.Subtract))
    position.Z += 0.02f;

This let's us change the position of the ship in all 3 axes. Of course, if you run the code it won't do anything just yet. We still need to link the position with the world matrix. Let's do that now. Right below the user input, put the following line of code:
world = Matrix.CreateTranslation(position);

Now you can control the ship with the arrow keys and +/-. Congratulations! You've written your first 3D animation! Of course, it looks pretty dumb, but it's a start.

Let's add the ability to rotate the ship about the Y-axis. Declare the following variable right below the ship and position object declarations:

float rotation;

Then right below the other keyboard inputs in Update() and before changing the world matrix, put these inputs:
if (keyboardState.IsKeyDown(Keys.A))
    rotation += MathHelper.ToRadians(1);
if (keyboardState.IsKeyDown(Keys.D))
    rotation -= MathHelper.ToRadians(1);

This code will allow the ship to rotate to the left by pressing the A key and right by pressing the D key.
We also need to change the world matrix assignment to be able to change rotation as well. Right below here, change this line of code:
world = Matrix.CreateTranslation(position);

to this:
world = Matrix.CreateRotationY(rotation) * Matrix.CreateTranslation(position);

You can see that we just added another matrix transformation to world. See how the RotationY matrix is first and the Translation matrix is second? That was done intentionally. Try switching those two matrices so instead of Matrix.CreateRotationY(rotation) * Matrix.CreateTranslation(position) is it Matrix.CreateTranslation(position) * Matrix.CreateRotationY(rotation) and see what it does. In order to notice the difference, you'll have to rotate and move the ship around. Is it the same? Do you see the difference? Do you remember back in College Algebra (or earlier) when the teacher said that matrix multiplication is not commutative? This is a perfect example of that. What we need to learn from this is the order that the matrix multiplication takes place. Translation should ALWAYS come after Scale and Rotate unless you have a really good reason not to do it that way.

Anyway, back to programming. The last thing we will want to do is pretty simple - get the camera to follow the ship instead of just sitting there. This is actually one line of code so that's nice. In Update(), right below where the world matrix is set, put the following line:

view = Matrix.CreateLookAt(cameraPosition, position, Vector3.Up);

That's it! You'll notice that the only change we are making to view is we're changing the second parameter, the cameraTarget, from Vector3.Zero to position.

There are a lot of little changes you can make to this tutorial to get it to behave differently so take some time and try things out and see if you can make things better.

  • Try changing rotation from a float to a Vector3 and changing several of the axis rotations at once. You'll quickly run across something known as Gimbal lock.
  • Try increasing the movement speed and see what happens when you fly far away. Kind of weird, huh?
  • Add a float scale variable and use Matrix.CreateScale(scale) in the world matrix computation. Do you remember where it goes? Try putting it after the Translation matrix when multiplying and see why it should always go before Translation.
  • Try using a different Rotation matrix. There are also CreateFromYawPitchRoll, CreateFromAxisAngle, and if you're feeling really ambitious, there's CreateFromQuaternion.

back to SIGXNA

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License