2 ******************************************************************************
4 * @file OSGViewport.cpp
5 * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2016.
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
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"
31 #include "utils/utility.h"
33 #include "OSGNode.hpp"
34 #include "OSGCamera.hpp"
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>
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
{
109 MyViewer() : osgViewer::CompositeViewer()
112 virtual bool checkNeedToDoFrame()
114 if (_requestRedraw
) {
117 if (_requestContinousUpdate
) {
121 for (RefViews::iterator itr
= _views
.begin(); itr
!= _views
.end(); ++itr
) {
122 osgViewer::View
*view
= itr
->get();
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) {
129 if (view
->getDatabasePager()->getDataToMergeListSize() > 0) {
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()) {
139 if (view
->getSceneData() && view
->getSceneData()->getUpdateCallback()) {
142 if (view
->getSceneData() && view
->getSceneData()->getNumChildrenRequiringUpdateTraversal() > 0) {
148 // check if events are available and need processing
153 if (_requestRedraw
) {
156 if (_requestContinousUpdate
) {
164 struct OSGViewport::Hidden
: public QObject
{
167 friend ViewportRenderer
;
170 OSGViewport
*const self
;
172 QQuickWindow
*window
;
178 osg::ref_ptr
<osg::GraphicsContext
> gc
;
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
;
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();
214 connect(self
, &OSGViewport::windowChanged
, this, &Hidden::onWindowChanged
);
225 void onWindowChanged(QQuickWindow
*window
)
227 // qDebug() << "OSGViewport::onWindowChanged" << window;
228 // osgQtQuick::openGLContextInfo(QOpenGLContext::currentContext(), "onWindowChanged");
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";
265 // emitted from the scene graph rendering thread (gl context bound)
266 void onAfterSynchronizing()
268 // qDebug() << "OSGViewport::onAfterSynchronizing";
272 bool acceptSceneNode(OSGNode
*node
)
274 // qDebug() << "OSGViewport::acceptSceneNode" << node;
275 if (sceneNode
== node
) {
280 disconnect(sceneNode
);
286 connect(sceneNode
, &OSGNode::nodeChanged
, this, &Hidden::onSceneNodeChanged
);
292 bool acceptCameraNode(OSGCamera
*node
)
294 // qDebug() << "OSGViewport::acceptCameraNode" << node;
295 if (cameraNode
== node
) {
300 disconnect(cameraNode
);
306 connect(cameraNode
, &OSGNode::nodeChanged
, this, &Hidden::onCameraNodeChanged
);
312 bool acceptManipulator(OSGCameraManipulator
*m
)
314 // qDebug() << "OSGViewport::acceptManipulator" << manipulator;
315 if (manipulator
== m
) {
325 void initializeResources()
327 // qDebug() << "OSGViewport::Hidden::initializeResources";
328 // osgQtQuick::openGLContextInfo(QOpenGLContext::currentContext(), "OSGViewport::Hidden::initializeResources");
330 // qWarning() << "OSGViewport::initializeResources - gc already created!";
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());
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);
360 view
->setCameraManipulator(NULL
, false);
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");
385 qWarning() << "OSGViewport::Hidden::releaseResources - gc is not valid!";
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();
402 osg::Camera
*camera
= (*itr
);
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());
418 if (viewer
.valid()) {
419 qWarning() << "OSGViewport::createViewer - viewer is valid";
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);
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
));
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);
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;
509 #if OSG_VERSION_GREATER_OR_EQUAL(3, 5, 3)
510 // The MyQt windowing system is registered in osgearth.cpp
511 traits
->windowingSystemPreference
= "MyQt";
513 traits
->windowDecoration
= false;
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);
536 if ((frameTimer
< 0) && (updateMode
!= UpdateMode::Continuous
)) {
537 // qDebug() << "OSGViewport::startTimer - starting timer";
538 frameTimer
= QObject::startTimer(33, Qt::PreciseTimer
);
544 if (frameTimer
>= 0) {
545 // qDebug() << "OSGViewport::stopTimer - killing timer";
546 QObject::killTimer(frameTimer
);
552 void timerEvent(QTimerEvent
*event
)
554 if (event
->timerId() == frameTimer
) {
559 QObject::timerEvent(event
);
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
{
579 OSGViewport::Hidden
*const h
;
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();
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";
610 if (!h
->view
.valid()) {
611 qWarning() << "ViewportRenderer::synchronize - invalid view";
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);
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) {
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();
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.
686 // qDebug() << "ViewportRenderer::render";
687 // osgQtQuick::openGLContextInfo(QOpenGLContext::currentContext(), "ViewportRenderer::render");
689 if (!h
->viewer
.valid()) {
690 qWarning() << "ViewportRenderer::render - invalid viewer";
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
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
);
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);
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
)
747 node
= QQuickFramebufferObject::updatePaintNode(node
, nodeData
);
748 QSGSimpleTextureNode
*n
= static_cast<QSGSimpleTextureNode
*>(node
);
750 n
->setTextureCoordinatesTransform(QSGSimpleTextureNode::MirrorVertically
);
754 return QQuickFramebufferObject::updatePaintNode(node
, nodeData
);
758 OSGViewport::~OSGViewport()
763 OSGNode
*OSGViewport::sceneNode() const
768 void OSGViewport::setSceneNode(OSGNode
*node
)
770 if (h
->acceptSceneNode(node
)) {
772 emit
sceneNodeChanged(node
);
776 OSGCamera
*OSGViewport::cameraNode() const
778 return h
->cameraNode
;
781 void OSGViewport::setCameraNode(OSGCamera
*node
)
783 if (h
->acceptCameraNode(node
)) {
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
835 void OSGViewport::setBusy(bool busy
)
837 if (h
->busy
!= busy
) {
839 emit
busyChanged(busy
);
843 osgViewer::View
*OSGViewport::asView() const
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
)
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
)
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
935 // if( _forwardKeyEvents )
936 // Inherited::keyPressEvent(event);
939 void OSGViewport::keyReleaseEvent(QKeyEvent
*event
)
941 if (event
->isAutoRepeat()) {
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
954 // if( _forwardKeyEvents )
955 // Inherited::keyReleaseEvent(event);
958 QPointF
OSGViewport::mousePoint(QMouseEvent
*event
)
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();
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"