1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the Qt Data Visualization module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:GPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU |
19 | ** General Public License version 3 or (at your option) any later version |
20 | ** approved by the KDE Free Qt Foundation. The licenses are as published by |
21 | ** the Free Software Foundation and appearing in the file LICENSE.GPL3 |
22 | ** included in the packaging of this file. Please review the following |
23 | ** information to ensure the GNU General Public License requirements will |
24 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
25 | ** |
26 | ** $QT_END_LICENSE$ |
27 | ** |
28 | ****************************************************************************/ |
29 | |
30 | #include "abstractdeclarative_p.h" |
31 | #include "declarativetheme_p.h" |
32 | #include "declarativerendernode_p.h" |
33 | #include <QtGui/QGuiApplication> |
34 | #if defined(Q_OS_IOS) |
35 | #include <QtCore/QTimer> |
36 | #endif |
37 | #if defined(Q_OS_OSX) |
38 | #include <qpa/qplatformnativeinterface.h> |
39 | #endif |
40 | |
41 | #if !defined(Q_OS_MAC) && !defined(Q_OS_ANDROID) && !defined(Q_OS_WINRT) |
42 | #define USE_SHARED_CONTEXT |
43 | #else |
44 | #include "glstatestore_p.h" |
45 | #endif |
46 | |
47 | QT_BEGIN_NAMESPACE_DATAVISUALIZATION |
48 | |
49 | static QList<const QQuickWindow *> clearList; |
50 | static QHash<AbstractDeclarative *, QQuickWindow *> graphWindowList; |
51 | static QHash<QQuickWindow *, bool> windowClearList; |
52 | |
53 | AbstractDeclarative::AbstractDeclarative(QQuickItem *parent) : |
54 | QQuickItem(parent), |
55 | m_controller(0), |
56 | m_contextWindow(0), |
57 | m_renderMode(RenderIndirect), |
58 | m_samples(0), |
59 | m_windowSamples(0), |
60 | m_initialisedSize(0, 0), |
61 | m_contextOrStateStore(0), |
62 | m_qtContext(0), |
63 | m_mainThread(QThread::currentThread()), |
64 | m_contextThread(0) |
65 | { |
66 | m_nodeMutex = QSharedPointer<QMutex>::create(); |
67 | |
68 | connect(sender: this, signal: &QQuickItem::windowChanged, receiver: this, slot: &AbstractDeclarative::handleWindowChanged); |
69 | |
70 | // Set contents to false in case we are in qml designer to make component look nice |
71 | m_runningInDesigner = QGuiApplication::applicationDisplayName() == "Qml2Puppet" ; |
72 | setFlag(flag: ItemHasContents, enabled: !m_runningInDesigner); |
73 | } |
74 | |
75 | AbstractDeclarative::~AbstractDeclarative() |
76 | { |
77 | destroyContext(); |
78 | |
79 | disconnect(sender: this, signal: 0, receiver: this, member: 0); |
80 | checkWindowList(window: 0); |
81 | |
82 | // Make sure not deleting locked mutex |
83 | QMutexLocker locker(&m_mutex); |
84 | locker.unlock(); |
85 | |
86 | m_nodeMutex.clear(); |
87 | } |
88 | |
89 | void AbstractDeclarative::setRenderingMode(AbstractDeclarative::RenderingMode mode) |
90 | { |
91 | if (mode == m_renderMode) |
92 | return; |
93 | |
94 | RenderingMode previousMode = m_renderMode; |
95 | |
96 | m_renderMode = mode; |
97 | |
98 | QQuickWindow *win = window(); |
99 | |
100 | switch (mode) { |
101 | case RenderDirectToBackground: |
102 | // Intentional flowthrough |
103 | case RenderDirectToBackground_NoClear: |
104 | m_initialisedSize = QSize(0, 0); |
105 | if (previousMode == RenderIndirect) { |
106 | update(); |
107 | setFlag(flag: ItemHasContents, enabled: false); |
108 | if (win) { |
109 | QObject::connect(sender: win, signal: &QQuickWindow::beforeRendering, receiver: this, |
110 | slot: &AbstractDeclarative::render, type: Qt::DirectConnection); |
111 | checkWindowList(window: win); |
112 | setAntialiasing(m_windowSamples > 0); |
113 | if (m_windowSamples != m_samples) |
114 | emit msaaSamplesChanged(samples: m_windowSamples); |
115 | } |
116 | } |
117 | break; |
118 | case RenderIndirect: |
119 | m_initialisedSize = QSize(0, 0); |
120 | setFlag(flag: ItemHasContents, enabled: !m_runningInDesigner); |
121 | update(); |
122 | if (win) { |
123 | QObject::disconnect(sender: win, signal: &QQuickWindow::beforeRendering, receiver: this, |
124 | slot: &AbstractDeclarative::render); |
125 | checkWindowList(window: win); |
126 | } |
127 | setAntialiasing(m_samples > 0); |
128 | if (m_windowSamples != m_samples) |
129 | emit msaaSamplesChanged(samples: m_samples); |
130 | break; |
131 | } |
132 | |
133 | updateWindowParameters(); |
134 | |
135 | emit renderingModeChanged(mode); |
136 | } |
137 | |
138 | AbstractDeclarative::RenderingMode AbstractDeclarative::renderingMode() const |
139 | { |
140 | return m_renderMode; |
141 | } |
142 | |
143 | QSGNode *AbstractDeclarative::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) |
144 | { |
145 | QSize boundingSize = boundingRect().size().toSize() * m_controller->scene()->devicePixelRatio(); |
146 | if (m_runningInDesigner || boundingSize.width() <= 0 || boundingSize.height() <= 0 |
147 | || m_controller.isNull() || !window()) { |
148 | delete oldNode; |
149 | return 0; |
150 | } |
151 | DeclarativeRenderNode *node = static_cast<DeclarativeRenderNode *>(oldNode); |
152 | |
153 | if (!node) { |
154 | node = new DeclarativeRenderNode(this, m_nodeMutex); |
155 | node->setController(m_controller.data()); |
156 | node->setQuickWindow(window()); |
157 | } |
158 | |
159 | node->setSize(boundingSize); |
160 | node->setSamples(m_samples); |
161 | node->update(); |
162 | node->markDirty(bits: QSGNode::DirtyMaterial); |
163 | |
164 | return node; |
165 | } |
166 | |
167 | Declarative3DScene* AbstractDeclarative::scene() const |
168 | { |
169 | return static_cast<Declarative3DScene *>(m_controller->scene()); |
170 | } |
171 | |
172 | void AbstractDeclarative::setTheme(Q3DTheme *theme) |
173 | { |
174 | m_controller->setActiveTheme(theme, force: isComponentComplete()); |
175 | } |
176 | |
177 | Q3DTheme *AbstractDeclarative::theme() const |
178 | { |
179 | return m_controller->activeTheme(); |
180 | } |
181 | |
182 | void AbstractDeclarative::clearSelection() |
183 | { |
184 | m_controller->clearSelection(); |
185 | } |
186 | |
187 | void AbstractDeclarative::setSelectionMode(SelectionFlags mode) |
188 | { |
189 | int intmode = int(mode); |
190 | m_controller->setSelectionMode(QAbstract3DGraph::SelectionFlags(intmode)); |
191 | } |
192 | |
193 | AbstractDeclarative::SelectionFlags AbstractDeclarative::selectionMode() const |
194 | { |
195 | int intmode = int(m_controller->selectionMode()); |
196 | return SelectionFlags(intmode); |
197 | } |
198 | |
199 | void AbstractDeclarative::setShadowQuality(ShadowQuality quality) |
200 | { |
201 | m_controller->setShadowQuality(QAbstract3DGraph::ShadowQuality(quality)); |
202 | } |
203 | |
204 | AbstractDeclarative::ShadowQuality AbstractDeclarative::shadowQuality() const |
205 | { |
206 | return ShadowQuality(m_controller->shadowQuality()); |
207 | } |
208 | |
209 | bool AbstractDeclarative::shadowsSupported() const |
210 | { |
211 | return m_controller->shadowsSupported(); |
212 | } |
213 | |
214 | int AbstractDeclarative::addCustomItem(QCustom3DItem *item) |
215 | { |
216 | return m_controller->addCustomItem(item); |
217 | } |
218 | |
219 | void AbstractDeclarative::removeCustomItems() |
220 | { |
221 | m_controller->deleteCustomItems(); |
222 | } |
223 | |
224 | void AbstractDeclarative::removeCustomItem(QCustom3DItem *item) |
225 | { |
226 | m_controller->deleteCustomItem(item); |
227 | } |
228 | |
229 | void AbstractDeclarative::removeCustomItemAt(const QVector3D &position) |
230 | { |
231 | m_controller->deleteCustomItem(position); |
232 | } |
233 | |
234 | void AbstractDeclarative::releaseCustomItem(QCustom3DItem *item) |
235 | { |
236 | m_controller->releaseCustomItem(item); |
237 | } |
238 | |
239 | int AbstractDeclarative::selectedLabelIndex() const |
240 | { |
241 | return m_controller->selectedLabelIndex(); |
242 | } |
243 | |
244 | QAbstract3DAxis *AbstractDeclarative::selectedAxis() const |
245 | { |
246 | return m_controller->selectedAxis(); |
247 | } |
248 | |
249 | int AbstractDeclarative::selectedCustomItemIndex() const |
250 | { |
251 | return m_controller->selectedCustomItemIndex(); |
252 | } |
253 | |
254 | QCustom3DItem *AbstractDeclarative::selectedCustomItem() const |
255 | { |
256 | return m_controller->selectedCustomItem(); |
257 | } |
258 | |
259 | QQmlListProperty<QCustom3DItem> AbstractDeclarative::customItemList() |
260 | { |
261 | return QQmlListProperty<QCustom3DItem>(this, this, |
262 | &AbstractDeclarative::appendCustomItemFunc, |
263 | &AbstractDeclarative::countCustomItemFunc, |
264 | &AbstractDeclarative::atCustomItemFunc, |
265 | &AbstractDeclarative::clearCustomItemFunc); |
266 | } |
267 | |
268 | void AbstractDeclarative::appendCustomItemFunc(QQmlListProperty<QCustom3DItem> *list, |
269 | QCustom3DItem *item) |
270 | { |
271 | AbstractDeclarative *decl = reinterpret_cast<AbstractDeclarative *>(list->data); |
272 | decl->addCustomItem(item); |
273 | } |
274 | |
275 | int AbstractDeclarative::countCustomItemFunc(QQmlListProperty<QCustom3DItem> *list) |
276 | { |
277 | return reinterpret_cast<AbstractDeclarative *>(list->data)->m_controller->m_customItems.size(); |
278 | } |
279 | |
280 | QCustom3DItem *AbstractDeclarative::atCustomItemFunc(QQmlListProperty<QCustom3DItem> *list, |
281 | int index) |
282 | { |
283 | return reinterpret_cast<AbstractDeclarative *>(list->data)->m_controller->m_customItems.at(i: index); |
284 | } |
285 | |
286 | void AbstractDeclarative::clearCustomItemFunc(QQmlListProperty<QCustom3DItem> *list) |
287 | { |
288 | AbstractDeclarative *decl = reinterpret_cast<AbstractDeclarative *>(list->data); |
289 | decl->removeCustomItems(); |
290 | } |
291 | |
292 | void AbstractDeclarative::setSharedController(Abstract3DController *controller) |
293 | { |
294 | Q_ASSERT(controller); |
295 | m_controller = controller; |
296 | |
297 | if (!m_controller->isOpenGLES()) |
298 | m_samples = 4; |
299 | setAntialiasing(m_samples > 0); |
300 | |
301 | // Reset default theme, as the default C++ theme is Q3DTheme, not DeclarativeTheme3D. |
302 | DeclarativeTheme3D *defaultTheme = new DeclarativeTheme3D; |
303 | defaultTheme->d_ptr->setDefaultTheme(true); |
304 | defaultTheme->setType(Q3DTheme::ThemeQt); |
305 | m_controller->setActiveTheme(theme: defaultTheme); |
306 | |
307 | QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::shadowQualityChanged, receiver: this, |
308 | slot: &AbstractDeclarative::handleShadowQualityChange); |
309 | QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::activeInputHandlerChanged, receiver: this, |
310 | slot: &AbstractDeclarative::inputHandlerChanged); |
311 | QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::activeThemeChanged, receiver: this, |
312 | slot: &AbstractDeclarative::themeChanged); |
313 | QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::selectionModeChanged, receiver: this, |
314 | slot: &AbstractDeclarative::handleSelectionModeChange); |
315 | QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::elementSelected, receiver: this, |
316 | slot: &AbstractDeclarative::handleSelectedElementChange); |
317 | |
318 | QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::axisXChanged, receiver: this, |
319 | slot: &AbstractDeclarative::handleAxisXChanged); |
320 | QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::axisYChanged, receiver: this, |
321 | slot: &AbstractDeclarative::handleAxisYChanged); |
322 | QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::axisZChanged, receiver: this, |
323 | slot: &AbstractDeclarative::handleAxisZChanged); |
324 | |
325 | QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::measureFpsChanged, receiver: this, |
326 | slot: &AbstractDeclarative::measureFpsChanged); |
327 | QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::currentFpsChanged, receiver: this, |
328 | slot: &AbstractDeclarative::currentFpsChanged); |
329 | |
330 | QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::orthoProjectionChanged, receiver: this, |
331 | slot: &AbstractDeclarative::orthoProjectionChanged); |
332 | |
333 | QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::aspectRatioChanged, receiver: this, |
334 | slot: &AbstractDeclarative::aspectRatioChanged); |
335 | QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::optimizationHintsChanged, receiver: this, |
336 | slot: &AbstractDeclarative::handleOptimizationHintChange); |
337 | QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::polarChanged, receiver: this, |
338 | slot: &AbstractDeclarative::polarChanged); |
339 | QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::radialLabelOffsetChanged, receiver: this, |
340 | slot: &AbstractDeclarative::radialLabelOffsetChanged); |
341 | QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::horizontalAspectRatioChanged, receiver: this, |
342 | slot: &AbstractDeclarative::horizontalAspectRatioChanged); |
343 | QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::reflectionChanged, receiver: this, |
344 | slot: &AbstractDeclarative::reflectionChanged); |
345 | QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::reflectivityChanged, receiver: this, |
346 | slot: &AbstractDeclarative::reflectivityChanged); |
347 | QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::localeChanged, receiver: this, |
348 | slot: &AbstractDeclarative::localeChanged); |
349 | QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::queriedGraphPositionChanged, receiver: this, |
350 | slot: &AbstractDeclarative::queriedGraphPositionChanged); |
351 | QObject::connect(sender: m_controller.data(), signal: &Abstract3DController::marginChanged, receiver: this, |
352 | slot: &AbstractDeclarative::marginChanged); |
353 | } |
354 | |
355 | void AbstractDeclarative::activateOpenGLContext(QQuickWindow *window) |
356 | { |
357 | // We can assume we are not in middle of AbstractDeclarative destructor when we are here, |
358 | // since m_context creation is always done when this function is called from |
359 | // synchDataToRenderer(), which blocks main thread -> no need to mutex. |
360 | if (!m_contextOrStateStore || !m_qtContext || m_contextWindow != window) { |
361 | QOpenGLContext *currentContext = QOpenGLContext::currentContext(); |
362 | |
363 | // Note: Changing graph to different window when using multithreaded renderer will break! |
364 | |
365 | delete m_contextOrStateStore; |
366 | |
367 | m_contextThread = QThread::currentThread(); |
368 | m_contextWindow = window; |
369 | m_qtContext = currentContext; |
370 | |
371 | #ifdef USE_SHARED_CONTEXT |
372 | m_context = new QOpenGLContext(); |
373 | m_context->setFormat(m_qtContext->format()); |
374 | m_context->setShareContext(m_qtContext); |
375 | m_context->create(); |
376 | m_context->makeCurrent(surface: window); |
377 | #else |
378 | // Shared contexts don't work properly in some platforms, so just store the |
379 | // context state on those |
380 | m_stateStore = new GLStateStore(QOpenGLContext::currentContext()); |
381 | m_stateStore->storeGLState(); |
382 | #endif |
383 | m_controller->initializeOpenGL(); |
384 | |
385 | // Make sure context / state store gets deleted. |
386 | QObject::connect(sender: m_contextThread, signal: &QThread::finished, receiver: this, |
387 | slot: &AbstractDeclarative::destroyContext, type: Qt::DirectConnection); |
388 | } else { |
389 | #ifdef USE_SHARED_CONTEXT |
390 | m_context->makeCurrent(surface: window); |
391 | #else |
392 | m_stateStore->storeGLState(); |
393 | #endif |
394 | } |
395 | } |
396 | |
397 | void AbstractDeclarative::doneOpenGLContext(QQuickWindow *window) |
398 | { |
399 | #ifdef USE_SHARED_CONTEXT |
400 | m_qtContext->makeCurrent(surface: window); |
401 | #else |
402 | Q_UNUSED(window) |
403 | m_stateStore->restoreGLState(); |
404 | #endif |
405 | } |
406 | |
407 | void AbstractDeclarative::synchDataToRenderer() |
408 | { |
409 | if (m_renderMode == RenderDirectToBackground && clearList.size()) |
410 | clearList.clear(); |
411 | |
412 | QQuickWindow *win = window(); |
413 | activateOpenGLContext(window: win); |
414 | m_controller->synchDataToRenderer(); |
415 | doneOpenGLContext(window: win); |
416 | } |
417 | |
418 | int AbstractDeclarative::msaaSamples() const |
419 | { |
420 | if (m_renderMode == RenderIndirect) |
421 | return m_samples; |
422 | else |
423 | return m_windowSamples; |
424 | } |
425 | |
426 | void AbstractDeclarative::setMsaaSamples(int samples) |
427 | { |
428 | if (m_renderMode != RenderIndirect) { |
429 | qWarning(msg: "Multisampling cannot be adjusted in this render mode" ); |
430 | } else { |
431 | if (m_controller->isOpenGLES()) { |
432 | if (samples > 0) |
433 | qWarning(msg: "Multisampling is not supported in OpenGL ES2" ); |
434 | } else if (m_samples != samples) { |
435 | m_samples = samples; |
436 | setAntialiasing(m_samples > 0); |
437 | emit msaaSamplesChanged(samples); |
438 | update(); |
439 | } |
440 | } |
441 | } |
442 | |
443 | void AbstractDeclarative::handleWindowChanged(QQuickWindow *window) |
444 | { |
445 | checkWindowList(window); |
446 | |
447 | if (!window) |
448 | return; |
449 | |
450 | #if defined(Q_OS_OSX) |
451 | bool previousVisibility = window->isVisible(); |
452 | // Enable touch events for Mac touchpads |
453 | window->setVisible(true); |
454 | typedef void * (*EnableTouch)(QWindow*, bool); |
455 | EnableTouch enableTouch = |
456 | (EnableTouch)QGuiApplication::platformNativeInterface()->nativeResourceFunctionForIntegration("registertouchwindow" ); |
457 | if (enableTouch) |
458 | enableTouch(window, true); |
459 | window->setVisible(previousVisibility); |
460 | #endif |
461 | |
462 | connect(sender: window, signal: &QObject::destroyed, receiver: this, slot: &AbstractDeclarative::windowDestroyed); |
463 | |
464 | int oldWindowSamples = m_windowSamples; |
465 | m_windowSamples = window->format().samples(); |
466 | if (m_windowSamples < 0) |
467 | m_windowSamples = 0; |
468 | |
469 | connect(sender: window, signal: &QQuickWindow::beforeSynchronizing, |
470 | receiver: this, slot: &AbstractDeclarative::synchDataToRenderer, |
471 | type: Qt::DirectConnection); |
472 | |
473 | if (m_renderMode == RenderDirectToBackground_NoClear |
474 | || m_renderMode == RenderDirectToBackground) { |
475 | connect(sender: window, signal: &QQuickWindow::beforeRendering, receiver: this, slot: &AbstractDeclarative::render, |
476 | type: Qt::DirectConnection); |
477 | setAntialiasing(m_windowSamples > 0); |
478 | if (m_windowSamples != oldWindowSamples) |
479 | emit msaaSamplesChanged(samples: m_windowSamples); |
480 | } |
481 | |
482 | connect(sender: m_controller.data(), signal: &Abstract3DController::needRender, receiver: window, slot: &QQuickWindow::update); |
483 | |
484 | updateWindowParameters(); |
485 | |
486 | #if defined(Q_OS_IOS) |
487 | // Scenegraph render cycle in iOS sometimes misses update after beforeSynchronizing signal. |
488 | // This ensures we don't end up displaying the graph without any data, in case update is |
489 | // skipped after synchDataToRenderer. |
490 | QTimer::singleShot(0, window, SLOT(update())); |
491 | #endif |
492 | } |
493 | |
494 | void AbstractDeclarative::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) |
495 | { |
496 | QQuickItem::geometryChanged(newGeometry, oldGeometry); |
497 | |
498 | m_cachedGeometry = newGeometry; |
499 | |
500 | updateWindowParameters(); |
501 | } |
502 | |
503 | void AbstractDeclarative::itemChange(ItemChange change, const ItemChangeData & value) |
504 | { |
505 | QQuickItem::itemChange(change, value); |
506 | updateWindowParameters(); |
507 | } |
508 | |
509 | void AbstractDeclarative::updateWindowParameters() |
510 | { |
511 | const QMutexLocker locker(&m_mutex); |
512 | |
513 | // Update the device pixel ratio, window size and bounding box |
514 | QQuickWindow *win = window(); |
515 | if (win && !m_controller.isNull()) { |
516 | Q3DScene *scene = m_controller->scene(); |
517 | if (win->devicePixelRatio() != scene->devicePixelRatio()) { |
518 | scene->setDevicePixelRatio(win->devicePixelRatio()); |
519 | win->update(); |
520 | } |
521 | |
522 | bool directRender = m_renderMode == RenderDirectToBackground |
523 | || m_renderMode == RenderDirectToBackground_NoClear; |
524 | QSize windowSize; |
525 | |
526 | if (directRender) |
527 | windowSize = win->size(); |
528 | else |
529 | windowSize = m_cachedGeometry.size().toSize(); |
530 | |
531 | if (windowSize != scene->d_ptr->windowSize()) { |
532 | scene->d_ptr->setWindowSize(windowSize); |
533 | win->update(); |
534 | } |
535 | |
536 | if (directRender) { |
537 | // Origin mapping is needed when rendering directly to background |
538 | QPointF point = QQuickItem::mapToScene(point: QPointF(0.0, 0.0)); |
539 | scene->d_ptr->setViewport(QRect(point.x() + 0.5f, point.y() + 0.5f, |
540 | m_cachedGeometry.width() + 0.5f, |
541 | m_cachedGeometry.height() + 0.5f)); |
542 | } else { |
543 | // No translation needed when rendering to FBO |
544 | scene->d_ptr->setViewport(QRect(0.0, 0.0, m_cachedGeometry.width() + 0.5f, |
545 | m_cachedGeometry.height() + 0.5f)); |
546 | } |
547 | } |
548 | } |
549 | |
550 | void AbstractDeclarative::handleSelectionModeChange(QAbstract3DGraph::SelectionFlags mode) |
551 | { |
552 | int intmode = int(mode); |
553 | emit selectionModeChanged(mode: SelectionFlags(intmode)); |
554 | } |
555 | |
556 | void AbstractDeclarative::handleShadowQualityChange(QAbstract3DGraph::ShadowQuality quality) |
557 | { |
558 | emit shadowQualityChanged(quality: ShadowQuality(quality)); |
559 | } |
560 | |
561 | void AbstractDeclarative::handleSelectedElementChange(QAbstract3DGraph::ElementType type) |
562 | { |
563 | emit selectedElementChanged(type: ElementType(type)); |
564 | } |
565 | |
566 | void AbstractDeclarative::handleOptimizationHintChange(QAbstract3DGraph::OptimizationHints hints) |
567 | { |
568 | int intHints = int(hints); |
569 | emit optimizationHintsChanged(hints: OptimizationHints(intHints)); |
570 | } |
571 | |
572 | void AbstractDeclarative::render() |
573 | { |
574 | updateWindowParameters(); |
575 | |
576 | // If we're not rendering directly to the background, return |
577 | if (m_renderMode != RenderDirectToBackground && m_renderMode != RenderDirectToBackground_NoClear) |
578 | return; |
579 | |
580 | // Clear the background once per window as that is not done by default |
581 | QQuickWindow *win = window(); |
582 | activateOpenGLContext(window: win); |
583 | QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions(); |
584 | if (m_renderMode == RenderDirectToBackground && !clearList.contains(t: win)) { |
585 | clearList.append(t: win); |
586 | QColor clearColor = win->color(); |
587 | funcs->glClearColor(red: clearColor.redF(), green: clearColor.greenF(), blue: clearColor.blueF(), alpha: 1.0f); |
588 | funcs->glClear(GL_COLOR_BUFFER_BIT); |
589 | } |
590 | |
591 | if (isVisible()) { |
592 | funcs->glDepthMask(GL_TRUE); |
593 | funcs->glEnable(GL_DEPTH_TEST); |
594 | funcs->glDepthFunc(GL_LESS); |
595 | funcs->glEnable(GL_CULL_FACE); |
596 | funcs->glCullFace(GL_BACK); |
597 | funcs->glDisable(GL_BLEND); |
598 | |
599 | m_controller->render(); |
600 | |
601 | funcs->glEnable(GL_BLEND); |
602 | } |
603 | doneOpenGLContext(window: win); |
604 | } |
605 | |
606 | QAbstract3DInputHandler* AbstractDeclarative::inputHandler() const |
607 | { |
608 | return m_controller->activeInputHandler(); |
609 | } |
610 | |
611 | void AbstractDeclarative::setInputHandler(QAbstract3DInputHandler *inputHandler) |
612 | { |
613 | m_controller->setActiveInputHandler(inputHandler); |
614 | } |
615 | |
616 | void AbstractDeclarative::mouseDoubleClickEvent(QMouseEvent *event) |
617 | { |
618 | m_controller->mouseDoubleClickEvent(event); |
619 | } |
620 | |
621 | void AbstractDeclarative::touchEvent(QTouchEvent *event) |
622 | { |
623 | m_controller->touchEvent(event); |
624 | window()->update(); |
625 | } |
626 | |
627 | void AbstractDeclarative::mousePressEvent(QMouseEvent *event) |
628 | { |
629 | QPoint mousePos = event->pos(); |
630 | m_controller->mousePressEvent(event, mousePos); |
631 | } |
632 | |
633 | void AbstractDeclarative::mouseReleaseEvent(QMouseEvent *event) |
634 | { |
635 | QPoint mousePos = event->pos(); |
636 | m_controller->mouseReleaseEvent(event, mousePos); |
637 | } |
638 | |
639 | void AbstractDeclarative::mouseMoveEvent(QMouseEvent *event) |
640 | { |
641 | QPoint mousePos = event->pos(); |
642 | m_controller->mouseMoveEvent(event, mousePos); |
643 | } |
644 | |
645 | #if QT_CONFIG(wheelevent) |
646 | void AbstractDeclarative::wheelEvent(QWheelEvent *event) |
647 | { |
648 | m_controller->wheelEvent(event); |
649 | } |
650 | #endif |
651 | |
652 | void AbstractDeclarative::checkWindowList(QQuickWindow *window) |
653 | { |
654 | QQuickWindow *oldWindow = graphWindowList.value(akey: this); |
655 | |
656 | graphWindowList[this] = window; |
657 | |
658 | if (oldWindow != window && oldWindow) { |
659 | QObject::disconnect(sender: oldWindow, signal: &QObject::destroyed, receiver: this, |
660 | slot: &AbstractDeclarative::windowDestroyed); |
661 | QObject::disconnect(sender: oldWindow, signal: &QQuickWindow::beforeSynchronizing, receiver: this, |
662 | slot: &AbstractDeclarative::synchDataToRenderer); |
663 | QObject::disconnect(sender: oldWindow, signal: &QQuickWindow::beforeRendering, receiver: this, |
664 | slot: &AbstractDeclarative::render); |
665 | if (!m_controller.isNull()) { |
666 | QObject::disconnect(sender: m_controller.data(), signal: &Abstract3DController::needRender, |
667 | receiver: oldWindow, slot: &QQuickWindow::update); |
668 | } |
669 | } |
670 | |
671 | QList<QQuickWindow *> windowList; |
672 | |
673 | foreach (AbstractDeclarative *graph, graphWindowList.keys()) { |
674 | if (graph->m_renderMode == RenderDirectToBackground |
675 | || graph->m_renderMode == RenderDirectToBackground_NoClear) { |
676 | windowList.append(t: graphWindowList.value(akey: graph)); |
677 | } |
678 | } |
679 | |
680 | if (oldWindow && !windowList.contains(t: oldWindow) |
681 | && windowClearList.contains(akey: oldWindow)) { |
682 | // Return window clear value |
683 | oldWindow->setClearBeforeRendering(windowClearList.value(akey: oldWindow)); |
684 | windowClearList.remove(akey: oldWindow); |
685 | } |
686 | |
687 | if (!window) { |
688 | graphWindowList.remove(akey: this); |
689 | return; |
690 | } |
691 | |
692 | if ((m_renderMode == RenderDirectToBackground |
693 | || m_renderMode == RenderDirectToBackground_NoClear) |
694 | && !windowClearList.contains(akey: window)) { |
695 | // Save old clear value |
696 | windowClearList[window] = window->clearBeforeRendering(); |
697 | // Disable clearing of the window as we render underneath |
698 | window->setClearBeforeRendering(false); |
699 | } |
700 | } |
701 | |
702 | void AbstractDeclarative::setMeasureFps(bool enable) |
703 | { |
704 | m_controller->setMeasureFps(enable); |
705 | } |
706 | |
707 | bool AbstractDeclarative::measureFps() const |
708 | { |
709 | return m_controller->measureFps(); |
710 | } |
711 | |
712 | qreal AbstractDeclarative::currentFps() const |
713 | { |
714 | return m_controller->currentFps(); |
715 | } |
716 | |
717 | void AbstractDeclarative::setOrthoProjection(bool enable) |
718 | { |
719 | m_controller->setOrthoProjection(enable); |
720 | } |
721 | |
722 | bool AbstractDeclarative::isOrthoProjection() const |
723 | { |
724 | return m_controller->isOrthoProjection(); |
725 | } |
726 | |
727 | AbstractDeclarative::ElementType AbstractDeclarative::selectedElement() const |
728 | { |
729 | return ElementType(m_controller->selectedElement()); |
730 | } |
731 | |
732 | void AbstractDeclarative::setAspectRatio(qreal ratio) |
733 | { |
734 | m_controller->setAspectRatio(ratio); |
735 | } |
736 | |
737 | qreal AbstractDeclarative::aspectRatio() const |
738 | { |
739 | return m_controller->aspectRatio(); |
740 | } |
741 | |
742 | void AbstractDeclarative::setOptimizationHints(OptimizationHints hints) |
743 | { |
744 | int intmode = int(hints); |
745 | m_controller->setOptimizationHints(QAbstract3DGraph::OptimizationHints(intmode)); |
746 | } |
747 | |
748 | AbstractDeclarative::OptimizationHints AbstractDeclarative::optimizationHints() const |
749 | { |
750 | int intmode = int(m_controller->optimizationHints()); |
751 | return OptimizationHints(intmode); |
752 | } |
753 | |
754 | void AbstractDeclarative::setPolar(bool enable) |
755 | { |
756 | m_controller->setPolar(enable); |
757 | } |
758 | |
759 | bool AbstractDeclarative::isPolar() const |
760 | { |
761 | return m_controller->isPolar(); |
762 | } |
763 | |
764 | void AbstractDeclarative::setRadialLabelOffset(float offset) |
765 | { |
766 | m_controller->setRadialLabelOffset(offset); |
767 | } |
768 | |
769 | float AbstractDeclarative::radialLabelOffset() const |
770 | { |
771 | return m_controller->radialLabelOffset(); |
772 | } |
773 | |
774 | void AbstractDeclarative::setHorizontalAspectRatio(qreal ratio) |
775 | { |
776 | m_controller->setHorizontalAspectRatio(ratio); |
777 | } |
778 | |
779 | qreal AbstractDeclarative::horizontalAspectRatio() const |
780 | { |
781 | return m_controller->horizontalAspectRatio(); |
782 | } |
783 | |
784 | void AbstractDeclarative::setReflection(bool enable) |
785 | { |
786 | m_controller->setReflection(enable); |
787 | } |
788 | |
789 | bool AbstractDeclarative::isReflection() const |
790 | { |
791 | return m_controller->reflection(); |
792 | } |
793 | |
794 | void AbstractDeclarative::setReflectivity(qreal reflectivity) |
795 | { |
796 | m_controller->setReflectivity(reflectivity); |
797 | } |
798 | |
799 | qreal AbstractDeclarative::reflectivity() const |
800 | { |
801 | return m_controller->reflectivity(); |
802 | } |
803 | |
804 | void AbstractDeclarative::setLocale(const QLocale &locale) |
805 | { |
806 | m_controller->setLocale(locale); |
807 | } |
808 | |
809 | QLocale AbstractDeclarative::locale() const |
810 | { |
811 | return m_controller->locale(); |
812 | } |
813 | |
814 | QVector3D AbstractDeclarative::queriedGraphPosition() const |
815 | { |
816 | return m_controller->queriedGraphPosition(); |
817 | } |
818 | |
819 | void AbstractDeclarative::setMargin(qreal margin) |
820 | { |
821 | m_controller->setMargin(margin); |
822 | } |
823 | |
824 | qreal AbstractDeclarative::margin() const |
825 | { |
826 | return m_controller->margin(); |
827 | } |
828 | |
829 | void AbstractDeclarative::windowDestroyed(QObject *obj) |
830 | { |
831 | // Remove destroyed window from window lists |
832 | QQuickWindow *win = static_cast<QQuickWindow *>(obj); |
833 | QQuickWindow *oldWindow = graphWindowList.value(akey: this); |
834 | |
835 | if (win == oldWindow) |
836 | graphWindowList.remove(akey: this); |
837 | |
838 | windowClearList.remove(akey: win); |
839 | } |
840 | |
841 | void AbstractDeclarative::destroyContext() |
842 | { |
843 | if (m_contextThread && m_contextThread != m_mainThread) { |
844 | if (m_contextOrStateStore) |
845 | m_contextOrStateStore->deleteLater(); |
846 | } else { |
847 | delete m_contextOrStateStore; |
848 | } |
849 | m_contextOrStateStore = 0; |
850 | |
851 | if (m_contextThread) { |
852 | QObject::disconnect(sender: m_contextThread, signal: &QThread::finished, receiver: this, |
853 | slot: &AbstractDeclarative::destroyContext); |
854 | m_contextThread = 0; |
855 | } |
856 | } |
857 | |
858 | QT_END_NAMESPACE_DATAVISUALIZATION |
859 | |