OSG 实现跟随节点的相机(转)

本章教程将继续使用回调和节点路径(NodePath)来检索节点的世界坐标。

本章目标:

在一个典型的仿真过程中,用户可能需要从场景中的各种车辆和人物里选择一个进行跟随。本章将介绍一种将摄像机“依附”到场景图形节点的方法。此时视口的摄像机将跟随节点的世界坐标进行放置。

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

概述:

视口类包括了一系列的矩阵控制器(osgGA::MatrixManipulator)。因而提供了“驱动控制(Drive)”,“轨迹球 (Trackball)”,“飞行(Fly)”等交互方法。矩阵控制器类用于更新摄像机位置矩阵。它通常用于回应GUI事件(鼠标点击,拖动,按键,等 等)。本文所述的功能需要依赖于相机位置矩阵,并参照场景图形节点的世界坐标。这样的话,相机就可以跟随场景图形中的节点进行运动了。

为了获得场景图形中节点的世界坐标,我们需要使用节点访问器的节点路径功能来具现一个新的类。这个类将提供一种方法将自己的实例关联到场景图形,并因此提
供访问任意节点世界坐标的方法。此坐标矩阵(场景中任意节点的世界坐标)将作为相机位置的矩阵,由osgGA::MatrixManipulator实例
使用。

实现:

首先我们创建一个类,计算场景图形中的多个变换矩阵的累加结果。很显然,所有的节点访问器都会访问当前的节点路径。节点路径本质上是根节点到当前节点的所有节点列表。有了节点路径的实例之后,我们就可以使用场景图形的方法computeWorldToLocal(
osg::NodePath)来获取表达节点世界坐标的矩阵了。
这个类的核心是使用更新回调来获取某个给定节点之前所有节点的矩阵和。整个类的定义如下:

struct updateAccumulatedMatrix :
public osg::NodeCallback
{
   virtual void
operator()(osg::Node* node, osg::NodeVisitor* nv)
   {
     
matrix = osg::computeWorldToLocal(nv->getNodePath() );
     
traverse(node,nv);
   }
   osg::Matrix matrix;
};

下一步,我们需要在场景图形的更新遍历中启动回调类。因此,我们将创建一个类,其中包括一个osg::Node实例作为数据成员。此节点数据成员的更新回
调是上述updateAccumulatedMatrix类的实例,同时此节点也将设置为场景的一部分。为了读取用于描绘节点世界坐标的矩阵(该矩阵与节
点实例相关联),我们需要为矩阵提供一个“get”方法。我们还需要提供添加节点到场景图形的方法。我们需要注意的是,用户应如何将节点关联到场景中。此
节点应当有且只有一个父节点。因此,为了保证这个类的实例只有一个相关联的节点,我们还需要记录这个类的父节点。类的定义如下面的代码所示:

struct transformAccumulator
{
public:
   transformAccumulator();
   bool attachToGroup(osg::Group*
g);
   osg::Matrix getMatrix();
protected:
   osg::ref_ptr parent;
   osg::Node* node;
   updateAccumulatedMatrix*
mpcb;
};

类的实现代码如下所示:

transformAccumulator::transformAccumulator()
{
   parent = NULL;
   node = new osg::Node;
   mpcb = new
updateAccumulatedMatrix();
  
node->setUpdateCallback(mpcb);
}

osg::Matrix
transformAccumulator::getMatrix()
{
   return mpcb->matrix;
}

bool
transformAccumulator::attachToGroup(osg::Group* g)
// 注意不要在回调中调用这个函数。
{
   bool success = false;
   if (parent != NULL)
   {
     
int n = parent->getNumChildren();
     
for (int i = 0; i < n; i++)
     
{
        
if (node == parent->getChild(i) )
        
{
           
parent->removeChild(i,1);
           
success = true;
        
}
     
}
     
if (! success)
     
{
        
return success;
     
}
   }
   g->addChild(node);
   return true;
}

现在,我们已经提供了类和方法来获取场景中节点的世界坐标矩阵,我们所需的只是学习如何使用这个矩阵来变换相机的位置。
osgGA::MatrixManipulator类即可提供一种更新相机位置矩阵的方法。我们可以从MatrixManipulator继承一个新的
类,以实现利用场景中某个节点的世界坐标矩阵来改变相机的位置。为了实现这一目的,这个类需要提供一个数据成员,作为上述的
accumulateTransform实例的句柄。新建类同时还需要保存相机位置矩阵的相应数据。

