Extending the Actor class

In this post we’ll extend the “Actor” class to include a few more useful properties. We’ll start of by creating a new folder under the “Models” folder called “Interfaces”. In this new folder, create an interface called “IEntity”. Add the following namespaces:

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

The interface will look like below:

interface IEntity
{
    BoundingBox BoundingBox { get; }

    Model Model { get; set; }

    Vector3 Position { get; set; }

    Vector3 Rotation { get; set; }

    Vector3 Scale { get; set; }
}

That’s all we need for the interface. Now we just need to hook that up with the “Actor” class. Open the “Actor” class and change the class declaration to:

public class Actor : IEntity

We need to add in the new properties from the interface into our “Actor” class:

public BoundingBox BoundingBox 
{ 
    get { return new BoundingBox(Position - Scale / 2f, Position + Scale / 2f); }
}

public Vector3 Position 
{ 
    get { return position; } 
    set { position = value; } 
}

public Vector3 Rotation 
{ 
    get { return rotation; } 
    set { rotation = value; } 
}

public Vector3 Scale 
{ 
    get { return scale; } 
    set { scale = value; } 
}

That’s it. We now have a calculated BoundingBox for our model which will come in handy when we introduce the Physics part of the engine.

New Actor class

After the tidy up, it was apparent that the current “SceneManager” wasn’t going to last much longer due to variables being hardcoded. So, I decided to create a small “Actor” class that will expand over time, but it will help out the “SceneManager” class and mean that we can position models.

In the “ProjectVanquish” project, create a new folder called “Models” and in there create a new class called “Actor”. Make the class public. Also, add the following namespaces:

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

We’ll add a couple of variables:

Model model;
Vector3 position, rotation, scale; 

So, we’ll store a Model and its position, rotation and scale. Simple enough. We’ll add a default constructor:

public Actor(Model model)
{
    this.model = model;
    position = Vector3.Zero;
    rotation = Vector3.Zero;
    scale = Vector3.One;
}

We’ll use this in our “SceneManager” class, but we’ll be adding 3 more constructor for completeness.

public Actor(Model model, Vector3 position)
{
    this.model = model;
    this.position = position;
    rotation = Vector3.Zero;
    scale = Vector3.One;
} 

public Actor(Model model, Vector3 position, Vector3 rotation)
{
    this.model = model;
    this.position = position;
    this.rotation = rotation;
    scale = Vector3.One;
} 

public Actor(Model model, Vector3 position, Vector3 rotation, Vector3 scale)
{
    this.model = model;
    this.position = position;
    this.rotation = rotation;
    this.scale = scale;
} 

So, you can see that we are allowing for the alterations of the Position, Rotation and Scale. I would like to have used Optional Parameters, but I can’t use Vector3.Zero as it’s a runtime constant and not compile-time.

To finish the class, we’ll add a couple of properties:

public Model Model
{
    get { return model; }
}

public Matrix World
{
    get
    {
        return Matrix.CreateTranlsation(position) * Matrix.CreateScale(scale)
             * Matrix.CreateRotationX(rotation.X) * Matrix.CreateRotationY(rotation.Y)
             * Matrix.CreateRotationZ(rotation.Z);
    }
}

That’s the new “Actor” class finished for the time being. All that is left is to alter the “SceneManager” class to cater for the new class. We will need to add our new namespace:

using ProjectVanquish.Models;

Then we’ll start with the Models variable:

static IList<Actor> models;

Amend the declaration in the constructor:

models = new List<Actor>();

We need to modify the Property:

public IList<Actor> Models
{
    get { return models; }
}

Now, we just need to modify the Members and we finished with the “ProjectVanquish” project:

public static void AddModel(Model model)
{
    models.Add(new Actor(model));
}

public void Draw()
{
    // RenderStates etc. here

    foreach (Actor actor in models)
        DrawModel(actor.Model, actor.World, camera);
}

The last change to make is to the Draw with Effect member. The change is basically going from:

for (int j = 0; j < models[i].Meshes.Count; j++)


Model mesh = models[i].Meshes[j];

to

for (int j = 0; j < models[i].Model.Meshes.Count; j++)


Model mesh = models[i].Model.Meshes[j];

Lastly, there is the Matrix that will need to be altered:

Matrix world = models[i].World;

That’s all. We have enabled 4 constructors in the “Actor” class, but we don’t have any means of using 3 of them. We’ll create the “AddModel” methods in the “SceneManager” class, just copy and paste the below:

public static void AddModel(Model model, Vector3 position)
{
    models.Add(new Actor(model, position));
}

