Note: This section is not necessary if you started with tutorial #1 after 12/10/2008

In this mini-tutorial, we will be making some minor updates to the engine. First, we will be changing the utility classes to static classes. Remove the instances of MathUtil and GraphicsUtil from the Engine class. Then, add the modifier ‘static’ before the class definition and methods for the two classes.

public static class GraphicsUtil
{
    // Creates a RenderTarget2D with the specified parameters
    public static RenderTarget2D CreateRenderTarget()
    {
        return CreateRenderTarget(Engine.GraphicsDevice.Viewport.Width,
            Engine.GraphicsDevice.Viewport.Height);
    }

    // Creates a RenderTarget2D with the specified parameters
    public static RenderTarget2D CreateRenderTarget(int Width, int Height)
    {
        return CreateRenderTarget(Width, Height,
            Engine.GraphicsDevice.DisplayMode.Format);
    }

    etc, etc...

Second, the Rectangle property in I2DComponent should be a Rectangle.

public interface I2DComponent
{
    Rectangle Rectangle { get; set; }
}

Now we will add a few more methods to the MathUtil class:

// Converts a rotation vector into a rotation matrix
public static Matrix Vector3ToMatrix(Vector3 Rotation)
{
    return Matrix.CreateFromYawPitchRoll(Rotation.Y, Rotation.X, Rotation.Z);
}

// Converts a rotation matrix into a rotation vector
public static Vector3 MatrixToVector3(Matrix Rotation)
{
    Quaternion q = Quaternion.CreateFromRotationMatrix(Rotation);
    return new Vector3(q.X, q.Y, q.Z);
}

Now change these methods in the MathUtil to use matrices for rotation:

// Creates a world matrix
public static Matrix CreateWorldMatrix(Vector3 Translation)
{
    return CreateWorldMatrix(Translation, Matrix.Identity);
}

// Creates a world matrix
public static Matrix CreateWorldMatrix(Vector3 Translation,
    Matrix Rotation)
{
    return CreateWorldMatrix(Translation, Rotation, Vector3.One);
}

// Creates a world matrix
public static Matrix CreateWorldMatrix(Vector3 Translation,
    Matrix Rotation, Vector3 Scale)
{
    return Matrix.CreateScale(Scale) *
        Rotation *
        Matrix.CreateTranslation(Translation);
}

In the Camera class:

Projection = Engine.Math.CreateProjectionMatrix();

becomes

Projection = MathUtil.CreateProjectionMatrix();

Now change the following in the Actor class:

Matrix world = Engine.Math.CreateWorldMatrix(Position, Rotation, Scale);

becomes

Matrix world = MathUtil.CreateWorldMatrix(Position, Rotation, Scale);
Rotation = Vector3.Zero;

becomes

EulerRotation = Vector3.Zero;

Third, representing rotations as a Vector3 is a bad way to do it, for several reasons. Change I3DComponent to look like this:

// Represents a 3D object. These objects will be drawn before
// 2D objects, and will have modifiers automatically provided
// in the editor.
public interface I3DComponent
{
    // Position in the Cartesian system (X, Y, Z)
    Vector3 Position { get; set; }

    // Rotation represented as a Vector3. This shouldn't
    // be used for calculations, it is left in so that
    // the rotation can be more easily modified by hand
    Vector3 EulerRotation { get; set; }
    // Rotation as a Matrix. This will give much smoother
    // and cleaner calculations that a Vector3
    Matrix Rotation { get; set; }

    // Scale for each axis (X, Y, Z)
    Vector3 Scale { get; set; }

