Saturday, November 22, 2008

XNA Series - Begining 3D - Part 1

I have been wanting to get some posts on here about 3D. But since it is a much more complex subject than 2D, I have been holding off as I am still getting my head around it. Perhaps we can work through this together and see what we come up with.

To start drawing in 3D, we need a model. I recommend something like Blender3D, since it has all the tools you need to create a working 3d model that you can import into XNA. This is not a tutorial about modeling in blender, perhaps I'll do that later. But once you have a model created in Blender, you want to export it in Autodesk FBX format. You can load that into your content pipeline in the same way you import a sprite. The difference is in the type of C# object we create to load it into. Rather than a texture2d object, we load in this model as a Model object. We will also create a Vector3 object to set it's position rather than a Vector2, since we need to put this model in 3d space.


Project Files


Model tower;
Vector3 pos = Vector3.Zero;
The next 4 objects in our game class declarations are a little more complex.

Vector3 camPos = new Vector3(0.0f, 60.0f, 160.0f);
Vector3 camLookAt = new Vector3(0.0f, 50.0f, 0.0f);
Matrix camProj;
Matrix camView;

Vector3 camPos will store the location of our 3d camera. Our 3d camera can be thought of as a video camera placed into the 3d world at a given location.

camLookAt is a point at which our camera will be pointed in 3d space.

the 2 Matrices camProj and camView are somewhat more complicated and we will set them up later.

To load in our Model's data, in LoadContent we will do this

tower = Content.Load<model>("tower");

that should look familiar, since it is the same way we loaded in images. Except we make sure to tell the Load function it is dealing with Model objects rather than Texture2D.

Next we initialize the camView matrix

camView = Matrix.CreateLookAt(camPos, camLookAt, Vector3.Up);

We create a 4x4 matrix from the position of the camera and the point it is looking at. This matrix gives us a way to alter the way we see the model so that it looks like we are seeing it from the camera. Then we set up the projection matrix

camProj = Matrix.CreatePerspectiveFieldOfView(
MathHelper.ToRadians(45.0f),
graphics.GraphicsDevice.Viewport.AspectRatio,
1.0f,
10000.0f);

which will alter the way we see the model in perspective. We pass this method the width of our view field (45 degrees). the aspect ratio of our graphics device and the near and far limits to what we can see (clipping planes). If you don't fully understand these matrix operations, its ok, just press on for now.


We are almost there, we need to create a method that applies all these things to our model. Since we will want to draw more than one model in the future, we will make a generic function. We call it DrawModel and will pass it a model to be drawn, it's position and a scale (to make it bigger or smaller). In a model object we can have many meshes. So we need to loop through them all. Then each mesh has a list of "BasicEffects" which control how that mesh will be drawn, so we loop through those too. For each basic effect we will do the following
  • enable lighting
  • set lighting preferences
  • set the World, Projection and View transformation matrices for the effect
Then for each mesh, we draw the mesh

void DrawModel(Model model, Vector3 modelPosition,float scale)
{
foreach(ModelMesh mesh in model.Meshes){
foreach (BasicEffect effect in mesh.Effects)
{
effect.EnableDefaultLighting();
effect.PreferPerPixelLighting = true;

effect.World =
Matrix.CreateTranslation(modelPosition)*
Matrix.CreateScale(scale)
;

effect.Projection = camProj;
effect.View = camView;
}
mesh.Draw();
}
}

All that is left at this point is to call DrawMesh from our Draw method with appropriate parameters.

DrawModel(tower, pos,10f);

I've added a couple extra calls to DrawModel in the project file so you can see a little "scene"


No comments: