Multiple views with OsgQtQuick

I wrote a sample application using OsgQtQuick that shows the Earth in two views:

with the following QML, that I copied from OsgQtQuick samples:

ApplicationWindow {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")
    id: window

    menuBar: MenuBar {
        Menu {
            title: qsTr("File")
            MenuItem {
                text: qsTr("&Open3")
                iconSource: ":/images/Test32x32.png"
                onTriggered: console.log("Open action triggered");
            }
            MenuItem {
                text: qsTr("Exit")
                onTriggered: Qt.quit();
            }
        }
    }

    OSG.Notify {
        id: notifier
        notifyLevel: OSG.Notify.WARN
        onNotify: console.log(message);
    }

    OSGDB.Loader {
        id: loader
        objectName: "loader"
        source: "file:///home/dmitry/examples/osgearth/tests/openstreetmap.earth"
        onNodeChanged: {
            console.log("loader.node: " + getNode())
            leftView.cameraManipulator.home()
            rightView.cameraManipulator.home()
        }
        onStatusChanged: {
            switch(status)
            {
            case OSGDB.Loader.Null:
                statusLabel.text = "";
                break;
            case OSGDB.Loader.Loading:
                statusLabel.text = "Loading...";
                break;
            case OSGDB.Loader.Ready:
                statusLabel.text = "Ready";
                statusLabel.visible = false;
                break;
            case OSGDB.Loader.Error:
                statusLabel.text = "Error";
                break;
            }
        }
    }

    GridLayout {

        columns: 2

        rowSpacing: 2
        columnSpacing: 2

        anchors.fill: parent
        anchors.margins: 2

        OSGViewer.View {
            id: leftView
            objectName: "leftView"
            Text {
                text: "View 1"
                color: "white"
                anchors.bottom: parent.bottom
            }
            cameraManipulator: OSGEarthUtil.EarthManipulator {}
            sceneData: loader
            Layout.fillHeight: true
            Layout.fillWidth: true
        }

        OSGViewer.View {
            id: rightView
            objectName: "rightView"
            Text {
                text: "View 2"
                color: "white"
                anchors.bottom: parent.bottom
            }
            cameraManipulator: OSGEarthUtil.EarthManipulator {}
            sceneData: loader
            Layout.fillHeight: true
            Layout.fillWidth: true
        }
    }

    Text {
        id: statusLabel
        text: "Starting..."
        color: "white"
        font.pointSize: 24
        anchors {
            centerIn: parent
            horizontalCenter: parent.horizontalCenter
        }
    }

    Text {
        text: "Both view should be identical"
        color: "white"
        anchors {
            top: parent.top
            horizontalCenter: parent.horizontalCenter
        }
    }

//    Component.onCompleted: {
//        leftView.cameraManipulator.home()
//        rightView.cameraManipulator.home()
//    }
}

It loads the Earth file from QML and adds some polygon with an annotation over the map in C++:

#include <osg/Node>
#include <osgEarth/MapNode>
#include <osgEarthSymbology/LineSymbol>
#include <osgEarthAnnotation/LabelNode>
#include <osgEarthAnnotation/FeatureNode>
#include <osgEarthFeatures/Feature>
#include <osg/NodeQtQml>
#include <osgDB/LoaderQtQml>

