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; };
#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); } }
#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本身并不简单,去读源代码就知道了,那个类还是比较大的.