FreeCAD

Форк
0
/
QuarterWidget.cpp 
1308 строк · 37.2 Кб
1
/**************************************************************************\
2
 * Copyright (c) Kongsberg Oil & Gas Technologies AS
3
 * All rights reserved.
4
 * 
5
 * Redistribution and use in source and binary forms, with or without
6
 * modification, are permitted provided that the following conditions are
7
 * met:
8
 * 
9
 * Redistributions of source code must retain the above copyright notice,
10
 * this list of conditions and the following disclaimer.
11
 * 
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.
15
 * 
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.
19
 * 
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
\**************************************************************************/
32

33
/*!
34
  \class SIM::Coin3D::Quarter::QuarterWidget QuarterWidget.h Quarter/QuarterWidget.h
35

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.
39

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.:
42

43
  \code
44
  QGLContext * context = new QGLContext(QGLFormat(QGL::SampleBuffers), viewer);
45
  if (context->create()) {
46
    viewer->setContext(context);
47
  }
48
  \endcode
49
*/
50

51
#ifdef _MSC_VER
52
#pragma warning(disable : 4267)
53
#endif
54

55
#include <cassert>
56

57
#if HAVE_CONFIG_H
58
# include <config.h>
59
# ifdef  HAVE_GL_GL_H
60
#  include <GL/gl.h>
61
# endif
62
#endif
63

64
#include <QAction>
65
#include <QApplication>
66
#include <QDebug>
67
#include <QEvent>
68
#include <QFile>
69
#include <QGuiApplication>
70
#include <QMetaObject>
71
#include <QOpenGLDebugLogger>
72
#include <QOpenGLDebugMessage>
73
#include <QPaintEvent>
74
#include <QResizeEvent>
75
#include <QWindow>
76

77
#include <Inventor/C/basic.h>
78
#if COIN_MAJOR_VERSION >= 4
79
#include <Inventor/SbByteBuffer.h>
80
#endif
81

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>
94

95
#include "QuarterWidget.h"
96
#include "InteractionMode.h"
97
#include "QuarterP.h"
98
#include "QuarterWidgetP.h"
99
#include "eventhandlers/EventFilter.h"
100
#include "eventhandlers/DragDropHandler.h"
101

102

103
using namespace SIM::Coin3D::Quarter;
104

105
/*!
106
  \enum SIM::Coin3D::Quarter::QuarterWidget::TransparencyType
107

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.
111

112
  See \ref SoGLRenderAction::TransparencyType for a full description of the modes
113
*/
114

115
/*!
116
  \enum SIM::Coin3D::Quarter::QuarterWidget::RenderMode
117

118
  Sets how rendering of primitives is done.
119

120
  See \ref SoRenderManager::RenderMode for a full description of the modes
121
*/
122

123
/*!
124
  \enum SIM::Coin3D::Quarter::QuarterWidget::StereoMode
125

126
  Sets how stereo rendering is performed.
127

128
  See \ref SoRenderManager::StereoMode for a full description of the modes
129
*/
130

131
  enum StereoMode {
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
137
  };
138

139
#define PRIVATE(obj) obj->pimpl
140

141
#ifndef GL_MULTISAMPLE_BIT_EXT
142
#define GL_MULTISAMPLE_BIT_EXT 0x20000000
143
#endif
144

145
//We need to avoid buffer swapping when initializing a QPainter on this widget
146
class CustomGLWidget : public QOpenGLWidget {
147
public:
148
    QSurfaceFormat myFormat;
149

150
    CustomGLWidget(const QSurfaceFormat& format, QWidget* parent = nullptr, const QOpenGLWidget* shareWidget = nullptr, Qt::WindowFlags f = Qt::WindowFlags())
151
     : QOpenGLWidget(parent, f), myFormat(format)
152
    {
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);
164
#endif
165
        setFormat(surfaceFormat);
166
    }
167
    ~CustomGLWidget() override
168
    {
169
    }
170
    void initializeGL() override
171
    {
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);
177

178
            if (logger->initialize())
179
                logger->startLogging(QOpenGLDebugLogger::SynchronousLogging);
