zoukankan      html  css  js  c++  java
  • 项目实战:Qt+OSG教育学科工具之地理三维星球

    若该文为原创文章,未经允许不得转载
    原博主博客地址:https://blog.csdn.net/qq21497936
    原博主博客导航:https://blog.csdn.net/qq21497936/article/details/102478062
    本文章博客地址:https://blog.csdn.net/qq21497936/article/details/105372492
    各位读者,知识无穷而人力有穷,要么改需求,要么找专业人士,要么自己研究

    红胖子(红模仿)的博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬结合等等)持续更新中...(点击传送门)

    Qt开发专栏:项目实战(点击传送门)

    OSG开发专栏(点击传送门)

    需求

            使用Qt开发内嵌的三维地理学科工具。

    原理

            使用Qt+Osg三维研发,依托Qt内嵌OSG。

    相关博客

    OSG三维开发专栏》:循序渐进学习OSG

    OSG开发笔记(一):OSG介绍、编译:OSG介绍与编译

    OSG开发笔记(四):OSG不使用osgQt重写类嵌入Qt应用程序:OSG源码嵌入Qt

    OSG开发笔记(十):OSG模型的变换之平移、旋转和缩放》:对于模型结点的基本操作

    OSG开发笔记(十四):OSG交互》:按键消息和鼠标消息的交互

    OSG开发笔记(十五):OSG光照》:光影的学习,产生立体感

    OSG开发笔记(十八):OSG鼠标拾取pick、拽托球体以及多光源》:pick拾取三维物体交互

    OSG开发笔记(二十一):OSG使用HUD绘制图形以及纹理混合模式》:hud绘制背景和前景

    OSG开发笔记(二十三):Qt使用QOpenGLWidget渲染OSG和地球仪》:基础版本的地球仪开发关键

    (以上是支撑该需求的三维技术博客)

    Demo v3.1.0

    相比于v2.0.0版本:修复了星球纹理贴图存在缝隙的问题;修复了缩放无限制的bug;对球体、贴图、2d/3d切换、缩放、旋转增加了序列化接口(demo为启动应用后恢复之前关闭的状态)。

    下载地址

    Demo v3.1.0运行包下载地址:https://download.csdn.net/download/qq21497936/12542665

    QQ群:1047134658(点击“文件”搜索“教育学科工具”,群内与博文同步更新)

    Demo v2.0.0

    相比于v1.0.0版本,增加了地球以外的八大行星,对布局进行了调整,适配了多种分辨率,并且优化了部分代码;

    下载地址

    Demo v2.0.0运行包下载地址:https://download.csdn.net/download/qq21497936/12312105

    QQ群:1047134658(点击“文件”搜索“教育学科工具”,群内与博文同步更新所有可开源的源码模板)

    Demo v1.0.0

    完成地理星球中地球的研发,包括基本操作、鼠标pick旋转、缩放等,包含海洋分布、人口分布、气候分布、海平线等等功能;

    下载地址

    Demo v1.0.0运行包下载地址:https://download.csdn.net/download/qq21497936/11489564

    QQ群:1047134658(点击“文件”搜索“教育学科工具”,群内与博文同步更新所有可开源的源码模板)

    关键代码

    获取背景Hud结点(传入文件)

    
    osg::ref_ptr<osg::Node> OsgStarWidget::getBackgroundNode(QString imageFile)
    {
        osg::ref_ptr<osg::Group> pGroup = new osg::Group();
        osg::ref_ptr<osg::Camera> pCamera = 0;
        osg::ref_ptr<osg::Geode> pGeode = 0;
        // 创建背景相机
        {
            // 步骤一:创建相机
            pCamera = new osg::Camera();
            // 步骤二:设置矩阵  显示得界面边坐标      左    右  下    上
            pCamera->setProjectionMatrixAsOrtho2D(0, 1920, 0, 1080);
            // 步骤三:设置视图矩阵
            pCamera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
            // 步骤四:不受父类矩阵影响
            pCamera->setViewMatrix(osg::Matrix::identity());
            // 步骤五:清除深度缓存
            pCamera->setClearMask(GL_DEPTH_BUFFER_BIT);
            // 步骤六:设置为不接受事件,让其得不到焦点
            pCamera->setAllowEventFocus(false);
            // 步骤七:设置渲染顺序
            pCamera->setRenderOrder(osg::Camera::NESTED_RENDER);    // 显示为背景HUD
            // 步骤八:关闭光照,通过osg::StateSet设置
            pGeode = new osg::Geode();
            osg::ref_ptr<osg::StateSet> pStateSet = pGeode->getOrCreateStateSet();
            pStateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
            // 步骤九:关闭深度测试
            pStateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
        }
        // 创建背景图形图片
        {
            // 步骤一:创建几何信息体
            osg::ref_ptr<osg::Geometry> pGeometry = new osg::Geometry;
            // 步骤二:绑定顶点
            osg::ref_ptr<osg::Vec3Array> pVec3Array = new osg::Vec3Array;
            pGeometry->setVertexArray(pVec3Array.get());
            // 步骤三:设置顶点(屏幕坐标)
            pVec3Array->push_back(osg::Vec3(   0,    0, 0));
            pVec3Array->push_back(osg::Vec3(1920,    0, 0));
            pVec3Array->push_back(osg::Vec3(1920, 1080, 0));
            pVec3Array->push_back(osg::Vec3(   0, 1080, 0));
            // 步骤四:读取图片(纹理图片)
            osg::ref_ptr<osg::Image> pImage = new osg::Image;
            pImage = osgDB::readImageFile(imageFile.toStdString());
            if(!pImage || !pImage->valid())
            {
                LOG_WARN(QString("Failed to load image file: %1").arg(imageFile));
                return 0;
            }
            // 步骤五:创建纹理
            osg::ref_ptr<osg::Texture2D> pTexture2D = new osg::Texture2D;
            pTexture2D->setImage(pImage);
            pTexture2D->setUnRefImageDataAfterApply(true);
            // 步骤六:绑定纹理坐标
            osg::ref_ptr<osg::Vec2Array> pVec2Array = new osg::Vec2Array;
            pGeometry->setTexCoordArray(0, pVec2Array.get());
            // 步骤七:设置纹理坐标
            pVec2Array->push_back(osg::Vec2(0.0, 0.0));
            pVec2Array->push_back(osg::Vec2(1.0, 0.0));
            pVec2Array->push_back(osg::Vec2(1.0, 1.0));
            pVec2Array->push_back(osg::Vec2(0.0, 1.0));
            // 步骤八:关联纹理状态
            osg::ref_ptr<osg::StateSet> pStateSet = pGeometry->getOrCreateStateSet();
            pStateSet->setTextureAttributeAndModes(0, pTexture2D.get(), osg::StateAttribute::ON);
            // 步骤九:绑定法线
            osg::ref_ptr<osg::Vec3Array> pVec3ArrayNormal = new osg::Vec3Array;
            pGeometry->setNormalArray(pVec3ArrayNormal.get());
            // 步骤十:设置法线方式
            pGeometry->setNormalBinding(osg::Geometry::BIND_OVERALL);
            // 步骤十一:设置法线方向
            pVec3ArrayNormal->push_back(osg::Vec3f(0.0, 1.0, 0.0));
            // 步骤十二:绘制四个顶点的图形
            pGeometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, 4));
            // 步骤十三:将图形添加进几何节点
            pGeode->addDrawable(pGeometry.get());
        }
        pCamera->addChild(pGeode);
        pGroup->addChild(pCamera);
        return pGroup;
    }
    

    更换地球纹理

    
    void OsgStarWidget::change3DImage(QString imageFile)
    {
        // 步骤一:读取图片(纹理图片)
        osg::ref_ptr<osg::Image> pImage = 0;
        pImage = osgDB::readImageFile(imageFile.toStdString());
        if(!pImage || !pImage->valid())
        {
            LOG_WARN(QString("Failed to load image file: %1").arg(imageFile));
            return;
        }
        // 步骤二:创建纹理
        osg::ref_ptr<osg::Texture2D> pTexture2D = new osg::Texture2D;
        pTexture2D->setImage(pImage);
        pTexture2D->setUnRefImageDataAfterApply(true);
        // 步骤三:球体体渲染纹理
        osg::ref_ptr<osg::StateSet> pStateSet = _pGeode->getOrCreateStateSet();
        pStateSet->setTextureAttribute(0, pTexture2D.get());
        pStateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::ON);
        _pGeode->setStateSet(pStateSet);
    
    }

    拾取pick、缩放

    MyUserPickEventHandler.h

    #ifndef MYUSERPICKEVENTHANDLER_H
    #define MYUSERPICKEVENTHANDLER_H
    
    #include <osg/Node>
    #include <osgViewer/Viewer>
    
    class MyUserPickEventHandler : public osgGA::GUIEventHandler
    {
    
    public:
        MyUserPickEventHandler();
    
    public:
        osg::ref_ptr<osg::MatrixTransform> getMatrixTransform() const;
        void setMatrixTransform(const osg::ref_ptr<osg::MatrixTransform> &pMatrixTransform);
        float getRadius() const;
        void setRadius(float radius);
        float getMinScale() const;
        void setMinScale(float minScale);
        float getMaxScale() const;
        void setMaxScale(float maxScale);
        float getZoomInStep() const;
        void setZoomInStep(float zoomInStep);
        float getZoomOutStep() const;
        void setZoomOutStep(float zoomOutStep);
    
    public:
        /** Handle event. Override the handle(..) method in your event handlers to respond to events. */
    //    virtual bool handle(osgGA::Event* event, osg::Object* pObject, osg::NodeVisitor* pNodeVisitor);
    
        /** Handle events, return true if handled, false otherwise. */
    //    virtual bool handle(const osgGA::GUIEventAdapter& guiEventAdapter, osgGA::GUIActionAdapter& guiActionAdapter, osg::Object* pObject, osg::NodeVisitor* pNodeVisitor);
    
        /** Deprecated, Handle events, return true if handled, false otherwise. */
        virtual bool handle(const osgGA::GUIEventAdapter& guiEventAdapter, osgGA::GUIActionAdapter& guiActionAdapter);
    
    
    
    protected:
        bool pick(const double x, const double y, osgViewer::Viewer *pViewer, osg::Vec3dArray *pVec3dArrayOut);
        bool pick(const double x, const double y, osgViewer::Viewer *pViewer, osg::Node *pNode, osg::Vec3dArray *pVec3dArrayOut);
        osg::Vec3d screen2Word(osg::Vec3d screenVec3d, osgViewer::Viewer *pViewer);
    
    private:
        bool _pickEarth;
        osg::Vec3d _originVec3d;
        osg::Vec3d _lastVec3d;
        float _radius;
        float _maxScale;
        float _minScale;
        float _zoomInStep;
        float _zoomOutStep;
        osg::ref_ptr<osg::MatrixTransform> _pMatrixTransform;
    };
    
    #endif // MYUSEREVENTHANDLER_H
    

    MyUserPickEventHandler.cpp

    #include "MyUserPickEventHandler.h"
    #include "define.h"
    #include "osg/MatrixTransform"
    #include <QtMath>
    #include "MyMath.h"
    
    MyUserPickEventHandler::MyUserPickEventHandler()
        : osgGA::GUIEventHandler(),
          _pickEarth(false)
    {
    
    }
    
    osg::ref_ptr<osg::MatrixTransform> MyUserPickEventHandler::getMatrixTransform() const
    {
        return _pMatrixTransform;
    }
    
    void MyUserPickEventHandler::setMatrixTransform(const osg::ref_ptr<osg::MatrixTransform> &pMatrixTransform)
    {
        _pMatrixTransform = pMatrixTransform;
    }
    
    float MyUserPickEventHandler::getRadius() const
    {
        return _radius;
    }
    
    void MyUserPickEventHandler::setRadius(float radius)
    {
        _radius = radius;
    }
    
    float MyUserPickEventHandler::getMaxScale() const
    {
        return _maxScale;
    }
    
    void MyUserPickEventHandler::setMaxScale(float maxScale)
    {
        _maxScale = maxScale;
    }
    
    float MyUserPickEventHandler::getMinScale() const
    {
        return _minScale;
    }
    
    void MyUserPickEventHandler::setMinScale(float minScale)
    {
        _minScale = minScale;
    }
    
    
    float MyUserPickEventHandler::getZoomInStep() const
    {
        return _zoomInStep;
    }
    
    void MyUserPickEventHandler::setZoomInStep(float zoomInStep)
    {
        _zoomInStep = zoomInStep;
    }
    
    //bool MyUserEventHandler::handle(osgGA::Event *event, osg::Object *object, osg::NodeVisitor *pNodeVisitor)
    //{
    //    LOG_DEBUG("");
    //    return false;
    //}
    
    //bool MyUserEventHandler::handle(const osgGA::GUIEventAdapter &guiEventAdapter, osgGA::GUIActionAdapter &guiActionAdapter, osg::Object *pObject, osg::NodeVisitor *pNodeVisitor)
    //{
    //    LOG_DEBUG("");
    //    return true;
    //}
    
    bool MyUserPickEventHandler::handle(const osgGA::GUIEventAdapter
                                    &guiEventAdapter, osgGA::GUIActionAdapter &guiActionAdapter)
    {
        switch (guiEventAdapter.getEventType())
        {
            case osgGA::GUIEventAdapter::EventType::SCROLL:
            case osgGA::GUIEventAdapter::EventType::PUSH:
            case osgGA::GUIEventAdapter::EventType::RELEASE:
            case osgGA::GUIEventAdapter::EventType::DRAG:
                break;
            default:
                return true;
                break;
        }
        bool flag = false;
        if(_pMatrixTransform.get() == 0)
        {
            LOG_INFO("Fialed to handle, because it's not set the node of transform!!!");
            return true;
        }
        // 使用智能指针就挂,原因未知
    //    osg::ref_ptr<osgViewer::Viewer> pViewer = dynamic_cast<osgViewer::Viewer*>(&guiActionAdapter);
        osgViewer::Viewer *pViewer = dynamic_cast<osgViewer::Viewer*>(&guiActionAdapter);
        if(pViewer == 0)
        {
            LOG_WARN("Fialed to get viewer!");
            return true;
        }
        osg::Matrix matrix = _pMatrixTransform->getMatrix();
        osg::Vec3d vec3d;
        osg::ref_ptr<osg::Vec3dArray> pVec3dArray = new osg::Vec3dArray();
        qreal offsetAngle;
        float nowRadius = qSqrt(_pMatrixTransform->getBound().radius2() / 3.0);
        switch (guiEventAdapter.getEventType())
        {
        case osgGA::GUIEventAdapter::EventType::SCROLL:
            switch (guiEventAdapter.getScrollingMotion())
            {
                case osgGA::GUIEventAdapter::SCROLL_UP:     // 鼠标滚轮向上放大
                    if(nowRadius / _radius >= _maxScale)
                    {
                        break;
                    }
                    matrix *= osg::Matrix::scale(_zoomInStep, _zoomInStep, _zoomInStep);
                    _pMatrixTransform->setMatrix(matrix);
                    flag = true;
                    break;
                case osgGA::GUIEventAdapter::SCROLL_DOWN:   // 鼠标滚轮向下缩小
                    if(nowRadius / _radius <= _minScale)
                    {
                        break;
                    }
                    matrix *= osg::Matrix::scale(_zoomOutStep, _zoomOutStep, _zoomOutStep);
                    _pMatrixTransform->setMatrix(matrix);
                    flag = true;
                    break;
                default:
                    break;
            }
            break;
        case osgGA::GUIEventAdapter::EventType::PUSH:
            switch (guiEventAdapter.getButton())
            {
            case osgGA::GUIEventAdapter::LEFT_MOUSE_BUTTON:
                if(pick(guiEventAdapter.getX(), guiEventAdapter.getY(), pViewer, _pMatrixTransform, pVec3dArray.get()))
                {
                    // 拾取到物体
                    _pickEarth = true;
                    _lastVec3d = pVec3dArray->at(0);
                }else{
                    _pickEarth = false;
                }
                break;
            default:
                break;
            }
            break;
        case osgGA::GUIEventAdapter::EventType::DRAG:
            if(pick(guiEventAdapter.getX(), guiEventAdapter.getY(), pViewer, _pMatrixTransform, pVec3dArray.get()))
            {
                if(_pickEarth == false)
                {
                    // 拾取到物体
                    _pickEarth = true;
                    _lastVec3d = pVec3dArray->at(0);
                    break;
                }
                // 相交点
                vec3d = pVec3dArray->at(0);
                // 计算x轴方向角度
                offsetAngle = MyMath::lineAngleRakeRatio(QPoint(0,0), QPointF(vec3d.y(), vec3d.z()))
                              -MyMath::lineAngleRakeRatio(QPoint(0,0), QPointF(_lastVec3d.y(), _lastVec3d.z()));
                matrix *= osg::Matrix::rotate(osg::DegreesToRadians(-offsetAngle), 1, 0, 0);
                // 计算z轴方向角度
                offsetAngle = MyMath::lineAngleRakeRatio(QPoint(0,0), QPointF(vec3d.y(), vec3d.x()))
                              -MyMath::lineAngleRakeRatio(QPoint(0,0), QPointF(_lastVec3d.y(), _lastVec3d.x()));
                matrix *= osg::Matrix::rotate(osg::DegreesToRadians(offsetAngle), 0, 0, 1);
                _pMatrixTransform->setMatrix(matrix);
                _lastVec3d = vec3d;
            }else{
                _pickEarth = false;
            }
            break;
        case osgGA::GUIEventAdapter::EventType::RELEASE:
            _pickEarth = false;
            break;
    
        default:
            break;
        }
        return true;
    }
    
    bool MyUserPickEventHandler::pick(const double x, const double y, osgViewer::Viewer *pViewer, osg::Vec3dArray *pVec3dArrayOut)
    {
        bool ret = false;
        // 判断场景
        if(!pViewer->getSceneData())
        {
            return false;
        }
        // 判断是否拾取到物体
        osgUtil::LineSegmentIntersector::Intersections intersections;
        if(pViewer->computeIntersections(x, y, intersections))
        {
            for(auto iter = intersections.begin();
                iter != intersections.end();
                iter++)
            {
                pVec3dArrayOut->push_back(iter->getWorldIntersectPoint());
                ret = true;
            }
        }
        return ret;
    }
    
    bool MyUserPickEventHandler::pick(const double x, const double y, osgViewer::Viewer *pViewer, osg::Node *pNode, osg::Vec3dArray *pVec3dArrayOut)
    {
        bool ret = false;
        // 判断场景
        if(!pViewer->getSceneData())
        {
            return false;
        }
        // 判断是否拾取到物体
        osgUtil::LineSegmentIntersector::Intersections intersections;
        if(pViewer->computeIntersections(x, y, intersections))
        {
            for(auto iter = intersections.begin();
                iter != intersections.end();
                iter++)
            {
                for(int index = 0; index < iter->nodePath.size();  index++)
                {
                    if(iter->nodePath.at(index)->asNode() == pNode)
                    {
                        pVec3dArrayOut->push_back(iter->getWorldIntersectPoint());
                        ret = true;
                        break;
                    }
                }
                break;
            }
        }
        return ret;
    }
    
    osg::Vec3d MyUserPickEventHandler::screen2Word(osg::Vec3d screenVec3d, osgViewer::Viewer *pViewer)
    {
        osg::ref_ptr<osg::Camera> pCamera = pViewer->getCamera();
        osg::Matrix matrix = pCamera->getViewMatrix() *
                             pCamera->getProjectionMatrix() *
                             pCamera->getViewport()->computeWindowMatrix();
        osg::Matrix intertMatrix = osg::Matrix::inverse(matrix);
        osg::Vec3d worldVec3d = screenVec3d * intertMatrix;
        return worldVec3d;
    }
    
    float MyUserPickEventHandler::getZoomOutStep() const
    {
        return _zoomOutStep;
    }
    
    void MyUserPickEventHandler::setZoomOutStep(float zoomOutStep)
    {
        _zoomOutStep = zoomOutStep;
    }
    

    原博主博客地址:https://blog.csdn.net/qq21497936
    原博主博客导航:https://blog.csdn.net/qq21497936/article/details/102478062
    本文章博客地址:https://blog.csdn.net/qq21497936/article/details/105372492

  • 相关阅读:
    第一节:SpringMVC概述
    SpringMVC【目录】
    Windows 系统快速查看文件MD5
    (error) ERR wrong number of arguments for 'hmset' command
    hive使用遇到的问题 cannot recognize input
    Overleaf支持的部分中文字体预览
    Understanding and Improving Fast Adversarial Training
    Django2实战示例 第十三章 上线
    Django2实战示例 第十二章 创建API
    Django2实战示例 第十一章 渲染和缓存课程内容
  • 原文地址:https://www.cnblogs.com/qq21497936/p/13179432.html
Copyright © 2011-2022 走看看