问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

OSG三维场景中拾取鼠标在模型表面的点击点

创作时间:
作者:
@小白创作中心

OSG三维场景中拾取鼠标在模型表面的点击点

引用
1
来源
1.
https://www.cnblogs.com/herd/p/18555625

在三维图形编程中,鼠标拾取(Picking)是一个常见的需求,它允许用户通过鼠标点击来选择场景中的对象或位置。本文将详细介绍如何在OpenSceneGraph(OSG)中实现这一功能,具体来说,就是如何获取鼠标点击在模型表面的具体位置,并在该位置显示一个红色球体作为标记。

在OSG中实现鼠标拾取功能主要涉及以下几个关键步骤:

  1. 创建一个继承自osgGA::GUIEventHandler的类,用于处理鼠标事件
  2. 在事件处理函数中获取鼠标点击位置
  3. 使用osgUtil::LineSegmentIntersector进行射线相交检测
  4. 处理相交结果并在场景中显示标记

下面是完整的代码实现:

#include <osg/Group>
#include <osg/Geode>
#include <osg/ShapeDrawable>
#include <osgDB/ReadFile>
#include <osgViewer/Viewer>
#include <osgGA/GUIEventHandler>
#include <osgGA/TrackballManipulator>
#include <osg/Material>
#include <osg/StateSet>
#include <osgUtil/LineSegmentIntersector>
#include <osgUtil/IntersectVisitor>
#include <iostream>

class PickHandler : public osgGA::GUIEventHandler {
public:
    PickHandler() {}

    virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa) {
        osgViewer::View* view = dynamic_cast<osgViewer::View*>(&aa);
        if (!view) return false;

        // 检查是否为鼠标左键点击事件
        if (ea.getEventType() == osgGA::GUIEventAdapter::RELEASE && ea.getButton() == osgGA::GUIEventAdapter::LEFT_MOUSE_BUTTON) {
            performPick(ea, *view);
            return true;
        }
        return false;
    }

    osg::ref_ptr<osg::Geode> createRedSphere(const osg::Vec3f& position, float radius) {
        // 创建一个球体形状
        osg::ref_ptr<osg::Sphere> sphere = new osg::Sphere(position, radius);
        // 创建一个形状绘制对象,并设置其颜色为红色
        osg::ref_ptr<osg::ShapeDrawable> sphereDrawable = new osg::ShapeDrawable(sphere);
        sphereDrawable->setColor(osg::Vec4(1.0f, 0.0f, 0.0f, 1.0f)); // 红色(RGBA)
        // 创建一个地理节点(Geode),并将形状绘制对象添加到其中
        osg::ref_ptr<osg::Geode> sphereGeode = new osg::Geode();
        sphereGeode->addDrawable(sphereDrawable);
        return sphereGeode;
    }

private:
    void performPick(const osgGA::GUIEventAdapter& ea, osgViewer::View& view) {
        // 将鼠标位置转换为窗口坐标
        int x = ea.getX();
        int y = ea.getY();

        // 执行相交检测
        osgUtil::IntersectionVisitor iv;
        osgUtil::LineSegmentIntersector::Intersections intersections;

        // 执行相交检测
        if (view.computeIntersections(x, y, intersections)) {
            // 检查是否有相交点
            if (!intersections.empty()) {
                for (const auto& intersection : intersections) {
                    const osg::Vec3& point = intersection.getWorldIntersectPoint();
                    std::cout << "Intersection point: " << point.x() << " " << point.y() << " " << point.z() << std::endl;
                    osg::ref_ptr<osg::Geode> redSphere = createRedSphere(point, 10.1f); // 半径为1的红色球体
                    view.getSceneData()->asGroup()->addChild(redSphere);
                    break; // 只处理第一个相交点
                }
            }
            else {
                std::cout << "No intersection found." << std::endl;
            }
        }
        else {
            std::cout << "No intersection found." << std::endl;
        }
    }
};

int main(int argc, char** argv) {
    osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer;
    // 加载场景 cow.osg excavator.OSGB library.OSGB
    osg::Node* root = osgDB::readNodeFile("library.OSGB");
    if (!root) {
        std::cerr << "Error loading model" << std::endl;
        return 1;
    }
    viewer->setSceneData(root);

    // 添加鼠标拾取事件处理器
    viewer->addEventHandler(new PickHandler());

    // 开始运行
    return viewer->run();
}

通过以上代码,你可以在OSG场景中实现基本的鼠标拾取功能。当用户在模型表面点击时,程序会计算出点击位置对应的三维坐标,并在该位置显示一个红色球体作为标记。这在许多三维应用中都是非常实用的功能,例如游戏开发、虚拟现实应用等。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号