OSG使用更新回调来更改模型
OSG使用更新回调来更改模型
转自:http://blog.sina.com.cn/s/blog_668aae7801017gl7.html
使用回调类实现对场景图形节点的更新。本节将讲解如何使用回调来实现在每帧的更新遍历(update traversal)中进行节点的更新。
回调概览
用户可以使用回调来实现与场景图形的交互。回调可以被理解成是一种用户自定义的函数,根据遍历方式的不同(更新update,拣选cull,绘制draw),回调函数将自动地执行。回调可以与个别的节点或者选定类型(及子类型)的节点相关联。在场景图形的各次遍历中,如果遇到的某个节点已经与用户定义的回调类和函数相关联,则这个节点的回调将被执行。
创建一个更新回调
更新回调将在场景图形每一次运行更新遍历时被执行。与更新回调相关的代码可以在每一帧被执行,且实现过程是在拣选回调之前,因此回调相关的代码可以插入到主仿真循环的viewer.update()和viewer.frame()函数之间。而OSG的回调也提供了维护更为方便的接口来实现上述的功能。善于使用回调的程序代码也可以在多线程的工作中更加高效地运行。
从前一个教程展开来说,如果我们需要自动更新与坦克模型的炮塔航向角和机枪倾角相关联的DOF(自由度)节点,我们可以采取多种方式来完成这一任务。譬如,针对我们将要操作的各个节点编写相应的回调函数:包括一个与机枪节点相关联的回调,一个与炮塔节点相关联的回调,等等。这种方法的缺陷是,与不同模型相关联的函数无法被集中化,因此增加了代码阅读、维护和更新的复杂性。另一种(极端的)方法是,只编写一个更新回调函数,来完成整个场景的节点操作。本质上来说,这种方法和上一种具有同样的问题,因为所有的代码都会集中到仿真循环当中。当仿真的复杂程度不断增加时,这个唯一的更新回调函数也会变得愈发难以阅读、维护和修改。关于编写场景中节点/子树回调函数的方法,并没有一定之规。在本例中我们将创建单一的坦克节点回调,这个回调函数将负责更新炮塔和机枪的自由度节点。
为了实现这一回调,我们需要在节点类原有的基础上添加新的数据。我们需要获得与炮塔和机枪相关联的DOF节点的句柄,以更新炮塔旋转和机枪俯仰的角度值。角度值的变化要建立在上一次变化的基础上。因为回调是作为场景遍历的一部分进行初始化的,我们所需的参数通常只有两个:一个是与回调相关联的节点指针,一个是用于执行遍历的节点访问器指针。为了获得更多的参数数据(炮塔和机枪DOF的句柄,旋转和俯仰角度值),我们可以使用节点类的userData数据成员。userData是一个指向用户定义类的指针,其中包含了关联某个特定节点时所需的一切数据集。而对于用户自定义类,只有一个条件是必需的,即,它必须继承自osg::Referenced类。Referenced类提供了智能指针的功能,用于协助用户管理内存分配。智能指针记录了分配给一个类的实例的引用计数值。这个类的实例只有在引用计数值到达0的时候才会被删除。有关osg::Referenced的更详细叙述,请参阅本章后面的部分。基于上述的需求,我们向坦克节点添加如下的代码:
- class tankDataType : public osg::Referenced
- {
- public:
- //公有成员
- protected:
- osgSim::DOFTransform* tankTurretNode;
- osgSim::DOFTransform* tankGunNode;
- double rotation; //(弧度值)
- double elevation; //(弧度值)
- };
为了正确实现tankData类,我们需要获取DOF节点的句柄。这一工作可以在类的构造函数中使用前一教程所述的findNodeVisitor类完成。findNodeVisitor将从一个起始节点开始遍历。本例中我们将从表示坦克的子树的根节点开始执行遍历,因此我们需要向tankDataType的构造函数传递坦克节点的指针。因此,tankDataType类的构造函数代码应当编写为:(向特定节点分配用户数据的步骤将随后给出)
- tankDataType::tankDataType(osg::Node* n)
- {
- rotation = 0;
- elevation = 0;
- findNodeVisitor findTurret("turret");
- n->accept(findTurret);
- tankTurretNode = dynamic_cast <osgSim::DOFTransform*> (findTurret.getFirst());
- findNodeVisitor findGun("gun");
- n->accept(findGun);
- tankGunNode = dynamic_cast< osgSim::DOFTransform*> (findGun.getFirst());
- }
我们也可以在tankDataType类中定义更新炮塔旋转和机枪俯仰的方法。现在我们只需要简单地让炮塔和机枪角度每帧改变一个固定值即可。对于机枪的俯仰角,我们需要判断它是否超过了实际情况的限制值。如果达到限制值,则重置仰角为0。炮塔的旋转可以在一个圆周内自由进行。
- void tankDataType::updateTurretRotation() //控制坦克的炮塔将旋转不同的角度
- {
- rotation += 0.01;
- tankTurretNode->setCurrentHPR( osg::Vec3(rotation,0,0) );
- }
- void tankDataType::updateGunElevation() //控制坦克的枪管在y方向上的仰角,控制枪管的升降
- {
- elevation += 0.01;
- tankGunNode->setCurrentHPR( osg::Vec3(0,elevation,0) );
- if (elevation > 0.5)
- elevation = 0.0;
- }
将上述代码添加到类的内容后,我们新定义的类如下所示:
- class tankDataType : public osg::Referenced
- {
- public:
- tankDataType(osg::Node*n);
- void updateTurretRotation();
- void updateGunElevation();
- protected:
- osgSim::DOFTransform* tankTurretNode;
- osgSim::DOFTransform* tankGunNode;
- double rotation; //(弧度值)
- double elevation; //(弧度值)
- };
下一个步骤是创建回调,并将其关联到坦克节点上。为了创建这个回调,我们需要重载“()”操作符,它包括两个参数:节点的指针和节点访问器的指针。在这个函数中我们将执行DOF节点的更新。因此,我们需要执行tankData实例的更新方法,其中tankData实例使用坦克节点的userData成员与坦克节点相关联。坦克节点的指针可以通过使用getUserData方法来获取。由于这个方法的返回值是一个osg::Referenced基类的指针,因此需要将其安全地转换为tankDataType类的指针。为了保证用户数据的引用计数值是正确的,我们使用模板类型osg::ref_ptr<tankDataType>指向用户数据。整个类的定义如下:
- {
- public:
- virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
- {
- osg::ref_ptr<tankDataType> tankData = dynamic_cast<tankDataType*> (node->getUserData() );
- if(tankData)
- {
- tankData->updateTurretRotation();
- tankData->updateGunElevation();
- }
- traverse(node, nv);
- }
- };
下一步的工作是“安装”回调:将其关联给我们要修改的坦克节点,以实现每帧的更新函数执行。因此,我们首先要保证坦克节点的用户数据(tankDataType类的实例)是正确的。然后,我们使用osg::Node类的setUpdateCallback方法将回调与正确的节点相关联。代码如下所示:
- osg::ref_ptr<osg::Node> tankNode = osgDB::readNodeFile("t72-tank_des.flt");
- tankDataType* tankData = new tankDataType(tankNode);
- tankNode->setUserData( tankData );
- tankNode->setUpdateCallback(new tankNodeCallback);
创建了回调之后,我们进入仿真循环。仿真循环的代码不用加以改变。当我们调用视口类实例的frame()方法时,我们即进入一个更新遍历。当更新遍历及至坦克节点时,将触发tankNodeCallback类的操作符“()”函数。
完整的源程序代码如下:
- #include <osgViewer/Viewer>
- #include <osgDB/ReadFile>
- #include <osg/NodeVisitor>
- #include <osg/Node>
- #include <osg/Group>
- #include <osgSim/DOFTransform>
- #include <osgUtil/Optimizer>
- #include <osg/NodeVisitor>
- #include <iostream>
- #include <vector>
- //模型中使用DOF节点,以便清晰表达坦克的某个部分。例如炮塔节点可以旋转,机枪节点可以升高
- class findNodeVisitor : public osg::NodeVisitor
- {
- public:
- findNodeVisitor();
- findNodeVisitor(const std::string &searchName) ;
- virtual void apply(osg::Node &searchNode);
- virtual void apply(osg::Transform &searchNode);
- void setNameToFind(const std::string &searchName);
- osg::Node* getFirst();
- typedef std::vector<osg::Node*> nodeListType;
- nodeListType& getNodeList() { return foundNodeList; }
- private:
- std::string searchForName;
- nodeListType foundNodeList;
- };
- findNodeVisitor::findNodeVisitor() : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN),searchForName()
- {
- }
- findNodeVisitor::findNodeVisitor(const std::string &searchName):osg::NodeVisitor(TRAVERSE_ALL_CHILDREN),searchForName(searchName)
- {
- }
- void findNodeVisitor::setNameToFind(const std::string &searchName)
- {
- searchForName = searchName;
- foundNodeList.clear();
- }
- osg::Node* findNodeVisitor::getFirst()
- {
- return *(foundNodeList.begin());
- }
- void findNodeVisitor::apply(osg::Node &searchNode)
- {
- if (searchNode.getName() == searchForName)
- {
- foundNodeList.push_back(&searchNode);
- }
- traverse(searchNode);
- }
- void findNodeVisitor::apply(osg::Transform &searchNode)
- {
- osgSim::DOFTransform* dofNode =
- dynamic_cast<osgSim::DOFTransform*> (&searchNode);
- if (dofNode)
- {
- dofNode->setAnimationOn(false);
- }
- apply ( (osg::Node&) searchNode);
- traverse(searchNode);
- }
- class tankDataType : public osg::Referenced
- {
- public:
- tankDataType(osg::Node*n);
- void updateTurretRotation();
- void updateGunElevation();
- protected:
- osgSim::DOFTransform* tankTurretNode;
- osgSim::DOFTransform* tankGunNode;
- double rotation; //(弧度值)
- double elevation; //(弧度值)
- };
- tankDataType::tankDataType(osg::Node* n)
- {
- rotation = 0;
- elevation = 0;
- findNodeVisitor findTurret("turret");
- n->accept(findTurret);
- tankTurretNode = dynamic_cast <osgSim::DOFTransform*> (findTurret.getFirst());
- findNodeVisitor findGun("gun");
- n->accept(findGun);
- tankGunNode = dynamic_cast< osgSim::DOFTransform*> (findGun.getFirst());
- }
- void tankDataType::updateTurretRotation() //控制坦克的炮塔将旋转不同的角度
- {
- rotation += 0.02;
- tankTurretNode->setCurrentHPR( osg::Vec3(rotation,0,0) );
- }
- void tankDataType::updateGunElevation() //控制坦克的枪管在y方向上的仰角,控制枪管的升降
- {
- //elevation += 0.02; //控制坦克的枪管在y方向上的升降
- tankGunNode->setCurrentHPR( osg::Vec3(0,elevation,0) );
- if (elevation > 0.5)
- elevation = 0.0;
- }
- class tankNodeCallback : public osg::NodeCallback
- {
- public:
- virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
- {
- osg::ref_ptr<tankDataType> tankData = dynamic_cast<tankDataType*> (node->getUserData() );
- if(tankData)
- {
- tankData->updateTurretRotation();
- tankData->updateGunElevation();
- }
- traverse(node, nv);
- }
- };
- int main(void)
- {
- // 初始化变量和模型,建立场景
- osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();
- osg::ref_ptr<osg::Group> root = new osg::Group();
- osg::ref_ptr<osg::Node> tankNode = osgDB::readNodeFile("t72-tank_des.flt");
- tankDataType* tankData = new tankDataType(tankNode);
- tankNode->setUserData( tankData );
- tankNode->setUpdateCallback(new tankNodeCallback);
- root->addChild(tankNode.get());
- //优化场景数据
- osgUtil::Optimizer optimizer;
- optimizer.optimize(root.get());
- //设置场景数据
- viewer->setSceneData(root.get());
- //初始化并创建窗口
- viewer->realize();
- //开始渲染
- viewer->run();
- return 0;
- }
最终的效果图如下所示:
OSG使用更新回调来更改模型的更多相关文章
- [osg]OSG使用更新回调来更改模型
使用回调类实现对场景图形节点的更新.本节将讲解如何使用回调来实现在每帧的更新遍历(update traversal)中进行节点的更新. 回调概览 用户可以使用回调来实现与场景 ...
- OSG程序设计之更新回调
更新回调(Update Callback)涉及到一个类:osg::NodeCallback.这个类重载了函数调用操作符.当回调动作发生时,将会执行这一操作符的内容. 如果节点绑定了更新回调函数,那么在 ...
- [osg]osgcallback各种回调使用的例子介绍
观察MyReadFileCallback结构体的内容,可以发现它继承自osgDB::Registry::ReadFileCallback,并重载了一个函数readNode,分析源代码可知,该函数在os ...
- MVC UpdateModel的未能更新XXXXX的类型模型
关于MVC UpdateModel的未能更新XXXXX的类型模型 的问题: 最近做MVC3的项目,相信很多人都碰到过这个问题,在此记录一下,异常:UpdateModel的未能更新XXXXX的类型模型 ...
- osg中使用MatrixTransform来实现模型的平移/旋转/缩放
osg中使用MatrixTransform来实现模型的平移/旋转/缩放 转自:http://www.cnblogs.com/kekec/archive/2011/08/15/2139893.html# ...
- Qt 5.3更新无数,更改C++控制台输出最为赞(这样就和普通C++ IDE没区别了)
转载请注明文章:Qt 5.3更新无数,更改C++控制台输出最为赞 出处:多客博图 本人觉得有了这个更新,Qt Creator可谓几乎没有缺点了,起码仅仅开发C/C++,是不用再去安装VS了. Qt 5 ...
- OSG中相机参数的更改
#pragma comment(lib, "osg.lib") #pragma comment(lib, "osgDB.lib") #pragma commen ...
- 【学习笔记】OSG中相机参数的更改
#pragma comment(lib, "osg.lib") #pragma comment(lib, "osgDB.lib") #pragma commen ...
- osg fbx 绘制坐标轴、控制模型影藏与显示
int main() { osg::ref_ptr<osgViewer::Viewer> viewer1 = new osgViewer::Viewer; osg::ref_ptr< ...
随机推荐
- CodeIgniter中驱动器的使用方法
驱动器“Drivers”是CodeIgniter框架从2.0版本开始加入的新特性.正如中文版译者所言: 笔者看了这三篇英文参考,加上自己的一些理解,对官方文档关于驱动器的这一部分进行了一些补充. 1. ...
- Android NDK开发实例教程
WINDOWS系统+ Eclipse + NDK+Android 最近开始学习Android平台开发,Android还没有玩转,Java也是刚入门,这又要开始在Android中调用C语言,需要利用ND ...
- string、math、random、datetime类
1.string类 变量.Replace("想要替换掉的字符或字符串","转换后的字符或字符串");//替换 练习:判断邮箱格式是否正确 ...
- POJ 1182 食物链 (经典带权并查集)
第三次复习了,最经典的并查集 题意:动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形.A吃B, B吃C,C吃A. 现有N个动物,以1-N编号.每个动物都是A,B,C中的一种,但是我们 ...
- Spring事务解析4-切面织入
BeanFactoryTransactionAttributeSourceAdvisor作为Advisor的实现类,自然要遵从Advisor的处理方式,当代理被调用时会调用这个类的增强方法,也就是此b ...
- css与js后边有?v=20160101
原文地址http://blog.csdn.net/zanychou/article/details/8813076 <span style="font-size:14px;" ...
- POJ 1159 回文串-LCS
题目链接:http://poj.org/problem?id=1159 题意:给定一个长度为N的字符串.问你最少要添加多少个字符才能使它变成回文串. 思路:最少要添加的字符个数=原串长度-原串最长回文 ...
- [自动运维]ant脚本打包,上传文件到指定服务器,并部署
1.根节点使用,表示根目录为当前目录,默认启动的target为build,项目名称为othersysm, <project basedir="." default=" ...
- 02_Java语言基础部分【总结】
1. 数据的输入/输出 标准输入输出流 字符输入: char c = (char)System.in.read(); 字符串输入: BufferedReader buf = new BufferedR ...
- 8659 Mine Sweeping
时间限制:500MS 内存限制:65535K提交次数:37 通过次数:15 题型: 编程题 语言: G++;GCC Description The opening ceremony of the ...