节点的更新回调和位置姿态控制
来源:互联网 发布:17nba新秀数据 编辑:程序博客网 时间:2024/05/29 13:57
原文地址:http://www.flmnware.com/
osgexample http://dis.dankook.ac.kr/lectures/med08/?page=1
目的:通过本教程,学会将自定义节点添加入场景图,并控制节点在每一帧的运动。
1、添加自定义节点
OpenSceneGraph提供的节点类(Node及其子类)实现了通用的场景图结构,但是不可能提供每一个用户需要的类,比如我要做一个飞行模拟程序,OpenSceneGraph不会给我提供一个Aircraft类。所以要实现自己的应用程序逻辑,就要自定义自己的类,这个类以OpenSceneGraph提供的类为基类,并能添加到场景图中,实现自己的特定功能。
一个飞机的运动,最简单情况是我们要考虑飞机的位置和姿态,而要想在OpenSceneGraph里控制一个模型的位置、旋转或缩放,需要在这个节点上面添加一个Transform的子类,这要用到矩阵运算,矩阵运算非常复杂,对于控制位置姿态这个经常用到的功能,OpenSceneGraph提供了类PositionAttitudeTransform,通过它可以方便控制位置姿态而不用理会复杂的矩阵运算。
飞机需要导入一个模型,在场景图中,我们就把这个模型加为这个类实例的儿子,这样,这个类可以这样定义,飞机类CCessna:
class CCessna :
public osg::PositionAttitudeTransform
{
public:
CCessna(void);
~CCessna(void);
private:
osg::ref_ptr<osg::Node> _Model;
};
class CCessna :
public osg::PositionAttitudeTransform
{
public:
CCessna(void);
~CCessna(void);
private:
osg::ref_ptr<osg::Node> _Model;
};
class CCessna :
public osg::PositionAttitudeTransform
{
public:
CCessna(void);
~CCessna(void);
private:
osg::ref_ptr<osg::Node> _Model;
};
class CCessna :
public osg::PositionAttitudeTransform
{
public:
CCessna(void);
~CCessna(void);
private:
osg::ref_ptr<osg::Node> _Model;
};
成员变量_Model是保存模型的指针。
这样,在构造函数里,将_Model加为儿子:
CCessna::CCessna(void)
{
_Model = osgDB::readNodeFile("cessna.osg");
this->addChild(_Model.get());
}
CCessna::CCessna(void)
{
_Model = osgDB::readNodeFile("cessna.osg");
this->addChild(_Model.get());
}
CCessna::CCessna(void)
{
_Model = osgDB::readNodeFile("cessna.osg");
this->addChild(_Model.get());
}
CCessna::CCessna(void)
{
_Model = osgDB::readNodeFile("cessna.osg");
this->addChild(_Model.get());
}
继续使用第一个教程的源码,将这一句:
osg::Node* node = osgDB::readNodeFile("cessna.osg");
viewer.setSceneData(node);
osg::Node* node = osgDB::readNodeFile("cessna.osg");
viewer.setSceneData(node);
osg::Node* node = osgDB::readNodeFile("cessna.osg");
viewer.setSceneData(node);
osg::Node* node = osgDB::readNodeFile("cessna.osg");
viewer.setSceneData(node);
改为:
CCessna* cessna = new Ccessna();
viewer.setSceneData(cessna);
CCessna* cessna = new Ccessna();
viewer.setSceneData(cessna);
CCessna* cessna = new Ccessna();
viewer.setSceneData(cessna);
CCessna* cessna = new Ccessna();
viewer.setSceneData(cessna);
就能看到效果了。
2、给节点添加更新回调并控制节点运动
为了控制飞机的运动,我们应该在每一帧根据速度等条件计算飞机的位置,并更新飞机节点,但是我们的更新代码应该加在哪里呢?
在程序的主循环里,可以看到这句:
viewer.update();
viewer.update();
viewer.update();
viewer.update();
在这个方法里,OpenSceneGraph会遍历场景图,调用每个节点的更新回调,我们只要把自己代码放在更新回调里,就能保证每帧代码得到运行,也就能实现我们要求的功能了。
在OpenSceneGraph中,每个回调都是类NodeCallback的子类,我们只需要继承它,就能实现功能了。为了保证飞机更新的时候能得到需要的足够信息,又不让飞机的内部实现暴露于外,我们让更新回调只进行一个转手的操作,而实际的更新函数放在CCessna类里。
更新回调代码如下:
class CCessnaUpdateCallback :
public osg::NodeCallback
{
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
{
CCessna* cessna = dynamic_cast<CCessna*>(node);
if(cessna != NULL)
{
cessna->update();
}
}
};
class CCessnaUpdateCallback :
public osg::NodeCallback
{
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
{
CCessna* cessna = dynamic_cast<CCessna*>(node);
if(cessna != NULL)
{
cessna->update();
}
}
};
class CCessnaUpdateCallback :
public osg::NodeCallback
{
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
{
CCessna* cessna = dynamic_cast<CCessna*>(node);
if(cessna != NULL)
{
cessna->update();
}
}
};
class CCessnaUpdateCallback :
public osg::NodeCallback
{
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
{
CCessna* cessna = dynamic_cast<CCessna*>(node);
if(cessna != NULL)
{
cessna->update();
}
}
};
这个类覆盖了基类对操作符()的重载,所以说它实际上是一个函数对象,因为这个重载是通用的,在函数里我们要先动态转换成 CCessna 类的指针,然后调用她的更新成员函数。
为了让这个回调起作用,不要忘了在 CCessna 类的构造函数里设置更新回调:
this->setUpdateCallback(new CCessnaUpdateCallback());
this->setUpdateCallback(new CCessnaUpdateCallback());
this->setUpdateCallback(new CCessnaUpdateCallback());
this->setUpdateCallback(new CCessnaUpdateCallback());
下面设计让飞机绕圈飞行,同时让飞机横滚,代码都是写计算操作,需要注意的是PositionAttitudeTransform类提供的设置姿态的方法不是使用通常习惯的HPR为参数,而是一个四元数,关于四元数数学请参考相关书籍,但是HPR向四元数的转换还是很直接的,这就是我们的setRotation方法:
void
CCessna::setRotation(osg::Vec3& rot)
{
osg::Quat q(rot.x(), osg::Vec3(1.0f, 0.0f, 0.0f),
rot.y(), osg::Vec3(0.0f, 1.0f, 0.0f),
rot.z(), osg::Vec3(0.0f, 0.0f, 1.0f));
this->setAttitude(q);
}
void
CCessna::setRotation(osg::Vec3& rot)
{
osg::Quat q(rot.x(), osg::Vec3(1.0f, 0.0f, 0.0f),
rot.y(), osg::Vec3(0.0f, 1.0f, 0.0f),
rot.z(), osg::Vec3(0.0f, 0.0f, 1.0f));
this->setAttitude(q);
}
void
CCessna::setRotation(osg::Vec3& rot)
{
osg::Quat q(rot.x(), osg::Vec3(1.0f, 0.0f, 0.0f),
rot.y(), osg::Vec3(0.0f, 1.0f, 0.0f),
rot.z(), osg::Vec3(0.0f, 0.0f, 1.0f));
this->setAttitude(q);
}
void
CCessna::setRotation(osg::Vec3& rot)
{
osg::Quat q(rot.x(), osg::Vec3(1.0f, 0.0f, 0.0f),
rot.y(), osg::Vec3(0.0f, 1.0f, 0.0f),
rot.z(), osg::Vec3(0.0f, 0.0f, 1.0f));
this->setAttitude(q);
}
在update方法里,通过设置位置和旋转来控制飞机运动,具体请查看代码。
3、完善程序
由于Viewer类在程序开始会根据模型的包围盒调整视点初始位置,这样能保证程序开始能看到模型,但是因为我们的模型是运动的,运行一点之后飞机会飞出视线,所以我们给飞机提供一个场地,这个场地就是一个正方体,代码如下:
osg::Group* root = new osg::Group();
osg::Geode* geode = new osg::Geode();
geode->addDrawable(new osg::ShapeDrawable(new osg::Box(osg::Vec3(0.0f, 0.0f, -20.0f), 200.0f, 200.0f, 2.0f)));
root->addChild(geode);
CCessna* cessna = new CCessna();
root->addChild(cessna);
viewer.setSceneData(root);
osg::Group* root = new osg::Group();
osg::Geode* geode = new osg::Geode();
geode->addDrawable(new osg::ShapeDrawable(new osg::Box(osg::Vec3(0.0f, 0.0f, -20.0f), 200.0f, 200.0f, 2.0f)));
root->addChild(geode);
CCessna* cessna = new CCessna();
root->addChild(cessna);
viewer.setSceneData(root);
osg::Group* root = new osg::Group();
osg::Geode* geode = new osg::Geode();
geode->addDrawable(new osg::ShapeDrawable(new osg::Box(osg::Vec3(0.0f, 0.0f, -20.0f), 200.0f, 200.0f, 2.0f)));
root->addChild(geode);
CCessna* cessna = new CCessna();
root->addChild(cessna);
viewer.setSceneData(root);
osg::Group* root = new osg::Group();
osg::Geode* geode = new osg::Geode();
geode->addDrawable(new osg::ShapeDrawable(new osg::Box(osg::Vec3(0.0f, 0.0f, -20.0f), 200.0f, 200.0f, 2.0f)));
root->addChild(geode);
CCessna* cessna = new CCessna();
root->addChild(cessna);
viewer.setSceneData(root);
这里用到了Geode 类和Drawable类的子类ShapeDrawable, Geode 是场景图的叶子节点,所有的几何体都包含在 Geode 里,而一个 Geode 里可以有多个Drawable的子类,我们看到的图像真正是Drawable画的,这个后面在讨论。
总结:要想实现自己的功能,加入自己的代码,就要定义自己的节点类,由于OpenSceneGraph使用了组合设计模式,我们自己定义的类在OpenSceneGraph看来和它自己的类是一样的,OpenSceneGraph的这种场景图结构,基本上是业界的一种标准,很多程序都实现了类似的结构。
- 节点的更新回调和位置姿态控制
- 姿态和位置,四旋翼的控制流程
- pixhawk 光流--位置估计--姿态估计--位置控制--姿态控制
- 多旋翼姿态控制mc_att_control源码简单分析-位置控制
- 控制TreeView选中后页面回传节点显示的位置
- pixhawk 姿态与控制部分的记录
- 回调和观察者模式的关系
- 【NodeJS】回调和异步调用的关系
- 捷联惯导中的姿态更新
- Pixhawk之姿态控制
- pixhawk姿态控制
- 姿态解算(二),姿态更新
- 基于四元素法的捷联惯导姿态更新算法
- 关于OpenCV的那些事——相机姿态更新
- 关于OpenCV的那些事——相机姿态更新
- 关于OpenCV的那些事——相机姿态更新
- 子节点的位置与父节点位置的关系
- 鼠标的位置控制
- javascript 小技巧
- 我对XML存储过程的理解
- 收集
- C#禁止应用程序同时运行的方法
- 无标题栏的窗口移动及窗口标题栏设计
- 节点的更新回调和位置姿态控制
- c调用lua脚本
- AdvStringGrid
- 让你的UltraEdit高亮显示多种语法收藏
- C#访问修饰符总结
- 数据库分表处理设计思想和实现
- 设计原则之开闭原则(一)
- 一年
- SQL Server 2005:你应知道的13件事