• 博客园logo
  • 会员
  • 周边
  • 新闻
  • 博问
  • 闪存
  • 众包
  • 赞助商
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
lizhengjin
博客园    首页    新随笔    联系   管理    订阅  订阅
关于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本身并不简单,去读源代码就知道了,那个类还是比较大的.

posted on 2010-02-25 22:17  lizhengjin  阅读(726)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3