MatrixManipulator类的核心是“handle”方法。这个方法用于检查选中的GUI事件并作出响应。对我们的类而言,唯一需要响应的
GUI事件就是“FRAME”事件。在每一个“帧事件”中,我们都需要设置相机位置矩阵与transformAccumulator矩阵的数值相等。我们
可以在类的成员中创建一个简单的updateMatrix方法来实现这一操作。由于我们使用了虚基类,因此某些方法必须在这里进行定义(矩阵的设置及读
取,以及反转)。综上所述,类的实现代码如下所示:

class followNodeMatrixManipulator :
public osgGA::MatrixManipulator
{
public:
   followNodeMatrixManipulator(
transformAccumulator* ta);
   bool handle (const
osgGA::GUIEventAdapter&ea,
osgGA::GUIActionAdapter&aa);
   void updateTheMatrix();
   virtual void setByMatrix(const
osg::Matrixd& mat) {theMatrix = mat;}
   virtual void
setByInverseMatrix(const osg::Matrixd&mat) {}
   virtual osg::Matrixd
getInverseMatrix() const;
   virtual osg::Matrixd
getMatrix() const;
protected:
   ~followNodeMatrixManipulator()
{}
   transformAccumulator*
worldCoordinatesOfNode;
   osg::Matrixd theMatrix;
};

The class implementation is as
follows:

followNodeMatrixManipulator::followNodeMatrixManipulator(
transformAccumulator* ta)
{
   worldCoordinatesOfNode = ta;
theMatrix = osg::Matrixd::identity();
}
void followNodeMatrixManipulator::updateTheMatrix()
{
   theMatrix =
worldCoordinatesOfNode->getMatrix();
}
osg::Matrixd followNodeMatrixManipulator::getMatrix() const
{
   return theMatrix;
}
osg::Matrixd followNodeMatrixManipulator::getInverseMatrix()
const
{
   // 将矩阵从Y轴向上旋转到Z轴向上
   osg::Matrixd m;
   m = theMatrix *
osg::Matrixd::rotate(-M_PI/2.0, osg::Vec3(1,0,0) );
   return m;
}
void followNodeMatrixManipulator::setByMatrix(const
osg::Matrixd& mat)
{
   theMatrix = mat;
}
void followNodeMatrixManipulator::setByInverseMatrix(const
osg::Matrixd& mat)
{
   theMatrix =
mat.inverse();
}

bool
followNodeMatrixManipulator::handle
(const osgGA::GUIEventAdapter&ea,
osgGA::GUIActionAdapter&aa)
{
  
switch(ea.getEventType())
   {
     
case (osgGA::GUIEventAdapter::FRAME):
     
{
        
updateTheMatrix();
        
return false;
     
}
   }
   return false;
}

上述的所有类都定义完毕之后,我们即可直接对其进行使用。我们需要声明一个transformAccumulator类的实例。该实例应当与场景图形中的某个节点相关联。然后,我们需要声明nodeFollowerMatrixManipulator类的实例。此操纵器类的构造函数将获取transformAccumulator实例的指针。最后,将新的矩阵操纵器添加到视口操控器列表中。上述步骤的实现如下:

//
设置场景和视口(包括tankTransform节点的添加)……

transformAccumulator*
tankWorldCoords = new transformAccumulator();
tankWorldCoords->attachToGroup(tankTransform);
followNodeMatrixManipulator* followTank =
   new
followNodeMatrixManipulator(tankWorldCoords);
osgGA::KeySwitchMatrixManipulator *ksmm = new
osgGA::KeySwitchMatrixManipulator();
if (!ksmm)

时间: 2024-08-23 08:10:59

OSG 实现跟随节点的相机(转)的相关文章

FirstPersonManipulator ---------osg与qt 第一人称相机封装

osg与qt配置环境的讲解: http://blog.csdn.net/sadasasdasd/article/details/44573637 osg与qt百度贴吧: http://tieba.baidu.com/f?ie=utf-8&kw=osg%E4%B8%8Eqt&fr=search 配置环境第一讲试看地址:链接:http://pan.baidu.com/s/1gdjEOkb 密码:l3bt 认识并简单的应用qt试看地址:   链接:http://pan.baidu.com/s/1

osg模型部分节点旋转

