本章教程将继续使用回调和节点路径(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)

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

  1. [原][osg][osgEarth]osg::Matrix 父子节点的变化关系

    //osg::Matrix offsetmatrix 计算出子节点在父节点下的绝对坐标 //osg::Matrix offposition 用来计算当前节点相对父节点的位置 osg::Matrix o ...

  2. osg模型部分节点旋转

    osg::ref_ptr<osg::Geode> CreateBox() { osg::ref_ptr<osg::Geode> geode = new osg::Geode; ...

  3. OSG 遍历fbx节点

    count:560 construction_worker 4294967295 osg::MatrixTransform1 Bip001 L Finger02 4294967295 osg::Mat ...

  4. OSG开源教程(转)

    例:geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,4)); 来指定要利用这些数据生成一个怎么样的形状. ...

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

          游戏中,我们经常会有这样的操作,点击场景中某个位置,角色自动移动到那个位置,同时角色一直是朝向那个位置移动的,而且相机也会一直跟着角色移动.有些游戏,鼠标滑动屏幕,相机就会围绕角色旋转. ...

  6. OSG学习:矩阵变换节点示例

    #include<osgViewer\Viewer> #include<osg\Node> #include<osg\Geode> #include<osg\ ...

  7. Unity中几种简单的相机跟随

    #unity中相机追随 固定相机跟随,这种相机有一个参考对象,它会保持与该参考对象固定的位置,跟随改参考对象发生移动 using UnityEngine; using System.Collectio ...

  8. osg实例介绍

    osg实例介绍 转自:http://blog.csdn.net/yungis/article/list/1 [原]osgmotionblur例子 该例子演示了运动模糊的效果.一下内容是转自网上的:原理 ...

  9. OSG动画学习

    OSG动画学习 转自:http://bbs.osgchina.org/forum.php?mod=viewthread&tid=3899&_dsign=2587a6a9 学习动画,看了 ...

随机推荐

  1. <转>selenium+python+API分类总结

    分类 方法 方法描述 客户端操作 __init__(self, host, port, browserStartCommand, browserURL) 构造函数.host:selenium serv ...

  2. html --- canvas --- javascript --- 在线画板

    canvas功能十分强大,制作一个简易画板易如反掌,主要涉及canvas的画线能力,javascript鼠标点击事件 如有问题请参考:http://www.html5party.com/857.htm ...

  3. 【Unity入门】编辑器常用视图介绍

    版权声明:本文为博主原创文章,转载请注明出处. 打开Unity编辑器的主窗口,在窗口的右上角可以看到有个“Layout”按钮.这是用来对Unity编辑器主窗口上面的各个窗口面板进行布局的.通常情况下我 ...

  4. Java 断点调试总结

    为了准备调试,你需要在代码中设置一个断点先,以便让调试器暂停执行允许你调试,否则,程序会从头执行到尾,你就没有机会调试了. 1. 条件断点 断点大家都比较熟悉,在Eclipse Java 编辑区的行头 ...

  5. bzoj 3289 Mato的文件管理(莫队算法+BIT)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=3289 [题意] 回答若干个询问:[l,r]区间内的逆序对个数. [思路] 莫队算法,B ...

  6. HUE 安装

    1.从github网下载hue-master.zip (源代码包) 地址:https://github.com/cloudera/hue#development-prerequisites 2.安装依 ...

  7. QCon2013上海站总结 -- 整体印象和感悟

    基本情况: QCon 2013(http://www.qconshanghai.com/)上海站的活动一共为期3天(周五.六和日).活动在上海的光大会展中心举行的. QCon(全球软件开发者大会)是由 ...

  8. 轻松学习Linux之入门篇

    1.Linux概述: 2.Linux优点 3.linux历史待上传 4.linux部分发行版 5.linux政府扶持 本文出自 "李晨光原创技术博客" 博客,谢绝转载!

  9. T-SQL游标

    游标是面向行的,它会使开发人员变懒,懒得去想用面向集合的查询方式实现某些功能. 在性能上,游标会迟更多的内存,减少可用的并发,占用带宽,锁定资源,当然还有更多的代码量. 用一个比喻来说明为什么游标会占 ...

  10. tinyxml2简单使用

    引入头文件 <span style="font-size:18px;">#include "HelloWorldScene.h" #include ...