OSG 之学习四:OSG 鼠标键盘交互

说明


  • OSG 入门看的,大佬绕道
  • 示例来源于《OSG程序设计教程》
  • 没有此电子书的小伙伴们,我已上传至CSDN
  • 代码已经整理,并且一些错误也得到更正

1. 交互过程


在这里插入图片描述

2. 交互详解


函数功能:鼠标右键单击时牛和飞机都隐藏,鼠标左键双击时牛和飞机都显示,按键盘上面的 LEFT(方向左键) 键,显示牛, 按键盘上面的 RIGHT(方向右键) 键显示飞机。其中显示与隐藏结点我们使用的是 setNodeMask(bool)

#include <osgDB/ReadFile>
#include <osgViewer/Viewer>
#include <osg/Node>

class UseEventHandler : public osgGA::GUIEventHandler {
public:

    // 实现虚函数,virtual 可以不写
    // 这里有两个极其重要的参数,
    // 一个是 const osgGA::GUIEventAdapter,这里 const 不能不写,
    // 因为是虚函数,参数必须与父类中虚函数相应一致,该参数是用来识别各种事件类型的参数
    // 第二个参数 osgGA::GUIActionAdapter,它是控制显示的参数,
    // 其实最重要的是它是 Viewer 的祖父类,由它可以得到 viewer,在操作中有体现
    virtual bool handle(const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &aa) {
        // 由参数二 osgGA::GUIActionAdapter 得到了 viewer
        osgViewer::Viewer *viewer = dynamic_cast<osgViewer::Viewer *>(&aa);
        if (!viewer) return false; // 没有创建成功的话直接退出

        switch (ea.getEventType()) {
        case osgGA::GUIEventAdapter::KEYDOWN: // 键盘事件
            if (ea.getKey() == 0xFF51) { // 键盘左键
                viewer->getSceneData()->asGroup()->getChild(1)->setNodeMask(0);
                viewer->getSceneData()->asGroup()->getChild(0)->setNodeMask(1);
            }
            if (ea.getKey() == 0xFF53) { // 键盘右键
                viewer->getSceneData()->asGroup()->getChild(0)->setNodeMask(0);
                viewer->getSceneData()->asGroup()->getChild(1)->setNodeMask(1);
            }
            break;

        case osgGA::GUIEventAdapter::PUSH: // 单击
            if (ea.getButton() == 4) { // 右键
                viewer->getSceneData()->asGroup()->getChild(0)->setNodeMask(0);
                viewer->getSceneData()->asGroup()->getChild(1)->setNodeMask(0);
            }
            break;

        case osgGA::GUIEventAdapter::DOUBLECLICK: // 双击
            if (ea.getButton() == 1) { // 左键
                viewer->getSceneData()->asGroup()->getChild(0)->setNodeMask(1);
                viewer->getSceneData()->asGroup()->getChild(1)->setNodeMask(1);
            }
            break;

        default:
            break;
        }
        return false;
    }
};

void main() {
    osgViewer::Viewer viewer;
    osg::ref_ptr<osg::Group> root = new osg::Group();
    root->addChild(osgDB::readNodeFile("cow.osg"));
    root->addChild(osgDB::readNodeFile("glider.osg"));
    viewer.setSceneData(root.get());
    viewer.addEventHandler(new UseEventHandler);
    viewer.realize();
    viewer.run();
}
  • OSG 中常用的事件类型
代码事件类型
NONE0无事件
PUSH1鼠标某键按下
RELEASE2鼠标某键弹起
DOUBLECLICK4鼠标某键双击
DRAG8鼠标某键拖动
MOVE16鼠标移动
KEYDOWN32键盘上某键按下
KEYUP64键盘上某键弹起
FRAME128应该是鼠标每帧
RESIZE256窗口大小改变时会有的事件
SCROLL512鼠标轮滚动
PEN_PRESSURE1024手写板的某事件
PEN_PROXIMITY_ENTER2048手写板的某事件
PEN_PROXIMITY_LEAVE4096手写板的某事件
CLOSE_WINDOWS8192关闭窗口
QUIT_APPLICATION16384退出程序
USER32768用户定义

