osg资料收集 联系客服

发布时间 : 星期日 文章osg资料收集更新完毕开始阅读

osg::Quat(osg::DegreesToRadians(-45.0), osg::Vec3(0,0,1) ) );

osgGA::TrackballManipulator *Tman = new osgGA::TrackballManipulator(); viewer.setCameraManipulator(Tman); viewer.setSceneData( root ); viewer.realize();

声明一个用于设置相机的矩阵。矩阵的位置设置为坦克模型后方60个单元,上方7个单元。同时设置矩阵的方向。

osg::Matrixd myCameraMatrix; osg::Matrixd cameraRotation; osg::Matrixd cameraTrans; cameraRotation.makeRotate(

osg::DegreesToRadians(-20.0), osg::Vec3(0,1,0), // 滚转角(Y轴) osg::DegreesToRadians(-15.0), osg::Vec3(1,0,0) , // 俯仰角(X轴) osg::DegreesToRadians( 10.0), osg::Vec3(0,0,1) ); // 航向角(Z轴) // 相机位于坦克之后60个单元,之上7个单元。 cameraTrans.makeTranslate( 10,-50,15 );

myCameraMatrix = cameraRotation * cameraTrans;

使用矩阵设置视口摄相机

场景的视口类实例使用当前MatrixManipulator控制器类(TrackballManipulator,DriveManipulator等)矩阵的逆矩阵来设置主摄像机的位置。为了在视口中使用我们自定义的摄像机位置和方向矩阵,我们需要首先计算自定义矩阵的逆矩阵。

除了求取逆矩阵之外,我们还需要提供世界坐标系的方向。通常osgGA::MatrixManipulator矩阵(osgProducer::Viewer中使用)使用的坐标系为Z轴向上。但是Producer和osg::Matrix(也就是上文所创建的)使用Y轴向上的坐标系系统。因此,在获得逆矩阵之后,我们需要将其从Y轴向上旋转到Z轴向上的形式。这一要求可以通过沿X轴旋转-90度来实现。其实现代码如下所示: while( !viewer.done() ) {

if (manuallyPlaceCamera) {

osg::Matrixd i = myCameraMatrix.inverse(myCameraMatrix); Tman->setByInverseMatrix( osg::Matrix(i.ptr() )

* osg::Matrix::rotate( -3.1415926/2.0, 1, 0, 0 ) ); }

viewer.frame(); }

注意:按下V键可以手动切换摄像机。

20:25 | 添加评论 | 固定链接 | 写入日志 | NPS教程 11月17日

Navy09.1 - 处理键盘输入实现更新回调 本章目标:

上一个教程我们讲解了键盘事件处理器类,它用于注册响应函数。本章提供了用于键盘输入

的更方便的方案。我们将重载一个GUIEventHandler类,而不必再创建和注册函数。在这个类中我们将添加新的代码,以便执行特定的键盘和鼠标事件响应动作。我们还将提出一种键盘事件处理器与更新回调通讯的方法。

----------------------------------------------------------------------

问题的提出:

教程08演示了如何将回调与DOF节点相关联,以实现场景中DOF节点位置的持续更新。那么,如果我们希望使用键盘输入来控制场景图形中的节点,应该如何处理呢?例如,如果我们有一个基于位置变换节点的坦克模型,并希望在按下w键的时候控制坦克向前运动,我们需要进行如下一些操作: 1. 读取键盘事件;

2. 保存键盘事件的结果;

3. 在更新回调中响应键盘事件。 解决方案:

第一步:基类osgGA::GUIEventHandler用于定义用户自己的GUI键盘和鼠标事件动作。我们可以从基类派生自己的类并重载其handle方法,以创建自定义的动作。同时还编写accept方法来实现GUIEventHandlerVisitor(OSG 2.0版本中此类已经废弃)的功能。其基本的框架结构如下所示:

class myKeyboardEventHandler : public osgGA::GUIEventHandler {

public:

virtual bool handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter&); virtual void accept(osgGA::GUIEventHandlerVisitor& v) { v.visit(*this); }; }; bool myKeyboardEventHandler::handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter& aa) {

switch(ea.getEventType()) {

case(osgGA::GUIEventAdapter::KEYDOWN): {

switch(ea.getKey()) {

case 'w':

std::cout << \ return false; break; default:

return false; } }

default:

return false; } }

