查看: 4230|回复: 5

在OSG中实现鼠标位置跟踪

[复制链接]

该用户从未签到

发表于 2009-11-2 15:09:01 | 显示全部楼层 |阅读模式
我想在OSG窗口里实现对MOVE消息的捕获,然后即时得到鼠标在屏幕上的位置,并将其坐标显示在对应的位置上,代码如下:

class SimpleTooltipHandler : public osgGA::GUIEventHandler
{
public:
    virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa);

public:
    SimpleTooltipHandler(osgViewer::View* view);

private:
    osg::ref_ptr<osgText::Text> text_;
    osg::ref_ptr<osg:rojection> projection_;
};

SimpleTooltipHandler::SimpleTooltipHandler(osgViewer::View *view)
{
    Node* root = view->getSceneData();
   
    Group* group = root->asGroup();
    if (group == NULL)
    {
        group = new Group;
        group->addChild(root);
        view->setSceneData(group);
    }

    projection_ = new Projection;
    group->addChild(projection_.get());

    ref_ptr<StateSet> ss = projection_->getOrCreateStateSet();
    ss->setMode(GL_DEPTH_TEST, StateAttribute::OFF);
    ss->setRenderingHint(StateSet::TRANSPARENT_BIN);
    ss->setMode(GL_LIGHTING, StateAttribute::OFF);
    ss->setRenderBinDetails(11, "RenderBin");

    ref_ptr<MatrixTransform> mt = new MatrixTransform;
    mt->setMatrix(Matrix::identity());
    mt->setReferenceFrame(Transform::ABSOLUTE_RF);
    projection_->addChild(mt.get());

    ref_ptr<Geode> geode = new Geode;
    mt->addChild(geode.get());

    text_ = new osgText::Text;
    text_->setAlignment(osgText::Text:EFT_BOTTOM);
    text_->setAxisAlignment(osgText::Text::SCREEN);
    geode->addDrawable(text_.get());
}

bool SimpleTooltipHandler::handle(const GUIEventAdapter& ea, GUIActionAdapter& aa)
{
    if (ea.getEventType() != GUIEventAdapter::MOVE)
        return false;

    const float width = ea.getWindowWidth();
    const float height = ea.getWindowHeight();

    projection_->setMatrix(Matrix:rtho2D(0, width, 0, height));

    const double x = (1.0 + ea.getXnormalized()) / 2.0 * width;
    const double y = (1.0 + ea.getYnormalized()) / 2.0 * height;

    char s[60];
    sprintf(s, "(%f,%f) (%f,%f)", x, y, ea.getXnormalized(), ea.getYnormalized());
    text_->setText(s);
    text_->setPosition(Vec3(x, y, -1));

    return false;
}

现在发现了几个问题,请指教:
1.在MFC中第一次打开窗口的时候,ea的width和height是1280*1024,不明白为什么?
2.在MFC中第一次打开窗口时,窗口大小对应的行为是正确的,但我改变窗口大小后,如最大化,行为就不正确了,此时屏幕上显示的坐标信息和鼠标位置不一致,会有一定的偏差?
3.为什么ea.getX()和ea.getY()返回的值也是-1到1之间的数,而不是正常的鼠标位置?

谢谢!

该用户从未签到

发表于 2009-11-3 14:03:17 | 显示全部楼层
1.第一次打开窗口的时候 ea的width和height是1280*1024,可能是你在程序中设置图形上下文特征对象的时候设置的值
2.程序运行后你改变了窗口的大小  但是图形上下文特征对象并没有随着改变  所以又偏差
3.返回的值在-1到1之间 是经过换算了的   场景中心是0,0点,所有的显示器的坐标都是-1,1之间,这样就屏蔽的分辨率的差异

该用户从未签到

发表于 2009-11-3 16:11:02 | 显示全部楼层
感谢zhufu0208的回答,对于1和3,我完全赞同。
对于3的补充:ea.getXnormalized()称作坐标的规范化,即总是将计算结果缩放到[-1,1]区间;可以对照法线的归一化概念来理解。

对于2,正确的做法是通过ea.getWindowX(),ea.getWindowY(),ea.getWindowWidth(),ea.getWindowHeight()获取当前窗口的起始位置和宽高值

该用户从未签到

 楼主| 发表于 2009-11-4 09:40:27 | 显示全部楼层
