Visitors – Part II: Implementation

In my last post I mentioned the reasons behind my decision of using visitors in order to perform scene traversal operations. This time I’m going to briefly describe how the Visitor pattern is implemented and executed.

Before starting let me point out that I’m going to make several references to Crimild’s source code, so it will be a good idea for you to download it if you don’t have it already.

Visitors in Crimild are implemented following GoF recommendations, although I made some changes on my own. On one hand, the NodeVisitor class (include/Core/NodeVisitor.hpp) defines an abstract visitor by declaring operations for each of the known Node-derived classes (visitNode, visitGeometryNode, visitGroupNode) as well as providing a basic implementation for the traverse method, which is used as the entry point for scene traversal (more on that later).

class NodeVisitor {
   virtual ~NodeVisitor( void );

   virtual void traverse( Node *node );

   virtual void visitNode( Node *node );
   virtual void visitGroupNode( GroupNode *group );
   virtual void visitGeometryNode( GeometryNode *geometry );

The Node class (include/Core/Node.hpp) provides the perform method which is the entry point of any scene traversal operaton.

class Node : public Object {

   void perform( NodeVisitor &visitor );
   void perform( const NodeVisitor &visitor );

   virtual void accept( NodeVisitor &visitor );

For those of you wondering about the reason of having two perform functions, there is a nice comment awaiting for you in the source code 😉

The accept method is part of the double-dispatch between Node and NodeVisitor. Each Node-derived class should override the accept method in oder to invoke the proper visit* function call. For example, the following code shows how this is done in both GroupNode and GeometryNode:

void Node::accept( NodeVisitor &visitor )
   visitor.visitNode( this );

void GroupNode::accept( NodeVisitor &visitor )
   visitor.visitGroupNode( this );

void GeometryNode::accept( NodeVisitor &visitor )
   visitor.visitGeometryNode( this );

Using a visitor is as simple as invoking the perform function on a Node instance passing a NodeVisitor object as argument:

aNode->perform( CustomVisitor() );

Now, it seems that the perform call is wasted since the only thing it does is to invoke the visitor’s traverse method and there is no option to override it. Why not using the traverse function directly instead of wasting a function call? While it’s technically doable (you can initiate the process by using the traverse function), I prefer the entry point for any scene operation to be on the scene itself instead of any external object.

There is one more thing I would like to mention before closing this post. Consider the case of a scene consisting of a GroupNode instance with three children. A scene traversal involves visiting all nodes in the hierarchy, does iterating between parents and children. But while some operation might be executed in a depth-first traversal, others might involve more complex traversals. All things considered, I decided to not implement children iteration in the GroupNode class but instead forcing each NodeVisitor implementation to do it by itself. This might lead to a bit of code duplication, but so far it’s acceptable.

In my next post I’m going to introduce you to several visitors that are bundled with the engine and I’ll do a quick tutorial about implementing one on your own.

Leave a Reply

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

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

Google photo

You are commenting using your Google 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 )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.