OpenSceneGraph: Integration mit dem Qt Framework



Einführung


Einerseits verfügt die OpenSceneGraph-Engine selbst über ein erweitertes Subsystem zum Verwalten von Fenstern, zum Verarbeiten von Benutzereingabeereignissen sowie zum Senden und Empfangen von Benutzernachrichten. Wir haben in früheren Artikeln dieser Reihe ausführlich darüber gesprochen. Im Allgemeinen reicht dies zusammen mit den Funktionen von C ++ / STL völlig aus, um beliebig komplexe Anwendungen zu entwickeln.

Ein Beispiel für die Integration von OSG in eine von QtDesigner entwickelte Anwendung. Dieses Beispiel wird nachstehend ausführlich erläutert.


Um die Entwicklung in C ++ zu beschleunigen, werden sowohl Bibliotheken von Drittanbietern verwendet, die die Funktionen dieser Sprache erweitern (z. B. Boost), als auch ganze Frameworks, die die Entwicklung plattformübergreifender Anwendungen mit breitem Funktionszweck vereinfachen. Ein solches Framework ist das äußerst beliebte Qt. Unabhängig davon, wie sie Qt für seinen Metaobjekt-Compiler und andere Mängel und Unannehmlichkeiten beschimpft haben, liegt die Stärke von Qt in einer umfangreichen Klassenbibliothek, die alle denkbaren Aufgaben der plattformübergreifenden Entwicklung löst, sowie im Konzept der "Signale - Slots", die das Messaging-Subsystem zwischen Klassen implementieren. Die Signale und Slots basieren auch auf den Interaktionsmethoden zwischen der Anwendung und dem Betriebssystem sowie auf der Interprozesskommunikation.

Und zum Teufel, es wäre sehr interessant, zwei Technologien zu kombinieren: Qt und OSG. Mein Team musste ein ähnliches Problem lösen, über das ich bereits in einer meiner Veröffentlichungen geschrieben habe . Ich möchte diese Frage jedoch etwas weiter öffnen, und dieser Artikel wird sich mit diesem Thema befassen.

Es gibt zwei Möglichkeiten, OSG und Qt zu integrieren:

  1. Verwenden von Qt-Signalen und -Slots zum Interagieren von Objekten in einer OSG-Anwendung
  2. Integration des OSG-Viewers in eine in C ++ / Qt entwickelte grafische Oberfläche, einschließlich der Verwendung des QtDesigner-Formulardesigners

Die erste Option ist anwendbar, wenn Sie die von Qt bereitgestellten GUI-Elemente nicht verwenden müssen, aber die Interaktion von Anwendungskomponenten über Signale und Slots sicherstellen möchten. Beispielsweise bestand für mich die Notwendigkeit, eine OSG-Anwendung mit einer Interprozess-Kommunikationsbibliothek über TCP-Sockets unter Verwendung von Qt zu integrieren.

Die zweite Option wird benötigt, wenn die Integration der OSG-Engine und eine mit Qt entwickelte grafische Anwendung erforderlich sind. Signale und Slots stehen uns zur Verfügung, und zusätzlich dazu die gesamte Palette standardisierter GUI-Elemente, die von Qt bereitgestellt werden.

1. Qt-Signale in einem OSG-Fenstersystem


Das erste Beispiel wird etwas synthetisch sein: Wir schreiben eine einfache OSG-Anwendung mit einer primitiven Szene; Erstellen Sie zwei Klassen, von denen eine Tastenanschläge verarbeitet und die andere der Konsole eine Meldung darüber anzeigt, welche Taste gedrückt wird. In diesem Fall generiert der Handler ein Qt-Signal mit einer Meldung über die gedrückte Taste als Parameter.

Um sich in Qt zu integrieren, reicht es aus, die folgenden drei Bedingungen zu erfüllen

  1. Erben Sie interagierende Klassen von QObject
  2. Organisieren Sie eine Signalverarbeitungsschleife
  3. Erstellen Sie eine Instanz der Klasse QApplication (oder QCoreApplication), die während des Anwendungsvorgangs im Speicher vorhanden ist

