本章教程将继续使用回调和节点路径(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. Linux基本命令(7)文件阅读的命令

    文件阅读的命令 命令 功能 head 查看文件的开头部分 tail 查看文件结尾的10行 less less是一个分页工具,它允许一页一页地(或一个屏幕一个屏幕地)查看信息 more more是一个分 ...

  2. DzzOffice结合office web Apps私有部署的实例

    很多朋友都想在自己使用的DzzOffice中,调用本地部署的office web Apps server. 下面我就直接从头到尾的将部署全部过程分享给大家. 准备条件:两台服务器,配置稍高点,以免卡顿 ...

  3. 面试java简答题

    1.  sleep() 和 wait() 有什么区别? 答:1.这两个方法来自不同的类分别是Thread和Object      2.最主要是sleep方法没有释放锁,而wait方法释放了锁,使得其他 ...

  4. spring依赖注入原理剖析

    PropertyDefinition.java package junit.test; public class PropertyDefinition { private String name; p ...

  5. jQuery hover demo

    先放效果图: 百度云下载地址:http://pan.baidu.com/s/1dDpn1Sl 代码如下: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTM ...

  6. tableView 显示区域偏移

    在SB拖了一个tableView , 在显示的时候显示区域和tableView的区域不一致, (UITableViewWrapperView 和 UITableView frame不一致) 在SB上看 ...

  7. 一排下去再上来的div

    <!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-Type" content ...

  8. dom 优酷得弹出

    <!doctype html> <html> <head> <meta charset="utf-8"> <title> ...

  9. [iOS微博项目 - 3.2] - 发送微博

    github: https://github.com/hellovoidworld/HVWWeibo   A.使用微博API发送微博 1.需求 学习发送微博API 发送文字微博 发送带有图片的微博   ...

  10. 【Linux笔记】Linux目录结构

    [Linux笔记]Linux目录结构   本文内容整理自网络,以作参考. /:根目录,位于linux文件系统目录结构的顶层,一般根目录下只存放目录,不要存放文件,/etc./bin./dev./lib ...