不好意思,感冒了,昨天没上班,不过放心,肯定不会是H1N1的
关于第一个问题,1280*1024,应该不是设置的图形上下文吧,因为我设置这里的代码如下:
    RECT rect;
    ::GetWindowRect(m_hWnd, &rect);

    // Init the GraphicsContext Traits
    osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;

    // Init the Windata Variable that holds the handle for the Window to display OSG in.
    osg::ref_ptr<osg::Referenced> windata = new osgViewer::GraphicsWindowWin32::WindowData(m_hWnd);

    // Setup the traits parameters
    traits->x = 0;
    traits->y = 0;
    traits->width = rect.right - rect.left;
    traits->height = rect.bottom - rect.top;
    traits->windowDecoration = false;
    traits->doubleBuffer = true;
    traits->sharedContext = 0;
    traits->setInheritedWindowPixelFormat = true;
    traits->inheritedWindowData = windata;

    // Create the Graphics Context
    osg::GraphicsContext* gc = osg::GraphicsContext::createGraphicsContext(traits.get());

    // Init a new Camera (Master for this View)
    osg::ref_ptr<osg::Camera> camera = new osg::Camera;

    // Assign Graphics Context to the Camera
    camera->setGraphicsContext(gc);

    // Set the viewport for the Camera
    camera->setViewport(new osg::Viewport(traits->x, traits->y, traits->width, traits->height));

    // Add the Camera to the Viewer
    viewer_->addSlave(camera.get());

    // Add the Camera Manipulator to the Viewer
    viewer_->setCameraManipulator(keyswitch_.get());

    // Set the Scene Data
    viewer_->setSceneData(root_.get());

    // Realize the Viewer
    viewer_->realize();

    // Correct aspect ratio
    double fovy,aspectRatio,z1,z2;
    viewer_->getCamera()->getProjectionMatrixAsPerspective(fovy,aspectRatio,z1,z2);
    aspectRatio=double(traits->width)/double(traits->height);
    viewer_->getCamera()->setProjectionMatrixAsPerspective(fovy,aspectRatio,z1,z2);

    viewer_->setKeyEventSetsDone(0);

    viewer_->addEventHandler(new SimpleTooltipHandler(viewer_));

然后我在跟踪的时候确实不是1280*1024的。

关于第2个问题,请问要如何设置,如何操作?

关于第3个问题,我在最简单的使用的时候,发现getX()和getY()的值不是-1到1之间的,代码如下:
class TooltipHandler : public GUIEventHandler
{
public:
    virtual bool handle(const GUIEventAdapter& ea, GUIActionAdapter& aa)
    {
        if (ea.getEventType() != GUIEventAdapter::MOVE)
            return false;

        const float x = ea.getX();
        const float y = ea.getY();

        text_->setPosition(Vec3(x, y, -1));

        char s[30];
        sprintf(s, "%.f,%.f", x, y);
        text_->setText(s);

        return false;
    }

public:
    TooltipHandler(ref_ptr<osgText::Text>& text) : text_(text) {}

private:
    ref_ptr<osgText::Text> text_;
};

