Merged in f5soh/librepilot/LP-575_fedora_package (pull request #491)
[librepilot.git] / ground / gcs / src / libs / osgearth / osgQtQuick / OSGViewport.cpp
bloba01a47280d96c54172abc93a86feef764abe4d43
1 /**
2 ******************************************************************************
4 * @file OSGViewport.cpp
5 * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2016.
6 * @addtogroup
7 * @{
8 * @addtogroup
9 * @{
10 * @brief
11 *****************************************************************************/
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 3 of the License, or
16 * (at your option) any later version.
18 * This program is distributed in the hope that it will be useful, but
19 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
20 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21 * for more details.
23 * You should have received a copy of the GNU General Public License along
24 * with this program; if not, write to the Free Software Foundation, Inc.,
25 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 #include "OSGViewport.hpp"
30 #include "osgearth.h"
31 #include "utils/utility.h"
33 #include "OSGNode.hpp"
34 #include "OSGCamera.hpp"
36 #include <osg/Node>
37 #include <osg/Vec4>
38 #include <osg/ApplicationUsage>
39 #include <osg/Version>
40 #include <osgDB/WriteFile>
41 #include <osgViewer/CompositeViewer>
42 #include <osgViewer/ViewerEventHandlers>
43 #include <osgGA/StateSetManipulator>
44 #include <osgGA/CameraManipulator>
45 #include <osgUtil/IncrementalCompileOperation>
47 #include <QOpenGLContext>
48 #include <QQuickWindow>
49 #include <QQuickFramebufferObject>
50 #include <QOpenGLFramebufferObject>
51 #include <QSGSimpleTextureNode>
52 #include <QOpenGLFunctions>
54 #include <QDebug>
56 #include <iterator>
59 Debugging tips
60 - export OSG_NOTIFY_LEVEL=DEBUG
62 Z-fighting can happen with coincident polygons, but it can also happen when the Z buffer has insufficient resolution
63 to represent the data in the scene. In the case where you are close up to an object (the helicopter)
64 and also viewing a far-off object (the earth) the Z buffer has to stretch to accommodate them both.
65 This can result in loss of precision and Z fighting.
67 Assuming you are not messing around with the near/far computations, and assuming you don't have any other objects
68 in the scene that are farther off than the earth, there are a couple things you can try.
70 Adjust the near/far ratio of the camera. Look at osgearth_viewer.cpp to see how.
71 Use LogarythmicDepthBuffer.
73 More complex : you can try parenting your helicopter with an osg::Camera in NESTED mode,
74 which will separate the clip plane calculations of the helicopter from those of the earth. *
76 TODO : add OSGView to handle multiple views for a given OSGViewport
80 that's a typical error when working with high-resolution (retina)
81 displays. The issue here is that on the high-resolution devices, the UI
82 operates with a virtual pixel size that is smaller than the real number
83 of pixels on the device. For example, you get coordinates from 0 to 2048
84 while the real device resolution if 4096 pixels. This factor has to be
85 taken into account when mapping from window coordinates to OpenGL, e.g.,
86 when calling glViewport.
88 How you can get this factor depends on the GUI library you are using. In
89 Qt, you can query it with QWindow::devicePixelRatio():
90 http://doc.qt.io/qt-5/qwindow.html#devicePixelRatio
92 So, there should be something like
93 glViewport(0, 0, window->width() * window->devicePixelRatio(),
94 window->height() * window->devicePixelRatio()).
96 Also keep in mind that you have to do the same e.g. for mouse coordinates.
98 I think osgQt already handles this correctly, so you shouldn't have to
99 worry about this if you use the classes provided by osgQt ...
102 namespace osgQtQuick {
103 // enum DirtyFlag { Scene = 1 << 0, Camera = 1 << 1, Manipulator = 1 << 2, UpdateMode = 1 << 3, IncrementalCompile = 1 << 4 };
105 class ViewportRenderer;
107 class MyViewer : public osgViewer::CompositeViewer {
108 public:
109 MyViewer() : osgViewer::CompositeViewer()
112 virtual bool checkNeedToDoFrame()
114 if (_requestRedraw) {
115 return true;
117 if (_requestContinousUpdate) {
118 return true;
121 for (RefViews::iterator itr = _views.begin(); itr != _views.end(); ++itr) {
122 osgViewer::View *view = itr->get();
123 if (view) {
124 // If the database pager is going to update the scene the render flag is
125 // set so that the updates show up
126 if (view->getDatabasePager()->getDataToCompileListSize() > 0) {
127 return true;
129 if (view->getDatabasePager()->getDataToMergeListSize() > 0) {
130 return true;
132 // if (view->getDatabasePager()->requiresUpdateSceneGraph()) return true;
133 // if (view->getDatabasePager()->getRequestsInProgress()) return true;
135 // if there update callbacks then we need to do frame.
136 if (view->getCamera()->getUpdateCallback()) {
137 return true;
139 if (view->getSceneData() && view->getSceneData()->getUpdateCallback()) {
140 return true;
142 if (view->getSceneData() && view->getSceneData()->getNumChildrenRequiringUpdateTraversal() > 0) {
143 return true;
148 // check if events are available and need processing
149 if (checkEvents()) {
150 return true;
153 if (_requestRedraw) {
154 return true;
156 if (_requestContinousUpdate) {
157 return true;
160 return false;
164 struct OSGViewport::Hidden : public QObject {
165 Q_OBJECT
167 friend ViewportRenderer;
169 private:
170 OSGViewport *const self;
172 QQuickWindow *window;
174 int frameTimer;
176 int frameCount;
178 osg::ref_ptr<osg::GraphicsContext> gc;
180 public:
181 OSGNode *sceneNode;
182 OSGCamera *cameraNode;
184 osg::ref_ptr<osgViewer::CompositeViewer> viewer;
185 osg::ref_ptr<osgViewer::View> view;
187 OSGCameraManipulator *manipulator;
189 UpdateMode::Enum updateMode;
191 bool incrementalCompile;
193 bool busy;
195 static osg::ref_ptr<osg::GraphicsContext> dummyGC;
197 static QtKeyboardMap keyMap;
199 Hidden(OSGViewport *self) : QObject(self), self(self), window(NULL), frameTimer(-1), frameCount(0),
200 sceneNode(NULL), cameraNode(NULL), manipulator(NULL),
201 updateMode(UpdateMode::OnDemand), incrementalCompile(false), busy(false)
203 OsgEarth::initialize();
205 // workaround to avoid using GraphicsContext #0
206 // when switching tabs textures are not rebound (see https://librepilot.atlassian.net/secure/attachment/11500/lost_textures.png)
207 // so we create and retain GraphicsContext #0 so it won't be used elsewhere
208 if (!dummyGC.valid()) {
209 dummyGC = createGraphicsContext();
212 createViewer();
214 connect(self, &OSGViewport::windowChanged, this, &Hidden::onWindowChanged);
217 ~Hidden()
219 stopTimer();
221 disconnect(self);
224 private slots:
225 void onWindowChanged(QQuickWindow *window)
227 // qDebug() << "OSGViewport::onWindowChanged" << window;
228 // osgQtQuick::openGLContextInfo(QOpenGLContext::currentContext(), "onWindowChanged");
229 if (window) {
230 // when hiding the QQuickWidget (happens when switching tab or re-parenting) the renderer is destroyed and sceneGraphInvalidated is signaled
231 // same happens when deleting the QQuickWidget
232 // problem is that there is no way to distinguish a hide and a delete so there is no good place to release gl objects, etc.
233 // it can't be done from other destructors as the gl context is not active at that time
234 // so we must delete the gl objects when hiding (and release it again when showing)
235 // deletion of the osg viewer will happen when the QQuickWidget is deleted. the gl context will not be active but it is ok because the
236 // viewer has no more gl objects to delete (we hope...)
237 // bad side effect is that when showing the scene after hiding it there is delay (on heavy scenes) to realise again the gl context.
238 // this is not happening on a separate thread (because of a limitation of QQuickWidget and Qt on windows related limitations)
239 // a workaround would be to not invalidate the scene when hiding/showing but that is not working atm...
240 // see https://bugreports.qt.io/browse/QTBUG-54133 for more details
241 // window->setPersistentSceneGraph(true);
243 // connect(window, &QQuickWindow::sceneGraphInitialized, this, &Hidden::onSceneGraphInitialized, Qt::DirectConnection);
244 connect(window, &QQuickWindow::sceneGraphInvalidated, this, &Hidden::onSceneGraphInvalidated, Qt::DirectConnection);
245 // connect(window, &QQuickWindow::afterSynchronizing, this, &Hidden::onAfterSynchronizing, Qt::DirectConnection);
246 connect(window, &QQuickWindow::afterSynchronizing, this, &Hidden::onAfterSynchronizing, Qt::DirectConnection);
248 this->window = window;
251 // emitted from the scene graph rendering thread (gl context bound)
252 void onSceneGraphInitialized()
254 // qDebug() << "OSGViewport::onSceneGraphInitialized";
255 initializeResources();
258 // emitted from the scene graph rendering thread (gl context bound)
259 void onSceneGraphInvalidated()
261 // qDebug() << "OSGViewport::onSceneGraphInvalidated";
262 releaseResources();
265 // emitted from the scene graph rendering thread (gl context bound)
266 void onAfterSynchronizing()
268 // qDebug() << "OSGViewport::onAfterSynchronizing";
271 public:
272 bool acceptSceneNode(OSGNode *node)
274 // qDebug() << "OSGViewport::acceptSceneNode" << node;
275 if (sceneNode == node) {
276 return true;
279 if (sceneNode) {
280 disconnect(sceneNode);
283 sceneNode = node;
285 if (sceneNode) {
286 connect(sceneNode, &OSGNode::nodeChanged, this, &Hidden::onSceneNodeChanged);
289 return true;
292 bool acceptCameraNode(OSGCamera *node)
294 // qDebug() << "OSGViewport::acceptCameraNode" << node;
295 if (cameraNode == node) {
296 return true;
299 if (cameraNode) {
300 disconnect(cameraNode);
303 cameraNode = node;
305 if (cameraNode) {
306 connect(cameraNode, &OSGNode::nodeChanged, this, &Hidden::onCameraNodeChanged);
309 return true;
312 bool acceptManipulator(OSGCameraManipulator *m)
314 // qDebug() << "OSGViewport::acceptManipulator" << manipulator;
315 if (manipulator == m) {
316 return true;
319 manipulator = m;
321 return true;
324 private:
325 void initializeResources()
327 // qDebug() << "OSGViewport::Hidden::initializeResources";
328 // osgQtQuick::openGLContextInfo(QOpenGLContext::currentContext(), "OSGViewport::Hidden::initializeResources");
329 if (gc.valid()) {
330 // qWarning() << "OSGViewport::initializeResources - gc already created!";
331 return;
334 // setup graphics context and camera
335 gc = createGraphicsContext();
337 // connect(QOpenGLContext::currentContext(), &QOpenGLContext::aboutToBeDestroyed, this, &Hidden::onAboutToBeDestroyed, Qt::DirectConnection);
339 cameraNode->setGraphicsContext(gc);
341 // qDebug() << "OSGViewport::initializeResources - camera" << cameraNode->asCamera();
342 view->setCamera(cameraNode->asCamera());
344 // qDebug() << "OSGViewport::initializeResources - scene data" << sceneNode->node();
345 view->setSceneData(sceneNode->node());
347 if (manipulator) {
348 osgGA::CameraManipulator *m = manipulator->asCameraManipulator();
349 // qDebug() << "OSGViewport::initializeResources - manipulator" << m;
351 // Setting the manipulator on the camera will change the manipulator node (used to compute the camera home position)
352 // to the view scene node. So we need to save and restore the manipulator node.
353 osg::Node *node = m->getNode();
354 view->setCameraManipulator(m, false);
355 if (node) {
356 m->setNode(node);
358 view->home();
359 } else {
360 view->setCameraManipulator(NULL, false);
363 installHanders();
365 view->init();
366 viewer->realize();
368 startTimer();
371 void onAboutToBeDestroyed()
373 // qDebug() << "OSGViewport::Hidden::onAboutToBeDestroyed";
374 osgQtQuick::openGLContextInfo(QOpenGLContext::currentContext(), "OSGViewport::Hidden::onAboutToBeDestroyed");
375 // context is not current and don't know how to make it current...
378 // see https://github.com/openscenegraph/OpenSceneGraph/commit/161246d864ea0514543ed0493422e1bf0e99afb7#diff-91ee382a4d543072ea66aab422e5106f
379 // see https://github.com/openscenegraph/OpenSceneGraph/commit/3e0435febd677f14aae5f42ef1f43e81307fec41#diff-cadcd928403543a531cf42712a3d1126
380 void releaseResources()
382 // qDebug() << "OSGViewport::Hidden::releaseResources";
383 // osgQtQuick::openGLContextInfo(QOpenGLContext::currentContext(), "OSGViewport::Hidden::releaseResources");
384 if (!gc.valid()) {
385 qWarning() << "OSGViewport::Hidden::releaseResources - gc is not valid!";
386 return;
388 deleteAllGLObjects();
391 // there should be a simpler way to do that...
392 // for now, we mimic what is done in GraphicsContext::close()
393 // calling gc->close() and later gc->realize() does not work
394 void deleteAllGLObjects()
396 // TODO switch off the graphics thread (see GraphicsContext::close()) ?
397 // setGraphicsThread(0);
399 for (osg::GraphicsContext::Cameras::iterator itr = gc->getCameras().begin();
400 itr != gc->getCameras().end();
401 ++itr) {
402 osg::Camera *camera = (*itr);
403 if (camera) {
404 OSG_INFO << "Releasing GL objects for Camera=" << camera << " _state=" << gc->getState() << std::endl;
405 camera->releaseGLObjects(gc->getState());
408 #if OSG_VERSION_GREATER_OR_EQUAL(3, 5, 0)
409 gc->getState()->releaseGLObjects();
410 osg::deleteAllGLObjects(gc->getState()->getContextID());
411 osg::flushAllDeletedGLObjects(gc->getState()->getContextID());
412 osg::discardAllGLObjects(gc->getState()->getContextID());
413 #endif
416 void createViewer()
418 if (viewer.valid()) {
419 qWarning() << "OSGViewport::createViewer - viewer is valid";
420 return;
422 // qDebug() << "OSGViewport::createViewer";
424 viewer = new MyViewer();
425 // viewer = new osgViewer::CompositeViewer();
427 viewer->setThreadingModel(osgViewer::ViewerBase::SingleThreaded);
429 // disable the default setting of viewer.done() by pressing Escape.
430 viewer->setKeyEventSetsDone(0);
431 // viewer->setQuitEventSetsDone(false);
433 view = createView();
434 viewer->addView(view);
437 osgViewer::View *createView()
439 // qDebug() << "OSGViewport::createView";
440 osgViewer::View *view = new osgViewer::View();
442 // TODO expose as Qml properties
443 view->setLightingMode(osgViewer::View::SKY_LIGHT);
444 view->getLight()->setAmbient(osg::Vec4(0.6f, 0.6f, 0.6f, 1.0f));
446 return view;
449 void installHanders()
451 // TODO will the handlers be destroyed???
452 // add the state manipulator
453 view->addEventHandler(new osgGA::StateSetManipulator(view->getCamera()->getOrCreateStateSet()));
454 // b : Toggle Backface Culling, l : Toggle Lighting, t : Toggle Texturing, w : Cycle Polygon Mode
456 // add the thread model handler
457 // view->addEventHandler(new osgViewer::ThreadingHandler);
459 // add the window size toggle handler
460 // view->addEventHandler(new osgViewer::WindowSizeHandler);
462 // add the stats handler
463 view->addEventHandler(new osgViewer::StatsHandler);
465 // add the help handler
466 // view->addEventHandler(new osgViewer::HelpHandler(arguments.getApplicationUsage()));
468 // add the record camera path handler
469 // view->addEventHandler(new osgViewer::RecordCameraPathHandler);
471 // add the LOD Scale handler
472 // view->addEventHandler(new osgViewer::LODScaleHandler);
474 // add the screen capture handler
475 // view->addEventHandler(new osgViewer::ScreenCaptureHandler);
478 osg::GraphicsContext *createGraphicsContext()
480 // qDebug() << "OSGViewport::createGraphicsContext";
482 osg::GraphicsContext::Traits *traits = getTraits();
484 // traitsInfo(*traits);
486 traits->pbuffer = true;
487 osg::GraphicsContext *graphicsContext = osg::GraphicsContext::createGraphicsContext(traits);
488 // if (!graphicsContext) {
489 // qWarning() << "Failed to create pbuffer, failing back to normal graphics window.";
490 // traits->pbuffer = false;
491 // graphicsContext = osg::GraphicsContext::createGraphicsContext(traits);
492 // }
494 return graphicsContext;
497 osg::GraphicsContext::Traits *getTraits()
499 osg::DisplaySettings *ds = osg::DisplaySettings::instance().get();
500 osg::GraphicsContext::Traits *traits = new osg::GraphicsContext::Traits(ds);
502 // traitsInfo(traits);
504 // traits->readDISPLAY();
505 // if (traits->displayNum < 0) {
506 // traits->displayNum = 0;
507 // }
509 #if OSG_VERSION_GREATER_OR_EQUAL(3, 5, 3)
510 // The MyQt windowing system is registered in osgearth.cpp
511 traits->windowingSystemPreference = "MyQt";
512 #endif
513 traits->windowDecoration = false;
514 traits->x = 0;
515 traits->y = 0;
517 int dpr = self->window() ? self->window()->devicePixelRatio() : 1;
518 traits->width = self->width() * dpr;
519 traits->height = self->height() * dpr;
521 traits->alpha = ds->getMinimumNumAlphaBits();
522 traits->stencil = ds->getMinimumNumStencilBits();
523 traits->sampleBuffers = ds->getMultiSamples();
524 traits->samples = ds->getNumMultiSamples();
526 traits->doubleBuffer = false; // ds->getDoubleBuffer();
527 traits->vsync = false;
528 // traits->sharedContext = gc;
529 // traits->inheritedWindowData = new osgQt::GraphicsWindowQt::WindowData(this);
531 return traits;
534 void startTimer()
536 if ((frameTimer < 0) && (updateMode != UpdateMode::Continuous)) {
537 // qDebug() << "OSGViewport::startTimer - starting timer";
538 frameTimer = QObject::startTimer(33, Qt::PreciseTimer);
542 void stopTimer()
544 if (frameTimer >= 0) {
545 // qDebug() << "OSGViewport::stopTimer - killing timer";
546 QObject::killTimer(frameTimer);
547 frameTimer = -1;
551 protected:
552 void timerEvent(QTimerEvent *event)
554 if (event->timerId() == frameTimer) {
555 if (self) {
556 self->update();
559 QObject::timerEvent(event);
563 private slots:
564 void onSceneNodeChanged(osg::Node *node)
566 qWarning() << "OSGViewport::onSceneNodeChanged - not implemented";
569 void onCameraNodeChanged(osg::Node *node)
571 qWarning() << "OSGViewport::onCameraNodeChanged - not implemented";
575 /* class ViewportRenderer */
577 class ViewportRenderer : public QQuickFramebufferObject::Renderer {
578 private:
579 OSGViewport::Hidden *const h;
581 bool initFrame;
582 bool needToDoFrame;
584 public:
585 ViewportRenderer(OSGViewport::Hidden *h) : h(h), initFrame(true), needToDoFrame(false)
587 // qDebug() << "ViewportRenderer::ViewportRenderer";
588 // osgQtQuick::openGLContextInfo(QOpenGLContext::currentContext(), "ViewportRenderer::ViewportRenderer");
589 h->initializeResources();
592 ~ViewportRenderer()
594 // qDebug() << "ViewportRenderer::~ViewportRenderer";
595 // osgQtQuick::openGLContextInfo(QOpenGLContext::currentContext(), "ViewportRenderer::~ViewportRenderer");
596 // h->releaseResources();
599 // This function is the only place when it is safe for the renderer and the item to read and write each others members.
600 void synchronize(QQuickFramebufferObject *item)
602 // qDebug() << "ViewportRenderer::synchronize";
603 // osgQtQuick::openGLContextInfo(QOpenGLContext::currentContext(), "ViewportRenderer::synchronize");
605 if (!h->viewer.valid()) {
606 qWarning() << "ViewportRenderer::synchronize - invalid viewer";
607 return;
610 if (!h->view.valid()) {
611 qWarning() << "ViewportRenderer::synchronize - invalid view";
612 return;
615 if (initFrame) {
616 // workaround for https://bugreports.qt.io/browse/QTBUG-54073
617 // busy indicator starting to spin indefinitly when switching tabs
618 h->self->setBusy(true);
619 h->self->setBusy(false);
622 // NOTES:
623 // - needToDoFrame is always orred so "last" value is preserved. Make sure to set it to false if needed.
624 // - when switching tabs or re-parenting, the renderer is destroyed and recreated
625 // some special care is taken in case a renderer is (re)created;
626 // a new renderer will have initFrame set to true for the duration of the first frame
628 // draw first frame of freshly initialized renderer
629 needToDoFrame |= initFrame;
631 // if not on-demand then do frame
632 if (h->updateMode != UpdateMode::OnDemand) {
633 needToDoFrame = true;
636 // check if viewport needs to be resized
637 // a redraw will be requested if necessary
638 // not really event driven...
639 int dpr = h->self->window()->devicePixelRatio();
640 int width = item->width() * dpr;
641 int height = item->height() * dpr;
642 osg::Viewport *viewport = h->view->getCamera()->getViewport();
643 if (initFrame || (viewport->width() != width) || (viewport->height() != height)) {
644 // qDebug() << "*** RESIZE" << h->frameCount << << initFrame << viewport->width() << "x" << viewport->height() << "->" << width << "x" << height;
645 needToDoFrame = true;
647 h->gc->resized(0, 0, width, height);
648 h->view->getEventQueue()->windowResize(0, 0, width, height /*, resizeTime*/);
650 // trick to force a "home" on first few frames to absorb initial spurious resizes
651 if (h->frameCount <= 2) {
652 h->view->home();
656 if (!needToDoFrame) {
657 // issue : UI events don't trigger a redraw
658 // this issue should be fixed here...
659 // event handling needs a lot of attention :
660 // - sometimes the scene is redrawing continuously (after a drag for example, and single click will stop continuous redraw)
661 // - some events (simple click for instance) trigger a redraw when not needed
662 // - in Earth View : continuous zoom (triggered by holding right button and moving mouse up/down) sometimes stops working when holding mouse still after initiating
663 needToDoFrame = !h->view->getEventQueue()->empty();
666 if (!needToDoFrame) {
667 needToDoFrame = h->viewer->checkNeedToDoFrame();
669 if (needToDoFrame) {
670 // qDebug() << "ViewportRenderer::synchronize - update scene" << h->frameCount;
671 h->viewer->advance();
672 h->viewer->eventTraversal();
673 h->viewer->updateTraversal();
676 // refresh busy state
677 // TODO state becomes busy when scene is loading or downloading tiles (should do it only for download)
678 // TODO also expose request list size to Qml
679 h->self->setBusy(h->view->getDatabasePager()->getRequestsInProgress());
682 // This function is called when the FBO should be rendered into.
683 // The framebuffer is bound at this point and the glViewport has been set up to match the FBO size.
684 void render()
686 // qDebug() << "ViewportRenderer::render";
687 // osgQtQuick::openGLContextInfo(QOpenGLContext::currentContext(), "ViewportRenderer::render");
689 if (!h->viewer.valid()) {
690 qWarning() << "ViewportRenderer::render - invalid viewer";
691 return;
694 if (needToDoFrame) {
695 // qDebug() << "ViewportRenderer::render - render scene" << h->frameCount;
697 // needed to properly render models without terrain (Qt bug?)
698 QOpenGLContext::currentContext()->functions()->glUseProgram(0);
700 h->viewer->renderingTraversals();
702 needToDoFrame = false;
705 if (h->updateMode == UpdateMode::Continuous) {
706 // trigger next update
707 update();
710 ++(h->frameCount);
711 initFrame = false;
714 QOpenGLFramebufferObject *createFramebufferObject(const QSize &size)
716 QOpenGLFramebufferObjectFormat format;
718 format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
719 // format.setSamples(4);
721 QOpenGLFramebufferObject *fbo = new QOpenGLFramebufferObject(size.width(), size.height(), format);
723 return fbo;
727 QtKeyboardMap OSGViewport::Hidden::keyMap = QtKeyboardMap();
729 osg::ref_ptr<osg::GraphicsContext> OSGViewport::Hidden::dummyGC;
731 /* class OSGViewport */
733 OSGViewport::OSGViewport(QQuickItem *parent) : Inherited(parent), h(new Hidden(this))
735 // setClearBeforeRendering(false);
736 #if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)
737 setMirrorVertically(true);
738 #endif
739 setAcceptHoverEvents(true);
740 setAcceptedMouseButtons(Qt::AllButtons);
743 #if QT_VERSION < QT_VERSION_CHECK(5, 6, 0)
744 QSGNode *OSGViewport::updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNodeData *nodeData)
746 if (!node) {
747 node = QQuickFramebufferObject::updatePaintNode(node, nodeData);
748 QSGSimpleTextureNode *n = static_cast<QSGSimpleTextureNode *>(node);
749 if (n) {
750 n->setTextureCoordinatesTransform(QSGSimpleTextureNode::MirrorVertically);
752 return node;
754 return QQuickFramebufferObject::updatePaintNode(node, nodeData);
756 #endif
758 OSGViewport::~OSGViewport()
760 delete h;
763 OSGNode *OSGViewport::sceneNode() const
765 return h->sceneNode;
768 void OSGViewport::setSceneNode(OSGNode *node)
770 if (h->acceptSceneNode(node)) {
771 // setDirty(Scene);
772 emit sceneNodeChanged(node);
776 OSGCamera *OSGViewport::cameraNode() const
778 return h->cameraNode;
781 void OSGViewport::setCameraNode(OSGCamera *node)
783 if (h->acceptCameraNode(node)) {
784 // setDirty(Camera);
785 emit cameraNodeChanged(node);
789 OSGCameraManipulator *OSGViewport::manipulator() const
791 return h->manipulator;
794 void OSGViewport::setManipulator(OSGCameraManipulator *manipulator)
796 if (h->acceptManipulator(manipulator)) {
797 emit manipulatorChanged(manipulator);
801 UpdateMode::Enum OSGViewport::updateMode() const
803 return h->updateMode;
806 void OSGViewport::setUpdateMode(UpdateMode::Enum mode)
808 if (h->updateMode != mode) {
809 h->updateMode = mode;
810 emit updateModeChanged(mode);
814 bool OSGViewport::incrementalCompile() const
816 return h->incrementalCompile;
819 void OSGViewport::setIncrementalCompile(bool incrementalCompile)
821 if (h->incrementalCompile != incrementalCompile) {
822 h->incrementalCompile = incrementalCompile;
823 // setDirty(IncrementalCompile);
824 // TODO not thread safe...
825 h->viewer->setIncrementalCompileOperation(incrementalCompile ? new osgUtil::IncrementalCompileOperation() : NULL);
826 emit incrementalCompileChanged(incrementalCompile);
830 bool OSGViewport::busy() const
832 return h->busy;
835 void OSGViewport::setBusy(bool busy)
837 if (h->busy != busy) {
838 h->busy = busy;
839 emit busyChanged(busy);
843 osgViewer::View *OSGViewport::asView() const
845 return h->view;
848 QQuickFramebufferObject::Renderer *OSGViewport::createRenderer() const
850 // qDebug() << "OSGViewport::createRenderer";
851 // osgQtQuick::openGLContextInfo(QOpenGLContext::currentContext(), "createRenderer");
852 return new ViewportRenderer(h);
855 void OSGViewport::classBegin()
857 // qDebug() << "OSGViewport::classBegin" << this;
858 Inherited::classBegin();
861 void OSGViewport::componentComplete()
863 // qDebug() << "OSGViewport::componentComplete" << this;
864 Inherited::componentComplete();
867 void OSGViewport::mousePressEvent(QMouseEvent *event)
869 int button = 0;
871 switch (event->button()) {
872 case Qt::LeftButton: button = 1; break;
873 case Qt::MidButton: button = 2; break;
874 case Qt::RightButton: button = 3; break;
875 case Qt::NoButton: button = 0; break;
876 default: button = 0; break;
878 setKeyboardModifiers(event);
879 QPointF pos = mousePoint(event);
880 if (h->view.valid()) {
881 h->view->getEventQueue()->mouseButtonPress(pos.x(), pos.y(), button);
885 void OSGViewport::mouseMoveEvent(QMouseEvent *event)
887 setKeyboardModifiers(event);
888 QPointF pos = mousePoint(event);
889 if (h->view.valid()) {
890 h->view->getEventQueue()->mouseMotion(pos.x(), pos.y());
894 void OSGViewport::mouseReleaseEvent(QMouseEvent *event)
896 int button = 0;
898 switch (event->button()) {
899 case Qt::LeftButton: button = 1; break;
900 case Qt::MidButton: button = 2; break;
901 case Qt::RightButton: button = 3; break;
902 case Qt::NoButton: button = 0; break;
903 default: button = 0; break;
905 setKeyboardModifiers(event);
906 QPointF pos = mousePoint(event);
907 if (h->view.valid()) {
908 h->view->getEventQueue()->mouseButtonRelease(pos.x(), pos.y(), button);
912 void OSGViewport::wheelEvent(QWheelEvent *event)
914 osgGA::GUIEventAdapter::ScrollingMotion motion =
915 event->orientation() == Qt::Vertical ?
916 (event->delta() > 0 ? osgGA::GUIEventAdapter::SCROLL_UP : osgGA::GUIEventAdapter::SCROLL_DOWN) :
917 (event->delta() > 0 ? osgGA::GUIEventAdapter::SCROLL_LEFT : osgGA::GUIEventAdapter::SCROLL_RIGHT);
919 if (h->view.valid()) {
920 h->view->getEventQueue()->mouseScroll(motion);
924 void OSGViewport::keyPressEvent(QKeyEvent *event)
926 setKeyboardModifiers(event);
927 int value = h->keyMap.remapKey(event);
928 if (h->view.valid()) {
929 h->view->getEventQueue()->keyPress(value);
932 // this passes the event to the regular Qt key event processing,
933 // among others, it closes popup windows on ESC and forwards the event to the parent widgets
934 // TODO implement
935 // if( _forwardKeyEvents )
936 // Inherited::keyPressEvent(event);
939 void OSGViewport::keyReleaseEvent(QKeyEvent *event)
941 if (event->isAutoRepeat()) {
942 event->ignore();
943 } else {
944 setKeyboardModifiers(event);
945 int value = h->keyMap.remapKey(event);
946 if (h->view.valid()) {
947 h->view->getEventQueue()->keyRelease(value);
951 // this passes the event to the regular Qt key event processing,
952 // among others, it closes popup windows on ESC and forwards the event to the parent widgets
953 // TODO implement
954 // if( _forwardKeyEvents )
955 // Inherited::keyReleaseEvent(event);
958 QPointF OSGViewport::mousePoint(QMouseEvent *event)
960 qreal x, y;
962 if (h->view.valid() && h->view->getEventQueue()->getUseFixedMouseInputRange()) {
963 x = 2.0 * (event->x() - width() / 2) / width();
964 y = 2.0 * (event->y() - height() / 2) / height();
965 } else {
966 x = event->x();
967 y = event->y();
969 return QPointF(x, y);
972 void OSGViewport::setKeyboardModifiers(QInputEvent *event)
974 int modkey = event->modifiers() & (Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier);
975 unsigned int mask = 0;
977 if (modkey & Qt::ShiftModifier) {
978 mask |= osgGA::GUIEventAdapter::MODKEY_SHIFT;
980 if (modkey & Qt::ControlModifier) {
981 mask |= osgGA::GUIEventAdapter::MODKEY_CTRL;
983 if (modkey & Qt::AltModifier) {
984 mask |= osgGA::GUIEventAdapter::MODKEY_ALT;
986 if (h->view.valid()) {
987 h->view->getEventQueue()->getCurrentEventState()->setModKeyMask(mask);
990 } // namespace osgQtQuick
992 #include "OSGViewport.moc"