180
        }
181
#endif
182
        if (context) {
183
            connect(context, &QOpenGLContext::aboutToBeDestroyed,
184
                this, &CustomGLWidget::aboutToDestroyGLContext, Qt::DirectConnection);
185
        }
186
        connect(this, &CustomGLWidget::resized, this, &CustomGLWidget::slotResized);
187
    }
188
    // paintGL() is invoked when e.g. using the method grabFramebuffer of this class
189
    // \code
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()
195
    // \endcode
196
    void paintGL() override
197
    {
198
        QuarterWidget* qw = qobject_cast<QuarterWidget*>(parentWidget());
199
        if (qw) {
200
            qw->redraw();
201
        }
202
    }
203
    void aboutToDestroyGLContext()
204
    {
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());
208
        if (!qw)
209
            return;
210
        QMetaObject::invokeMethod(parent(), "aboutToDestroyGLContext",
211
            Qt::DirectConnection,
212
            QGenericReturnArgument());
213
    }
214
    bool event(QEvent *e) override
215
    {
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
219
        // null.
220
        if (e->type() == QEvent::WindowChangeInternal) {
221
            if (!qApp->testAttribute(Qt::AA_ShareOpenGLContexts)) {
222
                QOpenGLDebugLogger* logger = this->findChild<QOpenGLDebugLogger*>();
223
                if (logger) {
224
                    logger->stopLogging();
225
                    delete logger;
226
                }
227
            }
228
        }
229

230
        return QOpenGLWidget::event(e);
231
    }
232
    void handleLoggedMessage(const QOpenGLDebugMessage &message)
233
    {
234
        qDebug() << message;
235
    }
236
    void showEvent(QShowEvent*) override
237
    {
238
        update(); // force update when changing window mode
239
    }
240
    void slotResized()
241
    {
242
        update(); // fixes flickering on some systems
243
    }
244
};
245

246
/*! constructor */
247
QuarterWidget::QuarterWidget(const QtGLFormat & format, QWidget * parent, const QtGLWidget * sharewidget, Qt::WindowFlags f)
248
  : inherited(parent)
249
{
250
  Q_UNUSED(f); 
251
  this->constructor(format, sharewidget);
252
}
253

254
/*! constructor */
255
QuarterWidget::QuarterWidget(QWidget * parent, const QtGLWidget * sharewidget, Qt::WindowFlags f)
256
  : inherited(parent)
257
{
258
  Q_UNUSED(f); 
259
  this->constructor(QtGLFormat(), sharewidget);
260
}
261

262
/*! constructor */
263
QuarterWidget::QuarterWidget(QtGLContext * context, QWidget * parent, const QtGLWidget * sharewidget, Qt::WindowFlags f)
264
  : inherited(parent)
265
{
266
  Q_UNUSED(f); 
267
  this->constructor(context->format(), sharewidget);
268
}
269

270
void
271
QuarterWidget::constructor(const QtGLFormat & format, const QtGLWidget * sharewidget)
272
{
273
  QGraphicsScene* scene = new QGraphicsScene(this);
274
  setScene(scene);
275
  setViewport(new CustomGLWidget(format, this, sharewidget)); 
276
  
277
  setFrameStyle(QFrame::NoFrame);
278
  setAutoFillBackground(false);
279
  viewport()->setAutoFillBackground(false);
280
  setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
281
  setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
282
    
283
  PRIVATE(this) = new QuarterWidgetP(this, sharewidget);
284

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;
290

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);
295

296
  PRIVATE(this)->currentStateMachine = nullptr;
297

298
  PRIVATE(this)->headlight = new SoDirectionalLight;
299
  PRIVATE(this)->headlight->ref();
300

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));
307

308
  PRIVATE(this)->soeventmanager->setNavigationState(SoEventManager::MIXED_NAVIGATION);
309

310
  // set up a cache context for the default SoGLRenderAction
311
  PRIVATE(this)->sorendermanager->getGLRenderAction()->setCacheContext(this->getCacheContextId());
312

313
  this->setMouseTracking(true);
314

315
  // Qt::StrongFocus means the widget will accept keyboard focus by