int main()
{
    ref_ptr<Geode> geode = new Geode;
    ref_ptr<Geometry> pyramidGeometry = new Geometry;
    geode->addDrawable(pyramidGeometry.get());

    osg::Vec3Array* vertices = new osg::Vec3Array;
    vertices->push_back( osg::Vec3( 0, 0, 0) ); // 左前
    vertices->push_back( osg::Vec3(10, 0, 0) ); // 右前
    vertices->push_back( osg::Vec3(10,10, 0) ); // 右后
    vertices->push_back( osg::Vec3( 0,10, 0) ); // 左后
    vertices->push_back( osg::Vec3( 5, 5,10) ); // 塔尖
    pyramidGeometry->setVertexArray(vertices);

    osg:rawElementsUInt* pyramidBase = new osg::DrawElementsUInt(osg:rimitiveSet:UADS, 0);
    pyramidBase->push_back(3);
    pyramidBase->push_back(2);
    pyramidBase->push_back(1);
    pyramidBase->push_back(0);
    pyramidGeometry->addPrimitiveSet(pyramidBase);

    osg::DrawElementsUInt* pyramidFaceOne = new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLES, 0);
    pyramidFaceOne->push_back(0);
    pyramidFaceOne->push_back(1);
    pyramidFaceOne->push_back(4);
    pyramidGeometry->addPrimitiveSet(pyramidFaceOne);

    osg::DrawElementsUInt* pyramidFaceTwo = new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLES, 0);
    pyramidFaceTwo->push_back(1);
    pyramidFaceTwo->push_back(2);
    pyramidFaceTwo->push_back(4);
    pyramidGeometry->addPrimitiveSet(pyramidFaceTwo);

    osg::DrawElementsUInt* pyramidFaceThree = new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLES, 0);
    pyramidFaceThree->push_back(2);
    pyramidFaceThree->push_back(3);
    pyramidFaceThree->push_back(4);
    pyramidGeometry->addPrimitiveSet(pyramidFaceThree);

    osg::DrawElementsUInt* pyramidFaceFour =
        new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLES, 0);
    pyramidFaceFour->push_back(3);
    pyramidFaceFour->push_back(0);
    pyramidFaceFour->push_back(4);
    pyramidGeometry->addPrimitiveSet(pyramidFaceFour);

    osg::Vec4Array* colors = new osg::Vec4Array;
    colors->push_back(osg::Vec4(1.0f, 0.0f, 0.0f, 1.0f) ); //索引0 红色
    colors->push_back(osg::Vec4(0.0f, 1.0f, 0.0f, 1.0f) ); //索引1 绿色
    colors->push_back(osg::Vec4(0.0f, 0.0f, 1.0f, 1.0f) ); //索引2 蓝色
    colors->push_back(osg::Vec4(1.0f, 1.0f, 1.0f, 1.0f) ); //索引3 白色

    osg::TemplateIndexArray
        <unsigned int, osg::Array::UIntArrayType,4,4> *colorIndexArray;
    colorIndexArray =
        new osg::TemplateIndexArray<unsigned int, osg::Array::UIntArrayType,4,4>;
    colorIndexArray->push_back(0); // vertex 0 assigned color array element 0
    colorIndexArray->push_back(1); // vertex 1 assigned color array element 1
    colorIndexArray->push_back(2); // vertex 2 assigned color array element 2
    colorIndexArray->push_back(3); // vertex 3 assigned color array element 3
    colorIndexArray->push_back(0); // vertex 4 assigned color array element 0

    pyramidGeometry->setColorArray(colors);
    pyramidGeometry->setColorIndices(colorIndexArray);
    pyramidGeometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX);

    ref_ptr<Group> root = new Group;
    root->addChild(geode.get());

    ref_ptr<rojection> projection = new Projection;
    projection->setMatrix(Matrix:rtho2D(0, 1024, 0, 768));
    root->addChild(projection.get());

    ref_ptr<MatrixTransform> mv = new MatrixTransform;
    mv->setMatrix(Matrix::identity());
    mv->setReferenceFrame(Transform::ABSOLUTE_RF);
    projection->addChild(mv.get());

    ref_ptr<Geode> geo = new Geode;
    mv->addChild(geo.get());

    ref_ptr<StateSet> ss = geo->getOrCreateStateSet();
    ss->setMode(GL_DEPTH_TEST, StateAttribute::OFF);
    ss->setRenderingHint(StateSet::TRANSPARENT_BIN);
    ss->setRenderBinDetails(11, "RenderBin");

    ref_ptr<osgText::Text> text = new osgText::Text;
    text->setText("Hello World!");
    text->setAxisAlignment(osgText::Text::SCREEN);
    text->setPosition(Vec3(50, 50, -1.5));
    geo->addDrawable(text.get());

    osgViewer::Viewer viewer;
    viewer.setSceneData(root.get());
    viewer.addEventHandler(new TooltipHandler(text));
    viewer.run();
}
其中的图形就是从osg网站教程上抄下来的,那个金字塔的图形

请指教,谢谢

该用户从未签到

发表于 2009-11-4 12:03:38 | 显示全部楼层
如果您是用addSlave()来添加窗口相机的话,那么ea.getX和ea.getY只能返回规范化的坐标值,窗口坐标值也是不正确的。对于大部分GUI来说都应当使用setCamera来设置主窗口相机,而MFC本身似乎在这方面有些缺陷。
此外不要粘贴大段的代码,没有人有时间细看

该用户从未签到

发表于 2011-10-28 16:54:29 | 显示全部楼层
我也遇到了同样的问题哦。在MFC中绘制,用的是addSlave()函数添加的相机,那么,如何能够避免这个问题呢?就没有好方法了么?头疼啊!
您需要登录后才可以回帖 登录 | 注册

本版积分规则

OSG中国官方论坛-有您OSG在中国才更好

网站简介:osgChina是国内首个三维相关技术开源社区,旨在为国内更多的技术开发人员提供最前沿的技术资讯,为更多的三维从业者提供一个学习、交流的技术平台。

联系我们

  • 工作时间:09:00--18:00
  • 反馈邮箱:1315785073@qq.com
快速回复 返回顶部 返回列表