关于NodeVisitor访问者模式

OSG通过对NodeVisitor的使用,实现了GOF的Visitor的模式.在Osg的实现中,这个模式实现了双派发.所以在Node中有一个虚方法为apply(NodeVisitor),而在NodeVisitor中有一个方法apply(Node&).这两个方法的调用是有区别的.我们应该总是调用Node::accept而别使用NodeVisitor::apply.因为只有调用前者才能享受到双派发的能力.(Node的具体类型和NodeVisitor的具体类型都参与到了Visitor模式)

那么我们就从Node::accept的方法看起:

void Node::accept(NodeVisitor& nv)

{

    if (nv.validNodeMask(*this)) 

    {

        nv.pushOntoNodePath(this);

        nv.apply(*this);

        nv.popFromNodePath();

    }

}

这个方法很简单,

1)首先nv会根据NodeMask来检查这个结点是否需要传递给当前的NodeVisitor.

2)处理NodePath,让当前被apply的节点成为NodePath中最后一个节点,NodePath将是一个从顶级根节点到当前节点的序列.

3)调用apply

4)恢复NodePath

这里涉及到NodeMask和NodePath的处理,在后面我们越来越了解Osg的时候,我们再来分析它们的作用,目前暂且不管.

可以看出要使用NodeVisitor来遍历场景图,只需要自己定义一个NodeVisitor派生来 然后对着用Viewer的Node调用accept就可以了.

下面是一个很简单的例子:我们写一个NodeVisitor来将系统中的非Geode都过滤掉,让系统恢复到最原始的几何世界:

1.首先定义我们的类HowtoNodeVisitor从NodeVisitor派生:

#pragma once

#include "stdafx.h"

class HowtoNodeVisitor :

  public NodeVisitor

{

public:

  HowtoNodeVisitor(void);

  ~HowtoNodeVisitor(void);



  void apply(Geode& node);

  Group* getFilterResult(){return mNodeGroup.get();}



private:  

  ref_ptr<Group> mNodeGroup;

};

2.实现这个类的代码也极为简单:

#include "StdAfx.h"

#include "HowtoNodeVisitor.h"



HowtoNodeVisitor::HowtoNodeVisitor(void):NodeVisitor(NodeVisitor::TRAVERSE_ALL_CHILDREN)

{

  mNodeGroup=new Group();

}



HowtoNodeVisitor::~HowtoNodeVisitor(void)

{

}

void HowtoNodeVisitor::apply(Geode& node)

{

  if(this->mNodeGroup->containsNode(&node)){

    return;

  }else{

    this->mNodeGroup->addChild(&node);

  }  

}

3.下面是使用的代码:

#include "stdafx.h"

#include "HowtoNodeVisitor.h"

int _tmain(int argc, _TCHAR* argv[])

{

    Viewer* viewer=new Viewer();

  //Node* rootNode=osgDB::readNodeFile("cow.osg");

  //Node* rootNode=osgDB::readNodeFile("axes.osg");

  Node* rootNode=osgDB::readNodeFile("spaceship.osg");



  HowtoNodeVisitor visitor;

  rootNode->accept(visitor);

  rootNode=visitor.getFilterResult();



  viewer->setSceneData (rootNode);

  viewer->addEventHandler(new osgGA::StateSetManipulator(viewer->getCamera()->getOrCreateStateSet()));

  viewer->addEventHandler (new StatsHandler());

 

  viewer->realize();

  return viewer->run();

}

如果把有下划线的那三行代码注释掉,你会看到一个喷火的航天飞机.但是加上我们的代码后,这个飞机就不喷火了. 因为喷火不是Geode结点能直接做出来的.

这个例子很简单,目的在于展示NodeVisitor这种模式. 实际上NodeVisitor本身并不简单,去读源代码就知道了,那个类还是比较大的.

你可能感兴趣的:(visitor)