1
0
Fork 0

QtQuick drawable for OpenSceneGraph

Still work in progress, especially different threading modes are not
supported yet, and may crash doing QSG synchronisation.
This commit is contained in:
James Turner 2017-06-06 17:21:16 +02:00
parent 7824571ec2
commit 45cb6849b2
6 changed files with 409 additions and 12 deletions

View file

@ -66,9 +66,6 @@ if (APPLE)
CocoaHelpers.mm)
endif()
if (HAVE_QT)
qt5_wrap_ui(uic_sources Launcher.ui
EditRatingsFilterDialog.ui
@ -159,10 +156,16 @@ if (HAVE_QT)
set_property(TARGET fglauncher PROPERTY AUTOMOC ON)
target_link_libraries(fglauncher Qt5::Core Qt5::Widgets Qt5::Network Qt5::Qml Qt5::Quick Qt5::QuickWidgets SimGearCore)
target_include_directories(fglauncher PRIVATE ${PROJECT_BINARY_DIR}/src/GUI)
target_include_directories(fglauncher PRIVATE ${PROJECT_BINARY_DIR}/src/GUI
${Qt5Quick_PRIVATE_INCLUDE_DIRS})
# needed for scrollbar helper, unfortunately
target_include_directories(fglauncher PRIVATE ${Qt5Quick_PRIVATE_INCLUDE_DIRS})
add_library(fgqmlui QQuickDrawable.cxx
QQuickDrawable.hxx
)
set_property(TARGET fgqmlui PROPERTY AUTOMOC ON)
target_link_libraries(fgqmlui Qt5::Quick Qt5::Network Qt5::Qml SimGearCore)
target_include_directories(fgqmlui PRIVATE ${PROJECT_BINARY_DIR}/src/GUI)
endif()

337
src/GUI/QQuickDrawable.cxx Normal file
View file

@ -0,0 +1,337 @@
#include "QQuickDrawable.hxx"
#include <QQuickRenderControl>
#include <QQmlComponent>
#include <QQmlEngine>
#include <QQmlContext>
#include <QThread>
#include <QOpenGLContext>
#include <QQuickWindow>
#include <QOpenGLFunctions>
#include <QQuickItem>
#include <QCoreApplication>
#include <osg/GraphicsContext>
#include <osgViewer/GraphicsWindow>
#include <GUI/FGQmlInstance.hxx>
#include <GUI/FGQmlPropertyNode.hxx>
#include <Viewer/GraphicsWindowQt5.hxx>
/**
* Helper run on Graphics thread to retrive its Qt wrapper
*/
class RetriveGraphicsThreadOperation : public osg::GraphicsOperation
{
public:
RetriveGraphicsThreadOperation()
: osg::GraphicsOperation("RetriveGraphcisThread", false)
{}
QThread* thread() const { return _result; }
QOpenGLContext* context() const { return _context; }
virtual void operator () (osg::GraphicsContext* context) override
{
_result = QThread::currentThread();
_context = QOpenGLContext::currentContext();
if (!_context) {
qFatal("Use a Qt-based GraphicsWindow/Context");
// TODO: use ability to create a QOpenGLContext from native
// however, this will also need event-handling to be re-written
//_context = qtContextFromOSG(context);
}
}
private:
QThread* _result = nullptr;
QOpenGLContext* _context = nullptr;
};
#if 0
class SyncPolishOperation : public osg::Operation
{
public:
SyncPolishOperation(QQuickRenderControl* rc) :
renderControl(rc)
{
setKeep(true);
}
virtual void operator () (osg::Object*) override
{
if (syncRequired) {
//syncRequired = false;
}
renderControl->polishItems();
}
bool syncRequired = true;
QQuickRenderControl* renderControl;
};
#endif
class CustomRenderControl : public QQuickRenderControl
{
public:
CustomRenderControl(QWindow* win)
: _qWindow(win)
{
Q_ASSERT(win);
}
QWindow* renderWindow(QPoint* offset) override
{
if (offset) {
*offset = QPoint(0,0);
}
return _qWindow;
}
private:
QWindow* _qWindow = nullptr;
};
class QQuickDrawablePrivate : public QObject
{
Q_OBJECT
public:
CustomRenderControl* renderControl = nullptr;
// the window our osgViewer is using
flightgear::GraphicsWindowQt5* graphicsWindow = nullptr;
QQmlComponent* qmlComponent = nullptr;
QQmlEngine* qmlEngine = nullptr;
bool syncRequired = true;
QQuickItem* rootItem = nullptr;
// this window is neither created()-ed nor shown but is needed by
// QQuickRenderControl for historical reasons, and gives us a place to
// inject events from the OSG side.
QQuickWindow* quickWindow = nullptr;
QOpenGLContext* qtContext = nullptr;
public slots:
void onComponentLoaded()
{
if (qmlComponent->isError()) {
QList<QQmlError> errorList = qmlComponent->errors();
Q_FOREACH (const QQmlError &error, errorList) {
qWarning() << error.url() << error.line() << error;
}
return;
}
QObject *rootObject = qmlComponent->create();
if (qmlComponent->isError()) {
QList<QQmlError> errorList = qmlComponent->errors();
Q_FOREACH (const QQmlError &error, errorList) {
qWarning() << error.url() << error.line() << error;
}
return;
}
rootItem = qobject_cast<QQuickItem *>(rootObject);
if (!rootItem) {
qWarning() << Q_FUNC_INFO << "root object not a QQuickItem" << rootObject;
delete rootObject;
return;
}
// The root item is ready. Associate it with the window.
rootItem->setParentItem(quickWindow->contentItem());
syncRequired = true;
}
void onSceneChanged()
{
syncRequired = true;
}
void onRenderRequested()
{
qWarning() << Q_FUNC_INFO;
}
bool eventFilter(QObject* obj, QEvent* event) override
{
if (obj != graphicsWindow->getGLWindow()) {
return false;
}
const auto eventType = event->type();
switch (eventType) {
case QEvent::MouseMove:
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
case QEvent::MouseButtonDblClick:
case QEvent::Wheel:
case QEvent::KeyPress:
case QEvent::KeyRelease:
{
bool result = QCoreApplication::sendEvent(quickWindow, event);
// result always seems to be true here, not useful
if (result && event->isAccepted()) {
return true;
}
}
default:
break;
}
return false;
}
};
static QObject* fgqmlinstance_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
{
Q_UNUSED(engine)
Q_UNUSED(scriptEngine)
FGQmlInstance* instance = new FGQmlInstance;
return instance;
}
QQuickDrawable::QQuickDrawable() :
d(new QQuickDrawablePrivate)
{
setUseDisplayList(false);
setDataVariance(Object::DYNAMIC);
osg::StateSet* stateSet = getOrCreateStateSet();
stateSet->setRenderBinDetails(1001, "RenderBin");
#if 0
// speed optimization?
stateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
// We can do translucent menus, so why not. :-)
stateSet->setAttribute(new osg::BlendFunc(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE_MINUS_SRC_ALPHA));
stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
stateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::OFF);
stateSet->setTextureAttribute(0, new osg::TexEnv(osg::TexEnv::MODULATE));
stateSet->setMode(GL_FOG, osg::StateAttribute::OFF);
stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
#endif
static bool doneQmlRegistration = false;
if (!doneQmlRegistration) {
doneQmlRegistration = true;
// singleton system object
qmlRegisterSingletonType<FGQmlInstance>("flightgear.org", 1, 0, "System", fgqmlinstance_provider);
// QML types
qmlRegisterType<FGQmlPropertyNode>("flightgear.org", 1, 0, "Property");
}
}
QQuickDrawable::~QQuickDrawable()
{
delete d->qmlEngine;
delete d->renderControl;
delete d;
}
void QQuickDrawable::setup(osgViewer::GraphicsWindow* gw)
{
osg::GraphicsContext* gc = gw;
// install operations on the graphics context to get the context
osg::ref_ptr<RetriveGraphicsThreadOperation> op(new RetriveGraphicsThreadOperation);
gc->add(op);
gc->runOperations();
// hopefully done now!
d->qtContext = op->context();
d->graphicsWindow = dynamic_cast<flightgear::GraphicsWindowQt5*>(gw);
if (!d->graphicsWindow) {
qFatal("QQuick drawable requires a GraphicsWindowQt5 for now");
}
d->renderControl = new CustomRenderControl(d->graphicsWindow->getGLWindow());
d->quickWindow = new QQuickWindow(d->renderControl);
d->quickWindow->setClearBeforeRendering(false);
d->qmlEngine = new QQmlEngine;
if (!d->qmlEngine->incubationController())
d->qmlEngine->setIncubationController(d->quickWindow->incubationController());
d->renderControl->initialize(d->qtContext);
d->renderControl->prepareThread(op->thread());
QObject::connect(d->renderControl, &QQuickRenderControl::sceneChanged,
d, &QQuickDrawablePrivate::onSceneChanged);
QObject::connect(d->renderControl, &QQuickRenderControl::renderRequested,
d, &QQuickDrawablePrivate::onRenderRequested);
// insert ourselves as a filter on the GraphicsWindow, so we can intercept
// events directly and pass on to our window
d->graphicsWindow->getGLWindow()->installEventFilter(d);
}
void QQuickDrawable::drawImplementation(osg::RenderInfo& renderInfo) const
{
if (d->syncRequired) {
// should happen on the main thread
d->renderControl->polishItems();
d->syncRequired = false;
// must ensure the GUI thread is blocked for this
d->renderControl->sync();
// can unblock now
}
QOpenGLFunctions* glFuncs = d->qtContext->functions();
// prepare any state QQ2 needs
d->quickWindow->resetOpenGLState();
// and reset these manually
glFuncs->glPixelStorei(GL_PACK_ALIGNMENT, 4);
glFuncs->glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glFuncs->glPixelStorei(GL_PACK_ROW_LENGTH, 0);
glFuncs->glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
d->renderControl->render();
glFuncs->glFlush();
}
void QQuickDrawable::setSource(QUrl url)
{
d->qmlComponent = new QQmlComponent(d->qmlEngine, url);
if (d->qmlComponent->isLoading()) {
QObject::connect(d->qmlComponent, &QQmlComponent::statusChanged,
d, &QQuickDrawablePrivate::onComponentLoaded);
} else {
d->onComponentLoaded();
}
}
void QQuickDrawable::resize(int width, int height)
{
// we need to unscale from physical pixels back to logical, otherwise we end up double-scaled.
const float currentPixelRatio = d->graphicsWindow->getGLWindow()->devicePixelRatio();
const int logicalWidth = static_cast<int>(width / currentPixelRatio);
const int logicalHeight = static_cast<int>(height / currentPixelRatio);
if (d->rootItem) {
d->rootItem->setWidth(logicalWidth);
d->rootItem->setHeight(logicalHeight);
}
d->quickWindow->setGeometry(0, 0, logicalWidth, logicalHeight);
}
#include "QQuickDrawable.moc"

