Search code examples
c++oopdesign-patternscallbackopenscenegraph

Scene Graph Update Callback Design


So Im using Open Scene graph to create an application and I have a callback class that extends from OSG's callback class. Its just a callback that is called each frame in the update event traversal of the scene graphs nodes.

I need different callback classes that do different operations on the nodes they are attached to. So I have a base callback class which I call controller base:

class ControllerBase : public osg::NodeCallback
{
public:
    ControllerBase();
private:
    // operator is overridden here from NodeCallback and is called each frame.
    virtual void operator()(osg::Node * n, osg::NodeVisitor * nv);
}

operator() gives me the node the callback is attached too and a Node Visitor. Now depending on the node it might be a different class such as a transform or a switch.

So I need to make a dynamic cast but for each possible type the node may be. So:

class ControllerBase : public osg::NodeCallback
{
public:
    ControllerBase();
private:
    virtual void operator()(osg::Node * n, osg::NodeVisitor * nv)
    {
      Type1 * t1 = dynamic_cast<Type1*>(node);
      Type2 * t2 = dynamic_cast<Type1*>(node);
      Type3 * t3 = dynamic_cast<Type1*>(node);

    }
}

Then send them out via a virtual method to be inherited by a specific controller class that I will attach to the node.

class ControllerBase : public osg::NodeCallback
{
public:
    ControllerBase();
protected:

private:

    virtual void On_Frame(Type1*, osg::NodeVisitor) = 0;
    virtual void On_Frame(Type2*, osg::NodeVisitor) = 0;
    virtual void On_Frame(Type3*, osg::NodeVisitor) = 0;

    virtual void operator()(osg::Node * n, osg::NodeVisitor * nv)
    {
      Type1 * t1 = dynamic_cast<Type1*>(node);
      Type2 * t2 = dynamic_cast<Type1*>(node);
      Type3 * t3 = dynamic_cast<Type1*>(node);

      if(t1)
        On_Frame(t1, nv);
      if(t2)
        On_Frame(t2, nv);
      if(t3)
        On_Frame(t3, nv);

    }
}

class Type1_Controller
{
    public:
        Type1_Controler();
    private:
        virtual void On_Frame(Type1 * type, osg::NodeVisitor *nv) override
        {
            // Do type 1 related stuff here. 
        }
        virtual void On_Frame(Type2 * type, osg::NodeVisitor *nv) override
        {
            // Leave empty, not needed. 
        }

        virtual void On_Frame(Type3 * type, osg::NodeVisitor *nv) override
        {
            // Leave empty, not needed. 
        }
}

So now for each type of controller i have, I have to implement the remaining empty methods. This feels like bad design, but I can't figure out how to write a better implementation. Maybe its not so bad with 3 types, but I may have more to add as I go along. I thought about using a template class, but if I'm not mistaken I can't have virtual methods in a template class. I could just use non pure virtual methods with empty implementation I suppose that could then be overriden at choice. What would a good approach be or suggested?


Solution

  • osg::NodeVisitor is more or less a textbook implementation of the Visitor Design Pattern. (see http://en.wikipedia.org/wiki/Design_Patterns for more info on the original "Gang of Four", or GoF, book).

    You can override accept(NodeVisitor) in your t1 node class to try casting to your t1 visitor type, eg:

     Type1_Visitor* vis = dynamic_cast<NodeVisitor> nv;
     if(vis)
        vis->Type1Apply(*this);
     else
        nv->apply(*this);
    

    Of course, there is a little more to it, but that is the general idea of double-dispatching as laid out in the GoF book.