316
  // both tabbing and clicking
317
  this->setFocusPolicy(Qt::StrongFocus);
318

319
  this->installEventFilter(PRIVATE(this)->eventfilter);
320
  this->installEventFilter(PRIVATE(this)->interactionmode);
321

322
  initialized = false;
323
}
324

325
void
326
QuarterWidget::replaceViewport()
327
{
328
  CustomGLWidget* oldvp = static_cast<CustomGLWidget*>(viewport());
329
  CustomGLWidget* newvp = new CustomGLWidget(oldvp->myFormat, this);
330
  PRIVATE(this)->replaceGLWidget(newvp);
331
  setViewport(newvp);
332

333
  setAutoFillBackground(false);
334
  viewport()->setAutoFillBackground(false);
335
}
336

337
void
338
QuarterWidget::aboutToDestroyGLContext()
339
{
340
}
341

342
/*! destructor */
343
QuarterWidget::~QuarterWidget()
344
{
345
  if (PRIVATE(this)->currentStateMachine) {
346
    this->removeStateMachine(PRIVATE(this)->currentStateMachine);
347
    delete PRIVATE(this)->currentStateMachine;
348
  }
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);
356
}
357

358
/*!
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
362
*/
363
void
364
QuarterWidget::setStateCursor(const SbName & state, const QCursor & cursor)
365
{
366
  assert(QuarterP::statecursormap);
367
  // will overwrite the value of an existing item
368
  QuarterP::statecursormap->insert(state, cursor);
369
}
370

371
/*!
372
  Maps a state to a cursor
373

374
  \param[in] state Named state in the statemachine
375
  \retval Cursor corresponding to the given state
376
*/
377
QCursor
378
QuarterWidget::stateCursor(const SbName & state)
379
{
380
  assert(QuarterP::statecursormap);
381
  return QuarterP::statecursormap->value(state);
382
}
383

384
/*!
385
  \property QuarterWidget::headlightEnabled
386

387
  \copydetails QuarterWidget::setHeadlightEnabled
388
*/
389

390
/*!
391
  Enable/disable the headlight. This will toggle the SoDirectionalLight::on
392
  field (returned from getHeadlight()).
393
*/
394
void
395
QuarterWidget::setHeadlightEnabled(bool onoff)
396
{
397
  PRIVATE(this)->headlight->on = onoff;
398
}
399

400
/*!
401
  Returns true if the headlight is on, false if it is off
402
*/
403
bool
404
QuarterWidget::headlightEnabled() const
405
{
406
  return PRIVATE(this)->headlight->on.getValue();
407
}
408

409
/*!
410
  Returns the light used for the headlight.
411
*/
412
SoDirectionalLight *
413
QuarterWidget::getHeadlight() const
414
{
415
  return PRIVATE(this)->headlight;
416
}
417

418
/*!
419
  \property QuarterWidget::clearZBuffer
420

421
  \copydetails QuarterWidget::setClearZBuffer
422
*/
423

424
/*!
425
  Specify if you want the z buffer to be cleared before
426
  redraw. This is on by default.
427
*/
428
void
429
QuarterWidget::setClearZBuffer(bool onoff)
430
{
431
  PRIVATE(this)->clearzbuffer = onoff;
432
}
433

434
/*!
435
  Returns true if the z buffer is cleared before rendering.
436
*/
437
bool
438
QuarterWidget::clearZBuffer() const
439
{
440
  return PRIVATE(this)->clearzbuffer;
441
}
442

443
/*!
444
  \property QuarterWidget::clearWindow
445

446
  \copydetails QuarterWidget::setClearWindow
447
*/
448

449
/*!
450
  Specify if you want the rendering buffer to be cleared before
451
  rendering. This is on by default.
452
 */
453
void
454
QuarterWidget::setClearWindow(bool onoff)
455
{
456
  PRIVATE(this)->clearwindow = onoff;
457
}
458

459
/*!
460
  Returns true if the rendering buffer is cleared before rendering.
461
*/
462
bool
463
QuarterWidget::clearWindow() const
464
{
465
  return PRIVATE(this)->clearwindow;
466
}
467

