Running on Empty

The few things I know, I like to share.

XNA Framework GameEngine Development. (Part 9, SceneGraphManager GameComponents)

Introduction

Welcome to Part 9 of the XNA Framework GameEngine Development series.  This article will focus on simple scene management using a scene graph.  This implementation is little more than a list of objects to be rendered each frame, but it is a nice and simple way to prepare for more complex implementations of scene management.

Essentially, the SceneGraph will run through all the objects in the game as quickly as possible and render them to the screen, nice and simple.

Interfaces

It is always nice to be able to be able to have custom objects and meshes in a game, but we need a common interface in order to deal with them.

IRoeSceneObject — common ancestor.

using System;
using System.Collections.Generic;
using System.Text;

namespace RoeEngine2.Interfaces
{
    public interface IRoeSceneObject
    {
    }
}

IRoeDrawable — determines if an object is drawable.

using System;
using System.Collections.Generic;
using System.Text;

namespace RoeEngine2.Interfaces
{
    public interface IRoeDrawable : IRoeSceneObject
    {
        void Draw();
    }
}

IRoeLoadable — determines if an object is loadable/unloadable.

using System;
using System.Collections.Generic;
using System.Text;

namespace RoeEngine2.Interfaces
{
    public interface IRoeLoadable : IRoeSceneObject
    {
        void LoadContent();
        void UnloadContent();
    }
}

IRoeUpdateable — determines if an object is Updateable.

using System;
using System.Collections.Generic;
using System.Text;

namespace RoeEngine2.Interfaces
{
    public interface IRoeUpdateable : IRoeSceneObject
    {
        void Update();
    }
}

SceneGraph — Lists of Nodes

Node.cs

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;

namespace RoeEngine2.SceneObject.SceneGraph
{
    public class Node
    {
        protected NodeList _nodes;
        public NodeList Nodes
        {
            get { return _nodes; }
        }

        public Node()
        {
            _nodes = new NodeList();
        }

        public void AddNode(Node newNode)
        {
            _nodes.Add(newNode);
        }

        public virtual void Update()
        {
            _nodes.ForEach(
                delegate(Node node)
                {
                    node.Update();
                });
        }

        public virtual void UnloadContent()
        {
            _nodes.ForEach(
               delegate(Node node)
               {
                   node.UnloadContent();
               });
        }

        public virtual void LoadContent()
        {
            _nodes.ForEach(
               delegate(Node node)
               {
                   node.LoadContent();
               });
        }

        public virtual void Draw(GameTime gameTime)
        {
            _nodes.ForEach(
               delegate(Node node)
               {
                   node.Draw(gameTime);
               });
        }
    }
}

 NodeList.cs

using System;
using System.Collections.Generic;
using System.Text;

namespace RoeEngine2.SceneObject.SceneGraph
{
    public class NodeList : List<Node>
    {
    }
}

 SceneObjectNode.cs

using System;
using System.Collections.Generic;
using System.Text;
using RoeEngine2.Interfaces;
using Microsoft.Xna.Framework;

namespace RoeEngine2.SceneObject.SceneGraph
{
    public class SceneObjectNode : Node
    {
        private RoeSceneObject _sceneObject;
        public RoeSceneObject SceneObject
        {
            get { return _sceneObject; }
            set { _sceneObject = value; }
        }

        public SceneObjectNode(RoeSceneObject newObject)
        {
            _sceneObject = newObject;
        }

        public override void Update()
        {
            if (SceneObject is IRoeUpdateable)
                ((IRoeUpdateable)SceneObject).Update();
        }

        public override void UnloadContent()
        {
            if (SceneObject is IRoeLoadable)
                ((IRoeLoadable)SceneObject).UnloadContent();
        }

        public override void LoadContent()
        {
            if (SceneObject is IRoeLoadable)
                ((IRoeLoadable)SceneObject).LoadContent();
        }

        public override void Draw(GameTime gameTime)
        {
            if (SceneObject.ReadyToRender)
                SceneObject.Draw(gameTime);
        }
    }
}