一般视为 1 代表左键,2 代表中键,4 代表右键

  • OSG 中支持的键盘码值

在这里插入图片描述
在这里插入图片描述

3. pick


在这里插入图片描述

  • 程序功能:场景是一飞机和一头加白边的牛。点击牛后牛白边会消失,为了简单起见没有做成单击消失,单击再出来这种效果。给物体加白边用的是 osgFX::Scribe 类,它是专门做这个的。要加边只要把结点加入到它下面,然后它再加到原先该结点要加的结点下面就加边了,看示例代码就清楚了
#include <osgDB/ReadFile>
#include <osgViewer/Viewer>
#include <osg/Group>
#include <osgFX/Scribe>
#include <osgGA/GUIEventHandler>
#include <osgUtil/LineSegmentIntersector> // 新增头文件

class CPickHandler : public osgGA::GUIEventHandler {
public:
    CPickHandler(osgViewer::Viewer *viewer) : mViewer(viewer) {}

    virtual bool handle(const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &aa) {
        switch (ea.getEventType()) {
        case(osgGA::GUIEventAdapter::PUSH):
            if (ea.getButton() == 1) { // 左键按下
                // 传入的两个参数分别为点击时的 XY 的屏幕坐标
                // 在屏幕上坐标是从 0\0 到 1\1 因此从原理上可以表示无限个点
                Pick(ea.getX(), ea.getY()); // 调用函数
            }
            return true;
        }
        return false;
    }

protected:

    void Pick(float x, float y) {
        // 申请一个相交测试的结果集,判断屏幕与场景相交后,得出的结果集放入此中
        osgUtil::LineSegmentIntersector::Intersections intersections;
        if (mViewer->computeIntersections(x, y, intersections)) {
            // 申请一个结果集遍历器,遍历该结果集
            for (osgUtil::LineSegmentIntersector::Intersections::iterator hitr = intersections.begin(); hitr != intersections.end(); ++hitr) {
                // 注意这里取的是 back()
                if (!hitr->nodePath.empty() && !(hitr->nodePath.back()->getName().empty())) {
                    // 得到遍历器中的 nodepath,以此可以判断该 path 中是否有想要的结点
                    const osg::NodePath &np = hitr->nodePath;
                    // 如果结果集中有所需要的结点,则设置隐藏该结点
                    // 其中有一个动态转换,如果可以转换成功则左值不为 NULL,否则为 NULL。
                    for (int i = np.size() - 1; i >= 0; --i) {
                        // 添加一个 scribe 结点,该结点下的模型会被加白描线高亮显示
                        osgFX::Scribe *sc = dynamic_cast<osgFX::Scribe *>(np[i]);
                        if (sc != NULL) {
                            if (sc->getNodeMask() != 0)
                                sc->setNodeMask(0);
                        }
                    }
                }
            }
        }
    }

    osgViewer::Viewer *mViewer; // mViewer
};

int main(int, char **) {
    osgViewer::Viewer viewer;
    osg::ref_ptr<osg::Group> root = new osg::Group();
    root->addChild(osgDB::readNodeFile("cessna.osg"));
    osg::ref_ptr<osg::Node> cow = osgDB::readNodeFile("cow.osg");

    osg::ref_ptr<osgFX::Scribe> sc = new osgFX::Scribe();
    sc->addChild(cow.get());

    root->addChild(cow.get());
    root->addChild(sc.get());
    viewer.setSceneData(root.get());

    viewer.addEventHandler(new CPickHandler(&viewer));
    viewer.realize();
    viewer.run();
    return 0;
}

在这里插入图片描述

在这里插入图片描述

这个说白了就是单击鼠标左键去除白边

4. 一些类自行看之


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值