Den vollständigen Beispielcode finden Sie hier in meinem OSG-Unterrichts-Repository , das alle Lektionen für diesen Zyklus enthält.

Erstellen Sie zunächst eine Klasse, die die Signalverarbeitungswarteschlange "verdreht".

qt-events.h

#ifndef QT_EVENTS_H #define QT_EVENTS_H #include <osgGA/GUIEventHandler> #include <QCoreApplication> //------------------------------------------------------------------------------ // //------------------------------------------------------------------------------ class QtEventsHandler : public osgGA::GUIEventHandler { public: QtEventsHandler(); virtual bool handle(const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &aa); protected: }; #endif // QT_EVENTS_H 

qt-events.cpp

 #include "qt-events.h" //------------------------------------------------------------------------------ // //------------------------------------------------------------------------------ QtEventsHandler::QtEventsHandler() { } //------------------------------------------------------------------------------ // //------------------------------------------------------------------------------ bool QtEventsHandler::handle(const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &aa) { Q_UNUSED(aa) switch (ea.getEventType()) { case osgGA::GUIEventAdapter::FRAME: { // Process qt signals and event QCoreApplication::processEvents(QEventLoop::AllEvents); break; } default: break; } return false; } 

Alles ist sehr einfach - diese Klasse ist ein Standard-OSG-Ereignishandler, der beim Rendern eines Frames die Verarbeitung der Qt-Signalwarteschlange durch Aufrufen initiiert

 QCoreApplication::processEvents(QEventLoop::AllEvents); 

Erstellen wir nun eine Klasse, die die Tastatur erneut mithilfe des in OSG integrierten Mechanismus verarbeitet, aber dennoch ein Qt-Signal senden kann. Zu diesem Zweck verwenden wir die kürzlich anathematisierte Mehrfachvererbung

keyhandler.h

 #ifndef KEY_HANDLER_H #define KEY_HANDLER_H #include <osgGA/GUIEventHandler> #include <QObject> //------------------------------------------------------------------------------ // //------------------------------------------------------------------------------ class KeyboardHandler : public QObject, public osgGA::GUIEventHandler { Q_OBJECT public: KeyboardHandler(QObject *parent = Q_NULLPTR) : QObject(parent) , osgGA::GUIEventHandler () { } bool handle(const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &aa) { switch (ea.getEventType()) { case osgGA::GUIEventAdapter::KEYDOWN: emit sendMessage("Pressed key " + QString(ea.getKey())); break; default: break; } return false; } signals: void sendMessage(QString msg); private: }; #endif // KEY_HANDLER_H 

Die Klasse verarbeitet eine Nachricht über eine gedrückte Taste und sendet ein Signal mit einer Nachricht, die den Tastencode enthält. Dieses Signal wird von einer Klasse empfangen, die in keiner Weise mit OSG verbunden ist, einem Nachkommen von QObject, das einen einzelnen Steckplatz enthält, der eine Nachricht an die Standardausgabe druckt

Empfänger.h

 #ifndef RECEIVER_H #define RECEIVER_H #include <QObject> #include <iostream> //------------------------------------------------------------------------------ // //------------------------------------------------------------------------------ class Receiver : public QObject { Q_OBJECT public: Receiver(QObject *parent = Q_NULLPTR) : QObject(parent) {} public slots: void printMessage(QString msg) { std::cout << msg.toStdString() << std::endl; } }; #endif // RECEIVER_H 

Fügen Sie nun alles zusammen, indem Sie eine OSG-Anwendung schreiben

main.h

 #ifndef MAIN_H #define MAIN_H #include <osgViewer/Viewer> #include <osgDB/ReadFile> #include <QCoreApplication> #include "qt-events.h" #include "keyhandler.h" #include "receiver.h" #endif 