public static void AddModel(Model model, Vector3 position, Vector3 rotation)
{
    models.Add(new Actor(model, position, rotation));
}

public static void AddModel(Model model, Vector3 position, Vector3 rotation, Vector3 scale)
{
    models.Add(new Actor(model, position, rotation, scale));
}

You’ll be able to use these new methods in the “Game1” class. That’s all for now. Will carry on banging head against keyboard until Shadow Mapping is working.

Rendering a model

We’ll start this post of by creating a very simple Scene manager. This is one of the aspects that I’d like the community to get involved in as there are so many differents ways of managing a Scene. We’ll just be using a simple list of models and rendering each one.

In the “Core” folder of the “ProjectVanquish” project, add a new class called “SceneManager”. Add the following namespaces:

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using ProjectVanquish.Cameras;

Create a few variables:

ContentManager content;
GraphicsDevice device;
FreeCamera camera;
IList<Model> models;

This will hold all of our models so we can iterate through them. If we were to create our own Model class, we could then add in things like visibility tests, so we can only render those that are visible.

Create a constructor:

public SceneManager(GraphicsDevice device, ContentManager content)
{
    this.content = content;
    this.device = device;
    camera = new FreeCamera(device, new Vector3(0, 10, 0), Vector3.Zero, device.Viewport.AspectRatio, 0.1f, 1000f);
    models = new List<Model>();
}

We now have the start of a simple Scene manager, but we need to be able to add models to it. Add a new method called “AddModel”:

public void AddModel(Model model)
{
    models.Add(model);
}

This method will add a Model to our list. We aren’t testing to see whether the list already contains the Model, as this is only a simple Scene manager.

Next, we’ll add the rendering methods:

public void Draw() 
{ 
    device.DepthStencilState = DepthStencilState.Default;
    device.RasterizerState = RasterizerState.CullCounterClockwise;
    device.BlendState = BlendState.Opaque;

    foreach (Model model in models)
        DrawModel(model, Matrix.Identity, camera);
}

void DrawModel(Model model, Matrix world, FreeCamera camera) 
{ 
    foreach (ModelMesh mesh in model.Meshes)
    {
        foreach (Effect effect in mesh.Effects)
        {
            effect.Parameters["World"].SetValue(world);
            effect.Parameters["View"].SetValue(camera.View);
            effect.Parameters["Projection"].SetValue(camera.Projection);
        }

        mesh.Draw();
    }
}

We have sorted out our Model rendering, but we haven’t updated our Camera. Create a new method called “Update”:

public void Update(GameTime gameTime)
{
    camera.Update(gameTime);
}

In this method we call the Cameras “Update” method, which in turn will update the Camera. That’s all for the Scene manager. As I said at the start, it’s a very simplistic approach, but I’ve included it so we can actually start rendering some models.

Go back to the “DeferredRenderer” class, add a new namespace:

using ProjectVanquish.Core;

Now we can declare our new “SceneManager” class:

private SceneManager sceneManager;

In the constructor, we’ll instantiate our new object:

sceneManager = new SceneManager(device, content);

Remember early on in the project when we were creating the skeleton structure of the project? We added a comment into the “Draw” method of the “DeferredRenderer” class. We can now replace this comment with the following:

sceneManager.Draw();

We also added a comment in the “Update” method, so we can also replace that with the following:

sceneManager.Update(gameTime);

Great! We’ve now included our Scene manager in the Deferred engine. But wait! We have an “AddModel” method in the “SceneManager” class, but we won’t be able to access that from our “ProjectVanquishTest” project due to the protection level of the class. This is easy to fix. We’ll create an “AddModel” method in the “DeferredRenderer” class which will call the “SceneManager”s method:

public void AddModel(Model model)
{
    sceneManager.AddModel(model);
}

We’ll add a property for the Camera so that the “DeferredRenderer” class can access it:

public FreeCamera Camera 
{ 
    get 
    { 
        return camera; 
    }
}

Build the solution to make sure that there are no errors. Head over to the “ProjectVanquishTest” project and open the “Game1” class. Locate the “LoadContent” method and under the TODO comment, add the following:

renderer.AddModel(Content.Load<Model>("Models/Ground"));

Building the solution will show no errors, but we’ll need to add the “Ground” Model to our “ProjectVanquishTestContent” project. Roy’s source code contains this Model, in the “DeferredLightingContent\Models” folder. You will need 4 files. Firstly, the “Ground.X” file. Then there are 3 JPG files, all starting with “ground_”. Extract these 4 files to your “ProjectVanquishContent\Models” directory. In Visual Studio, right mouse click the “Models” folder in the “ProjectVanquishTestContent” and click “Add” then “Existing Item”. Open up the “Models” folder and click “Ground.x” and then click “Add”.

