1
/**************************************************************************\
2
* Copyright (c) Kongsberg Oil & Gas Technologies AS
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions are
9
* Redistributions of source code must retain the above copyright notice,
10
* this list of conditions and the following disclaimer.
12
* Redistributions in binary form must reproduce the above copyright
13
* notice, this list of conditions and the following disclaimer in the
14
* documentation and/or other materials provided with the distribution.
16
* Neither the name of the copyright holder nor the names of its
17
* contributors may be used to endorse or promote products derived from
18
* this software without specific prior written permission.
20
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
\**************************************************************************/
34
\class SIM::Coin3D::Quarter::QuarterWidget QuarterWidget.h Quarter/QuarterWidget.h
36
\brief The QuarterWidget class is the main class in Quarter. It
37
provides a widget for Coin rendering. It provides scenegraph
38
management and event handling.
40
If you want to modify the GL format for an existing QuarterWidget, you can
41
set up a new GL context for the widget, e.g.:
44
QGLContext * context = new QGLContext(QGLFormat(QGL::SampleBuffers), viewer);
45
if (context->create()) {
46
viewer->setContext(context);
52
#pragma warning(disable : 4267)
65
#include <QApplication>
69
#include <QGuiApplication>
71
#include <QOpenGLDebugLogger>
72
#include <QOpenGLDebugMessage>
74
#include <QResizeEvent>
77
#include <Inventor/C/basic.h>
78
#if COIN_MAJOR_VERSION >= 4
79
#include <Inventor/SbByteBuffer.h>
82
#include <Inventor/SbColor.h>
83
#include <Inventor/SbViewportRegion.h>
84
#include <Inventor/SoDB.h>
85
#include <Inventor/SoEventManager.h>
86
#include <Inventor/SoRenderManager.h>
87
#include <Inventor/nodes/SoCamera.h>
88
#include <Inventor/nodes/SoDirectionalLight.h>
89
#include <Inventor/nodes/SoNode.h>
90
#include <Inventor/nodes/SoPerspectiveCamera.h>
91
#include <Inventor/nodes/SoSeparator.h>
92
#include <Inventor/scxml/ScXML.h>
93
#include <Inventor/scxml/SoScXMLStateMachine.h>
95
#include "QuarterWidget.h"
96
#include "InteractionMode.h"
98
#include "QuarterWidgetP.h"
99
#include "eventhandlers/EventFilter.h"
100
#include "eventhandlers/DragDropHandler.h"
103
using namespace SIM::Coin3D::Quarter;
106
\enum SIM::Coin3D::Quarter::QuarterWidget::TransparencyType
108
Various settings for how to do rendering of transparent objects in
109
the scene. Some of the settings will provide faster rendering, while
110
others gives you better quality rendering.
112
See \ref SoGLRenderAction::TransparencyType for a full description of the modes
116
\enum SIM::Coin3D::Quarter::QuarterWidget::RenderMode
118
Sets how rendering of primitives is done.
120
See \ref SoRenderManager::RenderMode for a full description of the modes
124
\enum SIM::Coin3D::Quarter::QuarterWidget::StereoMode
126
Sets how stereo rendering is performed.
128
See \ref SoRenderManager::StereoMode for a full description of the modes
132
MONO = SoRenderManager::MONO,
133
ANAGLYPH = SoRenderManager::ANAGLYPH,
134
QUAD_BUFFER = SoRenderManager::QUAD_BUFFER,
135
INTERLEAVED_ROWS = SoRenderManager::INTERLEAVED_ROWS,
136
INTERLEAVED_COLUMNS = SoRenderManager::INTERLEAVED_COLUMNS
139
#define PRIVATE(obj) obj->pimpl
141
#ifndef GL_MULTISAMPLE_BIT_EXT
142
#define GL_MULTISAMPLE_BIT_EXT 0x20000000
145
//We need to avoid buffer swapping when initializing a QPainter on this widget
146
class CustomGLWidget : public QOpenGLWidget {
148
QSurfaceFormat myFormat;
150
CustomGLWidget(const QSurfaceFormat& format, QWidget* parent = nullptr, const QOpenGLWidget* shareWidget = nullptr, Qt::WindowFlags f = Qt::WindowFlags())
151
: QOpenGLWidget(parent, f), myFormat(format)
153
Q_UNUSED(shareWidget);
154
QSurfaceFormat surfaceFormat(format);
155
surfaceFormat.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
156
// With the settings below we could determine deprecated OpenGL API
157
// but can't do this since otherwise it will complain about almost any
158
// OpenGL call in Coin3d
159
//surfaceFormat.setMajorVersion(3);
160
//surfaceFormat.setMinorVersion(2);
161
//surfaceFormat.setProfile(QSurfaceFormat::CoreProfile);
162
#if defined (_DEBUG) && 0
163
surfaceFormat.setOption(QSurfaceFormat::DebugContext);
165
setFormat(surfaceFormat);
167
~CustomGLWidget() override
170
void initializeGL() override
172
QOpenGLContext *context = QOpenGLContext::currentContext();
173
#if defined (_DEBUG) && 0
174
if (context && context->hasExtension(QByteArrayLiteral("GL_KHR_debug"))) {
175
QOpenGLDebugLogger *logger = new QOpenGLDebugLogger(this);
176
connect(logger, &QOpenGLDebugLogger::messageLogged, this, &CustomGLWidget::handleLoggedMessage);
178
if (logger->initialize())
179
logger->startLogging(QOpenGLDebugLogger::SynchronousLogging);
183
connect(context, &QOpenGLContext::aboutToBeDestroyed,
184
this, &CustomGLWidget::aboutToDestroyGLContext, Qt::DirectConnection);
186
connect(this, &CustomGLWidget::resized, this, &CustomGLWidget::slotResized);
188
// paintGL() is invoked when e.g. using the method grabFramebuffer of this class
190
// from PySide import QtWidgets
191
// mw = Gui.getMainWindow()
192
// mdi = mw.findChild(QtWidgets.QMdiArea)
193
// gl = mdi.findChild(QtWidgets.QOpenGLWidget)
194
// img = gl.grabFramebuffer()
196
void paintGL() override
198
QuarterWidget* qw = qobject_cast<QuarterWidget*>(parentWidget());
203
void aboutToDestroyGLContext()
205
// With Qt 5.9 a signal is emitted while the QuarterWidget is being destroyed.
206
// At this state its type is a QWidget, not a QuarterWidget any more.
207
QuarterWidget* qw = qobject_cast<QuarterWidget*>(parent());
210
QMetaObject::invokeMethod(parent(), "aboutToDestroyGLContext",
211
Qt::DirectConnection,
212
QGenericReturnArgument());
214
bool event(QEvent *e) override
216
// If a debug logger is activated then Qt's default implementation
217
// first releases the context before stopping the logger. However,
218
// the logger needs the active context and thus crashes because it's
220
if (e->type() == QEvent::WindowChangeInternal) {
221
if (!qApp->testAttribute(Qt::AA_ShareOpenGLContexts)) {
222
QOpenGLDebugLogger* logger = this->findChild<QOpenGLDebugLogger*>();
224
logger->stopLogging();
230
return QOpenGLWidget::event(e);
232
void handleLoggedMessage(const QOpenGLDebugMessage &message)
236
void showEvent(QShowEvent*) override
238
update(); // force update when changing window mode
242
update(); // fixes flickering on some systems
247
QuarterWidget::QuarterWidget(const QtGLFormat & format, QWidget * parent, const QtGLWidget * sharewidget, Qt::WindowFlags f)
251
this->constructor(format, sharewidget);
255
QuarterWidget::QuarterWidget(QWidget * parent, const QtGLWidget * sharewidget, Qt::WindowFlags f)
259
this->constructor(QtGLFormat(), sharewidget);
263
QuarterWidget::QuarterWidget(QtGLContext * context, QWidget * parent, const QtGLWidget * sharewidget, Qt::WindowFlags f)
267
this->constructor(context->format(), sharewidget);
271
QuarterWidget::constructor(const QtGLFormat & format, const QtGLWidget * sharewidget)
273
QGraphicsScene* scene = new QGraphicsScene(this);
275
setViewport(new CustomGLWidget(format, this, sharewidget));
277
setFrameStyle(QFrame::NoFrame);
278
setAutoFillBackground(false);
279
viewport()->setAutoFillBackground(false);
280
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
281
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
283
PRIVATE(this) = new QuarterWidgetP(this, sharewidget);
285
PRIVATE(this)->sorendermanager = new SoRenderManager;
286
PRIVATE(this)->initialsorendermanager = true;
287
PRIVATE(this)->soeventmanager = new SoEventManager;
288
PRIVATE(this)->initialsoeventmanager = true;
289
PRIVATE(this)->processdelayqueue = true;
291
//Mind the order of initialization as the XML state machine uses
292
//callbacks which depends on other state being initialized
293
PRIVATE(this)->eventfilter = new EventFilter(this);
294
PRIVATE(this)->interactionmode = new InteractionMode(this);
296
PRIVATE(this)->currentStateMachine = nullptr;
298
PRIVATE(this)->headlight = new SoDirectionalLight;
299
PRIVATE(this)->headlight->ref();
301
PRIVATE(this)->sorendermanager->setAutoClipping(SoRenderManager::VARIABLE_NEAR_PLANE);
302
PRIVATE(this)->sorendermanager->setRenderCallback(QuarterWidgetP::rendercb, this);
303
PRIVATE(this)->sorendermanager->setBackgroundColor(SbColor4f(0.0f, 0.0f, 0.0f, 0.0f));
304
PRIVATE(this)->sorendermanager->activate();
305
PRIVATE(this)->sorendermanager->addPreRenderCallback(QuarterWidgetP::prerendercb, PRIVATE(this));
306
PRIVATE(this)->sorendermanager->addPostRenderCallback(QuarterWidgetP::postrendercb, PRIVATE(this));
308
PRIVATE(this)->soeventmanager->setNavigationState(SoEventManager::MIXED_NAVIGATION);
310
// set up a cache context for the default SoGLRenderAction
311
PRIVATE(this)->sorendermanager->getGLRenderAction()->setCacheContext(this->getCacheContextId());
313
this->setMouseTracking(true);
315
// Qt::StrongFocus means the widget will accept keyboard focus by
316
// both tabbing and clicking
317
this->setFocusPolicy(Qt::StrongFocus);
319
this->installEventFilter(PRIVATE(this)->eventfilter);
320
this->installEventFilter(PRIVATE(this)->interactionmode);
326
QuarterWidget::replaceViewport()
328
CustomGLWidget* oldvp = static_cast<CustomGLWidget*>(viewport());
329
CustomGLWidget* newvp = new CustomGLWidget(oldvp->myFormat, this);
330
PRIVATE(this)->replaceGLWidget(newvp);
333
setAutoFillBackground(false);
334
viewport()->setAutoFillBackground(false);
338
QuarterWidget::aboutToDestroyGLContext()
343
QuarterWidget::~QuarterWidget()
345
if (PRIVATE(this)->currentStateMachine) {
346
this->removeStateMachine(PRIVATE(this)->currentStateMachine);
347
delete PRIVATE(this)->currentStateMachine;
349
PRIVATE(this)->headlight->unref();
350
PRIVATE(this)->headlight = nullptr;
351
this->setSceneGraph(nullptr);
352
this->setSoRenderManager(nullptr);
353
this->setSoEventManager(nullptr);
354
delete PRIVATE(this)->eventfilter;
355
delete PRIVATE(this);
359
You can set the cursor you want to use for a given navigation
360
state. See the Coin documentation on navigation for information
361
about available states
364
QuarterWidget::setStateCursor(const SbName & state, const QCursor & cursor)
366
assert(QuarterP::statecursormap);
367
// will overwrite the value of an existing item
368
QuarterP::statecursormap->insert(state, cursor);
372
Maps a state to a cursor
374
\param[in] state Named state in the statemachine
375
\retval Cursor corresponding to the given state
378
QuarterWidget::stateCursor(const SbName & state)
380
assert(QuarterP::statecursormap);
381
return QuarterP::statecursormap->value(state);
385
\property QuarterWidget::headlightEnabled
387
\copydetails QuarterWidget::setHeadlightEnabled
391
Enable/disable the headlight. This will toggle the SoDirectionalLight::on
392
field (returned from getHeadlight()).
395
QuarterWidget::setHeadlightEnabled(bool onoff)
397
PRIVATE(this)->headlight->on = onoff;
401
Returns true if the headlight is on, false if it is off
404
QuarterWidget::headlightEnabled() const
406
return PRIVATE(this)->headlight->on.getValue();
410
Returns the light used for the headlight.
413
QuarterWidget::getHeadlight() const
415
return PRIVATE(this)->headlight;
419
\property QuarterWidget::clearZBuffer
421
\copydetails QuarterWidget::setClearZBuffer
425
Specify if you want the z buffer to be cleared before
426
redraw. This is on by default.
429
QuarterWidget::setClearZBuffer(bool onoff)
431
PRIVATE(this)->clearzbuffer = onoff;
435
Returns true if the z buffer is cleared before rendering.
438
QuarterWidget::clearZBuffer() const
440
return PRIVATE(this)->clearzbuffer;
444
\property QuarterWidget::clearWindow
446
\copydetails QuarterWidget::setClearWindow
450
Specify if you want the rendering buffer to be cleared before
451
rendering. This is on by default.
454
QuarterWidget::setClearWindow(bool onoff)
456
PRIVATE(this)->clearwindow = onoff;
460
Returns true if the rendering buffer is cleared before rendering.
463
QuarterWidget::clearWindow() const
465
return PRIVATE(this)->clearwindow;
469
\property QuarterWidget::interactionModeEnabled
471
\copydetails QuarterWidget::setInteractionModeEnabled
475
Enable/disable interaction mode.
477
Specifies whether you may use the alt-key to enter interaction mode.
480
QuarterWidget::setInteractionModeEnabled(bool onoff)
482
PRIVATE(this)->interactionmode->setEnabled(onoff);
486
Returns true if interaction mode is enabled, false otherwise.
489
QuarterWidget::interactionModeEnabled() const
491
return PRIVATE(this)->interactionmode->enabled();
495
\property QuarterWidget::interactionModeOn
497
\copydetails QuarterWidget::setInteractionModeOn
501
Turn interaction mode on or off.
504
QuarterWidget::setInteractionModeOn(bool onoff)
506
PRIVATE(this)->interactionmode->setOn(onoff);
510
Returns true if interaction mode is on.
513
QuarterWidget::interactionModeOn() const
515
return PRIVATE(this)->interactionmode->on();
519
Returns the Coin cache context id for this widget.
522
QuarterWidget::getCacheContextId() const
524
return PRIVATE(this)->getCacheContextId();
528
\property QuarterWidget::transparencyType
530
\copydetails QuarterWidget::setTransparencyType
534
Sets the transparency type to be used for the scene.
537
QuarterWidget::setTransparencyType(TransparencyType type)
539
assert(PRIVATE(this)->sorendermanager);
540
PRIVATE(this)->sorendermanager->getGLRenderAction()->setTransparencyType((SoGLRenderAction::TransparencyType)type);
541
PRIVATE(this)->sorendermanager->scheduleRedraw();
545
\retval The current \ref TransparencyType
547
QuarterWidget::TransparencyType
548
QuarterWidget::transparencyType() const
550
assert(PRIVATE(this)->sorendermanager);
551
SoGLRenderAction * action = PRIVATE(this)->sorendermanager->getGLRenderAction();
552
return static_cast<QuarterWidget::TransparencyType>(action->getTransparencyType());
556
\property QuarterWidget::renderMode
558
\copydetails QuarterWidget::setRenderMode
565
QuarterWidget::setRenderMode(RenderMode mode)
567
assert(PRIVATE(this)->sorendermanager);
568
PRIVATE(this)->sorendermanager->setRenderMode(static_cast<SoRenderManager::RenderMode>(mode));
569
PRIVATE(this)->sorendermanager->scheduleRedraw();
573
\retval The current \ref RenderMode
575
QuarterWidget::RenderMode
576
QuarterWidget::renderMode() const
578
assert(PRIVATE(this)->sorendermanager);
579
return static_cast<RenderMode>(PRIVATE(this)->sorendermanager->getRenderMode());
583
\property QuarterWidget::stereoMode
585
\copydetails QuarterWidget::setStereoMode
592
QuarterWidget::setStereoMode(StereoMode mode)
594
assert(PRIVATE(this)->sorendermanager);
595
PRIVATE(this)->sorendermanager->setStereoMode(static_cast<SoRenderManager::StereoMode>(mode));
596
PRIVATE(this)->sorendermanager->scheduleRedraw();
601
\retval The current \ref StereoMode
603
QuarterWidget::StereoMode
604
QuarterWidget::stereoMode() const
606
assert(PRIVATE(this)->sorendermanager);
607
return static_cast<StereoMode>(PRIVATE(this)->sorendermanager->getStereoMode());
611
\property QuarterWidget::devicePixelRatio
613
\copydetails QuarterWidget::devicePixelRatio
617
The ratio between logical and physical pixel sizes -- obtained from the window that
618
the widget is located within, and updated whenever any change occurs, emitting a devicePixelRatioChanged signal. Only available for version Qt 5.6 and above (will be 1.0 for all previous versions)
622
QuarterWidget::devicePixelRatio() const
624
return PRIVATE(this)->device_pixel_ratio;
628
Sets the Inventor scenegraph to be rendered
631
QuarterWidget::setSceneGraph(SoNode * node)
633
if (node == PRIVATE(this)->scene) {
637
if (PRIVATE(this)->scene) {
638
PRIVATE(this)->scene->unref();
639
PRIVATE(this)->scene = nullptr;
642
SoCamera * camera = nullptr;
643
SoSeparator * superscene = nullptr;
644
bool viewall = false;
647
PRIVATE(this)->scene = node;
648
PRIVATE(this)->scene->ref();
650
superscene = new SoSeparator;
651
superscene->addChild(PRIVATE(this)->headlight);
653
// if the scene does not contain a camera, add one
654
if (!(camera = PRIVATE(this)->searchForCamera(node))) {
655
camera = new SoPerspectiveCamera;
656
superscene->addChild(camera);
660
superscene->addChild(node);
663
PRIVATE(this)->soeventmanager->setCamera(camera);
664
PRIVATE(this)->sorendermanager->setCamera(camera);
665
PRIVATE(this)->soeventmanager->setSceneGraph(superscene);
666
PRIVATE(this)->sorendermanager->setSceneGraph(superscene);
668
if (viewall) { this->viewAll(); }
669
if (superscene) { superscene->touch(); }
673
Returns pointer to root of scene graph
676
QuarterWidget::getSceneGraph() const
678
return PRIVATE(this)->scene;
682
Set the render manager for the widget.
685
QuarterWidget::setSoRenderManager(SoRenderManager * manager)
687
bool carrydata = false;
688
SoNode * scene = nullptr;
689
SoCamera * camera = nullptr;
691
if (PRIVATE(this)->sorendermanager && manager) {
692
scene = PRIVATE(this)->sorendermanager->getSceneGraph();
693
camera = PRIVATE(this)->sorendermanager->getCamera();
694
vp = PRIVATE(this)->sorendermanager->getViewportRegion(); // clazy:exclude=rule-of-two-soft
698
// ref before deleting the old scene manager to avoid that the nodes are deleted
699
if (scene) scene->ref();
700
if (camera) camera->ref();
702
if (PRIVATE(this)->initialsorendermanager) {
703
delete PRIVATE(this)->sorendermanager;
704
PRIVATE(this)->initialsorendermanager = false;
706
PRIVATE(this)->sorendermanager = manager;
708
PRIVATE(this)->sorendermanager->setSceneGraph(scene);
709
PRIVATE(this)->sorendermanager->setCamera(camera);
710
PRIVATE(this)->sorendermanager->setViewportRegion(vp);
713
if (scene) scene->unref();
714
if (camera) camera->unref();
718
Returns a pointer to the render manager.
721
QuarterWidget::getSoRenderManager() const
723
return PRIVATE(this)->sorendermanager;
727
Set the Coin event manager for the widget.
730
QuarterWidget::setSoEventManager(SoEventManager * manager)
732
bool carrydata = false;
733
SoNode * scene = nullptr;
734
SoCamera * camera = nullptr;
736
if (PRIVATE(this)->soeventmanager && manager) {
737
scene = PRIVATE(this)->soeventmanager->getSceneGraph();
738
camera = PRIVATE(this)->soeventmanager->getCamera();
739
vp = PRIVATE(this)->soeventmanager->getViewportRegion(); // clazy:exclude=rule-of-two-soft
743
// ref before deleting the old scene manager to avoid that the nodes are deleted
744
if (scene) scene->ref();
745
if (camera) camera->ref();
747
if (PRIVATE(this)->initialsoeventmanager) {
748
delete PRIVATE(this)->soeventmanager;
749
PRIVATE(this)->initialsoeventmanager = false;
751
PRIVATE(this)->soeventmanager = manager;
753
PRIVATE(this)->soeventmanager->setSceneGraph(scene);
754
PRIVATE(this)->soeventmanager->setCamera(camera);
755
PRIVATE(this)->soeventmanager->setViewportRegion(vp);
758
if (scene) scene->unref();
759
if (camera) camera->unref();
763
Returns a pointer to the event manager
766
QuarterWidget::getSoEventManager() const
768
return PRIVATE(this)->soeventmanager;
772
Returns a pointer to the event filter
775
QuarterWidget::getEventFilter() const
777
return PRIVATE(this)->eventfilter;
781
Reposition the current camera to display the entire scene
784
QuarterWidget::viewAll()
786
const SbName viewallevent("sim.coin3d.coin.navigation.ViewAll");
787
for (int c = 0; c < PRIVATE(this)->soeventmanager->getNumSoScXMLStateMachines(); ++c) {
788
SoScXMLStateMachine * sostatemachine =
789
PRIVATE(this)->soeventmanager->getSoScXMLStateMachine(c);
790
if (sostatemachine->isActive()) {
791
sostatemachine->queueEvent(viewallevent);
792
sostatemachine->processEventQueue();
798
Sets the current camera in seekmode, if supported by the underlying navigation system.
799
Camera typically seeks towards what the mouse is pointing at.
804
const SbName seekevent("sim.coin3d.coin.navigation.Seek");
805
for (int c = 0; c < PRIVATE(this)->soeventmanager->getNumSoScXMLStateMachines(); ++c) {
806
SoScXMLStateMachine * sostatemachine =
807
PRIVATE(this)->soeventmanager->getSoScXMLStateMachine(c);
808
if (sostatemachine->isActive()) {
809
sostatemachine->queueEvent(seekevent);
810
sostatemachine->processEventQueue();
816
QuarterWidget::updateDevicePixelRatio() {
817
qreal dev_pix_ratio = 1.0;
818
QWidget* winwidg = window();
819
QWindow* win = nullptr;
821
win = winwidg->windowHandle();
824
dev_pix_ratio = win->devicePixelRatio();
827
dev_pix_ratio = ((QGuiApplication*)QGuiApplication::instance())->devicePixelRatio();
829
if(PRIVATE(this)->device_pixel_ratio != dev_pix_ratio) {
830
PRIVATE(this)->device_pixel_ratio = dev_pix_ratio;
831
Q_EMIT devicePixelRatioChanged(dev_pix_ratio);
838
Overridden from QGLWidget to resize the Coin scenegraph
840
void QuarterWidget::resizeEvent(QResizeEvent* event)
842
updateDevicePixelRatio();
843
qreal dev_pix_ratio = devicePixelRatio();
844
int width = static_cast<int>(dev_pix_ratio * event->size().width());
845
int height = static_cast<int>(dev_pix_ratio * event->size().height());
847
SbViewportRegion vp(width, height);
848
PRIVATE(this)->sorendermanager->setViewportRegion(vp);
849
PRIVATE(this)->soeventmanager->setViewportRegion(vp);
851
scene()->setSceneRect(QRect(QPoint(0, 0), event->size()));
852
QGraphicsView::resizeEvent(event);
856
Overridden from QGLWidget to render the scenegraph
858
void QuarterWidget::paintEvent(QPaintEvent* event)
860
if (updateDevicePixelRatio()) {
861
qreal dev_pix_ratio = devicePixelRatio();
862
int width = static_cast<int>(dev_pix_ratio * this->width());
863
int height = static_cast<int>(dev_pix_ratio * this->height());
864
SbViewportRegion vp(width, height);
865
PRIVATE(this)->sorendermanager->setViewportRegion(vp);
866
PRIVATE(this)->soeventmanager->setViewportRegion(vp);
870
this->getSoRenderManager()->reinitialize();
874
getSoRenderManager()->activate();
876
glMatrixMode(GL_PROJECTION);
878
QtGLWidget* w = static_cast<QtGLWidget*>(this->viewport());
880
qWarning() << "No valid GL context found!";
883
//assert(w->isValid() && "No valid GL context found!");
884
// We might have to process the delay queue here since we don't know
885
// if paintGL() is called from Qt, and we might have some sensors
886
// waiting to trigger (the redraw sensor has a lower priority than a
887
// normal field sensor to guarantee that your sensor is processed
888
// before the next redraw). Disable autorendering while we do this
889
// to avoid recursive redraws.
891
// We set the PRIVATE(this)->processdelayqueue = false in redraw()
892
// to avoid processing the delay queue when paintGL() is triggered
893
// by us, and we don't want to process the delay queue in those
896
PRIVATE(this)->autoredrawenabled = false;
898
if(PRIVATE(this)->processdelayqueue && SoDB::getSensorManager()->isDelaySensorPending()) {
899
// processing the sensors might trigger a redraw in another
900
// context. Release this context temporarily
902
SoDB::getSensorManager()->processDelayQueue(false);
906
assert(w->isValid() && "No valid GL context found!");
908
// Causes an OpenGL error on resize
909
//glDrawBuffer(w->format().swapBehavior() == QSurfaceFormat::DoubleBuffer ? GL_BACK : GL_FRONT);
912
this->actualRedraw();
914
//start the standard graphics view processing for all widgets and graphic items. As
915
//QGraphicsView initaliizes a QPainter which changes the Opengl context in an unpredictable
916
//manner we need to store the context and recreate it after Qt is done.
917
glPushAttrib(GL_MULTISAMPLE_BIT_EXT);
918
inherited::paintEvent(event);
921
// Causes an OpenGL error on resize
922
//if (w->format().swapBehavior() == QSurfaceFormat::DoubleBuffer)
923
// w->context()->swapBuffers(w->context()->surface());
925
PRIVATE(this)->autoredrawenabled = true;
927
// process the delay queue the next time we enter this function,
928
// unless we get here after a call to redraw().
929
PRIVATE(this)->processdelayqueue = true;
932
bool QuarterWidget::viewportEvent(QEvent* event)
935
// If no item is selected still let the graphics scene handle it but
936
// additionally handle it by this viewer. This is e.g. needed when
937
// resizing a widget item because the cursor may already be outside
939
if (event->type() == QEvent::MouseButtonDblClick ||
940
event->type() == QEvent::MouseButtonPress) {
941
QMouseEvent* mouse = static_cast<QMouseEvent*>(event);
942
QGraphicsItem *item = itemAt(mouse->pos());
944
QGraphicsView::viewportEvent(event);
948
else if (event->type() == QEvent::MouseMove ||
949
event->type() == QEvent::MouseButtonRelease) {
950
QGraphicsScene* glScene = this->scene();
951
if (!(glScene && glScene->mouseGrabberItem())) {
952
QGraphicsView::viewportEvent(event);
956
else if (event->type() == QEvent::Wheel) {
957
auto wheel = static_cast<QWheelEvent*>(event);
958
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
959
QPoint pos = wheel->pos();
961
QPoint pos = wheel->position().toPoint();
963
QGraphicsItem* item = itemAt(pos);
965
QGraphicsView::viewportEvent(event);
970
return QGraphicsView::viewportEvent(event);
974
Used for rendering the scene. Usually Coin/Quarter will automatically redraw
975
the scene graph at regular intervals, after the scene is modified.
977
However, if you want to disable this functionality and gain full control over
978
when the scene is rendered yourself, you can turn off autoredraw in the
979
render manager and render the scene by calling this method.
982
QuarterWidget::redraw()
984
// we're triggering the next paintGL(). Set a flag to remember this
985
// to avoid that we process the delay queue in paintGL()
986
PRIVATE(this)->processdelayqueue = false;
988
// When stylesheet is used, there is recursive repaint warning caused by
989
// repaint() here. It happens when switching active documents. Based on call
990
// stacks, it happens like this, the repaint event first triggers a series
991
// calls of QWidgetPrivate::paintSiblingsRecrusive(), and then reaches one of
992
// the QuarterWidget. From its paintEvent(), it calls
993
// SoSensorManager::processDelayQueue(), which triggers redraw() of another
994
// QuarterWidget. And if repaint() is called here, it will trigger another
995
// series call of QWidgetPrivate::paintSiblingRecursive(), and eventually
996
// back to the first QuarterWidget, at which time the "Recursive repaint
997
// detected" Qt warning message will be printed.
999
// Note that, the recursive repaint is not infinite due to setting
1000
// 'processdelayqueue = false' above. However, it does cause annoying
1001
// flickering, and actually crash on Windows.
1002
this->viewport()->update();
1006
Overridden from QGLWidget to render the scenegraph
1009
QuarterWidget::actualRedraw()
1011
PRIVATE(this)->sorendermanager->render(PRIVATE(this)->clearwindow,
1012
PRIVATE(this)->clearzbuffer);
1017
Passes an event to the eventmanager.
1019
\param[in] event to pass
1020
\retval Returns true if the event was successfully processed
1023
QuarterWidget::processSoEvent(const SoEvent * event)
1027
PRIVATE(this)->soeventmanager &&
1028
PRIVATE(this)->soeventmanager->processEvent(event);
1032
\property QuarterWidget::backgroundColor
1033
\copydoc QuarterWidget::setBackgroundColor
1037
Set backgroundcolor to a given QColor
1039
Remember that QColors are given in integers between 0 and 255, as
1040
opposed to SbColor4f which is in [0 ,1]. The default alpha value for
1041
a QColor is 255, but you'll probably want to set it to zero before
1042
using it as an OpenGL clear color.
1045
QuarterWidget::setBackgroundColor(const QColor & color)
1047
SbColor4f bgcolor(SbClamp(color.red() / 255.0, 0.0, 1.0),
1048
SbClamp(color.green() / 255.0, 0.0, 1.0),
1049
SbClamp(color.blue() / 255.0, 0.0, 1.0),
1050
SbClamp(color.alpha() / 255.0, 0.0, 1.0));
1052
PRIVATE(this)->sorendermanager->setBackgroundColor(bgcolor);
1053
PRIVATE(this)->sorendermanager->scheduleRedraw();
1057
Returns color used for clearing the rendering area before
1058
rendering the scene.
1061
QuarterWidget::backgroundColor() const
1063
SbColor4f bg = PRIVATE(this)->sorendermanager->getBackgroundColor();
1065
return {SbClamp(int(bg[0] * 255.0), 0, 255),
1066
SbClamp(int(bg[1] * 255.0), 0, 255),
1067
SbClamp(int(bg[2] * 255.0), 0, 255),
1068
SbClamp(int(bg[3] * 255.0), 0, 255)};
1072
Returns the context menu used by the widget.
1075
QuarterWidget::getContextMenu() const
1077
return PRIVATE(this)->contextMenu();
1081
\retval Is context menu enabled?
1084
QuarterWidget::contextMenuEnabled() const
1086
return PRIVATE(this)->contextmenuenabled;
1090
\property QuarterWidget::contextMenuEnabled
1092
\copydetails QuarterWidget::setContextMenuEnabled
1096
Controls the display of the contextmenu
1098
\param[in] yes Context menu on?
1101
QuarterWidget::setContextMenuEnabled(bool yes)
1103
PRIVATE(this)->contextmenuenabled = yes;
1107
Convenience method that adds a state machine to the current
1108
SoEventManager. It also initializes the scene graph
1109
root and active camera for the state machine, and finally it sets
1110
up the default Quarter cursor handling.
1112
\sa removeStateMachine
1115
QuarterWidget::addStateMachine(SoScXMLStateMachine * statemachine)
1117
SoEventManager * em = this->getSoEventManager();
1118
em->addSoScXMLStateMachine(statemachine);
1119
statemachine->setSceneGraphRoot(this->getSoRenderManager()->getSceneGraph());
1120
statemachine->setActiveCamera(this->getSoRenderManager()->getCamera());
1121
statemachine->addStateChangeCallback(QuarterWidgetP::statechangecb, PRIVATE(this));
1125
Convenience method that removes a state machine to the current
1131
QuarterWidget::removeStateMachine(SoScXMLStateMachine * statemachine)
1133
SoEventManager * em = this->getSoEventManager();
1134
statemachine->setSceneGraphRoot(nullptr);
1135
statemachine->setActiveCamera(nullptr);
1136
em->removeSoScXMLStateMachine(statemachine);
1140
See \ref QWidget::minimumSizeHint
1143
QuarterWidget::minimumSizeHint() const
1148
/*! Returns a list of grouped actions that corresponds to the
1149
TransparencyType enum. If you want to create a menu in your
1150
application that controls the transparency type used in
1151
QuarterWidget, add these actions to the menu.
1154
QuarterWidget::transparencyTypeActions() const
1156
return PRIVATE(this)->transparencyTypeActions();
1159
/*! Returns a list of grouped actions that corresponds to the
1160
StereoMode enum. If you want to create a menu in your
1161
application that controls the stereo mode used in
1162
QuarterWidget, add these actions to the menu.
1165
QuarterWidget::stereoModeActions() const
1167
return PRIVATE(this)->stereoModeActions();
1170
/*! Returns a list of grouped actions that corresponds to the
1171
RenderMode enum. If you want to create a menu in your
1172
application that controls the render mode type used in
1173
QuarterWidget, add these actions to the menu.
1176
QuarterWidget::renderModeActions() const
1178
return PRIVATE(this)->renderModeActions();
1182
\property QuarterWidget::navigationModeFile
1184
A url pointing to a navigation mode file which is a scxml file
1185
that defines the possible states for the Coin navigation system
1188
\li \b coin for internal coinresources
1189
\li \b file for filesystem path to resources
1195
Removes any navigationModeFile set.
1198
QuarterWidget::resetNavigationModeFile() {
1199
this->setNavigationModeFile(QUrl());
1203
Sets a navigation mode file. Supports the schemes "coin" and "file"
1205
\param[in] url Url to the resource
1208
QuarterWidget::setNavigationModeFile(const QUrl & url)
1212
if (url.scheme()=="coin") {
1213
filename = url.path();
1215
//Workaround for differences between url scheme, and Coin internal
1216
//scheme in Coin 3.0.
1217
if (filename[0]=='/') {
1218
filename.remove(0,1);
1221
filename = url.scheme()+':'+filename;
1223
else if (url.scheme()=="file")
1224
filename = url.toLocalFile();
1225
else if (url.isEmpty()) {
1226
if (PRIVATE(this)->currentStateMachine) {
1227
this->removeStateMachine(PRIVATE(this)->currentStateMachine);
1228
delete PRIVATE(this)->currentStateMachine;
1229
PRIVATE(this)->currentStateMachine = nullptr;
1230
PRIVATE(this)->navigationModeFile = url;
1235
qDebug()<<url.scheme()<<"is not recognized";
1239
QByteArray filenametmp = filename.toLocal8Bit();
1240
ScXMLStateMachine * stateMachine = nullptr;
1242
if (filenametmp.startsWith("coin:")){
1243
stateMachine = ScXML::readFile(filenametmp.data());
1246
//Use Qt to read the file in case it is a Qt resource
1247
QFile file(filenametmp);
1248
if (file.open(QIODevice::ReadOnly)){
1249
QByteArray fileContents = file.readAll();
1250
#if COIN_MAJOR_VERSION >= 4
1251
stateMachine = ScXML::readBuffer(SbByteBuffer(fileContents.size(), fileContents.constData()));
1253
stateMachine = ScXML::readBuffer(fileContents.constData());
1260
stateMachine->isOfType(SoScXMLStateMachine::getClassTypeId())) {
1261
SoScXMLStateMachine * newsm =
1262
static_cast<SoScXMLStateMachine *>(stateMachine);
1263
if (PRIVATE(this)->currentStateMachine) {
1264
this->removeStateMachine(PRIVATE(this)->currentStateMachine);
1265
delete PRIVATE(this)->currentStateMachine;
1267
this->addStateMachine(newsm);
1268
newsm->initialize();
1269
PRIVATE(this)->currentStateMachine = newsm;
1273
delete stateMachine;
1275
qDebug()<<"Unable to load"<<url;
1279
//If we have gotten this far, we have successfully loaded the
1280
//navigation file, so we set the property
1281
PRIVATE(this)->navigationModeFile = url;
1283
if (QUrl(DEFAULT_NAVIGATIONFILE) == PRIVATE(this)->navigationModeFile ) {
1285
// set up default cursors for the examiner navigation states
1286
//FIXME: It may be overly restrictive to not do this for arbitrary
1287
//navigation systems? - BFG 20090117
1288
this->setStateCursor("interact", Qt::ArrowCursor);
1289
this->setStateCursor("idle", Qt::OpenHandCursor);
1290
this->setStateCursor("rotate", Qt::ClosedHandCursor);
1291
this->setStateCursor("pan", Qt::SizeAllCursor);
1292
this->setStateCursor("zoom", Qt::SizeVerCursor);
1293
this->setStateCursor("dolly", Qt::SizeVerCursor);
1294
this->setStateCursor("seek", Qt::CrossCursor);
1295
this->setStateCursor("spin", Qt::OpenHandCursor);
1300
\retval The current navigationModeFile
1303
QuarterWidget::navigationModeFile() const
1305
return PRIVATE(this)->navigationModeFile;