468
/*!
469
  \property QuarterWidget::interactionModeEnabled
470

471
  \copydetails QuarterWidget::setInteractionModeEnabled
472
*/
473

474
/*!
475
  Enable/disable interaction mode.
476

477
  Specifies whether you may use the alt-key to enter interaction mode.
478
*/
479
void
480
QuarterWidget::setInteractionModeEnabled(bool onoff)
481
{
482
  PRIVATE(this)->interactionmode->setEnabled(onoff);
483
}
484

485
/*!
486
  Returns true if interaction mode is enabled, false otherwise.
487
 */
488
bool
489
QuarterWidget::interactionModeEnabled() const
490
{
491
  return PRIVATE(this)->interactionmode->enabled();
492
}
493

494
/*!
495
  \property QuarterWidget::interactionModeOn
496

497
  \copydetails QuarterWidget::setInteractionModeOn
498
*/
499

500
/*!
501
  Turn interaction mode on or off.
502
*/
503
void
504
QuarterWidget::setInteractionModeOn(bool onoff)
505
{
506
  PRIVATE(this)->interactionmode->setOn(onoff);
507
}
508

509
/*!
510
  Returns true if interaction mode is on.
511
 */
512
bool
513
QuarterWidget::interactionModeOn() const
514
{
515
  return PRIVATE(this)->interactionmode->on();
516
}
517

518
/*!
519
  Returns the Coin cache context id for this widget.
520
*/
521
uint32_t
522
QuarterWidget::getCacheContextId() const
523
{
524
  return PRIVATE(this)->getCacheContextId();
525
}
526

527
/*!
528
  \property QuarterWidget::transparencyType
529

530
  \copydetails QuarterWidget::setTransparencyType
531
*/
532

533
/*!
534
  Sets the transparency type to be used for the scene.
535
*/
536
void
537
QuarterWidget::setTransparencyType(TransparencyType type)
538
{
539
  assert(PRIVATE(this)->sorendermanager);
540
  PRIVATE(this)->sorendermanager->getGLRenderAction()->setTransparencyType((SoGLRenderAction::TransparencyType)type);
541
  PRIVATE(this)->sorendermanager->scheduleRedraw();
542
}
543

544
/*!
545
  \retval The current \ref TransparencyType
546
*/
547
QuarterWidget::TransparencyType
548
QuarterWidget::transparencyType() const
549
{
550
  assert(PRIVATE(this)->sorendermanager);
551
  SoGLRenderAction * action = PRIVATE(this)->sorendermanager->getGLRenderAction();
552
  return static_cast<QuarterWidget::TransparencyType>(action->getTransparencyType());
553
}
554

555
/*!
556
  \property QuarterWidget::renderMode
557

558
  \copydetails QuarterWidget::setRenderMode
559
*/
560

561
/*!
562
  \copydoc RenderMode
563
*/
564
void
565
QuarterWidget::setRenderMode(RenderMode mode)
566
{
567
  assert(PRIVATE(this)->sorendermanager);
568
  PRIVATE(this)->sorendermanager->setRenderMode(static_cast<SoRenderManager::RenderMode>(mode));
569
  PRIVATE(this)->sorendermanager->scheduleRedraw();
570
}
571

572
/*!
573
  \retval The current \ref RenderMode
574
*/
575
QuarterWidget::RenderMode
576
QuarterWidget::renderMode() const
577
{
578
  assert(PRIVATE(this)->sorendermanager);
579
  return static_cast<RenderMode>(PRIVATE(this)->sorendermanager->getRenderMode());
580
}
581

582
/*!
583
  \property QuarterWidget::stereoMode
584

585
  \copydetails QuarterWidget::setStereoMode
586
*/
587

588
/*!
589
  \copydoc StereoMode
590
*/
591
void
592
QuarterWidget::setStereoMode(StereoMode mode)
593
{
594
  assert(PRIVATE(this)->sorendermanager);
595
  PRIVATE(this)->sorendermanager->setStereoMode(static_cast<SoRenderManager::StereoMode>(mode));
596
  PRIVATE(this)->sorendermanager->scheduleRedraw();
597
}
598

