OSG人机交互

3 篇文章 0 订阅

OSG人机交互

标签(空格分隔): OSG 键盘 鼠标 事件


Windows与OSG事件传递流程

OSG在Windows平台上的消息传递仍然遵守Windows的规则,底层调用的也仍然是Windows的API。OSG中真正处理事件的函数是handelNativeWindowingEvent,打开handelNativeWindowingEvent的定义可以发现待处理事件都是压入事件序列osgGA::EventQueue中。然后依次取出进行响应。

创建一个osg窗口

首先要解决的是如何使用OSG语句创建窗口以及在什么位置创建窗口。OSG定义了osg::GraphicsContext::Traits类来决定窗口大小、高宽等参数。通过将该类的一个对象传入到Windows注册窗口的函数中,定义OSG窗口的特征。

代码

#include <osgViewer/api/Win32/graphicswindowwin32>
int main()
{
    osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer;
    osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
    traits->x = 250;
    traits->y = 250;
    traits->width = 800;
    traits->height = 600;
    traits->windowDecoration = true;
    traits->doubleBuffer = true;
    traits->sharedContext = 0;
    //执行注册窗口的操作,内部调用了registerdWindowClasses
    osgViewer::GraphicsWindowWin32* gw = (osgViewer::GraphicsWindowWin32*)
        osg::GraphicsContext::createGraphicsContext(traits);
    gw->realizeImplementation();

    int a;
    std::cin >> a;
    return 0;
}

结果为一片白色的windows窗口

键盘和鼠标事件

事件被加入到osgGA::EventQueue中后,最终由osgGA::GUIEventHandler类处理,因此程序中我们只要继承osgGA::GUIEventHandler类,重载handle函数就可以实现对事件的处理。
osgGA::GUIEventHandler实际是一个回调函数,在每一帧渲染当中,OSG会遍历所有的回调函数。
osgGA::GUIEventHandler中的handle函数为
bool handle(const GUIEventAdapter&,GUIActionAdapter&)
第一个参数为GUIEventAdapter对象,它是一个事件适配器,里面定义了所有与事件相关的变量,包括键盘码,鼠标状态等。hangle函数通过这个对象来判断当前要处理事件的类别并获得相关参数。
第二个参数GUIActionAdapter为一个独立的类,里面包含asView方法,且视口类View是它的子类,因此可以在viewer当中进行事件处理。
总的来说就是通过继承osgGA::GUIEventHandler类,在重载handle函数的第一个回调参数中判断当前的待处理事件类别,在第二个回调参数中获得当前事件发生的视口,然后进行事件处理。
添加事件处理流程是这样的:从osgGA::GUIEventHandler公有派生类A并实现handle函数,然后使用viewer类的addEventHandler将其加入到事件处理序列中。

代码

实现的功能是按下键盘上左键时物体向左旋转,按下键盘上右键时物体向右旋转。

#include <osgViewer/viewer>
#include <osgViewer/api/Win32/graphicswindowwin32>
#include <osgViewer/viewerEventHandlers>
#include <osgGA/StateSetManipulator>
#include <osgGA/GUIEventHandler>
#include <osgDB/readfile>
#include <osg/matrixtransform>
class RotateGlider : public osgGA::GUIEventHandler
{
public:
    bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
    {
        osgViewer::Viewer* vw = dynamic_cast<osgViewer::Viewer*>(&aa);
        if (vw)
        {
            osg::MatrixTransform* mt = dynamic_cast<osg::MatrixTransform*>(vw->getSceneData());
            if (mt)
            {
                static float i = 0.1f;
                switch (ea.getEventType())
                {
                case osgGA::GUIEventAdapter::KEYDOWN:
                {
                    if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Left)
                    {
                        mt->setMatrix(osg::Matrix::rotate(i, osg::Vec3(0.f, 0.f, 1.f)));
                        i += 0.0f;
                    }
                    else if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Right)
                    {
                        mt->setMatrix(osg::Matrix::rotate(i, osg::Vec3(0.f, 0.f, 1.f)));
                        i -= 0.1f;
                    }
                }
                    break;
                default:
                    break;
                }





            }
        }

        return false;
    }
};

int main()
{
    osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer;
    viewer->setUpViewOnSingleScreen();

    osg::ref_ptr<osg::MatrixTransform> mt = new osg::MatrixTransform;
    osg::ref_ptr<osg::Node> glider = osgDB::readNodeFile("glider.osg");
    mt->addChild(glider);
    viewer->setSceneData(mt);
    viewer->addEventHandler(new RotateGlider);
    return viewer->run();
}

效果

鼠标Pick点选物体

在OSG中,Pick并不是OSG的库函数,需要自己实现。在pick的时候有2个重要参数,即单击时屏幕坐标xy,及这个点发出的垂直于屏幕的射线与场景中物体的交点。判断射线与viewer中物体相交是通过构造鼠标单击的法线并计算法线和场景中物体的交点,通过osgView::View::computeIntersections解决交点计算问题。下面看具体代码

#pragma comment(lib,"osgFXd.lib")
#pragma comment(lib,"osgUtild.lib")

#include <osgFX\scribe>
#include <osgUtil\linesegmentintersector>

class PickHandle : public osgGA::GUIEventHandler
{
public:
    PickHandle(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)
            {
                Pick(ea.getX(), ea.getY());
            }
            return true;
        default:
            break;
        }
        return false;

    }

protected:
    void Pick(float x, float y)
    {
        std::multiset< osgUtil::LineSegmentIntersector::Intersection> inters;
        if (mviewer->computeIntersections(x,y, inters))
        {
            for (auto iter = inters.begin(); iter != inters.end(); ++iter)
            {
                if (!iter->nodePath.empty() && !iter->nodePath.back()->getName().empty())
                {
                    const osg::NodePath& np = iter->nodePath;
                    for (int i = np.size()-1; i >0;  --i)
                    {
                        osgFX::Scribe* sc = dynamic_cast<osgFX::Scribe*>(np[i]);
                        if (sc != nullptr)
                        {
                            if (sc->getNodeMask() != 0)
                            {
                                sc->setNodeMask(0);
                            }
                        }
                    }
                }
            }
        }
    }


private:
    osgViewer::Viewer* mviewer;
};

int main()
{
osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer;
    viewer->setUpViewOnSingleScreen();

    osg::ref_ptr<osg::Group> root = new osg::Group;

    osg::ref_ptr<osg::Node> cow = osgDB::readNodeFile("cow.osg");


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

    root->addChild(cow);
    root->addChild(osgDB::readNodeFile("cessna.osg"));
    root->addChild(sc);

    viewer->setSceneData(root);

    viewer->addEventHandler(new PickHandle(viewer));
    return viewer->run();
}

效果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值