osg::ref_ptr<osg::Geode> CreateBox() { osg::ref_ptr<osg::Geode> geode = new osg::Geode; osg::ref_ptr<osg::TessellationHints> hints = new osg::TessellationHints; hints->setDetailRatio(0.5); osg::ref_ptr<osg::Material> material =

OSG开源教程(转)

整理:荣明.王伟 北 京 2008年4月 序 第一次接触OSG是在2001年,当时开源社区刚刚兴起,还没有现在这么火.下载了OSG源码,但是在看了几个Demo之后,感觉没有什么特别之处.时隔七年之后,我再次将目光投向OSG,发现OSG确实有其独到之处,很多3D效果已经不弱于甚至超过商业软件,有感于开源力量的巨大.但是,与当前主流3D商业软件如Vega.VegaPrime.VTree.Performer等相比,开源软件的缺点也很明显,其中文档缺乏可谓其致命弱点之一.开发者只能从浩瀚的源码中,进行编

unity3D:游戏分解之角色移动和相机跟随

游戏中,我们经常会有这样的操作,点击场景中某个位置,角色自动移动到那个位置,同时角色一直是朝向那个位置移动的,而且相机也会一直跟着角色移动.有些游戏,鼠标滑动屏幕,相机就会围绕角色旋转. 看似很简单的操作,那么到底是怎么实现的呢? 我们把上述操作分解为以下几个步骤 角色的移动 1. 移动到下一个路点,线性插值.曲线插值 2. 角色朝向,一直面朝下一个路点 相机跟随角色 1. 相机俯视角度,决定相机的高度 2. 相机跟随距离,前向距离或者直线距离(就是三角形的水平边长或者斜边长) 3. 相机一直看

OSG节点更新与事件回调

OSG中的节点主要使用回调(CallBack)来完成用户临时.需要每帧执行的工作.根据回调功能被调用的时机划分为更新回调(Update CallBack)和人机交互时间回调(Event CallBack).前者在每一帧中系统遍历到当前节点时调用,后者则由交互事件触发,如操作键盘.鼠标.关闭窗口.改变窗口大小等动作.回调类基类是osg::NodeCallBack(),主要函数如下: //虚函数,回调函数主要操作在此函数中,子类应当重写,已完成相应操作 void operator()(Node* n

osg实例介绍

转自:http://blog.csdn.net/yungis/article/list/1 [原]osgmotionblur例子 该例子演示了运动模糊的效果.一下内容是转自网上的:原理:引用内容对于运动画面,将当前帧画面与上一帧画面进行alpha融合,以产生出残影——运动模糊效果.通过使用累积缓存来完成这项工作.OpenGL提供一个累积缓存,可以用来存储当前指定的颜色缓存里面的内容,并进行一定的运算操作.通过函数glAccum可以对累积缓存进行操作. glAccum介绍如下:引用内容void g

OSG动画学习

转自:http://bbs.osgchina.org/forum.php?mod=viewthread&tid=3899&_dsign=2587a6a9 学习动画,看了osganimationskinning这个例子,感觉OSG的动画实现的太灵活了.一个简单的模型节点变换动画过程如下: 1.定义一些变换位置2.定义动画关键帧,包含了时间,位置,旋转等数据这里可以设置受变化作用的节点3.给节点设置一个动画管理器,这个动画管理器是继承自Osg::NodeCallback,所以其实是个Callb

探索未知种族之osg类生物---器官初始化二

那我们回到ViewerBase::frame函数中来,继续看看为什么osg生命刚刚出生的时候会大哭,除了初始化了eventQuene和cameraManipulator之外还对那些器官进行了初始化.在这之前我们先介绍一下上一节说到的osg的肢体或者器官但是没有展开介绍的. 前言osgGA::GUIEventAdapter,GUI事件适配器.它就是对所有平台windows linux mac平台上的鼠标.键盘.以及其他的窗口事件进行了封装,目的是使接口统一,用户在使用osg库的时候不用再自己区分平

OpenSceneGraph几个重要功能节点练习

OpenSceneGraph几个重要功能节点练习 一. 空间变换节点 空间变换中最重要的是坐标系和矩阵运算了.OSG坐标系中使用右手系,Z轴垂直向上,X轴水平向右,Y轴垂直屏幕向里,与OpenGL和DirectX都不同.相关缩放.旋转和平移主要由osg::Matrix, osg::Vec3, osg::Quat几个类来完成.局部坐标系向世界坐标系转换规则是:设在局部坐标系下顶点 V 转换成世界坐标系坐标 V': V' = V * Mn* Mn-1*……* M3* M2* M1* M0 其中M0到