上述类的核心部分就是我们从基类中重载的handle方法。这个方法有两个参数:一个GUIEventAdapter类的实例,用于接收GUI事件;另一个是GUIActionAdapter类的实例,用于生成并向GUI系统发送请求,例如重绘请求和持续更新请求。

我们需要根据第一个参数编写代码以包含更多的事件,例如KEYUP,DOUBLECLICK,DRAG等。如果要处理按下按键的事件,则应针对KEYDOWN这个分支条件来扩展相应的代码。 事件处理函数的返回值与事件处理器列表中当前处理器触发的键盘和鼠标事件相关。如果返回值为true,则系统认为事件已经处理,不再传递给下一个事件处理器。如果返回值为false,则传递给下一个事件处理器,继续执行对事件的响应。

为了“安装”我们的事件处理器,我们需要创建它的实例并添加到osgViewer::Viewer的事件处理器列表。代码如下:

myKeyboardEventHandler* myFirstEventHandler = new myKeyboardEventHandler(); viewer.getEventHandlerList().push_front(myFirstEventHandler);

第二步:到目前为止,我们的键盘处理器还并不完善。它的功能仅仅是在每次按下w键时向控制窗口输出。如果我们希望按下键时可以控制场景图形中的元素,则需要在键盘处理器和更新回调之间建立一个通讯结构。为此,我们将创建一个用于保存键盘状态的类。这个事件处理器类用于记录最近的键盘和鼠标事件状态。而更新回调类也需要建立与键盘处理器类的接口,以实现场景图形的正确更新。现在我们开始创建基本的框架结构。用户可以在此基础上进行自由的扩展。下面的代码是一个类的定义,用于允许键盘事件处理器和更新回调之间通讯。

class tankInputDeviceStateType {

public:

tankInputDeviceStateType::tankInputDeviceStateType() : moveFwdRequest(false) {} bool moveFwdRequest; };

下一步的工作是确认键盘事件处理器和更新回调都有正确的数据接口。这些数据将封装到tankInputdeviceStateType的实例中。因为我们仅使用一个事件处理器来控制坦克,因此可以在事件处理器中提供指向tankInputDeviceStateType实例的指针。我们将向事件处理器添加一个数据成员(指向tankInputDeviceStateType的实例)。同时我们还会将指针设置为构造函数的输入参量。以上所述的改动,即指向tankInputDeviceStateType实例的指针,以及新的构造函数如下所示:

class myKeyboardEventHandler : public osgGA::GUIEventHandler { public:

myKeyboardEventHandler(tankInputDeviceStateType* tids) {

tankInputDeviceState = tids; } // …… protected:

tankInputDeviceStateType* tankInputDeviceState; };

我们还需要修改handle方法,以实现除了输出到控制台之外更多的功能。我们通过修改标志参量的值,来发送坦克向前运动的请求。

bool myKeyboardEventHandler::handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter& aa) {

switch(ea.getEventType()) {

case(osgGA::GUIEventAdapter::KEYDOWN): {

switch(ea.getKey()) {

case 'w':

tankInputDeviceState->moveFwdRequest = true; return false; break; default:

return false; } }

default:

return false; } }

第三步:用于更新位置的回调类也需要编写键盘状态数据的接口。我们为更新回调添加与上述相同的参数。这其中包括一个指向同一tankInputDeviceStateType实例的指针。类的构造函数则负责将这个指针传递给成员变量。获得指针之后,我们就可以在回调内部使用其数值了。目前的回调只具备使坦克向前运动的代码,前提是用户执行了相应的键盘事件。回调类的内容如下所示:

class updateTankPosCallback : public osg::NodeCallback { public:

updateTankPosCallback::updateTankPosCallback(tankInputDeviceStateType* tankIDevState) : rotation(0.0) , tankPos(-15.,0.,0.) {

tankInputDeviceState = tankIDevState; }

virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) {

osg::PositionAttitudeTransform* pat =

dynamic_cast (node); if(pat) {

if (tankInputDeviceState->moveFwdRequest) {

tankPos.set(tankPos.x()+.01,0,0); pat->setPosition(tankPos); }