599

600
/*!
601
  \retval The current \ref StereoMode
602
*/
603
QuarterWidget::StereoMode
604
QuarterWidget::stereoMode() const
605
{
606
  assert(PRIVATE(this)->sorendermanager);
607
  return static_cast<StereoMode>(PRIVATE(this)->sorendermanager->getStereoMode());
608
}
609

610
/*!
611
  \property QuarterWidget::devicePixelRatio
612

613
  \copydetails QuarterWidget::devicePixelRatio
614
*/
615

616
/*!
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)
619
 */
620

621
qreal
622
QuarterWidget::devicePixelRatio() const
623
{
624
  return PRIVATE(this)->device_pixel_ratio;
625
}
626

627
/*!
628
  Sets the Inventor scenegraph to be rendered
629
 */
630
void
631
QuarterWidget::setSceneGraph(SoNode * node)
632
{
633
  if (node == PRIVATE(this)->scene) {
634
    return;
635
  }
636

637
  if (PRIVATE(this)->scene) {
638
    PRIVATE(this)->scene->unref();
639
    PRIVATE(this)->scene = nullptr;
640
  }
641

642
  SoCamera * camera = nullptr;
643
  SoSeparator * superscene = nullptr;
644
  bool viewall = false;
645

646
  if (node) {
647
    PRIVATE(this)->scene = node;
648
    PRIVATE(this)->scene->ref();
649

650
    superscene = new SoSeparator;
651
    superscene->addChild(PRIVATE(this)->headlight);
652

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);
657
      viewall = true;
658
    }
659

660
    superscene->addChild(node);
661
  }
662

663
  PRIVATE(this)->soeventmanager->setCamera(camera);
664
  PRIVATE(this)->sorendermanager->setCamera(camera);
665
  PRIVATE(this)->soeventmanager->setSceneGraph(superscene);
666
  PRIVATE(this)->sorendermanager->setSceneGraph(superscene);
667

668
  if (viewall) { this->viewAll(); }
669
  if (superscene) { superscene->touch(); }
670
}
671

672
/*!
673
  Returns pointer to root of scene graph
674
*/
675
SoNode *
676
QuarterWidget::getSceneGraph() const
677
{
678
  return PRIVATE(this)->scene;
679
}
680

681
/*!
682
  Set the render manager for the widget.
683
*/
684
void
685
QuarterWidget::setSoRenderManager(SoRenderManager * manager)
686
{
687
  bool carrydata = false;
688
  SoNode * scene = nullptr;
689
  SoCamera * camera = nullptr;
690
  SbViewportRegion vp;
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
695
    carrydata = true;
696
  }
697

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();
701
  
702
  if (PRIVATE(this)->initialsorendermanager) {
703
    delete PRIVATE(this)->sorendermanager;
704
    PRIVATE(this)->initialsorendermanager = false;
705
  }
706
  PRIVATE(this)->sorendermanager = manager;
707
  if (carrydata) {
708
    PRIVATE(this)->sorendermanager->setSceneGraph(scene);
709
    PRIVATE(this)->sorendermanager->setCamera(camera);
710
    PRIVATE(this)->sorendermanager->setViewportRegion(vp);
711
  }
712

713
  if (scene) scene->unref();
714
  if (camera) camera->unref();
715
}
716

717
/*!
718
  Returns a pointer to the render manager.
719
*/
720
SoRenderManager *
721
QuarterWidget::getSoRenderManager() const
722
{
723
  return PRIVATE(this)->sorendermanager;
724
}
725

726
/*!
727
  Set the Coin event manager for the widget.
728
*/
729
void
730
QuarterWidget::setSoEventManager(SoEventManager * manager)
731
{
732
  bool carrydata = false;
733
  SoNode * scene = nullptr;
734
  SoCamera * camera = nullptr;
735
  SbViewportRegion vp;
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
740
    carrydata = true;
741
  }
742

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();
746

747
  if (PRIVATE(this)->initialsoeventmanager) {
748
    delete PRIVATE(this)->soeventmanager;
749
    PRIVATE(this)->initialsoeventmanager = false;
750
  }
