您的位置:首页 > Web前端 > Node.js

关于NodeVisitor访问者模式

2010-02-25 22:17 405 查看
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本身并不简单,去读源代码就知道了,那个类还是比较大的.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: