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) //虚函数,向下一级节点推进访问器
NodeVisitor的traverse()函数实现如下:
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*
//调用临近中的下一个更新回调
void
traverse(Node* node,NodeVisitor* nv);
|
节点类中完成回调函数设置和获取:
1
2
3
4
5
6
|
//设置/获取节点的更新回调
void
setUpdateCallback(NodeCallback* );
NodeCallback*
//设置/获取节点的事件回调
void
setEventCallback(NodeCallback*);
NodeCallback*
|
对于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);
}
|
一个范例:使用回调实现旋转动画
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 }