751
  PRIVATE(this)->soeventmanager = manager;
752
  if (carrydata) {
753
    PRIVATE(this)->soeventmanager->setSceneGraph(scene);
754
    PRIVATE(this)->soeventmanager->setCamera(camera);
755
    PRIVATE(this)->soeventmanager->setViewportRegion(vp);
756
  }
757

758
  if (scene) scene->unref();
759
  if (camera) camera->unref();
760
}
761

762
/*!
763
  Returns a pointer to the event manager
764
*/
765
SoEventManager *
766
QuarterWidget::getSoEventManager() const
767
{
768
  return PRIVATE(this)->soeventmanager;
769
}
770

771
/*!
772
  Returns a pointer to the event filter
773
 */
774
EventFilter *
775
QuarterWidget::getEventFilter() const
776
{
777
  return PRIVATE(this)->eventfilter;
778
}
779

780
/*!
781
  Reposition the current camera to display the entire scene
782
 */
783
void
784
QuarterWidget::viewAll()
785
{
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();
793
    }
794
  }
795
}
796

797
/*!
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.
800
*/
801
void
802
QuarterWidget::seek()
803
{
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();
811
    }
812
  }
813
}
814

815
bool
816
QuarterWidget::updateDevicePixelRatio() {
817
    qreal dev_pix_ratio = 1.0;
818
    QWidget* winwidg = window();
819
    QWindow* win = nullptr;
820
    if(winwidg) {
821
        win = winwidg->windowHandle();
822
    }
823
    if(win) {
824
        dev_pix_ratio = win->devicePixelRatio();
825
    }
826
    else {
827
        dev_pix_ratio = ((QGuiApplication*)QGuiApplication::instance())->devicePixelRatio();
828
    }
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);
832
        return true;
833
    }
834
    return false;
835
}
836

837
/*!
838
  Overridden from QGLWidget to resize the Coin scenegraph
839
 */
840
void QuarterWidget::resizeEvent(QResizeEvent* event)
841
{
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());
846

847
    SbViewportRegion vp(width, height);
848
    PRIVATE(this)->sorendermanager->setViewportRegion(vp);
849
    PRIVATE(this)->soeventmanager->setViewportRegion(vp);
850
    if (scene())
851
        scene()->setSceneRect(QRect(QPoint(0, 0), event->size()));
852
    QGraphicsView::resizeEvent(event);
853
}
854

855
/*!
856
  Overridden from QGLWidget to render the scenegraph
857
*/
858
void QuarterWidget::paintEvent(QPaintEvent* event)
859
{
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);
867
    }
868

869
    if(!initialized) {
870
        this->getSoRenderManager()->reinitialize();
871
        initialized = true;
872
    }
873

874
    getSoRenderManager()->activate();
875

876
    glMatrixMode(GL_PROJECTION);
877

878
    QtGLWidget* w = static_cast<QtGLWidget*>(this->viewport());
879
    if (!w->isValid()) {
880
        qWarning() << "No valid GL context found!";
881
        return;
882
    }
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.
890

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
894
    // cases
895

896
    PRIVATE(this)->autoredrawenabled = false;
897

898
    if(PRIVATE(this)->processdelayqueue && SoDB::getSensorManager()->isDelaySensorPending()) {
899
        // processing the sensors might trigger a redraw in another
900
        // context. Release this context temporarily
901
        w->doneCurrent();
902
        SoDB::getSensorManager()->processDelayQueue(false);
903
        w->makeCurrent();
904
    }
905

906
    assert(w->isValid() && "No valid GL context found!");
907

908
    // Causes an OpenGL error on resize
909
    //glDrawBuffer(w->format().swapBehavior() == QSurfaceFormat::DoubleBuffer ? GL_BACK : GL_FRONT);
910

911
    w->makeCurrent();
912
    this->actualRedraw();
913

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);
919
    glPopAttrib();
920

921
    // Causes an OpenGL error on resize
922
    //if (w->format().swapBehavior() == QSurfaceFormat::DoubleBuffer)
