OSG节点访问、更新和回调

时间:2023-02-04 16:10:56

OSG中节点的访问使用的是一种访问器模式

  一个典型的访问器涉及抽象访问者角色(Visitor),具体访问者(Concrete Visitor), 节点角色(Node)

  OSG中访问者角色为NodeVisitor类,其基本结构如下:

  NodeVisitor(TraversalMode tm)    //构造函数,TraversalMode为节点树的遍历方式

  //TRAVERSE_NONE, 仅当前节点

  //TRAVERSE_PARENTS, 向当前节点的父节点遍历

  //TRAVERSE_ALL_CHILDREN, 向子节点遍历

  void traverse(Node& node)  //向下一个需要访问的节点推进

  void apply(Node& node)   //虚函数,访问各种节点类型,并执行访问器中的自定义操作

  void apply(Group& node)

  void apply(Geode& node)

  …………

  NodeVisitor 只是访问器角色的抽象接口,要使用访问器访问节点并执行自定义操作时,需要继承并重写apply(……)函数实现自定义功能。osg::Node类中的访问接口为 void accept(NodeVisitor& nv)对节点的访问从节点接受一个访问器开始,将一个具体的访问器对象传递给节点,节点反过来执行访问器的apply(...)函数,并将自己传入访问器。可如下简单表示:

  void Node::accept(NodeVisitor& nv)
  {
nv.apply(*ths) ;
  }

  遍历节点树:

osg::Node类中有两个辅助函数

  void ascend(NodeVisitor& nv)     //虚函数,向上一级节点推进访问器

  void traverse(NodeVisitor& nv)   //虚函数,向下一级节点推进访问器

NodeVisitortraverse()函数实现如下

inline void traverse(Node& node)
{
  if (_traversalMode == TRAVERSE_PARENTS)
   node.ascend(*this);
  else if (_traversalMode != TRAVERSE_NONE)
   node.traverse(*this);
}


OSG节点更新和回调

OSG中的节点主要使用回调(CallBack)来完成用户临时、需要每帧执行的工作。根据回调功能被调用的时机划分为更新回调(Update CallBack)和人机交互时间回调(Event CallBack)。前者在每一帧中系统遍历到当前节点时调用,后者则由交互事件触发,如操作键盘、鼠标、关闭窗口、改变窗口大小等动作。回调类基类是osg::NodeCallBack(),主要函数如下:

1
2
3
4
5
6
7
8
9
10
//虚函数,回调函数主要操作在此函数中,子类应当重写,已完成相应操作
void  operator()(Node* node, NodeVisitor* nv);
//为当前更新回调添加(删除)一个后继的回调对象
void    addNestedCallback(NodeCallback* nc);
void    removeNestedCallback(NodeCallback* nc);
//直接设置/获取一个最近的回调
void    (NodeCallback* nc);
NodeCallback*  
getNestedCallback();
//调用临近中的下一个更新回调
void  traverse(Node* node,NodeVisitor* nv);

节点类中完成回调函数设置和获取:

1
2
3
4
5
6
//设置/获取节点的更新回调
void   setUpdateCallback(NodeCallback* );
NodeCallback*
getUpdateCallback();
//设置/获取节点的事件回调
void   setEventCallback(NodeCallback*);
NodeCallback* 
getEventCallback();

对于addNestedCallback(……)函数,其源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
inline  void  addNestedCallback(NodeCallback* nc)
         {
             if  (nc)
             {
                 if  (_nestedCallback.valid())
                 {
                     nc->addNestedCallback(_nestedCallback.get());
                     _nestedCallback = nc;
                 }
                 else
                 {
                     _nestedCallback = nc;
                 }
             }
         }

在NodeCallback类中用一个ref_ptr<NodeCallback> _nestedCallback; 来存储下一个回调对象,利用链表构成一个回调对象序列,当要添加一个临近回调时,即调用addNestedCallback(NodeCallback* nc)时利用递归将两个(分别以this,nc为连表头的)序列合并,例如:this->callback1->callback2->callback3->null, nc->callback4->callback5->null。合并后新的序列为this->nc->callback1->callback4->callback2->callback5->callback3->null。至于removeNestedCallback(...),比较简单,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
inline  void  removeNestedCallback(NodeCallback* nc)
{
        if  (nc)
        {
                 if  (_nestedCallback==nc)
                 {
                     _nestedCallback = _nestedCallback->getNestedCallback();
                 }
                 else  if  (_nestedCallback.valid())
                 {
                     _nestedCallback->removeNestedCallback(nc);
                 }
         }
}

其中traverse()函数,其功能是对当前节点调用下一个临近回调函数,其代码如下:

1
2
3
4
5
6
7
8
9
void  NodeCallback::traverse(Node* node,NodeVisitor* nv)
{
     //如果有后续回调对象,则调用, 重载操作符"()"来实现
     if  (_nestedCallback.valid())
             (*_nestedCallback)(node,nv);
     //回调操作完成之后,访问该节点   
     else
             nv->traverse(*node);
}  

一个范例:使用回调实现旋转动画

OSG节点访问、更新和回调
OSG节点访问、更新和回调
 
  
1 #include < osg / Quat >
2 #include < osg / PositionAttitudeTransform >
3 #include < osg / io_utils >
4 #include < osgDB / ReadFile >
5 #include < osgViewer / Viewer >
6 #include < iostream >
7
8   class RotateCallBack: public osg::NodeCallback
9 {
10   public :
11 RotateCallBack():_rotateZ( 0.0 ) {}
12
13 virtual void operator ()(osg::Node * node, osg::NodeVisitor * nv)
14 {
15 osg::PositionAttitudeTransform * pat =
16 dynamic_cast < osg::PositionAttitudeTransform *> (node);
17 if (pat)
18 {
19 osg::Vec3 vec( 0 , 0 , 1 );
20 osg::Quat quat = osg::Quat(osg::DegreesToRadians(_rotateZ), osg::Z_AXIS);
21 pat -> setAttitude(quat);
22
23 _rotateZ += 0.10 ;
24 }
25
26 traverse(node, nv);
27 }
28
29   private :
30 double _rotateZ;
31 };
32
33
34   class InfoCallBack: public osg::NodeCallback
35 {
36   public :
37 virtual void operator ()(osg::Node * node, osg::NodeVisitor * nv)
38 {
39 osg::PositionAttitudeTransform * pat =
40 dynamic_cast < osg::PositionAttitudeTransform *> (node);
41
42 if (pat)
43 {
44 double angle = 0.0 ;
45 osg::Vec3 axis;
46 pat -> getAttitude().getRotate(angle, axis);
47
48 std::cout << " Node is rotate around the axis( " << axis << " ), "
49 << osg::RadiansToDegrees(angle) << " degrees " << std::endl;
50 }
51
52 traverse(node, nv);
53 }
54 };
55
56
57 int main( int argc, char ** argv)
58 {
59 osg::ArgumentParser argument( & argc, argv);
60 osg::Node * model = osgDB::readNodeFiles(argument);
61 if ( ! model)
62 model = osgDB::readNodeFile( " cow.osg " ) ;
63
64 osg::ref_ptr < osg::PositionAttitudeTransform > pat =
65 new osg::PositionAttitudeTransform();
66 pat -> addChild(model);
67
68 pat -> setUpdateCallback( new RotateCallBack() );
69 pat -> addUpdateCallback( new InfoCallBack() );
70
71 osgViewer::Viewer viewer;
72 viewer.setSceneData(pat. get () );
73 return viewer.run();
74 }
OSG节点访问、更新和回调