There are several articles gathering dust bunnies on the internet on creating a scene graph class in C++ for your 3D engine but most are pretty vague and quite old. Hopefully, this post will give you a foot in the door in creating your own scene graph for your engine.

To start off, let’s go over some basics. A scene graph is a tree-like data structure which holds information about the scene you want to display. Every node has a parent and every node may have children. In this post we’ll use the std::vector to store the scene nodes but you can substitute this with whatever you want. A scene graph can be visualized like so:

             [root node]
                  |
        o=========o=========o
        |         |         |
    [child 1] [child 2] [child 3]
        |
    o===o====o
    |        |
[child 4][child 5]
             |
         [child 6]

Any node may have any number of children who’s children may have any number of children, etc. Each node in the scene graph may have a name for lookup functionality so a very lookup system will be implemented. We will also need functionality to add a child node, remove a child node, set/get a node’s parent and an update function for updating the graph hierarchically.

Beware that this scene graph is for educational purposes only, I do not recommend that you implement this scene graph into your engine as it uses very simple algorithms and general-purpose classes from the C++ Standard Template Library. Also, keep in mind that a scene graph is not used for rendering your objects, it is used to keep hierarchical information and for applying hierarchical transformations.

Rendering your objects is done through a different type of structure such as a Binary Space Partitioning Tree (BSP-Tree) or an Octree which are structures more suitable for visibility testing and quite a bit faster. Your scene graph is updated each cycle of your simulation or game so that all the objects in your scene have their correct transformations.

Let’s start off with declaring our base class, the Node (below is the entire Node.h file):

#ifndef __node_h_
#define __node_h_ 1
#include <vector>

class Node
{
public:
	Node(Node* Parent = NULL, const char* Name = NULL);
	virtual ~Node(void);

	virtual void Update(void);

	Node* GetParentNode(void) const;
	void SetParentNode(Node* NewParent);

	void AddChildNode(Node* ChildNode);
	void RemoveChildNode(Node* ChildNode);

	const char* GetNodeName(void) const;
	const size_t CountChildNodes(const bool& RecursiveCount = false) const;
	virtual const bool IsRootNode(void) const = 0;

	Node* GetChildNodeByName(const char* SearchName);

private:
	Node* m_Parent;
	const char* m_Name;
	std::vector<Node*> m_Children;

}; // class Node

#endif

There are not many methods in this class so you have room for expansion if you feel like it. Beware that this class can only be used as a base class and cannot be initialized as new Node(). Let’s continue by examining the individual methods of this class:

Node::Node(Node* Parent, const char* Name)

Node::Node(Node* Parent, const char* Name)
	: m_Name(Name)
{
	m_Parent = Parent;
} // Constructor

As you can see I’ve kept the constructor very simple. The default for both parameters is NULL as both are optional. A node does not necessarily need a parent and a node does not require a name. You can of course change this by removing the = NULL sections out of the header file and throwing and exception if NULL was found in the source file.


Node::~Node(void)

Node::~Node(void)
{
	m_Parent = NULL;
	m_Children.clear();
} // Destructor

The class destructor is also very simple, it simply sets the pointer to the parent node to NULL and clears the children-vector.


void Node::Update(void)

void Node::Update(void)
{
	if(!m_Children.empty())
	{
		for(size_t i = 0; i < m_Children.size(); ++i)
		{
			if(NULL != m_Children[i])
			{
				m_Children[i]->Update();
			}
		}
	}
} // Update()

Still pretty simple, this method goes through the children-vector and calls the Update method on each child that does not equal NULL. The child object in return calls the Update method so the behavior is recursive.

This method was designed to be overriden and called as Node::Update() by the overriding method.


Node* Node::GetParentNode(void) const

Node* Node::GetParentNode(void) const
{
	return(m_Parent);
}; // GetParentNode()

A “property” which simply returns a pointer to the parent node.


void Node::SetParentNode(Node* NewParent)

void Node::SetParentNode(Node* NewParent)
{
	if(NULL != m_Parent)
	{
		m_Parent->RemoveChildNode(this);
	}
	m_Parent = NewParent;
}; // SetParentNode()

This method sets a new parent node for the selected node and removes the node from the old parent node’s children-vector.


void Node::AddChildNode(Node* ChildNode)

void Node::AddChildNode(Node* ChildNode)
{
	if(NULL != ChildNode)
	{
		if(NULL != ChildNode->GetParentNode())
		{
			ChildNode-<SetParentNode(this);
		}
		m_Children.push_back(ChildNode);
	}
}; // AddChildNode()

Adds the node to the children-vector and updates the parent to the new parent if a parent was set.


void Node::RemoveChildNode(Node* ChildNode)

void Node::RemoveChildNode(Node* ChildNode)
{
	if(NULL != ChildNode && !m_Children.empty())
	{
		for(size_t i = 0; i < m_Children.size(); ++i)
		{
			if(m_Children[i] == ChildNode)
			{
				m_Children.erase(m_Children.begin() + i);
				break; // break the for loop
			}
		}
	}
}; // RemoveChildNode()

This method loops through all of the child nodes and will remove the selected child node from the children-vector if found.


const char* Node::GetNodeName(void) const

const char* Node::GetNodeName(void) const
{
	return(m_Name);
}; // GetNodeName()

A “property” which returns the name of the current node.


const size_t Node::CountChildNodes(const bool &RecursiveCount) const

const size_t Node::CountChildNodes(const bool &RecursiveCount) const
{
	if(!RecursiveCount)
	{
		return(m_Children.size());
	}
	else
	{
		size_t Retval = m_Children.size();
		for(size_t i = 0; i < m_Children.size(); ++i)
		{
			Retval += m_Children[i]->CountChildNodes(true);
		}
		return(Retval);
	}
}; // CountChildNodes()

Method counts all of the child nodes, recursive if required. If the recursive Boolean is set to true, the function will iterate over the entire hierarchy from the current node down and return a full node-count.


Node* Node::GetChildNodeByName(const char *SearchName)

Node* Node::GetChildNodeByName(const char *SearchName)
{
	Node* Retval = NULL;
	if(!m_Children.empty())
	{
		for(size_t i = 0; i < m_Children.size(); ++i)
		{
			if(0 == strcmp(m_Children[i]->m_Name, SearchName))
			{
				Retval = m_Children[i];
				break; // break the for loop
			}
		}
	}
	return(Retval);
}; // GetChildNodeByName()

This method will return a node referenced by name if it exists, otherwise it will return NULL. The function is fairly expensive and should be replaced with something more efficient but is fine for small trees and demos.

Conclusion:
As you can see, the scene graph is quite simple but once you understand how to implement one customization isn’t hard. Things you might want to change are passing a time value to the Update() method of Node so that your nodes may have a notion of time passed between calls, something faster than std::vector for storage and implementing a type-checking mechanism.

Regardless of the above, if you’re slapping together a quick demo that’s not performance sensitive the code with tweaks should do fine. Let me know if I missed or something or screwed something up since this post has been sitting in limbo for quite a while.