main.cpp

 #include "main.h" //------------------------------------------------------------------------------ // //------------------------------------------------------------------------------ int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); osg::ref_ptr<osg::Node> scene = osgDB::readNodeFile("../data/cessnafire.osg"); osgViewer::Viewer viewer; viewer.setSceneData(scene.get()); viewer.addEventHandler(new QtEventsHandler); viewer.setUpViewInWindow(0, 0, 1024, 768); KeyboardHandler *keyboardHandler = new KeyboardHandler; Receiver *receiver = new Receiver; QObject::connect(keyboardHandler, &KeyboardHandler::sendMessage, receiver, &Receiver::printMessage); viewer.addEventHandler(keyboardHandler); return viewer.run(); } 

Zunächst erstellen wir eine Instanz der QCoreApplication-Klasse.

 QCoreApplication app(argc, argv); 

Dies ist erforderlich, damit die beschriebene Technologie funktioniert. Die QCoreApplication :: exec () -Methode wird jedoch nicht aufgerufen ! Stattdessen dreht sich die Signalverarbeitungsschleife in unserer osgViewer :: Viewer :: run () -Schleife, für die wir den entsprechenden Handler erstellen und registrieren

 viewer.addEventHandler(new QtEventsHandler); 

Wir erstellen Instanzen von Klassen, die über Qt-Signale interagieren und das Signal der einen mit dem Schlitz der anderen verbinden

 KeyboardHandler *keyboardHandler = new KeyboardHandler; Receiver *receiver = new Receiver; QObject::connect(keyboardHandler, &KeyboardHandler::sendMessage, receiver, &Receiver::printMessage); 

Registrieren Sie den Tastaturhandler

 viewer.addEventHandler(keyboardHandler); 

Alle führen den Viewer aus

 return viewer.run(); 

und so ein Bild sehen


Ja, das Beispiel ist etwas erfunden, aber es zeigt die Hauptprinzipien der Integration von Code mithilfe von Qt-Mechanismen in eine Anwendung mithilfe von OSG. Diese Idee, die aus dem OpenSceneGraph 3.-Kochbuch stammt , hat mir und meinem Entwicklungsteam viel Zeit und Nerven gespart und mir ermöglicht, ein Qt-basiertes Modul zu verwenden, das in unserer Codebasis in unserer Codebasis debuggt und standardisiert wurde.

Aber was ist, wenn wir OSG weiterhin in einer Qt-GUI-Anwendung verwenden möchten?

2. osgQt Bibliothek


osgQt ist eine Integrationsbibliothek für:

  1. Einbetten einer in OSG implementierten 3D-Szene in die grafische Oberfläche einer auf Qt entwickelten Anwendung
  2. Einbetten von Qt-Widgets in eine 3D-Geometrieoberfläche innerhalb einer OSG-Szene. Ja, Sie haben richtig gehört - Qt-Widgets können in der virtuellen Welt leise arbeiten. Eines Tages werde ich es definitiv demonstrieren

Es gab bestimmte Probleme mit dieser Bibliothek, die wir überwinden konnten, indem wir die beigefügten Beispiele sorgfältig studierten und das bereits erwähnte OpenSceneGraph 3. Kochbuch lasen

Die Bibliothek sollte zusammengebaut werden, und dieser Vorgang ähnelt dem Zusammenbau des Motors selbst, der im allerersten Artikel des Zyklus ausführlich beschrieben wird . Die einzige Bemerkung ist, dass -DCMAKE_INSTALL_PREFIX mit demselben ausgewählt werden sollte, der beim Erstellen der Engine angegeben wurde. Daher wird osgQt neben der Engine installiert und kann während der Entwicklung bequem verwendet werden.

3. Integration von osgViewer :: Viewer in die Qt-GUI


Das folgende Beispiel ist sehr nützlich. Wir werden einen Viewer schreiben, mit dem Sie Modelle im * .osg-Format mit Standard-Qt-Steuerelementen laden können. Darüber hinaus verwenden wir zur Entwicklung einer grafischen Oberfläche QtDeisgner.

Lassen Sie uns ein neues Projekt wie "Qt Widgets Application" erstellen.



In diesem Fall wird das Hauptanwendungsfenster mit einer Menüleiste, einer Symbolleiste und einer Statusleiste generiert. Fügen Sie in QtDesigner diesem Fenster eine QFrame-Komponente hinzu.



Wir werden den OSG-Viewer in diesem Rahmen platzieren. Der OSG-Viewer wird im Wesentlichen ein Qt-Widget sein. Für seine Implementierung schreiben wir die QViewerWidget-Klasse. Ich werde die vollständigen Quellen auf den Spoiler setzen, um die Präsentation nicht mit Codeblättern zu verwischen

qviewerwidget.h
 #ifndef QVIEWER_WIDGET_H #define QVIEWER_WIDGET_H #include <QWidget> #include <osgViewer/Viewer> #include <osgQt/GraphicsWindowQt> //------------------------------------------------------------------------------ // //------------------------------------------------------------------------------ class QViewerWidget : public QWidget { public: QViewerWidget(const QRect &geometry); virtual ~QViewerWidget(); osg::Group *getScene(); osgViewer::Viewer *getViewer(); protected: osg::ref_ptr<osg::Group> scene; osgViewer::Viewer viewer; private: osgQt::GraphicsWindowQt *createGraphicsWindow(const QRect &geometry); void initCamera(const QRect &geometry); void paintEvent(QPaintEvent *); }; #endif // QVIEWER_WIDGET_H 


qviewerwidget.cpp
 include "qviewerwidget.h" #include <osgViewer/ViewerEventHandlers> #include <osgGA/TrackballManipulator> #include <QGridLayout> //------------------------------------------------------------------------------ // //------------------------------------------------------------------------------ QViewerWidget::QViewerWidget(const QRect &geometry) : QWidget() , scene(new osg::Group) { initCamera(geometry); viewer.setSceneData(scene); viewer.addEventHandler(new osgViewer::StatsHandler); viewer.setCameraManipulator(new osgGA::TrackballManipulator); viewer.setThreadingModel(osgViewer::Viewer::SingleThreaded); osgQt::GraphicsWindowQt *gw = static_cast<osgQt::GraphicsWindowQt *>(viewer.getCamera()->getGraphicsContext()); QGridLayout *layout = new QGridLayout; if (layout != Q_NULLPTR) { layout->addWidget(gw->getGLWidget()); this->setLayout(layout); } } //------------------------------------------------------------------------------ // //------------------------------------------------------------------------------ QViewerWidget::~QViewerWidget() { } //------------------------------------------------------------------------------ // //------------------------------------------------------------------------------ osg::Group *QViewerWidget::getScene() { return scene.get(); } //------------------------------------------------------------------------------ // //------------------------------------------------------------------------------ osgViewer::Viewer *QViewerWidget::getViewer() { return &viewer; } //------------------------------------------------------------------------------ // //------------------------------------------------------------------------------ osgQt::GraphicsWindowQt *QViewerWidget::createGraphicsWindow(const QRect &geometry) { osg::DisplaySettings *ds = osg::DisplaySettings::instance().get(); osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits; traits->windowName = ""; traits->windowDecoration = false; traits->x = geometry.x(); traits->y = geometry.y(); traits->width = geometry.width(); traits->height = geometry.height(); if (traits->height == 0) traits->height = 1; traits->doubleBuffer = true; traits->alpha = ds->getMinimumNumAlphaBits(); traits->stencil = ds->getMinimumNumStencilBits(); traits->sampleBuffers = ds->getMultiSamples(); traits->samples = ds->getNumMultiSamples(); return new osgQt::GraphicsWindowQt(traits.get()); } //------------------------------------------------------------------------------ // //------------------------------------------------------------------------------ void QViewerWidget::initCamera(const QRect &geometry) { osg::Camera *camera = viewer.getCamera(); osg::ref_ptr<osgQt::GraphicsWindowQt> gw = createGraphicsWindow(geometry); gw->setTouchEventsEnabled(true); camera->setGraphicsContext(gw.get()); const osg::GraphicsContext::Traits *traits = gw->getTraits(); camera->setClearColor(osg::Vec4(0.7f, 0.7f, 0.7f, 1.0f)); camera->setViewport(0, 0, traits->width, traits->height); double aspect = static_cast<double>(traits->width) / static_cast<double>(traits->height); camera->setProjectionMatrixAsPerspective(30.0, aspect, 1.0, 1000.0); } //------------------------------------------------------------------------------ // //------------------------------------------------------------------------------ void QViewerWidget::paintEvent(QPaintEvent *) { viewer.frame(); } 


Die Hauptidee der Implementierung ist die Verwendung der osgQt :: GraphicsWindow-Klasse, die ein Grafikfenster basierend auf der QGLWidget-Klasse erstellt. Verwenden Sie die Methode, um dieses Fenster zu erstellen

 osgQt::GraphicsWindowQt *QViewerWidget::createGraphicsWindow(const QRect &geometry) { osg::DisplaySettings *ds = osg::DisplaySettings::instance().get(); osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits; traits->windowName = ""; traits->windowDecoration = false; traits->x = geometry.x(); traits->y = geometry.y(); traits->width = geometry.width(); traits->height = geometry.height(); if (traits->height == 0) traits->height = 1; traits->doubleBuffer = true; traits->alpha = ds->getMinimumNumAlphaBits(); traits->stencil = ds->getMinimumNumStencilBits(); traits->sampleBuffers = ds->getMultiSamples(); traits->samples = ds->getNumMultiSamples(); return new osgQt::GraphicsWindowQt(traits.get()); } 

Das Fenster wird gemäß den an die Eingabe übertragenen Geometrieparametern und den erforderlichen Einstellungen des OSG 3D-Renderings konfiguriert. Der zurückgegebene Zeiger ist der OSG-Grafikkontext, der an die Kamera übergeben werden soll. Daher besteht der nächste Schritt darin, die Kamera zu initialisieren

 void QViewerWidget::initCamera(const QRect &geometry) { osg::Camera *camera = viewer.getCamera(); osg::ref_ptr<osgQt::GraphicsWindowQt> gw = createGraphicsWindow(geometry); gw->setTouchEventsEnabled(true); camera->setGraphicsContext(gw.get()); const osg::GraphicsContext::Traits *traits = gw->getTraits(); camera->setClearColor(osg::Vec4(0.7f, 0.7f, 0.7f, 1.0f)); camera->setViewport(0, 0, traits->width, traits->height); double aspect = static_cast<double>(traits->width) / static_cast<double>(traits->height); camera->setProjectionMatrixAsPerspective(30.0, aspect, 1.0, 1000.0); } 

Der eigentliche Anruf

 camera->setGraphicsContext(gw.get()); 

und übergibt den gewünschten Kontext, der dem QGLWidget-Widget zugeordnet ist, an die Kamera. Wir haben die gesamte Widget-Routine in den Klassenkonstruktor eingefügt

 QViewerWidget::QViewerWidget(const QRect &geometry) : QWidget() , scene(new osg::Group) { initCamera(geometry); viewer.setSceneData(scene); viewer.addEventHandler(new osgViewer::StatsHandler); viewer.setCameraManipulator(new osgGA::TrackballManipulator); viewer.setThreadingModel(osgViewer::Viewer::SingleThreaded); osgQt::GraphicsWindowQt *gw = static_cast<osgQt::GraphicsWindowQt *>(viewer.getCamera()->getGraphicsContext()); QGridLayout *layout = new QGridLayout; if (layout != Q_NULLPTR) { layout->addWidget(gw->getGLWidget()); this->setLayout(layout); } } 

Hier konfigurieren wir den Viewer und achten besonders auf die Herausforderung

 viewer.setThreadingModel(osgViewer::Viewer::SingleThreaded); 

