osg获取鼠标在三维空间中的点击位置

 

 

#pragma once

#include <osgGA/TrackballManipulator>
#include<osgGA/CameraManipulator>
#include<osgGA/GUIActionAdapter>
#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 <osg/Texture>
#include <osg/Image>
#include <osg/ShapeDrawable>
#include <osg/Texture2D>
#include <osg/BlendFunc>
#include <osg/AlphaFunc>

#include <iostream>
#include<QDebug>
#include <QMap>
#include <QVector>


class VCTrackballManipulator :public osgGA::TrackballManipulator
{
public:
    VCTrackballManipulator();
    ~VCTrackballManipulator();

    void    moveToView(const    osgGA::GUIEventAdapter    &ea);

    void    moveToInit();

    void    viewTop();
    void    viewFront();
    void    viewLeft();
    //
    void performPick(const osgGA::GUIEventAdapter& ea, osgViewer::Viewer& view);
    osg::ref_ptr<osg::Geode> createRedSphere(const osg::Vec3f& position, float radius);
    void Pick(float x, float y, osgViewer::Viewer* mViewer);

    //
    osg::ref_ptr<osg::Geode> getClickLine()
    {
        if (clickLine !=nullptr) {
            return clickLine;
        }
        return nullptr;
    }

    QVector<float> getClickLineStartAndEndPointVec()
    {
        return  clickLineStartAndEndPointVec;
    }

    void clearClickLineStartAndEndPointVec()
    {
        this->clickLineStartAndEndPointVec.clear();
    }

    void setNodeTexture(osg::Node *nodeParam);
    void setViewer(osgViewer::Viewer* mViewer)
    {
        this->mViewer1 = mViewer;
    }

    void computeWorldRayFromWindowPoint(osg::Camera* camera, float x, float y, osg::Vec3& start, osg::Vec3& end) {
        // 将窗口坐标转换为视口坐标(通常是相同的,除非有视口偏移或缩放)
        float viewportX = x;
        float viewportY = camera->getViewport()->height() - y; // 注意Y坐标是反转的

        // 获取相机的投影矩阵和逆视图矩阵
        osg::Matrix projectionMatrix = camera->getProjectionMatrix();
        osg::Matrix viewMatrix = camera->getViewMatrix();
        osg::Matrix inverseViewMatrix = osg::Matrix::inverse(viewMatrix);
        osg::Matrix inverseProjectionMatrix = osg::Matrix::inverse(projectionMatrix);

        // 将视口坐标转换为裁剪空间坐标(-1 到 1)
        float nx = (2.0f * viewportX) / camera->getViewport()->width() - 1.0f;
        float ny = 1.0f - (2.0f * viewportY) / camera->getViewport()->height();

        // 计算裁剪空间中的近平面和远平面点
        osg::Vec3 nearPoint(nx, ny, -1.0f);
        osg::Vec3 farPoint(nx, ny, 1.0f);

        // 使用逆视图矩阵和投影矩阵将裁剪空间点转换为世界空间点
        //start = nearPoint * inverseViewMatrix;
        //end = farPoint * inverseViewMatrix;

        // 使用逆投影矩阵将裁剪空间点转换为视图空间点
        osg::Vec3 viewNearPoint = nearPoint * inverseProjectionMatrix;
        osg::Vec3 viewFarPoint = farPoint * inverseProjectionMatrix;

        // 使用逆视图矩阵将视图空间点转换为世界空间点
        osg::Vec3 worldNearPoint = viewNearPoint * inverseViewMatrix;
        osg::Vec3 worldFarPoint = viewFarPoint * inverseViewMatrix;

        // 现在我们有了从相机位置到点击位置的世界射线
        // worldNearPoint 是射线起点(近平面上的点)
        // worldFarPoint 是射线方向上的一个远点(远平面上的点)
        // 射线方向可以通过 worldFarPoint - worldNearPoint 得到

        osg::Vec3 rayDirection = worldFarPoint - worldNearPoint;
        rayDirection.normalize(); // 规范化射线方向
        // 现在我们有了从相机位置到点击位置的世界射线
        start = worldNearPoint;
        end = worldFarPoint;
    }

    
protected:
    bool handle(const    osgGA::GUIEventAdapter    &ea, osgGA::GUIActionAdapter    &us)
    {
        //
        switch (ea.getEventType())
        {
        case    osgGA::GUIEventAdapter::PUSH:
            if (ea.getButton()==ea.LEFT_MOUSE_BUTTON) {
                {
                    // 获取相机
                    osg::Camera* camera = us.asView()->getCamera();
                    if (!camera) return false;

                    // 获取鼠标点击的窗口坐标
                    float x = ea.getX();
                    float y = ea.getY();

                    // 计算世界射线
                    osg::Vec3 start, end;
                    computeWorldRayFromWindowPoint(camera, x, y, start, end);
                    {
                        // 创建一个几何体对象
                        osg::ref_ptr<osg::Geometry> geom = new osg::Geometry;
                        // 设置顶点模式为线条列表(GL_LINES)或线带(GL_LINE_STRIP)
                        osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array;
                        vertices->push_back(osg::Vec3(start.x(), start.y(), start.z())); // 线的起点
                        vertices->push_back(osg::Vec3(end.x(), end.y(), end.z())); // 线的终点
                        // 如果需要绘制多条线段,继续添加顶点...
                        geom->setVertexArray(vertices);

                        // 对于线条列表,每个线段由两个顶点定义
                        // 对于线带,顶点序列中的连续点将形成线段
                        osg::ref_ptr<osg::DrawArrays> drawArrays = new osg::DrawArrays(osg::PrimitiveSet::LINES, 0, vertices->size());
                        geom->addPrimitiveSet(drawArrays);
                        //
                        osg::ref_ptr<osg::Geode> lineGeodeObj = new osg::Geode();
                        lineGeodeObj->addChild(geom);
                        clickLine = lineGeodeObj;
                        //
                        this->clearClickLineStartAndEndPointVec();
                        clickLineStartAndEndPointVec.push_back(start.x());
                        clickLineStartAndEndPointVec.push_back(start.y());
                        clickLineStartAndEndPointVec.push_back(start.z());
                        clickLineStartAndEndPointVec.push_back(end.x());
                        clickLineStartAndEndPointVec.push_back(end.y());
                        clickLineStartAndEndPointVec.push_back(end.z());
                        //
                    }

                }
            }
            break;
        case osgGA::GUIEventAdapter::RELEASE:
            if (ea.getButton() == ea.LEFT_MOUSE_BUTTON) {
                
            }
            break;
        default:
            break;
        }

        if (ea.getHandled()) {
            return    false;
        }

        return    osgGA::StandardManipulator::handle(ea,us);
    }

private:
    float    ritateAngle1 = 0.0f;
    double _lastX, _lastY; // 用于记录上一次鼠标位置
    bool _dragging; // 标记是否正在拖动旋转
    
    osg::ref_ptr<osg::Geode> clickLine=nullptr;
    QVector<float> clickLineStartAndEndPointVec;

};

 

 

 

###########################

posted @ 2024-12-28 23:37  西北逍遥  阅读(183)  评论(0)    收藏  举报