View file

@ -0,0 +1,36 @@
#ifndef FG_VIEWER_QUICK_DRAWABLE_HXX
#define FG_VIEWER_QUICK_DRAWABLE_HXX
#include <osg/Drawable>
#include <QUrl>
#include <osgViewer/GraphicsWindow>
class QQuickDrawablePrivate;
class QQuickDrawable : public osg::Drawable
{
public:
QQuickDrawable();
virtual ~QQuickDrawable();
virtual osg::Object* cloneType() const { return 0; }
virtual osg::Object* clone(const osg::CopyOp& copyop) const { return 0; }
void drawImplementation(osg::RenderInfo& renderInfo) const override;
/** Return true, FGPanelNode does support accept(PrimitiveFunctor&). */
// virtual bool supports(const osg::PrimitiveFunctor&) const { return true; }
//virtual void accept(osg::PrimitiveFunctor& functor) const;
void setup(osgViewer::GraphicsWindow* gw);
void setSource(QUrl url);
void resize(int width, int height);
private:
QQuickDrawablePrivate* d;
};
#endif

View file

@ -184,6 +184,7 @@ endif()
if (Qt5Core_FOUND)
target_link_libraries(fgfs Qt5::Widgets fglauncher)
set_property(TARGET fgfs PROPERTY AUTOMOC ON)
target_link_libraries(fgfs fglauncher fgqmlui)
endif()
if(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")

View file

@ -112,6 +112,9 @@
#include "CameraGroup.hxx"
#include "FGEventHandler.hxx"
#include <GUI/QQuickDrawable.hxx>
#include <Viewer/GraphicsWindowQt5.hxx>
#include <plib/pu.h>
using namespace osg;
@ -445,6 +448,7 @@ FGRenderer::preinit( void )
_viewerSceneRoot->setName("viewerSceneRoot");
viewer->setSceneData(_viewerSceneRoot);
_quickDrawable = nullptr;
_splash = new SplashScreen;
if (_classicalRenderer) {
_viewerSceneRoot->addChild(_splash);
@ -1540,13 +1544,24 @@ FGRenderer::setupView( void )
// plug in the GUI
osg::Camera* guiCamera = getGUICamera(CameraGroup::getDefault());
if (guiCamera) {
osg::Geode* geode = new osg::Geode;
geode->addDrawable(new SGHUDDrawable);
geode->addDrawable(new SGPuDrawable);
osg::Geode* geode = new osg::Geode;
geode->addDrawable(new SGHUDDrawable);
geode->addDrawable(new SGPuDrawable);
// Draw first (eg. before Canvas GUI)
guiCamera->insertChild(0, geode);
guiCamera->insertChild(0, FGPanelNode::create2DPanelNode());
std::string rootQMLPath = fgGetString("/sim/gui/qml-root-path");
auto graphicsWindowQt = dynamic_cast<GraphicsWindowQt5*>(guiCamera->getGraphicsContext());
if (graphicsWindowQt && !rootQMLPath.empty()) {
_quickDrawable = new QQuickDrawable;
_quickDrawable->setup(graphicsWindowQt);
_quickDrawable->setSource(QUrl(QString::fromStdString(rootQMLPath)));
geode->addDrawable(_quickDrawable);
}
// Draw first (eg. before Canvas GUI)
guiCamera->insertChild(0, geode);
guiCamera->insertChild(0, FGPanelNode::create2DPanelNode());
}
osg::Switch* sw = new osg::Switch;
@ -1731,6 +1746,9 @@ FGRenderer::resize( int width, int height )
// update splash node if present ?
_splash->resize(width, height);
if (_quickDrawable) {
_quickDrawable->resize(width, height);
}
}
typedef osgUtil::LineSegmentIntersector::Intersection Intersection;

View file

@ -44,6 +44,7 @@ class CameraGroup;
class SGSky;
class SGUpdateVisitor;
class SplashScreen;
class QQuickDrawable;
typedef std::vector<SGSceneryPick> PickList;
@ -191,6 +192,7 @@ protected:
void setupRoot();
SplashScreen* _splash;
QQuickDrawable* _quickDrawable;
};
bool fgDumpSceneGraphToFile(const char* filename);