RoeSceneObject.cs – Update

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using RoeEngine2.Interfaces;
namespace RoeEngine2.SceneObject
{
    public class RoeSceneObject : IRoeSceneObject
    {
        private bool _readyToRender = false;
        /// <summary>
        /// Is this object ready to render?
        /// </summary>
        public bool ReadyToRender
        {
            get { return _readyToRender; }
            set { _readyToRender = value; }
        }
        private BoundingBox _boundingBox;
        /// <summary>
        /// The bounding box of this object, used for culling.
        /// </summary>
        public BoundingBox BoundingBox
        {
            get { return _boundingBox; }
            set { _boundingBox = value; }
        }
        private Vector3 _position = Vector3.Zero;
        /// <summary>
        /// The position of this object in 3d space.
        /// </summary>
        public Vector3 Position
        {
            get { return _position; }
            set { _position = value; }
        }
        private Vector3 _scale = Vector3.One;
        /// <summary>
        /// Scale of the object.
        /// </summary>
        public Vector3 Scale
        {
            get { return _scale; }
            set { _scale = value; }
        }
        private Quaternion _rotation = Quaternion.Identity;
        /// <summary>
        /// Yaw, pitch and roll of the object.
        /// </summary>
        public Quaternion Rotation
        {
            get { return _rotation; }
            set { _rotation = value; }
        }
        /// <summary>
        /// Draw the object.
        /// </summary>
        /// <param name="gameTime"></param>
        public void Draw(GameTime gameTime)
        {            
            if (this is IRoeDrawable)
            {
                //Draw the object
            }
        }
    }   
}

SceneGraphManager

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using RoeEngine2.SceneObject.SceneGraph;
using RoeEngine2.SceneObject;

namespace RoeEngine2.Managers
{
    public class SceneGraphManager : DrawableGameComponent
    {
        private static Node _root;
        /// <summary>
        /// The root of the scene graph
        /// </summary>
        public static Node Root
        {
            get { return _root; }
        }
    
        /// <summary>
        /// Create the scenegraph Managers.
        /// </summary>
        /// <param name="game"></param>
        public SceneGraphManager(Game game)
            : base(game)
        {
            _root = new Node();
        }

        /// <summary>
        /// Draw objects
        /// </summary>
        /// <param name="gameTime"></param>
        public override void Draw(GameTime gameTime)
        {
            base.Draw(gameTime);

            _root.Draw(gameTime);
        }

        /// <summary>
        /// Load the content of all the objects in the scenegraph
        /// </summary>
        protected override void LoadContent()
        {
            base.LoadContent();

            _root.LoadContent();
        }

        /// <summary>
        /// Unload the content of all the objects in the scenegraph
        /// </summary>
        protected override void UnloadContent()
        {
            base.UnloadContent();

            _root.UnloadContent();
        }

        /// <summary>
        /// Add an object to the scenegraph.
        /// </summary>
        /// <param name="newObject"></param>
        public static void AddObject(RoeSceneObject newObject)
        {
            SceneObjectNode node = new SceneObjectNode(newObject);
            _root.AddNode(node);
        }
    }
}

Using the SceneGraphManager DrawableGameComponent

Add the following code to the properties section of RoeEngine2

private static SceneGraphManager _sceneGraphManager = null;

And in the RoeEngine2 Constructor

// Init SceneGraph Managers
_sceneGraphManager = new SceneGraphManager(this);
Components.Add(_sceneGraphManager);

//TODO include other inits here!

Optional TDD:

[Test, Category("SceneGraphManager")]
public void SceneGraphManagerCreated()
{
    Assert.IsNotNull(SceneGraphManager.Root);
}

[Test, Category("SceneGraphManager")]
public void SceneObjectsLoaded()
{
    RoeSceneObject sceneObject = new RoeSceneObject();
    sceneObject.Position = new Vector3(100, 100, 100);
    SceneGraphManager.AddObject(sceneObject);
    Assert.AreEqual(1, SceneGraphManager.Root.Nodes.Count);
    Assert.AreEqual(sceneObject.Position, ((SceneObjectNode)SceneGraphManager.Root.Nodes[0]).SceneObject.Position);
}

Conclusion

In this article, I introduced a very simple implementation of a Scenegraph.  Scene Management is a very important part of good engine design, with this simple implementation we now have a very good backbone for creating better and more powerful scene management systems.

I look forward to your comments and suggestions.  Please feel free to leave suggestions for future articles.

About these ads

February 4, 2008 - Posted by | C#, GameComponent, TDD, XBOX360, XNA

3 Comments »

  1. Hi, who are RoeSceneObject? it´s SceneObject? Used to create Scene object child´s?

    thanks

    Comment by adriano | February 4, 2008 | Reply

  2. adriano,
    Very sorry man, I must have left that out of the original post. I updated to include that class.

    Comment by roecode | February 5, 2008 | Reply

  3. Are there any plans for further releases of an Octree implementation?

    Comment by J2t | August 5, 2008 | Reply


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: