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 "qabstract3dgraph.h" |
31 | #include "qabstract3dgraph_p.h" |
32 | #include "abstract3dcontroller_p.h" |
33 | #include "qabstract3dinputhandler_p.h" |
34 | #include "q3dscene_p.h" |
35 | #include "qutils.h" |
36 | #include "utils_p.h" |
37 | |
38 | #include <QtGui/QGuiApplication> |
39 | #include <QtGui/QOpenGLContext> |
40 | #include <QtGui/QOpenGLPaintDevice> |
41 | #include <QtGui/QPainter> |
42 | #include <QtGui/QOpenGLFramebufferObject> |
43 | #include <QtGui/QOffscreenSurface> |
44 | #if defined(Q_OS_OSX) |
45 | #include <qpa/qplatformnativeinterface.h> |
46 | #endif |
47 | |
48 | QT_BEGIN_NAMESPACE_DATAVISUALIZATION |
49 | |
50 | /*! |
51 | * \class QAbstract3DGraph |
52 | * \inmodule QtDataVisualization |
53 | * \brief The QAbstract3DGraph class provides a window and render loop for graphs. |
54 | * \since QtDataVisualization 1.0 |
55 | * |
56 | * This class subclasses a QWindow and provides render loop for graphs inheriting it. |
57 | * |
58 | * You should not need to use this class directly, but one of its subclasses instead. |
59 | * |
60 | * Anti-aliasing is turned on by default on C++, except in OpenGL ES2 |
61 | * environments, where anti-aliasing is not supported by Qt Data Visualization. |
62 | * To specify non-default anti-aliasing for a graph, give a custom surface format as |
63 | * a constructor parameter. You can use the convenience function \c QtDataVisualization::qDefaultSurfaceFormat() |
64 | * to create the surface format object. |
65 | * |
66 | * \note QAbstract3DGraph sets window flag \c Qt::FramelessWindowHint on by default. If you want to display |
67 | * graph windows as standalone windows with regular window frame, clear this flag after constructing |
68 | * the graph. For example: |
69 | * |
70 | * \code |
71 | * Q3DBars *graphWindow = new Q3DBars; |
72 | * graphWindow->setFlags(graphWindow->flags() ^ Qt::FramelessWindowHint); |
73 | * \endcode |
74 | * |
75 | * \sa Q3DBars, Q3DScatter, Q3DSurface, {Qt Data Visualization C++ Classes} |
76 | */ |
77 | |
78 | /*! |
79 | \enum QAbstract3DGraph::SelectionFlag |
80 | |
81 | Item selection modes. Values of this enumeration can be combined with OR operator. |
82 | |
83 | \value SelectionNone |
84 | Selection mode disabled. |
85 | \value SelectionItem |
86 | Selection highlights a single item. |
87 | \value SelectionRow |
88 | Selection highlights a single row. |
89 | \value SelectionItemAndRow |
90 | Combination flag for highlighting both item and row with different colors. |
91 | \value SelectionColumn |
92 | Selection highlights a single column. |
93 | \value SelectionItemAndColumn |
94 | Combination flag for highlighting both item and column with different colors. |
95 | \value SelectionRowAndColumn |
96 | Combination flag for highlighting both row and column. |
97 | \value SelectionItemRowAndColumn |
98 | Combination flag for highlighting item, row, and column. |
99 | \value SelectionSlice |
100 | Setting this mode flag indicates that the graph should take care of the slice view handling |
101 | automatically. If you wish to control the slice view yourself via Q3DScene, do not set this |
102 | flag. When setting this mode flag, either \c SelectionRow or \c SelectionColumn must also |
103 | be set, but not both. Slicing is supported by Q3DBars and Q3DSurface only. |
104 | When this flag is set, slice mode is entered in the following situations: |
105 | \list |
106 | \li When selection is changed explicitly via series API to a visible item |
107 | \li When selection is changed by clicking on the graph |
108 | \li When the selection mode changes and the selected item is visible |
109 | \endlist |
110 | \value SelectionMultiSeries |
111 | Setting this mode means that items for all series at same position are highlighted, instead |
112 | of just the selected item. The actual selection in the other series doesn't change. |
113 | Multi-series selection is not supported for Q3DScatter. |
114 | */ |
115 | |
116 | /*! |
117 | \enum QAbstract3DGraph::ShadowQuality |
118 | |
119 | Quality of shadows. |
120 | |
121 | \value ShadowQualityNone |
122 | Shadows are disabled. |
123 | \value ShadowQualityLow |
124 | Shadows are rendered in low quality. |
125 | \value ShadowQualityMedium |
126 | Shadows are rendered in medium quality. |
127 | \value ShadowQualityHigh |
128 | Shadows are rendered in high quality. |
129 | \value ShadowQualitySoftLow |
130 | Shadows are rendered in low quality with softened edges. |
131 | \value ShadowQualitySoftMedium |
132 | Shadows are rendered in medium quality with softened edges. |
133 | \value ShadowQualitySoftHigh |
134 | Shadows are rendered in high quality with softened edges. |
135 | */ |
136 | |
137 | /*! |
138 | \enum QAbstract3DGraph::ElementType |
139 | \since QtDataVisualization 1.1 |
140 | |
141 | Type of an element in the graph. |
142 | |
143 | \value ElementNone |
144 | No defined element. |
145 | \value ElementSeries |
146 | A series (that is, an item in a series). |
147 | \value ElementAxisXLabel |
148 | The x-axis label. |
149 | \value ElementAxisYLabel |
150 | The y-axis label. |
151 | \value ElementAxisZLabel |
152 | The z-axis label. |
153 | \value ElementCustomItem |
154 | A custom item. |
155 | */ |
156 | |
157 | /*! |
158 | \enum QAbstract3DGraph::OptimizationHint |
159 | \since Qt Data Visualization 1.1 |
160 | |
161 | The optimization hint for rendering. |
162 | |
163 | \value OptimizationDefault |
164 | Provides the full feature set at a reasonable performance. |
165 | \value OptimizationStatic |
166 | Optimizes the rendering of static data sets at the expense of some features. |
167 | */ |
168 | |
169 | /*! |
170 | * \internal |
171 | */ |
172 | QAbstract3DGraph::QAbstract3DGraph(QAbstract3DGraphPrivate *d, const QSurfaceFormat *format, |
173 | QWindow *parent) |
174 | : QWindow(parent), |
175 | d_ptr(d) |
176 | { |
177 | qRegisterMetaType<QAbstract3DGraph::ShadowQuality>(typeName: "QAbstract3DGraph::ShadowQuality" ); |
178 | qRegisterMetaType<QAbstract3DGraph::ElementType>(typeName: "QAbstract3DGraph::ElementType" ); |
179 | |
180 | // Default to frameless window, as typically graphs are not toplevel |
181 | setFlags(flags() | Qt::FramelessWindowHint); |
182 | |
183 | QSurfaceFormat surfaceFormat; |
184 | if (format) { |
185 | surfaceFormat = *format; |
186 | // Make sure renderable type is correct |
187 | surfaceFormat.setRenderableType(QSurfaceFormat::DefaultRenderableType); |
188 | } else { |
189 | surfaceFormat = qDefaultSurfaceFormat(); |
190 | } |
191 | |
192 | d_ptr->m_context = new QOpenGLContext(this); |
193 | setSurfaceType(QWindow::OpenGLSurface); |
194 | setFormat(surfaceFormat); |
195 | |
196 | create(); |
197 | |
198 | d_ptr->m_context->setFormat(requestedFormat()); |
199 | d_ptr->m_context->create(); |
200 | bool makeSuccess = d_ptr->m_context->makeCurrent(surface: this); |
201 | |
202 | // If we fail to get context, just abort |
203 | if (!makeSuccess || !QOpenGLContext::currentContext()) |
204 | return; |
205 | |
206 | initializeOpenGLFunctions(); |
207 | |
208 | const GLubyte *shaderVersion = glGetString(GL_SHADING_LANGUAGE_VERSION); |
209 | #ifndef QT_NO_DEBUG |
210 | const GLubyte *openGLVersion = glGetString(GL_VERSION); |
211 | qDebug() << "OpenGL version:" << (const char *)openGLVersion; |
212 | qDebug() << "GLSL version:" << (const char *)shaderVersion; |
213 | #endif |
214 | |
215 | if (!Utils::isOpenGLES()) { |
216 | // If we have real OpenGL, GLSL version must be 1.2 or over. Quit if not. |
217 | QStringList splitversionstr = |
218 | QString::fromLatin1(str: (const char *)shaderVersion).split(sep: QChar::fromLatin1(c: ' ')); |
219 | if (splitversionstr[0].toFloat() < 1.2) |
220 | qFatal(msg: "GLSL version must be 1.20 or higher. Try installing latest display drivers." ); |
221 | } |
222 | |
223 | d_ptr->m_initialized = true; |
224 | |
225 | d_ptr->renderLater(); |
226 | |
227 | #if defined(Q_OS_OSX) |
228 | // Enable touch events for Mac touchpads |
229 | typedef void * (*EnableTouch)(QWindow*, bool); |
230 | EnableTouch enableTouch = |
231 | (EnableTouch)QGuiApplication::platformNativeInterface()->nativeResourceFunctionForIntegration("registertouchwindow" ); |
232 | if (enableTouch) |
233 | enableTouch(this, true); |
234 | #endif |
235 | } |
236 | |
237 | /*! |
238 | * Destroys QAbstract3DGraph. |
239 | */ |
240 | QAbstract3DGraph::~QAbstract3DGraph() |
241 | { |
242 | } |
243 | |
244 | /*! |
245 | * Adds the given \a inputHandler to the graph. The input handlers added via addInputHandler |
246 | * are not taken in to use directly. Only the ownership of the \a inputHandler is given to the graph. |
247 | * The \a inputHandler must not be null or already added to another graph. |
248 | * |
249 | * \sa releaseInputHandler(), setActiveInputHandler() |
250 | */ |
251 | void QAbstract3DGraph::addInputHandler(QAbstract3DInputHandler *inputHandler) |
252 | { |
253 | d_ptr->m_visualController->addInputHandler(inputHandler); |
254 | } |
255 | |
256 | /*! |
257 | * Releases the ownership of the \a inputHandler back to the caller, if it was added to this graph. |
258 | * If the released \a inputHandler is in use there will be no input handler active after this call. |
259 | * |
260 | * If the default input handler is released and added back later, it behaves as any other input handler would. |
261 | * |
262 | * \sa addInputHandler(), setActiveInputHandler() |
263 | */ |
264 | void QAbstract3DGraph::releaseInputHandler(QAbstract3DInputHandler *inputHandler) |
265 | { |
266 | d_ptr->m_visualController->releaseInputHandler(inputHandler); |
267 | } |
268 | |
269 | /*! |
270 | * \property QAbstract3DGraph::activeInputHandler |
271 | * |
272 | * \brief The active input handler used in the graph. |
273 | */ |
274 | |
275 | /*! |
276 | * Sets \a inputHandler as the active input handler used in the graph. |
277 | * Implicitly calls addInputHandler() to transfer ownership of \a inputHandler |
278 | * to this graph. |
279 | * |
280 | * If \a inputHandler is null, no input handler will be active after this call. |
281 | * |
282 | * \sa addInputHandler(), releaseInputHandler() |
283 | */ |
284 | void QAbstract3DGraph::setActiveInputHandler(QAbstract3DInputHandler *inputHandler) |
285 | { |
286 | d_ptr->m_visualController->setActiveInputHandler(inputHandler); |
287 | } |
288 | |
289 | QAbstract3DInputHandler *QAbstract3DGraph::activeInputHandler() const |
290 | { |
291 | return d_ptr->m_visualController->activeInputHandler(); |
292 | } |
293 | |
294 | /*! |
295 | * Returns the list of all added input handlers. |
296 | * |
297 | * \sa addInputHandler() |
298 | */ |
299 | QList<QAbstract3DInputHandler *> QAbstract3DGraph::inputHandlers() const |
300 | { |
301 | return d_ptr->m_visualController->inputHandlers(); |
302 | } |
303 | |
304 | /*! |
305 | * Adds the given \a theme to the graph. The themes added via addTheme are not taken in to use |
306 | * directly. Only the ownership of the theme is given to the graph. |
307 | * The \a theme must not be null or already added to another graph. |
308 | * |
309 | * \sa releaseTheme(), setActiveTheme() |
310 | */ |
311 | void QAbstract3DGraph::addTheme(Q3DTheme *theme) |
312 | { |
313 | d_ptr->m_visualController->addTheme(theme); |
314 | } |
315 | |
316 | /*! |
317 | * Releases the ownership of the \a theme back to the caller, if it was added to this graph. |
318 | * If the released \a theme is in use, a new default theme will be created and set active. |
319 | * |
320 | * If the default theme is released and added back later, it behaves as any other theme would. |
321 | * |
322 | * \sa addTheme(), setActiveTheme() |
323 | */ |
324 | void QAbstract3DGraph::releaseTheme(Q3DTheme *theme) |
325 | { |
326 | d_ptr->m_visualController->releaseTheme(theme); |
327 | } |
328 | |
329 | /*! |
330 | * \property QAbstract3DGraph::activeTheme |
331 | * |
332 | * \brief The active theme of the graph. |
333 | */ |
334 | |
335 | /*! |
336 | * Sets \a theme as the active theme to be used for the graph. Implicitly calls |
337 | * addTheme() to transfer the ownership of the theme to this graph. |
338 | * |
339 | * If \a theme is null, a temporary default theme is created. This temporary theme is destroyed |
340 | * if any theme is explicitly set later. |
341 | * Properties of the theme can be modified even after setting it, and the modifications take |
342 | * effect immediately. |
343 | */ |
344 | void QAbstract3DGraph::setActiveTheme(Q3DTheme *theme) |
345 | { |
346 | d_ptr->m_visualController->setActiveTheme(theme); |
347 | } |
348 | |
349 | |
350 | Q3DTheme *QAbstract3DGraph::activeTheme() const |
351 | { |
352 | return d_ptr->m_visualController->activeTheme(); |
353 | } |
354 | |
355 | /*! |
356 | * Returns the list of all added themes. |
357 | * |
358 | * \sa addTheme() |
359 | */ |
360 | QList<Q3DTheme *> QAbstract3DGraph::themes() const |
361 | { |
362 | return d_ptr->m_visualController->themes(); |
363 | } |
364 | |
365 | /*! |
366 | * \property QAbstract3DGraph::selectionMode |
367 | * |
368 | * \brief Item selection mode. |
369 | * |
370 | * A combination of SelectionFlags. By default, \c SelectionItem. |
371 | * Different graph types support different selection modes. |
372 | * |
373 | * \sa SelectionFlags |
374 | */ |
375 | void QAbstract3DGraph::setSelectionMode(SelectionFlags mode) |
376 | { |
377 | d_ptr->m_visualController->setSelectionMode(mode); |
378 | } |
379 | |
380 | QAbstract3DGraph::SelectionFlags QAbstract3DGraph::selectionMode() const |
381 | { |
382 | return d_ptr->m_visualController->selectionMode(); |
383 | } |
384 | |
385 | /*! |
386 | * \property QAbstract3DGraph::shadowQuality |
387 | * |
388 | * \brief The quality of the shadow. |
389 | * |
390 | * One of the ShadowQuality enum values. By default, \c ShadowQualityMedium. |
391 | * |
392 | * \note If setting the shadow quality to a certain level fails, the level is lowered |
393 | * until it is successfully set. The \c shadowQualityChanged signal is emitted each time |
394 | * a change is made. |
395 | * |
396 | * \sa ShadowQuality |
397 | */ |
398 | void QAbstract3DGraph::setShadowQuality(ShadowQuality quality) |
399 | { |
400 | d_ptr->m_visualController->setShadowQuality(quality); |
401 | } |
402 | |
403 | QAbstract3DGraph::ShadowQuality QAbstract3DGraph::shadowQuality() const |
404 | { |
405 | return d_ptr->m_visualController->shadowQuality(); |
406 | } |
407 | |
408 | /*! |
409 | * Returns \c true if shadows are supported with the current configuration. |
410 | * OpenGL ES2 configurations do not support shadows. |
411 | */ |
412 | bool QAbstract3DGraph::shadowsSupported() const |
413 | { |
414 | return d_ptr->m_visualController->shadowsSupported(); |
415 | } |
416 | |
417 | /*! |
418 | * \property QAbstract3DGraph::scene |
419 | * |
420 | * \brief The Q3DScene pointer that can be used to manipulate the scene and |
421 | * access the scene elements, such as the active camera. |
422 | * |
423 | * This property is read-only. |
424 | */ |
425 | Q3DScene *QAbstract3DGraph::scene() const |
426 | { |
427 | return d_ptr->m_visualController->scene(); |
428 | } |
429 | |
430 | /*! |
431 | * Clears selection from all attached series. |
432 | */ |
433 | void QAbstract3DGraph::clearSelection() |
434 | { |
435 | d_ptr->m_visualController->clearSelection(); |
436 | } |
437 | |
438 | /*! |
439 | * Adds a QCustom3DItem \a item to the graph. Graph takes ownership of the added item. |
440 | * |
441 | * Returns the index to the added item if the add operation was successful, -1 |
442 | * if trying to add a null item, and the index of the item if trying to add an |
443 | * already added item. |
444 | * |
445 | * Items are rendered in the order they have been inserted. The rendering order needs to |
446 | * be taken into account when having solid and transparent items. |
447 | * |
448 | * \sa removeCustomItems(), removeCustomItem(), removeCustomItemAt(), customItems() |
449 | * |
450 | * \since QtDataVisualization 1.1 |
451 | */ |
452 | int QAbstract3DGraph::addCustomItem(QCustom3DItem *item) |
453 | { |
454 | return d_ptr->m_visualController->addCustomItem(item); |
455 | } |
456 | |
457 | /*! |
458 | * Removes all custom items. Deletes the resources allocated to them. |
459 | * |
460 | * \since QtDataVisualization 1.1 |
461 | */ |
462 | void QAbstract3DGraph::removeCustomItems() |
463 | { |
464 | d_ptr->m_visualController->deleteCustomItems(); |
465 | } |
466 | |
467 | /*! |
468 | * Removes the custom \a {item}. Deletes the resources allocated to it. |
469 | * |
470 | * \since QtDataVisualization 1.1 |
471 | */ |
472 | void QAbstract3DGraph::removeCustomItem(QCustom3DItem *item) |
473 | { |
474 | d_ptr->m_visualController->deleteCustomItem(item); |
475 | } |
476 | |
477 | /*! |
478 | * Removes all custom items at \a {position}. Deletes the resources allocated to them. |
479 | * |
480 | * \since QtDataVisualization 1.1 |
481 | */ |
482 | void QAbstract3DGraph::removeCustomItemAt(const QVector3D &position) |
483 | { |
484 | d_ptr->m_visualController->deleteCustomItem(position); |
485 | } |
486 | |
487 | /*! |
488 | * Gets ownership of given \a item back and removes the \a item from the graph. |
489 | * |
490 | * \since QtDataVisualization 1.1 |
491 | * |
492 | * \note If the same item is added back to the graph, the texture or the texture file needs to be |
493 | * re-set. |
494 | * |
495 | * \sa QCustom3DItem::setTextureImage(), QCustom3DItem::setTextureFile() |
496 | */ |
497 | void QAbstract3DGraph::releaseCustomItem(QCustom3DItem *item) |
498 | { |
499 | return d_ptr->m_visualController->releaseCustomItem(item); |
500 | } |
501 | |
502 | /*! |
503 | * Returns the list of all added custom items. |
504 | * \since QtDataVisualization 1.2 |
505 | * \sa addCustomItem() |
506 | */ |
507 | QList<QCustom3DItem *> QAbstract3DGraph::customItems() const |
508 | { |
509 | return d_ptr->m_visualController->customItems(); |
510 | } |
511 | |
512 | /*! |
513 | * Can be used to query the index of the selected label after receiving \c selectedElementChanged |
514 | * signal with any label type. Selection is valid until the next \c selectedElementChanged signal. |
515 | * |
516 | * Returns the index of the selected label, or -1. |
517 | * |
518 | * \since QtDataVisualization 1.1 |
519 | * |
520 | * \sa selectedElement |
521 | */ |
522 | int QAbstract3DGraph::selectedLabelIndex() const |
523 | { |
524 | return d_ptr->m_visualController->selectedLabelIndex(); |
525 | } |
526 | |
527 | /*! |
528 | * Can be used to get the selected axis after receiving \c selectedElementChanged signal with any label |
529 | * type. Selection is valid until the next \c selectedElementChanged signal. |
530 | * |
531 | * Returns the pointer to the selected axis, or null. |
532 | * |
533 | * \since QtDataVisualization 1.1 |
534 | * |
535 | * \sa selectedElement |
536 | */ |
537 | QAbstract3DAxis *QAbstract3DGraph::selectedAxis() const |
538 | { |
539 | return d_ptr->m_visualController->selectedAxis(); |
540 | } |
541 | |
542 | /*! |
543 | * Can be used to query the index of the selected custom item after receiving \c selectedElementChanged |
544 | * signal with QAbstract3DGraph::ElementCustomItem type. Selection is valid until the next |
545 | * \c selectedElementChanged signal. |
546 | * |
547 | * Returns the index of the selected custom item, or -1. |
548 | * |
549 | * \since QtDataVisualization 1.1 |
550 | * |
551 | * \sa selectedElement |
552 | */ |
553 | int QAbstract3DGraph::selectedCustomItemIndex() const |
554 | { |
555 | return d_ptr->m_visualController->selectedCustomItemIndex(); |
556 | } |
557 | |
558 | /*! |
559 | * Can be used to get the selected custom item after receiving \c selectedElementChanged signal with |
560 | * QAbstract3DGraph::ElementCustomItem type. Ownership of the item remains with the graph. |
561 | * Selection is valid until the next \c selectedElementChanged signal. |
562 | * |
563 | * Returns the pointer to the selected custom item, or null. |
564 | * |
565 | * \since QtDataVisualization 1.1 |
566 | * |
567 | * \sa selectedElement |
568 | */ |
569 | QCustom3DItem *QAbstract3DGraph::selectedCustomItem() const |
570 | { |
571 | return d_ptr->m_visualController->selectedCustomItem(); |
572 | } |
573 | |
574 | /*! |
575 | * \property QAbstract3DGraph::selectedElement |
576 | * |
577 | * \brief The element selected in the graph. |
578 | * |
579 | * This property can be used to query the selected element type. The type is |
580 | * valid until a new selection is made in the graph and the |
581 | * \c selectedElementChanged signal is emitted. |
582 | * |
583 | * The signal can be used for example for implementing custom input handlers, as |
584 | * demonstrated by the \l {Axis Range Dragging With Labels Example}. |
585 | * |
586 | * \sa selectedLabelIndex(), selectedAxis(), selectedCustomItemIndex(), selectedCustomItem(), |
587 | * Q3DBars::selectedSeries(), Q3DScatter::selectedSeries(), Q3DSurface::selectedSeries(), |
588 | * Q3DScene::setSelectionQueryPosition() |
589 | * |
590 | * \since QtDataVisualization 1.1 |
591 | */ |
592 | QAbstract3DGraph::ElementType QAbstract3DGraph::selectedElement() const |
593 | { |
594 | return d_ptr->m_visualController->selectedElement(); |
595 | } |
596 | |
597 | /*! |
598 | * Renders current frame to an image of \a imageSize. Default size is the window size. Image is |
599 | * rendered with antialiasing level given in \a msaaSamples. Default level is \c{0}. |
600 | * |
601 | * \since QtDataVisualization 1.1 |
602 | * |
603 | * Returns the rendered image. |
604 | * |
605 | * \note OpenGL ES2 does not support anitialiasing, so \a msaaSamples is always forced to \c{0}. |
606 | */ |
607 | QImage QAbstract3DGraph::renderToImage(int msaaSamples, const QSize &imageSize) |
608 | { |
609 | QSize renderSize = imageSize; |
610 | if (renderSize.isEmpty()) |
611 | renderSize = size(); |
612 | return d_ptr->renderToImage(msaaSamples, imageSize: renderSize); |
613 | } |
614 | |
615 | /*! |
616 | * \property QAbstract3DGraph::measureFps |
617 | * \since QtDataVisualization 1.1 |
618 | * |
619 | * \brief Whether rendering is done continuously instead of on demand. |
620 | * |
621 | * If \c {true}, rendering is continuous and the value of the currentFps |
622 | * property is updated. Defaults to \c{false}. |
623 | * |
624 | * \sa currentFps |
625 | */ |
626 | void QAbstract3DGraph::setMeasureFps(bool enable) |
627 | { |
628 | d_ptr->m_visualController->setMeasureFps(enable); |
629 | } |
630 | |
631 | bool QAbstract3DGraph::measureFps() const |
632 | { |
633 | return d_ptr->m_visualController->measureFps(); |
634 | } |
635 | |
636 | /*! |
637 | * \property QAbstract3DGraph::currentFps |
638 | * \since QtDataVisualization 1.1 |
639 | * |
640 | * \brief The rendering results for the last second. |
641 | * |
642 | * The results are stored in this read-only property when FPS measuring is |
643 | * enabled. It takes at least a second before this value is updated after |
644 | * measuring is activated. |
645 | * |
646 | * \sa measureFps |
647 | */ |
648 | qreal QAbstract3DGraph::currentFps() const |
649 | { |
650 | return d_ptr->m_visualController->currentFps(); |
651 | } |
652 | |
653 | /*! |
654 | * \property QAbstract3DGraph::orthoProjection |
655 | * \since QtDataVisualization 1.1 |
656 | * |
657 | * \brief Whether orthographic projection is used for displaying the graph. |
658 | * |
659 | * Defaults to \c{false}. |
660 | * \note Shadows will be disabled when set to \c{true}. |
661 | * |
662 | * \sa QAbstract3DAxis::labelAutoRotation, Q3DCamera::cameraPreset |
663 | */ |
664 | void QAbstract3DGraph::setOrthoProjection(bool enable) |
665 | { |
666 | d_ptr->m_visualController->setOrthoProjection(enable); |
667 | } |
668 | |
669 | bool QAbstract3DGraph::isOrthoProjection() const |
670 | { |
671 | return d_ptr->m_visualController->isOrthoProjection(); |
672 | } |
673 | |
674 | /*! |
675 | * \property QAbstract3DGraph::aspectRatio |
676 | * \since QtDataVisualization 1.1 |
677 | * |
678 | * \brief The ratio of the graph scaling between the longest axis on the |
679 | * horizontal plane and the y-axis. |
680 | * |
681 | * Defaults to \c{2.0}. |
682 | * |
683 | * \note Has no effect on Q3DBars. |
684 | * |
685 | * \sa horizontalAspectRatio |
686 | */ |
687 | void QAbstract3DGraph::setAspectRatio(qreal ratio) |
688 | { |
689 | d_ptr->m_visualController->setAspectRatio(ratio); |
690 | } |
691 | |
692 | qreal QAbstract3DGraph::aspectRatio() const |
693 | { |
694 | return d_ptr->m_visualController->aspectRatio(); |
695 | } |
696 | |
697 | /*! |
698 | * \property QAbstract3DGraph::optimizationHints |
699 | * |
700 | * \brief Whether the default or static mode is used for rendering optimization. |
701 | * |
702 | * The default mode provides the full feature set at a reasonable level of |
703 | * performance. The static mode optimizes graph rendering and is ideal for |
704 | * large non-changing data sets. It is slower with dynamic data changes and item rotations. |
705 | * Selection is not optimized, so using the static mode with massive data sets is not advisable. |
706 | * Static optimization works only on scatter graphs. |
707 | * Defaults to \l{OptimizationDefault}. |
708 | * |
709 | * \note On some environments, large graphs using static optimization may not render, because |
710 | * all of the items are rendered using a single draw call, and different graphics drivers |
711 | * support different maximum vertice counts per call. |
712 | * This is mostly an issue on 32bit and OpenGL ES2 platforms. |
713 | * To work around this issue, choose an item mesh with a low vertex count or use |
714 | * the point mesh. |
715 | * |
716 | * \sa QAbstract3DSeries::mesh |
717 | */ |
718 | void QAbstract3DGraph::setOptimizationHints(OptimizationHints hints) |
719 | { |
720 | d_ptr->m_visualController->setOptimizationHints(hints); |
721 | } |
722 | |
723 | QAbstract3DGraph::OptimizationHints QAbstract3DGraph::optimizationHints() const |
724 | { |
725 | return d_ptr->m_visualController->optimizationHints(); |
726 | } |
727 | |
728 | /*! |
729 | * \property QAbstract3DGraph::polar |
730 | * \since QtDataVisualization 1.2 |
731 | * |
732 | * \brief Whether horizontal axes are changed into polar axes. |
733 | * |
734 | * If \c {true}, the x-axis becomes the angular axis and the z-axis becomes the |
735 | * radial axis. |
736 | * Polar mode is not available for bar graphs. |
737 | * |
738 | * Defaults to \c{false}. |
739 | * |
740 | * \sa orthoProjection, radialLabelOffset |
741 | */ |
742 | void QAbstract3DGraph::setPolar(bool enable) |
743 | { |
744 | d_ptr->m_visualController->setPolar(enable); |
745 | } |
746 | |
747 | bool QAbstract3DGraph::isPolar() const |
748 | { |
749 | return d_ptr->m_visualController->isPolar(); |
750 | } |
751 | |
752 | /*! |
753 | * \property QAbstract3DGraph::radialLabelOffset |
754 | * \since QtDataVisualization 1.2 |
755 | * |
756 | * \brief The normalized horizontal offset for the axis labels of the radial |
757 | * polar axis. |
758 | * |
759 | * The value \c 0.0 indicates that the labels should be drawn next to the 0-angle |
760 | * angular axis grid line. The value \c 1.0 indicates that the labels are drawn |
761 | * in their usual place at the edge of the graph background. Defaults to \c 1.0. |
762 | * |
763 | * This property is ignored if the \l polar property value is \c{false}. |
764 | * |
765 | * \sa polar |
766 | */ |
767 | void QAbstract3DGraph::setRadialLabelOffset(float offset) |
768 | { |
769 | d_ptr->m_visualController->setRadialLabelOffset(offset); |
770 | } |
771 | |
772 | float QAbstract3DGraph::radialLabelOffset() const |
773 | { |
774 | return d_ptr->m_visualController->radialLabelOffset(); |
775 | } |
776 | |
777 | /*! |
778 | * \property QAbstract3DGraph::horizontalAspectRatio |
779 | * \since QtDataVisualization 1.2 |
780 | * |
781 | * \brief The ratio of the graph scaling between the x-axis and z-axis. |
782 | * |
783 | * The value of \c 0.0 indicates automatic scaling according to axis ranges. |
784 | * Defaults to \c{0.0}. |
785 | * |
786 | * Has no effect on Q3DBars, which handles scaling on the horizontal plane via |
787 | * the \l{Q3DBars::barThickness}{barThickness} and \l{Q3DBars::barSpacing}{barSpacing} properties. |
788 | * Polar graphs also ignore this property. |
789 | * |
790 | * \sa aspectRatio, polar, Q3DBars::barThickness, Q3DBars::barSpacing |
791 | */ |
792 | void QAbstract3DGraph::setHorizontalAspectRatio(qreal ratio) |
793 | { |
794 | d_ptr->m_visualController->setHorizontalAspectRatio(ratio); |
795 | } |
796 | |
797 | qreal QAbstract3DGraph::horizontalAspectRatio() const |
798 | { |
799 | return d_ptr->m_visualController->horizontalAspectRatio(); |
800 | } |
801 | |
802 | /*! |
803 | * \property QAbstract3DGraph::reflection |
804 | * \since QtDataVisualization 1.2 |
805 | * |
806 | * \brief Whether floor reflections are on or off. |
807 | * |
808 | * Defaults to \c{false}. |
809 | * |
810 | * Affects only Q3DBars. However, in Q3DBars graphs holding both positive and |
811 | * negative values, reflections are not supported for custom items that |
812 | * intersect the floor plane. In that case, reflections should be turned off |
813 | * to avoid incorrect rendering. |
814 | * |
815 | * If using a custom surface format, the stencil buffer needs to be defined |
816 | * (QSurfaceFormat::setStencilBufferSize()) for reflections to work. |
817 | * |
818 | * \sa reflectivity |
819 | */ |
820 | void QAbstract3DGraph::setReflection(bool enable) |
821 | { |
822 | d_ptr->m_visualController->setReflection(enable); |
823 | } |
824 | |
825 | bool QAbstract3DGraph::isReflection() const |
826 | { |
827 | return d_ptr->m_visualController->reflection(); |
828 | } |
829 | |
830 | /*! |
831 | * \property QAbstract3DGraph::reflectivity |
832 | * \since QtDataVisualization 1.2 |
833 | * |
834 | * \brief Floor reflectivity. |
835 | * |
836 | * Larger numbers make the floor more reflective. The valid range is \c{[0...1]}. |
837 | * Defaults to \c{0.5}. |
838 | * |
839 | * \note Affects only Q3DBars. |
840 | * |
841 | * \sa reflection |
842 | */ |
843 | void QAbstract3DGraph::setReflectivity(qreal reflectivity) |
844 | { |
845 | d_ptr->m_visualController->setReflectivity(reflectivity); |
846 | } |
847 | |
848 | qreal QAbstract3DGraph::reflectivity() const |
849 | { |
850 | return d_ptr->m_visualController->reflectivity(); |
851 | } |
852 | |
853 | /*! |
854 | * \property QAbstract3DGraph::locale |
855 | * \since QtDataVisualization 1.2 |
856 | * |
857 | * \brief The locale used for formatting various numeric labels. |
858 | * |
859 | * Defaults to the \c{"C"} locale. |
860 | * |
861 | * \sa QValue3DAxis::labelFormat |
862 | */ |
863 | void QAbstract3DGraph::setLocale(const QLocale &locale) |
864 | { |
865 | d_ptr->m_visualController->setLocale(locale); |
866 | } |
867 | |
868 | QLocale QAbstract3DGraph::locale() const |
869 | { |
870 | return d_ptr->m_visualController->locale(); |
871 | } |
872 | |
873 | /*! |
874 | * \property QAbstract3DGraph::queriedGraphPosition |
875 | * \since QtDataVisualization 1.2 |
876 | * |
877 | * \brief The latest queried graph position values along each axis. |
878 | * |
879 | * This read-only property contains the results from |
880 | * Q3DScene::graphPositionQuery. The values are normalized to the range \c{[-1, 1]}. |
881 | * If the queried position was outside the graph bounds, the values |
882 | * will not reflect the real position, but will instead indicate an undefined position outside |
883 | * the range \c{[-1, 1]}. The value will be undefined until a query is made. |
884 | * |
885 | * There is no single correct 3D coordinate to match a particular screen position, so to be |
886 | * consistent, the queries are always done against the inner sides of an invisible box surrounding |
887 | * the graph. |
888 | * |
889 | * \note Bar graphs only allow querying graph position at the graph floor level, |
890 | * so the y-value is always zero for bar graphs and the valid queries can be only made at |
891 | * screen positions that contain the floor of the graph. |
892 | * |
893 | * \sa Q3DScene::graphPositionQuery |
894 | */ |
895 | QVector3D QAbstract3DGraph::queriedGraphPosition() const |
896 | { |
897 | return d_ptr->m_visualController->queriedGraphPosition(); |
898 | } |
899 | |
900 | /*! |
901 | * \property QAbstract3DGraph::margin |
902 | * \since QtDataVisualization 1.2 |
903 | * |
904 | * \brief The absolute value used for the space left between the edge of the |
905 | * plottable graph area and the edge of the graph background. |
906 | * |
907 | * If the margin value is negative, the margins are determined automatically and can vary according |
908 | * to the size of the items in the series and the type of the graph. |
909 | * The value is interpreted as a fraction of the y-axis range if the graph |
910 | * aspect ratios have not beed changed from the default values. |
911 | * Defaults to \c{-1.0}. |
912 | * |
913 | * \note Setting a smaller margin for a scatter graph than the automatically |
914 | * determined margin can cause the scatter items at the edges of the graph to |
915 | * overlap with the graph background. |
916 | * |
917 | * \note On scatter and surface graphs, if the margin is small in comparison to the axis label |
918 | * size, the positions of the edge labels of the axes are adjusted to avoid overlap with |
919 | * the edge labels of the neighboring axes. |
920 | */ |
921 | void QAbstract3DGraph::setMargin(qreal margin) |
922 | { |
923 | d_ptr->m_visualController->setMargin(margin); |
924 | } |
925 | |
926 | qreal QAbstract3DGraph::margin() const |
927 | { |
928 | return d_ptr->m_visualController->margin(); |
929 | } |
930 | |
931 | /*! |
932 | * Returns \c{true} if the OpenGL context of the graph has been successfully initialized. |
933 | * Trying to use a graph when the context initialization has failed typically results in a crash. |
934 | * A common reason for a context initialization failure is lack of sufficient platform support |
935 | * for OpenGL. |
936 | */ |
937 | bool QAbstract3DGraph::hasContext() const |
938 | { |
939 | if (d_ptr->m_initialized) |
940 | return true; |
941 | else |
942 | return false; |
943 | } |
944 | |
945 | /*! |
946 | * \internal |
947 | */ |
948 | bool QAbstract3DGraph::event(QEvent *event) |
949 | { |
950 | switch (event->type()) { |
951 | case QEvent::UpdateRequest: |
952 | d_ptr->renderNow(); |
953 | return true; |
954 | case QEvent::TouchBegin: |
955 | case QEvent::TouchCancel: |
956 | case QEvent::TouchUpdate: |
957 | case QEvent::TouchEnd: |
958 | d_ptr->m_visualController->touchEvent(event: static_cast<QTouchEvent *>(event)); |
959 | return true; |
960 | default: |
961 | return QWindow::event(event); |
962 | } |
963 | } |
964 | |
965 | /*! |
966 | * \internal |
967 | */ |
968 | void QAbstract3DGraph::resizeEvent(QResizeEvent *event) |
969 | { |
970 | Q_UNUSED(event); |
971 | |
972 | if (d_ptr->m_visualController) { |
973 | Q3DScene *scene = d_ptr->m_visualController->scene(); |
974 | scene->d_ptr->setWindowSize(QSize(width(), height())); |
975 | scene->d_ptr->setViewport(QRect(0, 0, width(), height())); |
976 | } |
977 | } |
978 | |
979 | /*! |
980 | * \internal |
981 | */ |
982 | void QAbstract3DGraph::exposeEvent(QExposeEvent *event) |
983 | { |
984 | Q_UNUSED(event); |
985 | |
986 | if (isExposed()) |
987 | d_ptr->renderNow(); |
988 | } |
989 | |
990 | /*! |
991 | * \internal |
992 | */ |
993 | void QAbstract3DGraph::mouseDoubleClickEvent(QMouseEvent *event) |
994 | { |
995 | d_ptr->m_visualController->mouseDoubleClickEvent(event); |
996 | } |
997 | |
998 | /*! |
999 | * \internal |
1000 | */ |
1001 | void QAbstract3DGraph::touchEvent(QTouchEvent *event) |
1002 | { |
1003 | d_ptr->m_visualController->touchEvent(event); |
1004 | } |
1005 | |
1006 | /*! |
1007 | * \internal |
1008 | */ |
1009 | void QAbstract3DGraph::mousePressEvent(QMouseEvent *event) |
1010 | { |
1011 | d_ptr->m_visualController->mousePressEvent(event, mousePos: event->pos()); |
1012 | } |
1013 | |
1014 | /*! |
1015 | * \internal |
1016 | */ |
1017 | void QAbstract3DGraph::mouseReleaseEvent(QMouseEvent *event) |
1018 | { |
1019 | d_ptr->m_visualController->mouseReleaseEvent(event, mousePos: event->pos()); |
1020 | } |
1021 | |
1022 | /*! |
1023 | * \internal |
1024 | */ |
1025 | void QAbstract3DGraph::mouseMoveEvent(QMouseEvent *event) |
1026 | { |
1027 | d_ptr->m_visualController->mouseMoveEvent(event, mousePos: event->pos()); |
1028 | } |
1029 | |
1030 | #if QT_CONFIG(wheelevent) |
1031 | /*! |
1032 | * \internal |
1033 | */ |
1034 | void QAbstract3DGraph::wheelEvent(QWheelEvent *event) |
1035 | { |
1036 | d_ptr->m_visualController->wheelEvent(event); |
1037 | } |
1038 | #endif |
1039 | |
1040 | QAbstract3DGraphPrivate::QAbstract3DGraphPrivate(QAbstract3DGraph *q) |
1041 | : QObject(0), |
1042 | q_ptr(q), |
1043 | m_updatePending(false), |
1044 | m_visualController(0), |
1045 | m_devicePixelRatio(1.f), |
1046 | m_offscreenSurface(0), |
1047 | m_initialized(false) |
1048 | { |
1049 | } |
1050 | |
1051 | QAbstract3DGraphPrivate::~QAbstract3DGraphPrivate() |
1052 | { |
1053 | if (m_offscreenSurface) { |
1054 | m_offscreenSurface->destroy(); |
1055 | delete m_offscreenSurface; |
1056 | } |
1057 | if (m_context) |
1058 | m_context->makeCurrent(surface: q_ptr); |
1059 | |
1060 | delete m_visualController; |
1061 | } |
1062 | |
1063 | void QAbstract3DGraphPrivate::setVisualController(Abstract3DController *controller) |
1064 | { |
1065 | m_visualController = controller; |
1066 | |
1067 | QObject::connect(sender: m_visualController, signal: &Abstract3DController::activeInputHandlerChanged, receiver: q_ptr, |
1068 | slot: &QAbstract3DGraph::activeInputHandlerChanged); |
1069 | QObject::connect(sender: m_visualController, signal: &Abstract3DController::activeThemeChanged, receiver: q_ptr, |
1070 | slot: &QAbstract3DGraph::activeThemeChanged); |
1071 | QObject::connect(sender: m_visualController, signal: &Abstract3DController::selectionModeChanged, receiver: q_ptr, |
1072 | slot: &QAbstract3DGraph::selectionModeChanged); |
1073 | QObject::connect(sender: m_visualController, signal: &Abstract3DController::shadowQualityChanged, receiver: q_ptr, |
1074 | slot: &QAbstract3DGraph::shadowQualityChanged); |
1075 | QObject::connect(sender: m_visualController, signal: &Abstract3DController::optimizationHintsChanged, receiver: q_ptr, |
1076 | slot: &QAbstract3DGraph::optimizationHintsChanged); |
1077 | QObject::connect(sender: m_visualController, signal: &Abstract3DController::elementSelected, receiver: q_ptr, |
1078 | slot: &QAbstract3DGraph::selectedElementChanged); |
1079 | |
1080 | QObject::connect(sender: m_visualController, signal: &Abstract3DController::needRender, receiver: this, |
1081 | slot: &QAbstract3DGraphPrivate::renderLater); |
1082 | |
1083 | QObject::connect(sender: m_visualController, signal: &Abstract3DController::axisXChanged, receiver: this, |
1084 | slot: &QAbstract3DGraphPrivate::handleAxisXChanged); |
1085 | QObject::connect(sender: m_visualController, signal: &Abstract3DController::axisYChanged, receiver: this, |
1086 | slot: &QAbstract3DGraphPrivate::handleAxisYChanged); |
1087 | QObject::connect(sender: m_visualController, signal: &Abstract3DController::axisZChanged, receiver: this, |
1088 | slot: &QAbstract3DGraphPrivate::handleAxisZChanged); |
1089 | |
1090 | QObject::connect(sender: m_visualController, signal: &Abstract3DController::measureFpsChanged, receiver: q_ptr, |
1091 | slot: &QAbstract3DGraph::measureFpsChanged); |
1092 | QObject::connect(sender: m_visualController, signal: &Abstract3DController::currentFpsChanged, receiver: q_ptr, |
1093 | slot: &QAbstract3DGraph::currentFpsChanged); |
1094 | |
1095 | QObject::connect(sender: m_visualController, signal: &Abstract3DController::orthoProjectionChanged, receiver: q_ptr, |
1096 | slot: &QAbstract3DGraph::orthoProjectionChanged); |
1097 | |
1098 | QObject::connect(sender: m_visualController, signal: &Abstract3DController::aspectRatioChanged, receiver: q_ptr, |
1099 | slot: &QAbstract3DGraph::aspectRatioChanged); |
1100 | QObject::connect(sender: m_visualController, signal: &Abstract3DController::polarChanged, receiver: q_ptr, |
1101 | slot: &QAbstract3DGraph::polarChanged); |
1102 | QObject::connect(sender: m_visualController, signal: &Abstract3DController::radialLabelOffsetChanged, receiver: q_ptr, |
1103 | slot: &QAbstract3DGraph::radialLabelOffsetChanged); |
1104 | QObject::connect(sender: m_visualController, signal: &Abstract3DController::horizontalAspectRatioChanged, receiver: q_ptr, |
1105 | slot: &QAbstract3DGraph::horizontalAspectRatioChanged); |
1106 | |
1107 | QObject::connect(sender: m_visualController, signal: &Abstract3DController::reflectionChanged, receiver: q_ptr, |
1108 | slot: &QAbstract3DGraph::reflectionChanged); |
1109 | QObject::connect(sender: m_visualController, signal: &Abstract3DController::reflectivityChanged, receiver: q_ptr, |
1110 | slot: &QAbstract3DGraph::reflectivityChanged); |
1111 | QObject::connect(sender: m_visualController, signal: &Abstract3DController::localeChanged, receiver: q_ptr, |
1112 | slot: &QAbstract3DGraph::localeChanged); |
1113 | QObject::connect(sender: m_visualController, signal: &Abstract3DController::queriedGraphPositionChanged, receiver: q_ptr, |
1114 | slot: &QAbstract3DGraph::queriedGraphPositionChanged); |
1115 | QObject::connect(sender: m_visualController, signal: &Abstract3DController::marginChanged, receiver: q_ptr, |
1116 | slot: &QAbstract3DGraph::marginChanged); |
1117 | } |
1118 | |
1119 | void QAbstract3DGraphPrivate::handleDevicePixelRatioChange() |
1120 | { |
1121 | if (q_ptr->devicePixelRatio() == m_devicePixelRatio || !m_visualController) |
1122 | return; |
1123 | |
1124 | m_devicePixelRatio = q_ptr->devicePixelRatio(); |
1125 | m_visualController->scene()->setDevicePixelRatio(m_devicePixelRatio); |
1126 | } |
1127 | |
1128 | void QAbstract3DGraphPrivate::render() |
1129 | { |
1130 | handleDevicePixelRatioChange(); |
1131 | m_visualController->synchDataToRenderer(); |
1132 | m_visualController->render(); |
1133 | } |
1134 | |
1135 | void QAbstract3DGraphPrivate::renderLater() |
1136 | { |
1137 | if (!m_updatePending) { |
1138 | m_updatePending = true; |
1139 | QCoreApplication::postEvent(receiver: q_ptr, event: new QEvent(QEvent::UpdateRequest)); |
1140 | } |
1141 | } |
1142 | |
1143 | void QAbstract3DGraphPrivate::renderNow() |
1144 | { |
1145 | if (!q_ptr->isExposed()) |
1146 | return; |
1147 | |
1148 | m_updatePending = false; |
1149 | |
1150 | m_context->makeCurrent(surface: q_ptr); |
1151 | |
1152 | render(); |
1153 | |
1154 | m_context->swapBuffers(surface: q_ptr); |
1155 | } |
1156 | |
1157 | QImage QAbstract3DGraphPrivate::renderToImage(int msaaSamples, const QSize &imageSize) |
1158 | { |
1159 | QImage image; |
1160 | QOpenGLFramebufferObject *fbo; |
1161 | QOpenGLFramebufferObjectFormat fboFormat; |
1162 | if (!m_offscreenSurface) { |
1163 | // Create an offscreen surface for rendering to images without rendering on screen |
1164 | m_offscreenSurface = new QOffscreenSurface(q_ptr->screen()); |
1165 | m_offscreenSurface->setFormat(q_ptr->requestedFormat()); |
1166 | m_offscreenSurface->create(); |
1167 | } |
1168 | // Render the wanted frame offscreen |
1169 | m_context->makeCurrent(surface: m_offscreenSurface); |
1170 | fboFormat.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); |
1171 | if (!Utils::isOpenGLES()) { |
1172 | fboFormat.setInternalTextureFormat(GL_RGB); |
1173 | fboFormat.setSamples(msaaSamples); |
1174 | } |
1175 | fbo = new QOpenGLFramebufferObject(imageSize, fboFormat); |
1176 | if (fbo->isValid()) { |
1177 | QRect originalViewport = m_visualController->m_scene->viewport(); |
1178 | m_visualController->m_scene->d_ptr->setWindowSize(imageSize); |
1179 | m_visualController->m_scene->d_ptr->setViewport(QRect(0, 0, |
1180 | imageSize.width(), |
1181 | imageSize.height())); |
1182 | m_visualController->synchDataToRenderer(); |
1183 | fbo->bind(); |
1184 | m_visualController->requestRender(fbo); |
1185 | image = fbo->toImage(); |
1186 | fbo->release(); |
1187 | m_visualController->m_scene->d_ptr->setWindowSize(originalViewport.size()); |
1188 | m_visualController->m_scene->d_ptr->setViewport(originalViewport); |
1189 | } |
1190 | delete fbo; |
1191 | m_context->makeCurrent(surface: q_ptr); |
1192 | |
1193 | return image; |
1194 | } |
1195 | |
1196 | QT_END_NAMESPACE_DATAVISUALIZATION |
1197 | |