【OSG学习笔记】Day 14: 操作器(Manipulator)的深度使用

发布于:2025-05-01 ⋅ 阅读:(34) ⋅ 点赞:(0)

请添加图片描述

OpenSceneGraph (OSG) 中,操作器(Manipulator)是用于控制视图和场景交互的核心组件。

常见的操作器包括 TrackballManipulator(轨迹球模式)、FlightManipulator(飞行模式)等。

通过使用这些操作器,用户可以以不同的方式与场景进行交互。

今天我们将深入探讨如何切换轨迹球模式和飞行模式,并实现一个自定义的操作器。


切换轨迹球模式和飞行模式

  • 轨迹球模式 (TrackballManipulator):模拟一个虚拟的球体,允许用户通过鼠标拖动来旋转、平移和缩放场景。
  • 飞行模式 (FlightManipulator):模拟飞行器的行为,可以通过键盘和鼠标控制视角的移动,适合大范围场景的浏览。

实战:切换操作器

在 OSG 中,可以通过 osgViewer::ViewersetCameraManipulator() 方法来动态切换操作器。

#include <osgViewer/Viewer>
#include <osgGA/TrackballManipulator>
#include <osgGA/FlightManipulator>
#include <osgDB/ReadFile>
#include <iostream>

int main()
{
    // 创建 Viewer
    osgViewer::Viewer viewer;

    // 加载模型
    osg::ref_ptr<osg::Node> model = osgDB::readNodeFile("cessna.osg");
    if (!model)
    {
        std::cerr << "无法加载模型!" << std::endl;
        return -1;
    }
    viewer.setSceneData(model);

    // 默认使用轨迹球操作器
    viewer.setCameraManipulator(new osgGA::TrackballManipulator);

    // 主循环中监听按键切换操作器
    while (!viewer.done())
    {
        viewer.frame();

        // 检测按键
        if (viewer.getEventQueue()->keyDown('t'))
        {
            std::cout << "切换到轨迹球模式..." << std::endl;
            viewer.setCameraManipulator(new osgGA::TrackballManipulator);
        }
        else if (viewer.getEventQueue()->keyDown('f'))
        {
            std::cout << "切换到飞行模式..." << std::endl;
            viewer.setCameraManipulator(new osgGA::FlightManipulator);
        }
    }

    return 0;
}
运行效果
  • 按下 t 键切换到轨迹球模式。
  • 按下 f 键切换到飞行模式。

请添加图片描述

实战:实现自定义操作器

如果内置的操作器无法满足需求,我们可以继承 osgGA::CameraManipulator 类来实现自定义操作器。

自定义操作器的基本结构

自定义操作器需要重写以下方法:

  • home():重置操作器到初始状态。
  • handle():处理事件(如鼠标、键盘输入)。
  • getMatrix()getInverseMatrix():返回摄像机的变换矩阵和逆矩阵。

代码实例

以下是一个简单的自定义操作器示例,它会根据鼠标拖动改变摄像机的位置。

#include <osgGA/CameraManipulator>
#include <osg/Matrixd>
#include <osg/Quat>
#include <osgViewer/Viewer>
#include <osgDB/ReadFile>
#include <iostream>

class CustomManipulator : public osgGA::CameraManipulator
{
public:
    CustomManipulator() : _center(0, 0, 0), _distance(10.0) {}

    // 重置到初始状态
    void home(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa) override
    {
        _center = osg::Vec3(0, 0, 0);
        _distance = 10.0;
    }

    // 处理事件
    bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa) override
    {
        switch (ea.getEventType())
        {
        case osgGA::GUIEventAdapter::DRAG:
            // 根据鼠标移动改变中心点
            _center.x() += ea.getDX() * 0.1;
            _center.y() -= ea.getDY() * 0.1;
            return true;

        default:
            return false;
        }
    }

    // 获取摄像机矩阵
    osg::Matrixd getMatrix() const override
    {
        return osg::Matrixd::translate(osg::Vec3(0, 0, _distance)) *
               osg::Matrixd::translate(_center);
    }

    // 获取逆矩阵
    osg::Matrixd getInverseMatrix() const override
    {
        return osg::Matrixd::inverse(getMatrix());
    }

private:
    osg::Vec3 _center;   // 相机注视的中心点
    double _distance;    // 相机到中心点的距离
};

int main()
{
    // 创建 Viewer
    osgViewer::Viewer viewer;

    // 加载模型
    osg::ref_ptr<osg::Node> model = osgDB::readNodeFile("cessna.osg");
    if (!model)
    {
        std::cerr << "无法加载模型!" << std::endl;
        return -1;
    }
    viewer.setSceneData(model);

    // 使用自定义操作器
    viewer.setCameraManipulator(new CustomManipulator);

    // 开始渲染循环
    return viewer.run();
}
运行效果

在这里插入图片描述


在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到