Once the Model is added to the project, we need to change the “Content Processor” to our “ProjectVanquishContentPipeline.ContentProcessor”. To do this, click the “Ground.x” item and view the “Properties”. The “Content Processor” is the fourth item in the list of properties. Click the drop-down arrow and locate the “ProjectVanquishContentPipeline.ContentProcessor” and click. Expand the “Content Processor” items so you can see all of the items for the processor. Locate “Normal Map Texture” and type in:

ground_normal.jpg

Then, locate “Specular Map Texture” and type in:

ground_specular.jpg

We’ve now configured our Model to use our “Content Processor”. If you build and run this code, you will should see a large black screen, but the Debug textures are now appearing. This is because we have not implementated any lighting.

Well done! In the next part, we’ll look at implementing Directional lighting.

Rendering a model – Creating a Camera

The engine is finally beginning to take shape.  Before we can render a model, we’ll need to create a camera.  We’ll create two new classses, a base camera class and a free camera.  We’ll inherit from the base camera class to create our free camera.  Let’s start off with the base camera class.  In the “ProjectVanquish” project, add a new Class file to the Cameras folder called “BaseCamera”.  Declare the class as:

public abstract class BaseCamera
{
}

Add the following namespaces:

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

Add in the following variables:

protected Matrix viewMatrix;
protected Matrix projectionMatrix;
protected Matrix rotationMatrix;
protected GraphicsDevice device;
protected Vector3 target;
protected Vector3 position;
protected Vector3 rotation;
protected Vector3 up;
protected float speed;
float near;
float far;

Add the following properties:

public Matrix View { get { return viewMatrix; } }
public Matrix Projection { get { return projectionMatrix; } }
public float NearClip { get { return near; } }
public float FarClip { get { return far; } }
public Vector3 Position
{
    get { return position; }
    set
    {
        position = value;
        UpdateView();
    }
}

public Vector3 Target
{
    get { return target; }
    set
    {
        target = value;
        viewMatrix = Matrix.CreateLookAt(Position, Target, new Vector3(0, 1, 0));
    }
}

public Vector3 Rotation
{
    get { return rotation; }
    set
    {
        rotation = value;
        rotationMatrix = Matrix.CreateRotationX(rotation.X) 
                       * Matrix.CreateRotationY(rotation.Y);
        UpdateView();
    }
}

public Matrix World
{
    get
    {
        return Matrix.CreateTranslation(Position.X, Position.Y, Position.Z)
             * Matrix.CreateRotationX(rotation.X)
             * Matrix.CreateRotationY(rotation.Y)
             * Matrix.CreateRotationZ(rotation.Z);
    }
}

Now we need a constructor for the class:

public BaseCamera(GraphicsDevice device, Vector3 position, Vector3 target, float aspectRatio, float near, float far)
{
    Position = position;
    Rotation = Vector3.Zero;
    Target = target;
    this.speed = 30;
    this.near = near;
    this.far = far;
    this.device = device;
    this.up = Vector3.Up;

    // Setup Field of View, Aspect Ratio and Clipping Planes
    projectionMatrix = Matrix.CreatePerspectiveFieldOfView(
                         MathHelper.PiOver4, 
                         aspectRatio, 
                         near, 
                         far
                       );

    // Update the View
    UpdateView();
}

The last line in the constructor is calling an “UpdateView” method. Let’s add this in:

public virtual void UpdateView()
{
    Vector3 cameraOriginalTarget = new Vector3(0, 0, -1);
    Vector3 cameraRotatedTarget = Vector3.Transform(cameraOriginalTarget, rotationMatrix);
    Vector3 cameraFinalTarget = position + cameraRotatedTarget;
    Vector3 cameraRotatedUpVector = Vector3.Transform(up, rotationMatrix);

    viewMatrix = Matrix.CreateLookAt(Position, cameraFinalTarget, cameraRotatedUpVector);
}

So, that is the camera initialisation code out of the way. Let’s add a few more methods so our other classes can inherit the functionality, but override them if the developer chooses too:

public virtual void Move(Vector3 vector)
{
}

public virtual void Update(GameTime gameTime)
{
}

Excellent. We now have a good base class that we can create new Cameras from. In the next part, we’ll create a “Free” camera for our first camera and to use in testing.