    // BoundingBox to use for picking and pre-collision
    BoundingBox BoundingBox { get; }
}

Now we need to fix this in a few other classes. Change the following in the Actor class:

// I3DComponent values
public virtual Vector3 Position { get; set; }
public Vector3 EulerRotation
{
    get { return MathUtil.MatrixToVector3(Rotation); }
    set { Rotation = MathUtil.Vector3ToMatrix(value); }
}
public virtual Matrix Rotation { get; set; }
public virtual Vector3 Scale { get; set; }
public virtual BoundingBox BoundingBox
{
    get
    {
        return new BoundingBox(
            Position - (Scale / 2),
            Position + (Scale / 2)
        );
    }
}

Now change the following in PhysicsActor:

// Override the rotation of the base class to that
// of the physics object
public override Matrix Rotation
{
    get
    {
        if (PhysicsObject != null)
            return PhysicsObject.Rotation;
        else
            return Matrix.Identity;
    }
    set
    {
        if (PhysicsObject != null)
            PhysicsObject.Rotation = value;
    }
}

Change the Camera class to look like this:

using Microsoft.Xna.Framework;
using System;
using System.Runtime.Serialization;

namespace Innovation
{
    // Basic Camera class
    public class Camera : Component, I3DComponent
    {
        // Internal values
        Vector3 position = Vector3.Zero;
        Matrix rotationMatrix = Matrix.Identity;
        Vector3 target = new Vector3(0, 0, -1);
        Vector3 up = Vector3.Up;
        Matrix view;
        Matrix projection;

        // The point the camera is looking at
        public virtual Vector3 Target
        { get { return target; } set { target = value; } }

        // The View and Projection matrices commonly used for rendering
        public virtual Matrix View
        { get { return view; } set { view = value; } }
        public virtual Matrix Projection
        { get { return projection; } set { projection = value; } }

        public virtual Vector3 Up
        { get { return up; } set { up = value; } }

        // Public I3DComponent values
        public virtual Vector3 Position { get { return position; } set { position = value; } }
        public virtual Vector3 Scale { get { return Vector3.One; } set { } }
        public Vector3 EulerRotation
        {
            get { return MathUtil.MatrixToVector3(Rotation); }
            set { Rotation = MathUtil.Vector3ToMatrix(value); }
        }
        // The rotation matrix used by the camera and the current up vector
        public virtual Matrix Rotation
        { get { return rotationMatrix; } set { rotationMatrix = value; } }

        public virtual BoundingBox BoundingBox
        { get { return new BoundingBox(Position - Vector3.One, Position + Vector3.One); } }

        // Constructors
        public Camera(GameScreen Parent) : base(Parent) { }
        public Camera() : base() { }

        // Update the camera
        public override void Update()
        {
            // Calculate the direction from the position to the target, and normalize
            Vector3 newForward = Target - Position;
            newForward.Normalize();

            // Set the rotation matrix's forward to this vector
            Matrix rotationMatrixCopy = this.Rotation;
            rotationMatrixCopy.Forward = newForward;

            // Save a copy of "Up" (0, 1, 0)
            Vector3 referenceVector = Vector3.Up;

            // On the slim chance that the camera is pointed perfectly parallel with
            // the Y Axis, we cannot use cross product with a parallel axis, so we
            // change the reference vector to the forward axis (Z).
            if (rotationMatrixCopy.Forward.Y == referenceVector.Y
                || rotationMatrixCopy.Forward.Y == -referenceVector.Y)
                referenceVector = Vector3.Backward;

            // Calculate the other parts of the rotation matrix
            rotationMatrixCopy.Right = Vector3.Cross(this.Rotation.Forward,
                referenceVector);
            rotationMatrixCopy.Up = Vector3.Cross(this.Rotation.Right,
                this.Rotation.Forward);

            this.Rotation = rotationMatrixCopy;

            // Use the rotation matrix to find the new up
            Up = Rotation.Up;

            // Recalculate View and Projection using the new Position, Target, and Up
            View = Matrix.CreateLookAt(Position, Target, Up);
            Projection = MathUtil.CreateProjectionMatrix();
        }
    }
}

Change the following in the PhysicsObject class:

// The PhysicsObject's rotation as a Matrix
public Matrix Rotation
{
    get { return Body.Orientation; }
    set { Body.MoveTo(Body.Position, value); }
}
// The PhysicsObject's rotation as a Euler Vector
public Vector3 EulerRotation
{
    get { return MathUtil.MatrixToVector3(Rotation); }
    set { Rotation = MathUtil.Vector3ToMatrix(value); }
}

Remove the Orientation property from PhysicsObject. Now go through the various physics object, and change any references to Rotation to EulerRotation, and any references to Orientation to Rotation.

Download the code

« »