Umschalten des Viewers in den Single-Threaded-Modus. Dies ist eine notwendige Maßnahme bei der Integration von OSG in Qt, da das Programm bei einigen Linux-Distributionen abstürzt, wenn das von OSG standardmäßig verwendete Multithread-Rendering verwendet wird. Die Gründe hierfür erfordern eine separate Nachbesprechung. Beachten Sie daher diesen Code

 osgQt::GraphicsWindowQt *gw = static_cast<osgQt::GraphicsWindowQt *>(viewer.getCamera()->getGraphicsContext()); QGridLayout *layout = new QGridLayout; if (layout != Q_NULLPTR) { layout->addWidget(gw->getGLWidget()); this->setLayout(layout); } 

In diesem Bereich erstellen wir eine Ebene, die ein vom Grafikkontext der Kamera zurückgegebenes QGLWidget stört und in einen osgQt :: GraphicsWindows-Zeiger konvertiert wird. Wir fügen die erstellte Ebene unserem Widget QViewerWidget durch Aufrufen hinzu

 this->setLayout(layout); 

Damit unser Widget und damit die Szene aktualisiert werden, wenn das Fenster aktualisiert wird, müssen Sie den QPaintEvent-Ereignishandler neu definieren

 void QViewerWidget::paintEvent(QPaintEvent *) { viewer.frame(); } 

In diesem Fall initiieren wir das Rendern des Frames durch Aufrufen der Methode osgViewer :: Viewer :: frame ().

Ok, der Code unseres Widgets ist fertig, jetzt binden wir ihn in einen Rahmen ein, der sich auf dem Formular befindet. Dazu schreiben wir im Konstruktor der MainWindow-Klasse solchen Code

 MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) , qviewer(Q_NULLPTR) { ui->setupUi(this); QGridLayout *layout = new QGridLayout; qviewer = new QViewerWidget(QRect(0, 0, ui->frame->width(), ui->frame->height())); layout->addWidget(qviewer); ui->frame->setLayout(layout); this->setCentralWidget(ui->frame); connect(&timer, &QTimer::timeout, this, &MainWindow::update); timer.start(40); connect(ui->actionQuit, &QAction::triggered, this, &MainWindow::quit); connect(ui->actionClean, &QAction::triggered, this, &MainWindow::clean); connect(ui->actionOpen, &QAction::triggered, this, &MainWindow::open); this->setWindowTitle("QViewerWidget example"); } 

oder besser gesagt, wir sind immer noch an diesem Teil interessiert

 QGridLayout *layout = new QGridLayout; qviewer = new QViewerWidget(QRect(0, 0, ui->frame->width(), ui->frame->height())); layout->addWidget(qviewer); ui->frame->setLayout(layout); this->setCentralWidget(ui->frame); 

Wenn Sie eine Ebene erstellen, erstellen Sie unser Widget mit Abmessungen, die der Größe des Rahmens entsprechen, fügen Sie das erstellte Widget der Ebene hinzu und hängen Sie die Ebene an den Rahmen an. Um das Layout in diesem Beispiel nicht zu beeinträchtigen, dehnen wir den Rahmen auf den gesamten Client-Bereich des Fensters aus und machen ihn zum zentralen Widget.

Zum Rendern sollten Sie eine regelmäßige Fensteraktualisierung nach Timer veranlassen. Erstellen Sie dazu einen Timer mit einem Intervall von 40 Millisekunden (25 Bilder pro Sekunde) und ordnen Sie das Zeitlimitsignal dem Fensteraktualisierungssteckplatz zu. Ich mache es so mit Qt5-Syntax

 connect(&timer, &QTimer::timeout, this, &MainWindow::update); timer.start(40); 

zuvor den Aktualisierungssteckplatz für die Fensterklasse auf diese Weise definiert haben

 void MainWindow::update() { QMainWindow::update(this->geometry()); } 