void addAnnotation(osgEarth::MapNode * mapNode)
{
    using namespace osgEarth;
    using namespace osgEarth::Symbology;
    using namespace osgEarth::Annotation;
    using namespace osgEarth::Features;

    auto root = mapNode;

    // Group to hold all our annotation elements.
    osg::Group* annoGroup = new osg::Group();
    root->addChild( annoGroup );

    // Make a group for labels
    osg::Group* labelGroup = new osg::Group();
    annoGroup->addChild( labelGroup );

    // Style our labels:
    Style labelStyle;
    labelStyle.getOrCreate<TextSymbol>()->alignment() = TextSymbol::ALIGN_CENTER_CENTER;
    labelStyle.getOrCreate<TextSymbol>()->fill()->color() = Color::Yellow;

    // A lat/long SRS for specifying points.
    const SpatialReference* geoSRS = mapNode->getMapSRS()->getGeographicSRS();

    // a box that follows lines of latitude (rhumb line interpolation, the default)
    {
        Geometry* geom = new Polygon();
        geom->push_back( osg::Vec3d(0,   40, 0) );
        geom->push_back( osg::Vec3d(-60, 40, 0) );
        geom->push_back( osg::Vec3d(-60, 60, 0) );
        geom->push_back( osg::Vec3d(0,   60, 0) );

        Feature* feature = new Feature(geom, geoSRS);
        feature->geoInterp() = GEOINTERP_RHUMB_LINE;

        Style geomStyle;
        geomStyle.getOrCreate<LineSymbol>()->stroke()->color() = Color::Cyan;
        geomStyle.getOrCreate<LineSymbol>()->stroke()->width() = 5.0f;
        geomStyle.getOrCreate<LineSymbol>()->tessellationSize() = 75000;
        geomStyle.getOrCreate<AltitudeSymbol>()->clamping() = AltitudeSymbol::CLAMP_TO_TERRAIN;
        geomStyle.getOrCreate<AltitudeSymbol>()->technique() = AltitudeSymbol::TECHNIQUE_GPU;

        FeatureNode* fnode = new FeatureNode(mapNode, feature, geomStyle);

        annoGroup->addChild( fnode );

        labelGroup->addChild( new LabelNode(mapNode, GeoPoint(geoSRS,-30, 50), "Rhumb line polygon", labelStyle) );
    }
}

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    QApplication::setWindowIcon(QIcon(":/images/app.ico"));

    QQmlApplicationEngine engine;
    engine.addImportPath("/home/dmitry/examples/install/qml");
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    QObject * topObject = engine.rootObjects().value(0);
    QQuickWindow * window = qobject_cast<QQuickWindow *>(topObject);

    if (window != nullptr)
    {
        qDebug("Main window has been created successfully.");

        osgDB::LoaderQtQml * loader = window->findChild<osgDB::LoaderQtQml *>("loader");

        if (loader != nullptr)
        {
            qDebug("loader found!.");

            QObject::connect(loader, &osgDB::LoaderQtQml::nodeChanged, &app, [](osg::NodeQtQml* qmlNode)
            {
                osg::Node * node = qmlNode->node();

                qDebug() << "Node has been changed:" << node->className();

                osgEarth::MapNode * mapNode = dynamic_cast<osgEarth::MapNode *>(node);

                if (mapNode != nullptr)
                {
                    qDebug() << "Map Node found!:";

                    addAnnotation(mapNode);
                }

            });

            return app.exec();
        }
        else
        {
            qDebug("loader not found!.");
        }
    }
}

It works not quite correct, because if I rotate the Earth in a view so that the text label (annotation) becomes invisible, the Earth in other view becomes black. See video demonstrating this effect. OSG version is 3.4 and OsgEarth version is 2.8. It is noteworthy that the original OsgEarth sample osgearth_overlayviewer with the same nodes added over the map works fine:

#include <osg/Notify>
#include <osg/Depth>
#include <osg/LineWidth>
#include <osg/LineStipple>
#include <osgGA/StateSetManipulator>
#include <osgGA/AnimationPathManipulator>
#include <osgViewer/CompositeViewer>
#include <osgViewer/ViewerEventHandlers>
#include <osgEarth/OverlayDecorator>
#include <osgEarth/MapNode>
#include <osgEarthUtil/EarthManipulator>
#include <osgEarthUtil/ExampleResources>
#include <osgEarthUtil/Controls>
#include <osgEarthSymbology/Color>

#include <osgEarthSymbology/LineSymbol>
#include <osgEarthAnnotation/LabelNode>
#include <osgEarthAnnotation/FeatureNode>
#include <osgEarthFeatures/Feature>

using namespace osgEarth::Util;
using namespace osgEarth::Util::Controls;
using namespace osgEarth::Symbology;

//------------------------------------------------------------------------