923
    //    w->context()->swapBuffers(w->context()->surface());
924

925
    PRIVATE(this)->autoredrawenabled = true;
926

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;
930
}
931

932
bool QuarterWidget::viewportEvent(QEvent* event)
933
{
934

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
938
    // this widget.
939
    if (event->type() == QEvent::MouseButtonDblClick ||
940
        event->type() == QEvent::MouseButtonPress) {
941
        QMouseEvent* mouse = static_cast<QMouseEvent*>(event);
942
        QGraphicsItem *item = itemAt(mouse->pos());
943
        if (!item) {
944
            QGraphicsView::viewportEvent(event);
945
            return false;
946
        }
947
    }
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);
953
            return false;
954
        }
955
    }
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();
960
#else
961
        QPoint pos = wheel->position().toPoint();
962
#endif
963
        QGraphicsItem* item = itemAt(pos);
964
        if (!item) {
965
            QGraphicsView::viewportEvent(event);
966
            return false;
967
        }
968
    }
969

970
    return QGraphicsView::viewportEvent(event);
971
}
972

973
/*!
974
  Used for rendering the scene. Usually Coin/Quarter will automatically redraw
975
  the scene graph at regular intervals, after the scene is modified.
976

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.
980
*/
981
void
982
QuarterWidget::redraw()
983
{
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;
987

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.
998
  //
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();
1003
}
1004

1005
/*!
1006
  Overridden from QGLWidget to render the scenegraph
1007
 */
1008
void
1009
QuarterWidget::actualRedraw()
1010
{
1011
  PRIVATE(this)->sorendermanager->render(PRIVATE(this)->clearwindow,
1012
                                         PRIVATE(this)->clearzbuffer);
1013
}
1014

1015

1016
/*!
1017
  Passes an event to the eventmanager.
1018

1019
  \param[in] event to pass
1020
  \retval Returns true if the event was successfully processed
1021
*/
1022
bool
1023
QuarterWidget::processSoEvent(const SoEvent * event)
1024
{
1025
  return
1026
    event &&
1027
    PRIVATE(this)->soeventmanager &&
1028
    PRIVATE(this)->soeventmanager->processEvent(event);
1029
}
1030

1031
/*!
1032
  \property QuarterWidget::backgroundColor
1033
  \copydoc QuarterWidget::setBackgroundColor
1034
*/
1035

1036
/*!
1037
  Set backgroundcolor to a given QColor
1038

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.
1043
 */
1044
void
1045
QuarterWidget::setBackgroundColor(const QColor & color)
1046
{
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));
1051

1052
  PRIVATE(this)->sorendermanager->setBackgroundColor(bgcolor);
1053
  PRIVATE(this)->sorendermanager->scheduleRedraw();
1054
}
1055

1056
/*!
1057
  Returns color used for clearing the rendering area before
1058
  rendering the scene.
1059
 */
1060
QColor
1061
QuarterWidget::backgroundColor() const
1062
{
1063
  SbColor4f bg = PRIVATE(this)->sorendermanager->getBackgroundColor();
1064

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)};
1069
}
1070

1071
/*!
1072
  Returns the context menu used by the widget.
1073
*/
1074
QMenu *
1075
QuarterWidget::getContextMenu() const
1076
{
1077
  return PRIVATE(this)->contextMenu();
1078
}
1079

1080
/*!
1081
  \retval Is context menu enabled?
1082
*/
1083
bool
1084
QuarterWidget::contextMenuEnabled() const
1085
{
1086
  return PRIVATE(this)->contextmenuenabled;
1087
}
1088

1089
/*!
1090
  \property QuarterWidget::contextMenuEnabled
1091

1092
  \copydetails QuarterWidget::setContextMenuEnabled
1093
*/
1094

1095
/*!
1096
  Controls the display of the contextmenu
1097

1098
  \param[in] yes Context menu on?
1099
*/
1100
void
1101
QuarterWidget::setContextMenuEnabled(bool yes)
1102
{
1103
  PRIVATE(this)->contextmenuenabled = yes;
1104
}
1105