Warum, weil Sie das Timer-Signal direkt mit dem QMainWindow :: Update-Slot verknüpfen können, wie es die meisten osgQt-Beispiele zeigen

 connect(&timer, SIGNAL(timeout), this, SLOT(update)); 

Tatsache ist, dass die Syntax mit den Makros SIGNAL () und SLOT () veraltet ist und im Vorgriff auf den Übergang zu Qt6 aufgegeben werden muss. Gleichzeitig hat die QMainWindow-Klasse keine Überladung des update () - Slots ohne Parameter, was zu einem Fehler beim Bind-Aufruf während der Kompilierung führt. Dazu musste ich meinen update () - Slot ohne Parameter definieren, die Basis QMainWindow :: update () darin aufrufen und dort den Client-Bereich des Fensters übergeben.

Wenn Sie diesen Ort ergänzen und das Programm ausführen, erhalten Sie ein bestimmtes Ergebnis



Durch Drücken von "S" können wir den OSG-Statistikmonitor aktivieren und sicherstellen, dass unser Widget ordnungsgemäß funktioniert und eine leere Szene zeichnet.

Welche Art von Statistikmonitor?
Um den Artikel nicht zu überladen, werde ich hier darüber schreiben. OSG verfügt über einen integrierten Monitor, der Motorstatistiken in Echtzeit anzeigt. Um es dem Viewer hinzuzufügen, fügen wir die Header-Datei hinzu

 #include <osgViewer/ViewerEventHandlers> 

und fügen Sie dem Viewer einen Handler hinzu

 viewer.addEventHandler(new osgViewer::StatsHandler); 

dann jederzeit durch Drücken von "S", um viele nützliche Informationen anzuzeigen.

4. Beenden Sie unseren Viewer: Fügen Sie ein Menü hinzu


Im Formular-Designer passen wir das Menü mithilfe der "mausorientierten" Programmierung an (was mir gleichgültig ist, aber ja, manchmal ist es praktisch). Am Ende werden wir so etwas bekommen



Jetzt starten wir die entsprechenden Prozessor-Slots, sodass Sie das Modell über den im Dialogfeld ausgewählten Pfad laden, die Szene löschen und die Anwendung beenden können

Menü-Handler-Code
 //------------------------------------------------------------------------------ // //------------------------------------------------------------------------------ void MainWindow::open() { osg::Group *scene = qviewer->getScene(); if (scene == nullptr) return; QString path = QFileDialog::getOpenFileName(Q_NULLPTR, tr("Open model file"), "./", tr("OpenSceneGraph (*.osg *.osgt *.osgb *.ivi)")); if (path.isEmpty()) return; scene->removeChildren(0, scene->getNumChildren()); osg::ref_ptr<osg::Node> model = osgDB::readNodeFile(path.toStdString()); scene->addChild(model.get()); } //------------------------------------------------------------------------------ // //------------------------------------------------------------------------------ void MainWindow::clean() { osg::Group *scene = qviewer->getScene(); if (scene == nullptr) return; scene->removeChildren(0, scene->getNumChildren()); } //------------------------------------------------------------------------------ // //------------------------------------------------------------------------------ void MainWindow::quit() { QApplication::quit(); } 


Danach erhalten wir einen sehr praktischen Viewer für Modelle im * .osg-Format.



Eine Demonstration seiner Arbeit zeigt das Video am Anfang des Artikels. Der vollständige Quellcode für dieses Beispiel ist hier verfügbar.

Fazit


Wie wir gesehen haben, ist die Integration von OSG und Qt weder beim Verständnis noch bei der Implementierung besonders schwierig. Dies ist eine große Hilfe beim Erstellen plattformübergreifender Anwendungen für die technische Visualisierung und möglicherweise für Spiele.

Dieser Artikel eröffnet eine Fortsetzung der OSG-Reihe, in der komplexe Entwicklungstechniken beschrieben werden. Ich denke, sie ist erfolgreich herausgekommen. Vielen Dank und bis bald!

Source: https://habr.com/ru/post/de438654/


All Articles