namespace
{
    void addAnnotation(osgEarth::MapNode * mapNode)
    {
        using namespace osgEarth;
        using namespace osgEarth::Symbology;
        using namespace osgEarth::Annotation;
        using namespace osgEarth::Features;

        osg::Group * root = mapNode;

        // Group to hold all our annotation elements.
        osg::Group* annoGroup = new osg::Group();
        root->addChild( annoGroup );

        // Make a group for labels
        osg::Group* labelGroup = new osg::Group();
        annoGroup->addChild( labelGroup );

        // Style our labels:
        Style labelStyle;
        labelStyle.getOrCreate<TextSymbol>()->alignment() = TextSymbol::ALIGN_CENTER_CENTER;
        labelStyle.getOrCreate<TextSymbol>()->fill()->color() = Color::Yellow;

        // A lat/long SRS for specifying points.
        const SpatialReference* geoSRS = mapNode->getMapSRS()->getGeographicSRS();

        // a box that follows lines of latitude (rhumb line interpolation, the default)
        {
            Geometry* geom = new Polygon();
            geom->push_back( osg::Vec3d(0,   40, 0) );
            geom->push_back( osg::Vec3d(-60, 40, 0) );
            geom->push_back( osg::Vec3d(-60, 60, 0) );
            geom->push_back( osg::Vec3d(0,   60, 0) );

            Feature* feature = new Feature(geom, geoSRS);
            feature->geoInterp() = GEOINTERP_RHUMB_LINE;

            Style geomStyle;
            geomStyle.getOrCreate<LineSymbol>()->stroke()->color() = Color::Cyan;
            geomStyle.getOrCreate<LineSymbol>()->stroke()->width() = 5.0f;
            geomStyle.getOrCreate<LineSymbol>()->tessellationSize() = 75000;
            geomStyle.getOrCreate<AltitudeSymbol>()->clamping() = AltitudeSymbol::CLAMP_TO_TERRAIN;
            geomStyle.getOrCreate<AltitudeSymbol>()->technique() = AltitudeSymbol::TECHNIQUE_GPU;

            FeatureNode* fnode = new FeatureNode(mapNode, feature, geomStyle);

            annoGroup->addChild( fnode );

            labelGroup->addChild( new LabelNode(mapNode, GeoPoint(geoSRS,-30, 50), "Rhumb line polygon", labelStyle) );
        }
    }
}

int main(int argc, char** argv)
{
    osg::ArgumentParser arguments(&argc,argv);

    osgViewer::CompositeViewer viewer(arguments);
    viewer.setThreadingModel( osgViewer::CompositeViewer::SingleThreaded );

    // query the screen size.
    osg::GraphicsContext::ScreenIdentifier si;
    si.readDISPLAY();
    if ( si.displayNum < 0 ) si.displayNum = 0; osg::GraphicsContext::WindowingSystemInterface* wsi = osg::GraphicsContext::getWindowingSystemInterface(); unsigned width, height; wsi->getScreenResolution( si, width, height );
    unsigned b = 50;

    osgViewer::View* mainView = new osgViewer::View();
    mainView->getCamera()->setNearFarRatio(0.00002);
    EarthManipulator* em = new EarthManipulator();
    em->getSettings()->setMinMaxPitch(-90, 0);
    mainView->setCameraManipulator( em );
    //mainView->setUpViewInWindow( 50, 50, 600, 600 );
    mainView->setUpViewInWindow( b, b, (width/2)-b*2, (height-b*4) );
    viewer.addView( mainView );

    osgViewer::View* overlayView = new osgViewer::View();
    overlayView->getCamera()->setNearFarRatio(0.00002);
    //overlayView->getCamera()->setProjectionMatrixAsOrtho2D(-1,1,-1,1);
    overlayView->setCameraManipulator( new EarthManipulator() );
    
    //overlayView->setUpViewInWindow( 700, 50, 600, 600 );
    overlayView->setUpViewInWindow( (width/2), b, (width/2)-b*2, (height-b*4) );
    //overlayView->addEventHandler(new osgGA::StateSetManipulator(overlayView->getCamera()->getOrCreateStateSet()));
    viewer.addView( overlayView );

    std::string pathfile;
    double animationSpeed = 1.0;
    if (arguments.read("-p", pathfile))
    {
        mainView->setCameraManipulator( new osgGA::AnimationPathManipulator(pathfile) );
    }

    osg::Node* node = MapNodeHelper().load( arguments, mainView );
    if ( node )
    {
        mainView->setSceneData( node );

        MapNode* mapNode = MapNode::get(node);

        OE_WARN << "Adding annotation." << std::endl; addAnnotation( mapNode ); //osg::Group* group = new osg::Group(); //group->addChild( mapNode );
        overlayView->setSceneData( mapNode );

        //setupOverlayView( overlayView, group, MapNode::get(node) );

        return viewer.run();
    }
    else return -1;
}

Leave a Reply

Your email address will not be published. Required fields are marked *