• 项目实战: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

  • 相关阅读:
    2016九大前端必备动画库
    关于页面跳转,登录刷新
    关于换行
    c++ vector 的使用
    c++ namespace的使用
    u盘文件系统故障的修复方法
    nfs的使用
    ubuntu 无声音的解决
    Yii 视图中的 $this
    Apache vhost
  • 原文地址:https://www.cnblogs.com/qq21497936/p/13179432.html
Copyright © 2020-2023  润新知