1106
/*!
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.
1111

1112
  \sa removeStateMachine
1113
*/
1114
void
1115
QuarterWidget::addStateMachine(SoScXMLStateMachine * statemachine)
1116
{
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));
1122
}
1123

1124
/*!
1125
  Convenience method that removes a state machine to the current
1126
  SoEventManager.
1127

1128
  \sa addStateMachine
1129
*/
1130
void
1131
QuarterWidget::removeStateMachine(SoScXMLStateMachine * statemachine)
1132
{
1133
  SoEventManager * em = this->getSoEventManager();
1134
  statemachine->setSceneGraphRoot(nullptr);
1135
  statemachine->setActiveCamera(nullptr);
1136
  em->removeSoScXMLStateMachine(statemachine);
1137
}
1138

1139
/*!
1140
  See \ref QWidget::minimumSizeHint
1141
 */
1142
QSize
1143
QuarterWidget::minimumSizeHint() const
1144
{
1145
  return {50, 50};
1146
}
1147

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.
1152
 */
1153
QList<QAction *>
1154
QuarterWidget::transparencyTypeActions() const
1155
{
1156
  return PRIVATE(this)->transparencyTypeActions();
1157
}
1158

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.
1163
 */
1164
QList<QAction *>
1165
QuarterWidget::stereoModeActions() const
1166
{
1167
  return PRIVATE(this)->stereoModeActions();
1168
}
1169

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.
1174
 */
1175
QList<QAction *>
1176
QuarterWidget::renderModeActions() const
1177
{
1178
  return PRIVATE(this)->renderModeActions();
1179
}
1180

1181
/*!
1182
  \property QuarterWidget::navigationModeFile
1183

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
1186

1187
  Supports:
1188
  \li \b coin for internal coinresources
1189
  \li \b file for filesystem path to resources
1190

1191
  \sa scxml
1192
*/
1193

1194
/*!
1195
  Removes any navigationModeFile set.
1196
*/
1197
void
1198
QuarterWidget::resetNavigationModeFile() {
1199
  this->setNavigationModeFile(QUrl());
1200
}
1201

1202
/*!
1203
  Sets a navigation mode file. Supports the schemes "coin" and "file"
1204

1205
  \param[in] url Url to the resource
1206
*/
1207
void
1208
QuarterWidget::setNavigationModeFile(const QUrl & url)
1209
{
1210
  QString filename;
1211

1212
  if (url.scheme()=="coin") {
1213
    filename = url.path();
1214

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);
1219
    }
1220

1221
    filename = url.scheme()+':'+filename;
1222
  }
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;
1231
    }
1232
    return;
1233
  }
1234
  else {
1235
    qDebug()<<url.scheme()<<"is not recognized";
1236
    return;
1237
  }
1238

1239
  QByteArray filenametmp = filename.toLocal8Bit();
1240
  ScXMLStateMachine * stateMachine = nullptr;
1241

1242
  if (filenametmp.startsWith("coin:")){
1243
    stateMachine = ScXML::readFile(filenametmp.data());
1244
  }
1245
  else {
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()));
1252
#else
1253
      stateMachine = ScXML::readBuffer(fileContents.constData());
1254
#endif
1255
      file.close();
1256
    }
1257
  }
1258

1259
  if (stateMachine &&
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;
1266
    }
1267
    this->addStateMachine(newsm);
1268
    newsm->initialize();
1269
    PRIVATE(this)->currentStateMachine = newsm;
1270
  }
1271
  else {
1272
    if (stateMachine)
1273
      delete stateMachine;
1274
    qDebug()<<filename;
1275
    qDebug()<<"Unable to load"<<url;
1276
    return;
1277
  }
1278

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;
1282

1283
  if (QUrl(DEFAULT_NAVIGATIONFILE) == PRIVATE(this)->navigationModeFile ) {
1284

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);
1296
  }
1297
}
1298

1299
/*!
1300
  \retval The current navigationModeFile
1301
*/
1302
const QUrl &
1303
QuarterWidget::navigationModeFile() const
1304
{
1305
  return PRIVATE(this)->navigationModeFile;
1306
}
1307

1308
#undef PRIVATE
1309

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.