osg第16讲(NodeVisitor遍历器)

osg自带的遍历器,关键接口:

  1. apply
void NodeVisitor::apply(Node& node)
{
    traverse(node);
}
void NodeVisitor::apply(Drawable& drawable)
{
    apply(static_cast<Node&>(drawable));
}

重载函数,根据类型进行不同的接口,默认都调用apply(Node& node);我们若需要对不同类型进行操作,只需重写自己需要的类型即可。
2. traverse

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

由源码可知其最后调用的是每个不同类中的traverse,跟踪一下(group为例)

void Group::traverse(NodeVisitor& nv)
{
    for(NodeList::iterator itr=_children.begin();
        itr!=_children.end();
        ++itr)
    {
        (*itr)->accept(nv);
    }
}

可见其遍历了自己的子节点,并进行遍历。

  1. accept
void Node::accept(NodeVisitor& nv)
{
    if (nv.validNodeMask(*this))
    {
        nv.pushOntoNodePath(this);
        nv.apply(*this);
        nv.popFromNodePath();
    }
}

由源码可以调用了apply();

由上面三个接口便可以完成一个完整的遍历过程,其中NodeVisitor的参数可以设置遍历模式,TraversalMode::TRAVERSE_ACTIVE_CHILDREN遍历所有活动的子节点
TraversalMode::TRAVERSE_PARENTS遍历所有的父节点
TraversalMode::TRAVERSE_ALL_CHILDREN遍历所有的子节点
以下为示例代码:

#include "OsgCommon.h"

class MyNodeVisitor:public osg::NodeVisitor
{
public:
	MyNodeVisitor() :osg::NodeVisitor(TraversalMode::TRAVERSE_ACTIVE_CHILDREN){}

	virtual void apply(osg::Geode & node)
	{
		int num=node.getNumDrawables();
		for (int i=0;i<num;i++)
		{
			osg::Drawable *geo = node.getDrawable(i);
			osg::ref_ptr<osg::Geometry> gm = dynamic_cast<osg::Geometry*>(geo);
			osg::Vec3Array *vx = dynamic_cast<osg::Vec3Array*>(gm->getVertexArray());
			for (int j=0;j<vx->size();j++)
			{
				osg::Vec3d pos= vx->at(j);
				osg::ref_ptr<osg::Geode>temp = new osg::Geode;
				temp->addDrawable(new osg::ShapeDrawable(new osg::Sphere(pos, 0.02)));
				group->addChild(temp);
			}
		}
		traverse(node);
	}
	void setGroup(osg::Group *gp)
	{
		group = gp;
	}
private:
	osg::ref_ptr<osg::Group> group;
};
int main()
{
	osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer;
	osg::ref_ptr<osg::Node> glider = osgDB::readNodeFile("glider.osg");
	MyNodeVisitor bv;
	osg::ref_ptr<osg::Group> group = new osg::Group;
	group->setName("root");
	bv.setGroup(group);
	glider->accept(bv);

	group->addChild(glider);

	viewer->setSceneData(group);

	return viewer->run();
}

在这里插入图片描述

要利用键盘操作(`osgGA::GUIEventHandler`)来对从 `NodeVisitor` 遍历得到的节点进行旋转、平移等操作,您可以使用 OpenSceneGraph (OSG) 提供的事件处理机制。以下是一个示例代码片段,展示了如何实现这样的操作: ```cpp #include <osg/NodeVisitor> #include <osg/MatrixTransform> #include <osgGA/GUIEventHandler> #include <osgViewer/Viewer> // 自定义的 NodeVisitor 子类 class MyVisitor : public osg::NodeVisitor { public: MyVisitor() : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) {} // 重写 apply 方法,在遍历每个节点时执行操作 virtual void apply(osg::Node& node) { // 在这里添加旋转和平移的代码 osg::MatrixTransform* transform = dynamic_cast<osg::MatrixTransform*>(&node); if (transform) { // 旋转节点 transform->setMatrix(osg::Matrix::rotate(rotationAngle_, osg::Vec3(0, 0, 1))); // 平移节点 transform->setMatrix(transform->getMatrix() * osg::Matrix::translate(translationOffset_)); } // 继续遍历子节点 traverse(node); } void setRotationAngle(float angle) { rotationAngle_ = angle; } void setTranslationOffset(const osg::Vec3& offset) { translationOffset_ = offset; } private: float rotationAngle_ = 0.0f; osg::Vec3 translationOffset_; }; // 自定义的 GUIEventHandler 子类 class MyEventHandler : public osgGA::GUIEventHandler { public: MyEventHandler(MyVisitor* visitor) : visitor_(visitor) {} // 重写 handle 方法,在接收到按键事件时执行操作 virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa) { if (ea.getEventType() == osgGA::GUIEventAdapter::KEYDOWN) { switch (ea.getKey()) { case osgGA::GUIEventAdapter::KEY_Left: visitor_->setRotationAngle(-0.1f); break; case osgGA::GUIEventAdapter::KEY_Right: visitor_->setRotationAngle(0.1f); break; case osgGA::GUIEventAdapter::KEY_Up: visitor_->setTranslationOffset(osg::Vec3(0, 0.1f, 0)); break; case osgGA::GUIEventAdapter::KEY_Down: visitor_->setTranslationOffset(osg::Vec3(0, -0.1f, 0)); break; } return true; } return false; } private: MyVisitor* visitor_; }; int main() { // 创建场景图(省略加载场景文件的步骤) // 创建 MyVisitor 对象并应用于场景节点 MyVisitor visitor; scene->accept(visitor); // 创建查看并设置场景数据 osgViewer::Viewer viewer; viewer.setSceneData(scene); // 创建事件处理并绑定到查看 MyEventHandler eventHandler(&visitor); viewer.addEventHandler(&eventHandler); // 运行场景图的渲染循环 viewer.run(); return 0; } ``` 在上面的示例中,我们创建了一个名为 `MyVisitor` 的自定义 `NodeVisitor` 子类,在 `apply` 方法中实现了旋转和平移操作。我们还创建了一个名为 `MyEventHandler` 的自定义 `GUIEventHandler` 子类,用于处理键盘事件。 在 `main` 函数中,我们创建了 `MyVisitor` 对象并将其应用于场景节点。然后,我们创建了一个 `osgViewer::Viewer` 对象,并将场景数据设置为场景图。接下来,我们创建了一个 `MyEventHandler` 对象,并将其绑定到查看上。 最后,我们运行场景图的渲染循环。当用户按下左、右、上、下方向键时,`MyEventHandler` 类的 `handle` 方法会被调用,根据按键事件设置旋转角度和平移偏移量。然后,在每次遍历节点时,`MyVisitor` 类会根据这些设置执行相应的旋转和平移操作。 请注意,上述示例中的旋转和平移操作仅作为演示。您可以根据实际需求修改旋转角度和平移偏移量的设置。同时,您可能需要添加其他按键事件和相应的操作。 希望对您有所帮助!如果有任何进一步的问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值