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 "abstract3drenderer_p.h"
31 #include "texturehelper_p.h"
32 #include "q3dcamera_p.h"
33 #include "q3dtheme_p.h"
34 #include "qvalue3daxisformatter_p.h"
35 #include "shaderhelper_p.h"
36 #include "qcustom3ditem_p.h"
37 #include "qcustom3dlabel_p.h"
38 #include "qcustom3dvolume_p.h"
39 #include "scatter3drenderer_p.h"
40 
41 #include <QtCore/qmath.h>
42 #include <QtGui/QOffscreenSurface>
43 #include <QtCore/QThread>
44 
45 QT_BEGIN_NAMESPACE_DATAVISUALIZATION
46 
47 // Defined in shaderhelper.cpp
48 extern void discardDebugMsgs(QtMsgType type, const QMessageLogContext &context, const QString &msg);
49 
50 const qreal doublePi(M_PI * 2.0);
51 const int polarGridRoundness(64);
52 const qreal polarGridAngle(doublePi / qreal(polarGridRoundness));
53 const float polarGridAngleDegrees(float(360.0 / qreal(polarGridRoundness)));
54 const qreal polarGridHalfAngle(polarGridAngle / 2.0);
55 
Abstract3DRenderer(Abstract3DController * controller)56 Abstract3DRenderer::Abstract3DRenderer(Abstract3DController *controller)
57     : QObject(0),
58       m_hasNegativeValues(false),
59       m_cachedTheme(new Q3DTheme()),
60       m_drawer(new Drawer(m_cachedTheme)),
61       m_cachedShadowQuality(QAbstract3DGraph::ShadowQualityMedium),
62       m_autoScaleAdjustment(1.0f),
63       m_cachedSelectionMode(QAbstract3DGraph::SelectionNone),
64       m_cachedOptimizationHint(QAbstract3DGraph::OptimizationDefault),
65       m_textureHelper(0),
66       m_depthTexture(0),
67       m_cachedScene(new Q3DScene()),
68       m_selectionDirty(true),
69       m_selectionState(SelectNone),
70       m_devicePixelRatio(1.0f),
71       m_selectionLabelDirty(true),
72       m_clickResolved(false),
73       m_graphPositionQueryPending(false),
74       m_graphPositionQueryResolved(false),
75       m_clickedSeries(0),
76       m_clickedType(QAbstract3DGraph::ElementNone),
77       m_selectedLabelIndex(-1),
78       m_selectedCustomItemIndex(-1),
79       m_selectionLabelItem(0),
80       m_visibleSeriesCount(0),
81       m_customItemShader(0),
82       m_volumeTextureShader(0),
83       m_volumeTextureLowDefShader(0),
84       m_volumeTextureSliceShader(0),
85       m_volumeSliceFrameShader(0),
86       m_labelShader(0),
87       m_cursorPositionShader(0),
88       m_cursorPositionFrameBuffer(0),
89       m_cursorPositionTexture(0),
90       m_useOrthoProjection(false),
91       m_xFlipped(false),
92       m_yFlipped(false),
93       m_zFlipped(false),
94       m_yFlippedForGrid(false),
95       m_backgroundObj(0),
96       m_gridLineObj(0),
97       m_labelObj(0),
98       m_positionMapperObj(0),
99       m_graphAspectRatio(2.0f),
100       m_graphHorizontalAspectRatio(0.0f),
101       m_polarGraph(false),
102       m_radialLabelOffset(1.0f),
103       m_polarRadius(2.0f),
104       m_xRightAngleRotation(QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, 90.0f)),
105       m_yRightAngleRotation(QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, 90.0f)),
106       m_zRightAngleRotation(QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, 90.0f)),
107       m_xRightAngleRotationNeg(QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, -90.0f)),
108       m_yRightAngleRotationNeg(QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, -90.0f)),
109       m_zRightAngleRotationNeg(QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, -90.0f)),
110       m_xFlipRotation(QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, -180.0f)),
111       m_zFlipRotation(QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, -180.0f)),
112       m_requestedMargin(-1.0f),
113       m_vBackgroundMargin(0.1f),
114       m_hBackgroundMargin(0.1f),
115       m_scaleXWithBackground(0.0f),
116       m_scaleYWithBackground(0.0f),
117       m_scaleZWithBackground(0.0f),
118       m_oldCameraTarget(QVector3D(2000.0f, 2000.0f, 2000.0f)), // Just random invalid target
119       m_reflectionEnabled(false),
120       m_reflectivity(0.5),
121 #if !defined(QT_OPENGL_ES_2)
122       m_funcs_2_1(0),
123 #endif
124       m_context(0),
125       m_isOpenGLES(true)
126 
127 {
128     initializeOpenGLFunctions();
129     m_isOpenGLES = Utils::isOpenGLES();
130 #if !defined(QT_OPENGL_ES_2)
131     if (!m_isOpenGLES) {
132         // Discard warnings about deprecated functions
133         QtMessageHandler handler = qInstallMessageHandler(discardDebugMsgs);
134 
135         m_funcs_2_1 = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_2_1>();
136         if (m_funcs_2_1)
137             m_funcs_2_1->initializeOpenGLFunctions();
138 
139         // Restore original message handler
140         qInstallMessageHandler(handler);
141 
142         if (!m_funcs_2_1)
143             qFatal("OpenGL version is too low, at least 2.1 is required");
144     }
145 #endif
146     QObject::connect(m_drawer, &Drawer::drawerChanged, this, &Abstract3DRenderer::updateTextures);
147     QObject::connect(this, &Abstract3DRenderer::needRender, controller,
148                      &Abstract3DController::needRender, Qt::QueuedConnection);
149     QObject::connect(this, &Abstract3DRenderer::requestShadowQuality, controller,
150                      &Abstract3DController::handleRequestShadowQuality, Qt::QueuedConnection);
151 }
152 
~Abstract3DRenderer()153 Abstract3DRenderer::~Abstract3DRenderer()
154 {
155     contextCleanup();
156     delete m_drawer;
157     delete m_cachedScene;
158     delete m_cachedTheme;
159     delete m_selectionLabelItem;
160     delete m_customItemShader;
161     delete m_volumeTextureShader;
162     delete m_volumeTextureLowDefShader;
163     delete m_volumeSliceFrameShader;
164     delete m_volumeTextureSliceShader;
165     delete m_labelShader;
166     delete m_cursorPositionShader;
167 
168     foreach (SeriesRenderCache *cache, m_renderCacheList) {
169         cache->cleanup(m_textureHelper);
170         delete cache;
171     }
172     m_renderCacheList.clear();
173 
174     foreach (CustomRenderItem *item, m_customRenderCache) {
175         GLuint texture = item->texture();
176         m_textureHelper->deleteTexture(&texture);
177         delete item;
178     }
179     m_customRenderCache.clear();
180 
181     ObjectHelper::releaseObjectHelper(this, m_backgroundObj);
182     ObjectHelper::releaseObjectHelper(this, m_gridLineObj);
183     ObjectHelper::releaseObjectHelper(this, m_labelObj);
184     ObjectHelper::releaseObjectHelper(this, m_positionMapperObj);
185 
186     if (m_textureHelper) {
187         m_textureHelper->deleteTexture(&m_depthTexture);
188         m_textureHelper->deleteTexture(&m_cursorPositionTexture);
189         delete m_textureHelper;
190     }
191 
192     m_axisCacheX.clearLabels();
193     m_axisCacheY.clearLabels();
194     m_axisCacheZ.clearLabels();
195 }
196 
contextCleanup()197 void Abstract3DRenderer::contextCleanup()
198 {
199     if (QOpenGLContext::currentContext())
200         m_textureHelper->glDeleteFramebuffers(1, &m_cursorPositionFrameBuffer);
201 }
202 
initializeOpenGL()203 void Abstract3DRenderer::initializeOpenGL()
204 {
205     m_context = QOpenGLContext::currentContext();
206 
207     // Set OpenGL features
208     glEnable(GL_DEPTH_TEST);
209     glDepthFunc(GL_LESS);
210     glEnable(GL_CULL_FACE);
211     glCullFace(GL_BACK);
212 
213 #if !defined(QT_OPENGL_ES_2)
214     if (!m_isOpenGLES) {
215         glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
216         glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
217         glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
218     }
219 #endif
220 
221     m_textureHelper = new TextureHelper();
222     m_drawer->initializeOpenGL();
223 
224     axisCacheForOrientation(QAbstract3DAxis::AxisOrientationX).setDrawer(m_drawer);
225     axisCacheForOrientation(QAbstract3DAxis::AxisOrientationY).setDrawer(m_drawer);
226     axisCacheForOrientation(QAbstract3DAxis::AxisOrientationZ).setDrawer(m_drawer);
227 
228     initLabelShaders(QStringLiteral(":/shaders/vertexLabel"),
229                      QStringLiteral(":/shaders/fragmentLabel"));
230 
231     initCursorPositionShaders(QStringLiteral(":/shaders/vertexPosition"),
232                               QStringLiteral(":/shaders/fragmentPositionMap"));
233 
234     loadLabelMesh();
235     loadPositionMapperMesh();
236 
237     QObject::connect(m_context.data(), &QOpenGLContext::aboutToBeDestroyed,
238                      this, &Abstract3DRenderer::contextCleanup);
239 }
240 
render(const GLuint defaultFboHandle)241 void Abstract3DRenderer::render(const GLuint defaultFboHandle)
242 {
243     if (defaultFboHandle) {
244         glDepthMask(true);
245         glEnable(GL_DEPTH_TEST);
246         glDepthFunc(GL_LESS);
247         glEnable(GL_CULL_FACE);
248         glCullFace(GL_BACK);
249         glDisable(GL_BLEND); // For QtQuick2 blending is enabled by default, but we don't want it to be
250     }
251 
252     // Clear the graph background to the theme color
253     glViewport(m_viewport.x(),
254                m_viewport.y(),
255                m_viewport.width(),
256                m_viewport.height());
257     glScissor(m_viewport.x(),
258               m_viewport.y(),
259               m_viewport.width(),
260               m_viewport.height());
261     glEnable(GL_SCISSOR_TEST);
262     QVector4D clearColor = Utils::vectorFromColor(m_cachedTheme->windowColor());
263     glClearColor(clearColor.x(), clearColor.y(), clearColor.z(), 1.0f);
264     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
265     glDisable(GL_SCISSOR_TEST);
266 }
267 
updateSelectionState(SelectionState state)268 void Abstract3DRenderer::updateSelectionState(SelectionState state)
269 {
270     m_selectionState = state;
271 }
272 
initGradientShaders(const QString & vertexShader,const QString & fragmentShader)273 void Abstract3DRenderer::initGradientShaders(const QString &vertexShader,
274                                              const QString &fragmentShader)
275 {
276     // Do nothing by default
277     Q_UNUSED(vertexShader)
278     Q_UNUSED(fragmentShader)
279 }
280 
initStaticSelectedItemShaders(const QString & vertexShader,const QString & fragmentShader,const QString & gradientVertexShader,const QString & gradientFragmentShader)281 void Abstract3DRenderer::initStaticSelectedItemShaders(const QString &vertexShader,
282                                                        const QString &fragmentShader,
283                                                        const QString &gradientVertexShader,
284                                                        const QString &gradientFragmentShader)
285 {
286     // Do nothing by default
287     Q_UNUSED(vertexShader)
288     Q_UNUSED(fragmentShader)
289     Q_UNUSED(gradientVertexShader)
290     Q_UNUSED(gradientFragmentShader)
291 }
292 
initCustomItemShaders(const QString & vertexShader,const QString & fragmentShader)293 void Abstract3DRenderer::initCustomItemShaders(const QString &vertexShader,
294                                                const QString &fragmentShader)
295 {
296     delete m_customItemShader;
297     m_customItemShader = new ShaderHelper(this, vertexShader, fragmentShader);
298     m_customItemShader->initialize();
299 }
300 
initVolumeTextureShaders(const QString & vertexShader,const QString & fragmentShader,const QString & fragmentLowDefShader,const QString & sliceShader,const QString & sliceFrameVertexShader,const QString & sliceFrameShader)301 void Abstract3DRenderer::initVolumeTextureShaders(const QString &vertexShader,
302                                                   const QString &fragmentShader,
303                                                   const QString &fragmentLowDefShader,
304                                                   const QString &sliceShader,
305                                                   const QString &sliceFrameVertexShader,
306                                                   const QString &sliceFrameShader)
307 {
308 
309     delete m_volumeTextureShader;
310     m_volumeTextureShader = new ShaderHelper(this, vertexShader, fragmentShader);
311     m_volumeTextureShader->initialize();
312 
313     delete m_volumeTextureLowDefShader;
314     m_volumeTextureLowDefShader = new ShaderHelper(this, vertexShader, fragmentLowDefShader);
315     m_volumeTextureLowDefShader->initialize();
316 
317     delete m_volumeTextureSliceShader;
318     m_volumeTextureSliceShader = new ShaderHelper(this, vertexShader, sliceShader);
319     m_volumeTextureSliceShader->initialize();
320 
321     delete m_volumeSliceFrameShader;
322     m_volumeSliceFrameShader = new ShaderHelper(this, sliceFrameVertexShader, sliceFrameShader);
323     m_volumeSliceFrameShader->initialize();
324 }
325 
initLabelShaders(const QString & vertexShader,const QString & fragmentShader)326 void Abstract3DRenderer::initLabelShaders(const QString &vertexShader, const QString &fragmentShader)
327 {
328     delete m_labelShader;
329     m_labelShader = new ShaderHelper(this, vertexShader, fragmentShader);
330     m_labelShader->initialize();
331 }
332 
initCursorPositionShaders(const QString & vertexShader,const QString & fragmentShader)333 void Abstract3DRenderer::initCursorPositionShaders(const QString &vertexShader,
334                                                    const QString &fragmentShader)
335 {
336     // Init the shader
337     delete m_cursorPositionShader;
338     m_cursorPositionShader = new ShaderHelper(this, vertexShader, fragmentShader);
339     m_cursorPositionShader->initialize();
340 }
341 
initCursorPositionBuffer()342 void Abstract3DRenderer::initCursorPositionBuffer()
343 {
344     m_textureHelper->deleteTexture(&m_cursorPositionTexture);
345     m_textureHelper->glDeleteFramebuffers(1, &m_cursorPositionFrameBuffer);
346     m_cursorPositionFrameBuffer = 0;
347 
348     if (m_primarySubViewport.size().isEmpty())
349         return;
350 
351     m_cursorPositionTexture =
352             m_textureHelper->createCursorPositionTexture(m_primarySubViewport.size(),
353                                                          m_cursorPositionFrameBuffer);
354 }
355 
updateTheme(Q3DTheme * theme)356 void Abstract3DRenderer::updateTheme(Q3DTheme *theme)
357 {
358     // Synchronize the controller theme with renderer
359     bool updateDrawer = theme->d_ptr->sync(*m_cachedTheme->d_ptr);
360 
361     if (updateDrawer)
362         m_drawer->setTheme(m_cachedTheme);
363 }
364 
updateScene(Q3DScene * scene)365 void Abstract3DRenderer::updateScene(Q3DScene *scene)
366 {
367     m_viewport = scene->d_ptr->glViewport();
368     m_secondarySubViewport = scene->d_ptr->glSecondarySubViewport();
369 
370     if (m_primarySubViewport != scene->d_ptr->glPrimarySubViewport()) {
371         // Resize of primary subviewport means resizing shadow and selection buffers
372         m_primarySubViewport = scene->d_ptr->glPrimarySubViewport();
373         handleResize();
374     }
375 
376     if (m_devicePixelRatio != scene->devicePixelRatio()) {
377         m_devicePixelRatio = scene->devicePixelRatio();
378         handleResize();
379     }
380 
381     QPoint logicalPixelPosition = scene->selectionQueryPosition();
382     m_inputPosition = QPoint(logicalPixelPosition.x() * m_devicePixelRatio,
383                              logicalPixelPosition.y() * m_devicePixelRatio);
384 
385     QPoint logicalGraphPosition = scene->graphPositionQuery();
386     m_graphPositionQuery = QPoint(logicalGraphPosition.x() * m_devicePixelRatio,
387                                   logicalGraphPosition.y() * m_devicePixelRatio);
388 
389     // Synchronize the renderer scene to controller scene
390     scene->d_ptr->sync(*m_cachedScene->d_ptr);
391 
392     updateCameraViewport();
393 
394     if (Q3DScene::invalidSelectionPoint() == logicalPixelPosition) {
395         updateSelectionState(SelectNone);
396     } else {
397         if (scene->isSlicingActive()) {
398             if (scene->isPointInPrimarySubView(logicalPixelPosition))
399                 updateSelectionState(SelectOnOverview);
400             else if (scene->isPointInSecondarySubView(logicalPixelPosition))
401                 updateSelectionState(SelectOnSlice);
402             else
403                 updateSelectionState(SelectNone);
404         } else {
405             updateSelectionState(SelectOnScene);
406         }
407     }
408 
409     if (Q3DScene::invalidSelectionPoint() != logicalGraphPosition)
410         m_graphPositionQueryPending = true;
411 
412     // Queue up another render when we have a query that needs resolving.
413     // This is needed because QtQuick scene graph can sometimes do a sync without following it up
414     // with a render.
415     if (m_graphPositionQueryPending || m_selectionState != SelectNone)
416         emit needRender();
417 }
418 
updateTextures()419 void Abstract3DRenderer::updateTextures()
420 {
421     m_axisCacheX.updateTextures();
422     m_axisCacheY.updateTextures();
423     m_axisCacheZ.updateTextures();
424 }
425 
reInitShaders()426 void Abstract3DRenderer::reInitShaders()
427 {
428     if (!m_isOpenGLES) {
429         if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
430             if (m_cachedOptimizationHint.testFlag(QAbstract3DGraph::OptimizationStatic)
431                     && qobject_cast<Scatter3DRenderer *>(this)) {
432                 initGradientShaders(QStringLiteral(":/shaders/vertexShadow"),
433                                     QStringLiteral(":/shaders/fragmentShadow"));
434                 initStaticSelectedItemShaders(QStringLiteral(":/shaders/vertexShadow"),
435                                               QStringLiteral(":/shaders/fragmentShadowNoTex"),
436                                               QStringLiteral(":/shaders/vertexShadow"),
437                                               QStringLiteral(":/shaders/fragmentShadowNoTexColorOnY"));
438                 initShaders(QStringLiteral(":/shaders/vertexShadowNoMatrices"),
439                             QStringLiteral(":/shaders/fragmentShadowNoTex"));
440             } else {
441                 initGradientShaders(QStringLiteral(":/shaders/vertexShadow"),
442                                     QStringLiteral(":/shaders/fragmentShadowNoTexColorOnY"));
443                 initShaders(QStringLiteral(":/shaders/vertexShadow"),
444                             QStringLiteral(":/shaders/fragmentShadowNoTex"));
445             }
446             initBackgroundShaders(QStringLiteral(":/shaders/vertexShadow"),
447                                   QStringLiteral(":/shaders/fragmentShadowNoTex"));
448             initCustomItemShaders(QStringLiteral(":/shaders/vertexShadow"),
449                                   QStringLiteral(":/shaders/fragmentShadow"));
450         } else {
451             if (m_cachedOptimizationHint.testFlag(QAbstract3DGraph::OptimizationStatic)
452                     && qobject_cast<Scatter3DRenderer *>(this)) {
453                 initGradientShaders(QStringLiteral(":/shaders/vertexTexture"),
454                                     QStringLiteral(":/shaders/fragmentTexture"));
455                 initStaticSelectedItemShaders(QStringLiteral(":/shaders/vertex"),
456                                               QStringLiteral(":/shaders/fragment"),
457                                               QStringLiteral(":/shaders/vertex"),
458                                               QStringLiteral(":/shaders/fragmentColorOnY"));
459                 initShaders(QStringLiteral(":/shaders/vertexNoMatrices"),
460                             QStringLiteral(":/shaders/fragment"));
461             } else {
462                 initGradientShaders(QStringLiteral(":/shaders/vertex"),
463                                     QStringLiteral(":/shaders/fragmentColorOnY"));
464                 initShaders(QStringLiteral(":/shaders/vertex"),
465                             QStringLiteral(":/shaders/fragment"));
466             }
467             initBackgroundShaders(QStringLiteral(":/shaders/vertex"),
468                                   QStringLiteral(":/shaders/fragment"));
469             initCustomItemShaders(QStringLiteral(":/shaders/vertexTexture"),
470                                   QStringLiteral(":/shaders/fragmentTexture"));
471         }
472         initVolumeTextureShaders(QStringLiteral(":/shaders/vertexTexture3D"),
473                                  QStringLiteral(":/shaders/fragmentTexture3D"),
474                                  QStringLiteral(":/shaders/fragmentTexture3DLowDef"),
475                                  QStringLiteral(":/shaders/fragmentTexture3DSlice"),
476                                  QStringLiteral(":/shaders/vertexPosition"),
477                                  QStringLiteral(":/shaders/fragment3DSliceFrames"));
478     } else  {
479         if (m_cachedOptimizationHint.testFlag(QAbstract3DGraph::OptimizationStatic)
480                 && qobject_cast<Scatter3DRenderer *>(this)) {
481             initGradientShaders(QStringLiteral(":/shaders/vertexTexture"),
482                                 QStringLiteral(":/shaders/fragmentTextureES2"));
483             initStaticSelectedItemShaders(QStringLiteral(":/shaders/vertex"),
484                                           QStringLiteral(":/shaders/fragmentES2"),
485                                           QStringLiteral(":/shaders/vertex"),
486                                           QStringLiteral(":/shaders/fragmentColorOnYES2"));
487             initShaders(QStringLiteral(":/shaders/vertexNoMatrices"),
488                         QStringLiteral(":/shaders/fragmentES2"));
489         } else {
490             initGradientShaders(QStringLiteral(":/shaders/vertex"),
491                                 QStringLiteral(":/shaders/fragmentColorOnYES2"));
492             initShaders(QStringLiteral(":/shaders/vertex"),
493                         QStringLiteral(":/shaders/fragmentES2"));
494         }
495         initBackgroundShaders(QStringLiteral(":/shaders/vertex"),
496                               QStringLiteral(":/shaders/fragmentES2"));
497         initCustomItemShaders(QStringLiteral(":/shaders/vertexTexture"),
498                               QStringLiteral(":/shaders/fragmentTextureES2"));
499     }
500 }
501 
handleShadowQualityChange()502 void Abstract3DRenderer::handleShadowQualityChange()
503 {
504     reInitShaders();
505 
506     if (m_cachedScene->activeLight()->isAutoPosition()
507             || m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
508         m_cachedScene->d_ptr->setLightPositionRelativeToCamera(defaultLightPos);
509         emit needRender();
510     }
511     if (m_isOpenGLES && m_cachedShadowQuality != QAbstract3DGraph::ShadowQualityNone) {
512         emit requestShadowQuality(QAbstract3DGraph::ShadowQualityNone);
513         qWarning("Shadows are not yet supported for OpenGL ES2");
514         m_cachedShadowQuality = QAbstract3DGraph::ShadowQualityNone;
515     }
516 }
517 
updateSelectionMode(QAbstract3DGraph::SelectionFlags mode)518 void Abstract3DRenderer::updateSelectionMode(QAbstract3DGraph::SelectionFlags mode)
519 {
520     m_cachedSelectionMode = mode;
521     m_selectionDirty = true;
522 }
523 
updateAspectRatio(float ratio)524 void Abstract3DRenderer::updateAspectRatio(float ratio)
525 {
526     m_graphAspectRatio = ratio;
527     foreach (SeriesRenderCache *cache, m_renderCacheList)
528         cache->setDataDirty(true);
529 }
530 
updateHorizontalAspectRatio(float ratio)531 void Abstract3DRenderer::updateHorizontalAspectRatio(float ratio)
532 {
533     m_graphHorizontalAspectRatio = ratio;
534     foreach (SeriesRenderCache *cache, m_renderCacheList)
535         cache->setDataDirty(true);
536 }
537 
updatePolar(bool enable)538 void Abstract3DRenderer::updatePolar(bool enable)
539 {
540     m_polarGraph = enable;
541     foreach (SeriesRenderCache *cache, m_renderCacheList)
542         cache->setDataDirty(true);
543 }
544 
updateRadialLabelOffset(float offset)545 void Abstract3DRenderer::updateRadialLabelOffset(float offset)
546 {
547     m_radialLabelOffset = offset;
548 }
549 
updateMargin(float margin)550 void Abstract3DRenderer::updateMargin(float margin)
551 {
552     m_requestedMargin = margin;
553 }
554 
updateOptimizationHint(QAbstract3DGraph::OptimizationHints hint)555 void Abstract3DRenderer::updateOptimizationHint(QAbstract3DGraph::OptimizationHints hint)
556 {
557     m_cachedOptimizationHint = hint;
558     foreach (SeriesRenderCache *cache, m_renderCacheList)
559         cache->setDataDirty(true);
560 }
561 
handleResize()562 void Abstract3DRenderer::handleResize()
563 {
564     if (m_primarySubViewport.width() == 0 || m_primarySubViewport.height() == 0)
565         return;
566 
567     // Recalculate zoom
568     calculateZoomLevel();
569 
570     // Re-init selection buffer
571     initSelectionBuffer();
572 
573     // Re-init depth buffer
574     updateDepthBuffer();
575 
576     initCursorPositionBuffer();
577 }
578 
calculateZoomLevel()579 void Abstract3DRenderer::calculateZoomLevel()
580 {
581     // Calculate zoom level based on aspect ratio
582     GLfloat div;
583     GLfloat zoomAdjustment;
584     div = qMin(m_primarySubViewport.width(), m_primarySubViewport.height());
585     zoomAdjustment = defaultRatio
586             * ((m_primarySubViewport.width() / div)
587                / (m_primarySubViewport.height() / div));
588     m_autoScaleAdjustment = qMin(zoomAdjustment, 1.0f); // clamp to 1.0f
589 }
590 
updateAxisType(QAbstract3DAxis::AxisOrientation orientation,QAbstract3DAxis::AxisType type)591 void Abstract3DRenderer::updateAxisType(QAbstract3DAxis::AxisOrientation orientation,
592                                         QAbstract3DAxis::AxisType type)
593 {
594     axisCacheForOrientation(orientation).setType(type);
595 }
596 
updateAxisTitle(QAbstract3DAxis::AxisOrientation orientation,const QString & title)597 void Abstract3DRenderer::updateAxisTitle(QAbstract3DAxis::AxisOrientation orientation,
598                                          const QString &title)
599 {
600     axisCacheForOrientation(orientation).setTitle(title);
601 }
602 
updateAxisLabels(QAbstract3DAxis::AxisOrientation orientation,const QStringList & labels)603 void Abstract3DRenderer::updateAxisLabels(QAbstract3DAxis::AxisOrientation orientation,
604                                           const QStringList &labels)
605 {
606     axisCacheForOrientation(orientation).setLabels(labels);
607 }
608 
updateAxisRange(QAbstract3DAxis::AxisOrientation orientation,float min,float max)609 void Abstract3DRenderer::updateAxisRange(QAbstract3DAxis::AxisOrientation orientation,
610                                          float min, float max)
611 {
612     AxisRenderCache &cache = axisCacheForOrientation(orientation);
613     cache.setMin(min);
614     cache.setMax(max);
615 
616     foreach (SeriesRenderCache *cache, m_renderCacheList)
617         cache->setDataDirty(true);
618 }
619 
updateAxisSegmentCount(QAbstract3DAxis::AxisOrientation orientation,int count)620 void Abstract3DRenderer::updateAxisSegmentCount(QAbstract3DAxis::AxisOrientation orientation,
621                                                 int count)
622 {
623     AxisRenderCache &cache = axisCacheForOrientation(orientation);
624     cache.setSegmentCount(count);
625 }
626 
updateAxisSubSegmentCount(QAbstract3DAxis::AxisOrientation orientation,int count)627 void Abstract3DRenderer::updateAxisSubSegmentCount(QAbstract3DAxis::AxisOrientation orientation,
628                                                    int count)
629 {
630     AxisRenderCache &cache = axisCacheForOrientation(orientation);
631     cache.setSubSegmentCount(count);
632 }
633 
updateAxisLabelFormat(QAbstract3DAxis::AxisOrientation orientation,const QString & format)634 void Abstract3DRenderer::updateAxisLabelFormat(QAbstract3DAxis::AxisOrientation orientation,
635                                                const QString &format)
636 {
637     axisCacheForOrientation(orientation).setLabelFormat(format);
638 }
639 
updateAxisReversed(QAbstract3DAxis::AxisOrientation orientation,bool enable)640 void Abstract3DRenderer::updateAxisReversed(QAbstract3DAxis::AxisOrientation orientation,
641                                             bool enable)
642 {
643     axisCacheForOrientation(orientation).setReversed(enable);
644     foreach (SeriesRenderCache *cache, m_renderCacheList)
645         cache->setDataDirty(true);
646 }
647 
updateAxisFormatter(QAbstract3DAxis::AxisOrientation orientation,QValue3DAxisFormatter * formatter)648 void Abstract3DRenderer::updateAxisFormatter(QAbstract3DAxis::AxisOrientation orientation,
649                                              QValue3DAxisFormatter *formatter)
650 {
651     AxisRenderCache &cache = axisCacheForOrientation(orientation);
652     if (cache.ctrlFormatter() != formatter) {
653         delete cache.formatter();
654         cache.setFormatter(formatter->createNewInstance());
655         cache.setCtrlFormatter(formatter);
656     }
657     formatter->d_ptr->populateCopy(*(cache.formatter()));
658     cache.markPositionsDirty();
659 
660     foreach (SeriesRenderCache *cache, m_renderCacheList)
661         cache->setDataDirty(true);
662 }
663 
updateAxisLabelAutoRotation(QAbstract3DAxis::AxisOrientation orientation,float angle)664 void Abstract3DRenderer::updateAxisLabelAutoRotation(QAbstract3DAxis::AxisOrientation orientation,
665                                                      float angle)
666 {
667     AxisRenderCache &cache = axisCacheForOrientation(orientation);
668     if (cache.labelAutoRotation() != angle)
669         cache.setLabelAutoRotation(angle);
670 }
671 
updateAxisTitleVisibility(QAbstract3DAxis::AxisOrientation orientation,bool visible)672 void Abstract3DRenderer::updateAxisTitleVisibility(QAbstract3DAxis::AxisOrientation orientation,
673                                                    bool visible)
674 {
675     AxisRenderCache &cache = axisCacheForOrientation(orientation);
676     if (cache.isTitleVisible() != visible)
677         cache.setTitleVisible(visible);
678 }
679 
updateAxisTitleFixed(QAbstract3DAxis::AxisOrientation orientation,bool fixed)680 void Abstract3DRenderer::updateAxisTitleFixed(QAbstract3DAxis::AxisOrientation orientation,
681                                               bool fixed)
682 {
683     AxisRenderCache &cache = axisCacheForOrientation(orientation);
684     if (cache.isTitleFixed() != fixed)
685         cache.setTitleFixed(fixed);
686 }
687 
modifiedSeriesList(const QVector<QAbstract3DSeries * > & seriesList)688 void Abstract3DRenderer::modifiedSeriesList(const QVector<QAbstract3DSeries *> &seriesList)
689 {
690     foreach (QAbstract3DSeries *series, seriesList) {
691         SeriesRenderCache *cache = m_renderCacheList.value(series, 0);
692         if (cache)
693             cache->setDataDirty(true);
694     }
695 }
696 
fixMeshFileName(QString & fileName,QAbstract3DSeries::Mesh mesh)697 void Abstract3DRenderer::fixMeshFileName(QString &fileName, QAbstract3DSeries::Mesh mesh)
698 {
699     // Default implementation does nothing.
700     Q_UNUSED(fileName)
701     Q_UNUSED(mesh)
702 }
703 
updateSeries(const QList<QAbstract3DSeries * > & seriesList)704 void Abstract3DRenderer::updateSeries(const QList<QAbstract3DSeries *> &seriesList)
705 {
706     foreach (SeriesRenderCache *cache, m_renderCacheList)
707         cache->setValid(false);
708 
709     m_visibleSeriesCount = 0;
710     int seriesCount = seriesList.size();
711     for (int i = 0; i < seriesCount; i++) {
712         QAbstract3DSeries *series = seriesList.at(i);
713         SeriesRenderCache *cache = m_renderCacheList.value(series);
714         bool newSeries = false;
715         if (!cache) {
716             cache = createNewCache(series);
717             m_renderCacheList[series] = cache;
718             newSeries = true;
719         }
720         cache->setValid(true);
721         cache->populate(newSeries);
722         if (cache->isVisible())
723             m_visibleSeriesCount++;
724     }
725 
726     // Remove non-valid objects from the cache list
727     foreach (SeriesRenderCache *cache, m_renderCacheList) {
728         if (!cache->isValid())
729             cleanCache(cache);
730     }
731 }
732 
updateCustomData(const QList<QCustom3DItem * > & customItems)733 void Abstract3DRenderer::updateCustomData(const QList<QCustom3DItem *> &customItems)
734 {
735     if (customItems.isEmpty() && m_customRenderCache.isEmpty())
736         return;
737 
738     foreach (CustomRenderItem *item, m_customRenderCache)
739         item->setValid(false);
740 
741     int itemCount = customItems.size();
742     // Check custom item list for items that are not yet in render item cache
743     for (int i = 0; i < itemCount; i++) {
744         QCustom3DItem *item = customItems.at(i);
745         CustomRenderItem *renderItem = m_customRenderCache.value(item);
746         if (!renderItem)
747             renderItem = addCustomItem(item);
748         renderItem->setValid(true);
749         renderItem->setIndex(i); // always update index, as it must match the custom item index
750     }
751 
752     // Check render item cache and remove items that are not in customItems list anymore
753     foreach (CustomRenderItem *renderItem, m_customRenderCache) {
754         if (!renderItem->isValid()) {
755             m_customRenderCache.remove(renderItem->itemPointer());
756             GLuint texture = renderItem->texture();
757             m_textureHelper->deleteTexture(&texture);
758             delete renderItem;
759         }
760     }
761 
762     m_customItemDrawOrder.clear();
763     m_customItemDrawOrder = QList<QCustom3DItem *>(customItems);
764 }
765 
updateCustomItems()766 void Abstract3DRenderer::updateCustomItems()
767 {
768     // Check all items
769     foreach (CustomRenderItem *item, m_customRenderCache)
770         updateCustomItem(item);
771 }
772 
createNewCache(QAbstract3DSeries * series)773 SeriesRenderCache *Abstract3DRenderer::createNewCache(QAbstract3DSeries *series)
774 {
775     return new SeriesRenderCache(series, this);
776 }
777 
cleanCache(SeriesRenderCache * cache)778 void Abstract3DRenderer::cleanCache(SeriesRenderCache *cache)
779 {
780     m_renderCacheList.remove(cache->series());
781     cache->cleanup(m_textureHelper);
782     delete cache;
783 }
784 
axisCacheForOrientation(QAbstract3DAxis::AxisOrientation orientation)785 AxisRenderCache &Abstract3DRenderer::axisCacheForOrientation(
786         QAbstract3DAxis::AxisOrientation orientation)
787 {
788     switch (orientation) {
789     case QAbstract3DAxis::AxisOrientationX:
790         return m_axisCacheX;
791     case QAbstract3DAxis::AxisOrientationY:
792         return m_axisCacheY;
793     case QAbstract3DAxis::AxisOrientationZ:
794         return m_axisCacheZ;
795     default:
796         qFatal("Abstract3DRenderer::axisCacheForOrientation");
797         return m_axisCacheX;
798     }
799 }
800 
lowerShadowQuality()801 void Abstract3DRenderer::lowerShadowQuality()
802 {
803     QAbstract3DGraph::ShadowQuality newQuality = QAbstract3DGraph::ShadowQualityNone;
804 
805     switch (m_cachedShadowQuality) {
806     case QAbstract3DGraph::ShadowQualityHigh:
807         qWarning("Creating high quality shadows failed. Changing to medium quality.");
808         newQuality = QAbstract3DGraph::ShadowQualityMedium;
809         break;
810     case QAbstract3DGraph::ShadowQualityMedium:
811         qWarning("Creating medium quality shadows failed. Changing to low quality.");
812         newQuality = QAbstract3DGraph::ShadowQualityLow;
813         break;
814     case QAbstract3DGraph::ShadowQualityLow:
815         qWarning("Creating low quality shadows failed. Switching shadows off.");
816         newQuality = QAbstract3DGraph::ShadowQualityNone;
817         break;
818     case QAbstract3DGraph::ShadowQualitySoftHigh:
819         qWarning("Creating soft high quality shadows failed. Changing to soft medium quality.");
820         newQuality = QAbstract3DGraph::ShadowQualitySoftMedium;
821         break;
822     case QAbstract3DGraph::ShadowQualitySoftMedium:
823         qWarning("Creating soft medium quality shadows failed. Changing to soft low quality.");
824         newQuality = QAbstract3DGraph::ShadowQualitySoftLow;
825         break;
826     case QAbstract3DGraph::ShadowQualitySoftLow:
827         qWarning("Creating soft low quality shadows failed. Switching shadows off.");
828         newQuality = QAbstract3DGraph::ShadowQualityNone;
829         break;
830     default:
831         // You'll never get here
832         break;
833     }
834 
835     emit requestShadowQuality(newQuality);
836     updateShadowQuality(newQuality);
837 }
838 
drawAxisTitleY(const QVector3D & sideLabelRotation,const QVector3D & backLabelRotation,const QVector3D & sideLabelTrans,const QVector3D & backLabelTrans,const QQuaternion & totalSideRotation,const QQuaternion & totalBackRotation,AbstractRenderItem & dummyItem,const Q3DCamera * activeCamera,float labelsMaxWidth,const QMatrix4x4 & viewMatrix,const QMatrix4x4 & projectionMatrix,ShaderHelper * shader)839 void Abstract3DRenderer::drawAxisTitleY(const QVector3D &sideLabelRotation,
840                                         const QVector3D &backLabelRotation,
841                                         const QVector3D &sideLabelTrans,
842                                         const QVector3D &backLabelTrans,
843                                         const QQuaternion &totalSideRotation,
844                                         const QQuaternion &totalBackRotation,
845                                         AbstractRenderItem &dummyItem,
846                                         const Q3DCamera *activeCamera,
847                                         float labelsMaxWidth,
848                                         const QMatrix4x4 &viewMatrix,
849                                         const QMatrix4x4 &projectionMatrix,
850                                         ShaderHelper *shader)
851 {
852     float scaleFactor = m_drawer->scaledFontSize() / m_axisCacheY.titleItem().size().height();
853     float titleOffset = 2.0f * (labelMargin + (labelsMaxWidth * scaleFactor));
854     float yRotation;
855     QVector3D titleTrans;
856     QQuaternion totalRotation;
857     if (m_xFlipped == m_zFlipped) {
858         yRotation = backLabelRotation.y();
859         titleTrans = backLabelTrans;
860         totalRotation = totalBackRotation;
861     } else {
862         yRotation = sideLabelRotation.y();
863         titleTrans = sideLabelTrans;
864         totalRotation = totalSideRotation;
865     }
866 
867     QQuaternion offsetRotator = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, yRotation);
868     QVector3D titleOffsetVector =
869             offsetRotator.rotatedVector(QVector3D(-titleOffset, 0.0f, 0.0f));
870 
871     QQuaternion titleRotation;
872     if (m_axisCacheY.isTitleFixed()) {
873         titleRotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, yRotation)
874                 * m_zRightAngleRotation;
875     } else {
876         titleRotation = totalRotation * m_zRightAngleRotation;
877     }
878     dummyItem.setTranslation(titleTrans + titleOffsetVector);
879 
880     m_drawer->drawLabel(dummyItem, m_axisCacheY.titleItem(), viewMatrix,
881                         projectionMatrix, zeroVector, titleRotation, 0,
882                         m_cachedSelectionMode, shader, m_labelObj, activeCamera,
883                         true, true, Drawer::LabelMid, Qt::AlignBottom);
884 }
885 
drawAxisTitleX(const QVector3D & labelRotation,const QVector3D & labelTrans,const QQuaternion & totalRotation,AbstractRenderItem & dummyItem,const Q3DCamera * activeCamera,float labelsMaxWidth,const QMatrix4x4 & viewMatrix,const QMatrix4x4 & projectionMatrix,ShaderHelper * shader,bool radial)886 void Abstract3DRenderer::drawAxisTitleX(const QVector3D &labelRotation,
887                                         const QVector3D &labelTrans,
888                                         const QQuaternion &totalRotation,
889                                         AbstractRenderItem &dummyItem,
890                                         const Q3DCamera *activeCamera,
891                                         float labelsMaxWidth,
892                                         const QMatrix4x4 &viewMatrix,
893                                         const QMatrix4x4 &projectionMatrix,
894                                         ShaderHelper *shader,
895                                         bool radial)
896 {
897     float scaleFactor = m_drawer->scaledFontSize() / m_axisCacheX.titleItem().size().height();
898     float titleOffset;
899     if (radial)
900         titleOffset = -2.0f * (labelMargin + m_drawer->scaledFontSize());
901     else
902         titleOffset = 2.0f * (labelMargin + (labelsMaxWidth * scaleFactor));
903     float zRotation = 0.0f;
904     float yRotation = 0.0f;
905     float xRotation = -90.0f + labelRotation.z();
906     float offsetRotation = labelRotation.z();
907     float extraRotation = -90.0f;
908     Qt::AlignmentFlag alignment = Qt::AlignTop;
909     if (m_yFlippedForGrid) {
910         alignment = Qt::AlignBottom;
911         zRotation = 180.0f;
912         if (m_zFlipped) {
913             titleOffset = -titleOffset;
914             if (m_xFlipped) {
915                 offsetRotation = -offsetRotation;
916                 extraRotation = -extraRotation;
917             } else {
918                 xRotation = -90.0f - labelRotation.z();
919             }
920         } else {
921             yRotation = 180.0f;
922             if (m_xFlipped) {
923                 offsetRotation = -offsetRotation;
924                 xRotation = -90.0f - labelRotation.z();
925             } else {
926                 extraRotation = -extraRotation;
927             }
928         }
929     } else {
930         if (m_zFlipped) {
931             titleOffset = -titleOffset;
932             if (m_xFlipped) {
933                 yRotation = 180.0f;
934                 offsetRotation = -offsetRotation;
935             } else {
936                 yRotation = 180.0f;
937                 xRotation = -90.0f - labelRotation.z();
938                 extraRotation = -extraRotation;
939             }
940         } else {
941             if (m_xFlipped) {
942                 offsetRotation = -offsetRotation;
943                 xRotation = -90.0f - labelRotation.z();
944                 extraRotation = -extraRotation;
945             }
946         }
947     }
948 
949     if (radial) {
950         if (m_zFlipped) {
951             titleOffset = -titleOffset;
952         } else {
953             if (m_yFlippedForGrid)
954                 alignment = Qt::AlignTop;
955             else
956                 alignment = Qt::AlignBottom;
957         }
958     }
959 
960     if (offsetRotation == 180.0f || offsetRotation == -180.0f)
961         offsetRotation = 0.0f;
962     QQuaternion offsetRotator = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, offsetRotation);
963     QVector3D titleOffsetVector =
964             offsetRotator.rotatedVector(QVector3D(0.0f, 0.0f, titleOffset));
965 
966     QQuaternion titleRotation;
967     if (m_axisCacheX.isTitleFixed()) {
968         titleRotation = QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, zRotation)
969                 * QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, yRotation)
970                 * QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, xRotation);
971     } else {
972         titleRotation = totalRotation
973                 * QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, extraRotation);
974     }
975     dummyItem.setTranslation(labelTrans + titleOffsetVector);
976 
977     m_drawer->drawLabel(dummyItem, m_axisCacheX.titleItem(), viewMatrix,
978                         projectionMatrix, zeroVector, titleRotation, 0,
979                         m_cachedSelectionMode, shader, m_labelObj, activeCamera,
980                         true, true, Drawer::LabelMid, alignment);
981 }
982 
drawAxisTitleZ(const QVector3D & labelRotation,const QVector3D & labelTrans,const QQuaternion & totalRotation,AbstractRenderItem & dummyItem,const Q3DCamera * activeCamera,float labelsMaxWidth,const QMatrix4x4 & viewMatrix,const QMatrix4x4 & projectionMatrix,ShaderHelper * shader)983 void Abstract3DRenderer::drawAxisTitleZ(const QVector3D &labelRotation,
984                                         const QVector3D &labelTrans,
985                                         const QQuaternion &totalRotation,
986                                         AbstractRenderItem &dummyItem,
987                                         const Q3DCamera *activeCamera,
988                                         float labelsMaxWidth,
989                                         const QMatrix4x4 &viewMatrix,
990                                         const QMatrix4x4 &projectionMatrix,
991                                         ShaderHelper *shader)
992 {
993     float scaleFactor = m_drawer->scaledFontSize() / m_axisCacheZ.titleItem().size().height();
994     float titleOffset = 2.0f * (labelMargin + (labelsMaxWidth * scaleFactor));
995     float zRotation = labelRotation.z();
996     float yRotation = -90.0f;
997     float xRotation = -90.0f;
998     float extraRotation = 90.0f;
999     Qt::AlignmentFlag alignment = Qt::AlignTop;
1000     if (m_yFlippedForGrid) {
1001         alignment = Qt::AlignBottom;
1002         xRotation = -xRotation;
1003         if (m_zFlipped) {
1004             if (m_xFlipped) {
1005                 titleOffset = -titleOffset;
1006                 zRotation = -zRotation;
1007                 extraRotation = -extraRotation;
1008             } else {
1009                 zRotation = -zRotation;
1010                 yRotation = -yRotation;
1011             }
1012         } else {
1013             if (m_xFlipped) {
1014                 titleOffset = -titleOffset;
1015             } else {
1016                 extraRotation = -extraRotation;
1017                 yRotation = -yRotation;
1018             }
1019         }
1020     } else {
1021         if (m_zFlipped) {
1022             zRotation = -zRotation;
1023             if (m_xFlipped) {
1024                 titleOffset = -titleOffset;
1025             } else {
1026                 extraRotation = -extraRotation;
1027                 yRotation = -yRotation;
1028             }
1029         } else {
1030             if (m_xFlipped) {
1031                 titleOffset = -titleOffset;
1032                 extraRotation = -extraRotation;
1033             } else {
1034                 yRotation = -yRotation;
1035             }
1036         }
1037     }
1038 
1039     float offsetRotation = zRotation;
1040     if (offsetRotation == 180.0f || offsetRotation == -180.0f)
1041         offsetRotation = 0.0f;
1042     QQuaternion offsetRotator = QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, offsetRotation);
1043     QVector3D titleOffsetVector =
1044             offsetRotator.rotatedVector(QVector3D(titleOffset, 0.0f, 0.0f));
1045 
1046     QQuaternion titleRotation;
1047     if (m_axisCacheZ.isTitleFixed()) {
1048         titleRotation = QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, zRotation)
1049                 * QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, yRotation)
1050                 * QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, xRotation);
1051     } else {
1052         titleRotation = totalRotation
1053                 * QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, extraRotation);
1054     }
1055     dummyItem.setTranslation(labelTrans + titleOffsetVector);
1056 
1057     m_drawer->drawLabel(dummyItem, m_axisCacheZ.titleItem(), viewMatrix,
1058                         projectionMatrix, zeroVector, titleRotation, 0,
1059                         m_cachedSelectionMode, shader, m_labelObj, activeCamera,
1060                         true, true, Drawer::LabelMid, alignment);
1061 }
1062 
loadGridLineMesh()1063 void Abstract3DRenderer::loadGridLineMesh()
1064 {
1065     ObjectHelper::resetObjectHelper(this, m_gridLineObj,
1066                                     QStringLiteral(":/defaultMeshes/plane"));
1067 }
1068 
loadLabelMesh()1069 void Abstract3DRenderer::loadLabelMesh()
1070 {
1071     ObjectHelper::resetObjectHelper(this, m_labelObj,
1072                                     QStringLiteral(":/defaultMeshes/plane"));
1073 }
1074 
loadPositionMapperMesh()1075 void Abstract3DRenderer::loadPositionMapperMesh()
1076 {
1077     ObjectHelper::resetObjectHelper(this, m_positionMapperObj,
1078                                     QStringLiteral(":/defaultMeshes/barFull"));
1079 }
1080 
generateBaseColorTexture(const QColor & color,GLuint * texture)1081 void Abstract3DRenderer::generateBaseColorTexture(const QColor &color, GLuint *texture)
1082 {
1083     m_textureHelper->deleteTexture(texture);
1084     *texture = m_textureHelper->createUniformTexture(color);
1085 }
1086 
fixGradientAndGenerateTexture(QLinearGradient * gradient,GLuint * gradientTexture)1087 void Abstract3DRenderer::fixGradientAndGenerateTexture(QLinearGradient *gradient,
1088                                                        GLuint *gradientTexture)
1089 {
1090     // Readjust start/stop to match gradient texture size
1091     gradient->setStart(qreal(gradientTextureWidth), qreal(gradientTextureHeight));
1092     gradient->setFinalStop(0.0, 0.0);
1093 
1094     m_textureHelper->deleteTexture(gradientTexture);
1095 
1096     *gradientTexture = m_textureHelper->createGradientTexture(*gradient);
1097 }
1098 
selectionLabelItem()1099 LabelItem &Abstract3DRenderer::selectionLabelItem()
1100 {
1101     if (!m_selectionLabelItem)
1102         m_selectionLabelItem = new LabelItem;
1103     return *m_selectionLabelItem;
1104 }
1105 
setSelectionLabel(const QString & label)1106 void Abstract3DRenderer::setSelectionLabel(const QString &label)
1107 {
1108     if (m_selectionLabelItem)
1109         m_selectionLabelItem->clear();
1110     m_selectionLabel = label;
1111 }
1112 
selectionLabel()1113 QString &Abstract3DRenderer::selectionLabel()
1114 {
1115     return m_selectionLabel;
1116 }
1117 
indexToSelectionColor(GLint index)1118 QVector4D Abstract3DRenderer::indexToSelectionColor(GLint index)
1119 {
1120     GLubyte idxRed = index & 0xff;
1121     GLubyte idxGreen = (index & 0xff00) >> 8;
1122     GLubyte idxBlue = (index & 0xff0000) >> 16;
1123 
1124     return QVector4D(idxRed, idxGreen, idxBlue, 0);
1125 }
1126 
addCustomItem(QCustom3DItem * item)1127 CustomRenderItem *Abstract3DRenderer::addCustomItem(QCustom3DItem *item)
1128 {
1129     CustomRenderItem *newItem = new CustomRenderItem();
1130     newItem->setRenderer(this);
1131     newItem->setItemPointer(item); // Store pointer for render item updates
1132     newItem->setMesh(item->meshFile());
1133     newItem->setOrigPosition(item->position());
1134     newItem->setOrigScaling(item->scaling());
1135     newItem->setScalingAbsolute(item->isScalingAbsolute());
1136     newItem->setPositionAbsolute(item->isPositionAbsolute());
1137     QImage textureImage = item->d_ptr->textureImage();
1138     bool facingCamera = false;
1139     GLuint texture = 0;
1140     if (item->d_ptr->m_isLabelItem) {
1141         QCustom3DLabel *labelItem = static_cast<QCustom3DLabel *>(item);
1142         newItem->setLabelItem(true);
1143         float pointSize = labelItem->font().pointSizeF();
1144         // Check do we have custom visuals or need to use theme
1145         if (!labelItem->dptr()->m_customVisuals) {
1146             // Recreate texture using theme
1147             labelItem->dptr()->createTextureImage(m_cachedTheme->labelBackgroundColor(),
1148                                                   m_cachedTheme->labelTextColor(),
1149                                                   m_cachedTheme->isLabelBackgroundEnabled(),
1150                                                   m_cachedTheme->isLabelBorderEnabled());
1151             pointSize = m_cachedTheme->font().pointSizeF();
1152             textureImage = item->d_ptr->textureImage();
1153         }
1154         // Calculate scaling based on text (texture size), font size and asked scaling
1155         float scaledFontSize = (0.05f + pointSize / 500.0f) / float(textureImage.height());
1156         QVector3D scaling = newItem->origScaling();
1157         scaling.setX(scaling.x() * textureImage.width() * scaledFontSize);
1158         scaling.setY(scaling.y() * textureImage.height() * scaledFontSize);
1159         newItem->setOrigScaling(scaling);
1160         // Check if facing camera
1161         facingCamera = labelItem->isFacingCamera();
1162     } else if (item->d_ptr->m_isVolumeItem && !m_isOpenGLES) {
1163         QCustom3DVolume *volumeItem = static_cast<QCustom3DVolume *>(item);
1164         newItem->setTextureWidth(volumeItem->textureWidth());
1165         newItem->setTextureHeight(volumeItem->textureHeight());
1166         newItem->setTextureDepth(volumeItem->textureDepth());
1167         if (volumeItem->textureFormat() == QImage::Format_Indexed8)
1168             newItem->setColorTable(volumeItem->colorTable());
1169         newItem->setTextureFormat(volumeItem->textureFormat());
1170         newItem->setVolume(true);
1171         newItem->setBlendNeeded(true);
1172         texture = m_textureHelper->create3DTexture(volumeItem->textureData(),
1173                                                    volumeItem->textureWidth(),
1174                                                    volumeItem->textureHeight(),
1175                                                    volumeItem->textureDepth(),
1176                                                    volumeItem->textureFormat());
1177         newItem->setSliceIndexX(volumeItem->sliceIndexX());
1178         newItem->setSliceIndexY(volumeItem->sliceIndexY());
1179         newItem->setSliceIndexZ(volumeItem->sliceIndexZ());
1180         newItem->setAlphaMultiplier(volumeItem->alphaMultiplier());
1181         newItem->setPreserveOpacity(volumeItem->preserveOpacity());
1182         newItem->setUseHighDefShader(volumeItem->useHighDefShader());
1183 
1184         newItem->setDrawSlices(volumeItem->drawSlices());
1185         newItem->setDrawSliceFrames(volumeItem->drawSliceFrames());
1186         newItem->setSliceFrameColor(volumeItem->sliceFrameColor());
1187         newItem->setSliceFrameWidths(volumeItem->sliceFrameWidths());
1188         newItem->setSliceFrameGaps(volumeItem->sliceFrameGaps());
1189         newItem->setSliceFrameThicknesses(volumeItem->sliceFrameThicknesses());
1190     }
1191     recalculateCustomItemScalingAndPos(newItem);
1192     newItem->setRotation(item->rotation());
1193 
1194     // In OpenGL ES we simply draw volumes as regular custom item placeholders.
1195     if (!item->d_ptr->m_isVolumeItem || m_isOpenGLES)
1196     {
1197         newItem->setBlendNeeded(textureImage.hasAlphaChannel());
1198         texture = m_textureHelper->create2DTexture(textureImage, true, true, true);
1199     }
1200     newItem->setTexture(texture);
1201     item->d_ptr->clearTextureImage();
1202     newItem->setVisible(item->isVisible());
1203     newItem->setShadowCasting(item->isShadowCasting());
1204     newItem->setFacingCamera(facingCamera);
1205     m_customRenderCache.insert(item, newItem);
1206     return newItem;
1207 }
1208 
recalculateCustomItemScalingAndPos(CustomRenderItem * item)1209 void Abstract3DRenderer::recalculateCustomItemScalingAndPos(CustomRenderItem *item)
1210 {
1211     if (!m_polarGraph && !item->isLabel() && !item->isScalingAbsolute()
1212             && !item->isPositionAbsolute()) {
1213         QVector3D scale = item->origScaling() / 2.0f;
1214         QVector3D pos = item->origPosition();
1215         QVector3D minBounds(pos.x() - scale.x(),
1216                             pos.y() - scale.y(),
1217                             pos.z() + scale.z());
1218         QVector3D maxBounds(pos.x() + scale.x(),
1219                             pos.y() + scale.y(),
1220                             pos.z() - scale.z());
1221         QVector3D minCorner = convertPositionToTranslation(minBounds, false);
1222         QVector3D maxCorner = convertPositionToTranslation(maxBounds, false);
1223         scale = QVector3D(qAbs(maxCorner.x() - minCorner.x()),
1224                           qAbs(maxCorner.y() - minCorner.y()),
1225                           qAbs(maxCorner.z() - minCorner.z())) / 2.0f;
1226         if (item->isVolume()) {
1227             // Only volume items need to scale and reposition according to bounds
1228             QVector3D minBoundsNormal = minCorner;
1229             QVector3D maxBoundsNormal = maxCorner;
1230             // getVisibleItemBounds returns bounds normalized for fragment shader [-1,1]
1231             // Y and Z are also flipped.
1232             getVisibleItemBounds(minBoundsNormal, maxBoundsNormal);
1233             item->setMinBounds(minBoundsNormal);
1234             item->setMaxBounds(maxBoundsNormal);
1235             // For scaling calculations, we want [0,1] normalized values
1236             minBoundsNormal = item->minBoundsNormal();
1237             maxBoundsNormal = item->maxBoundsNormal();
1238 
1239             // Rescale and reposition the item so that it doesn't go over the edges
1240             QVector3D adjScaling =
1241                     QVector3D(scale.x() * (maxBoundsNormal.x() - minBoundsNormal.x()),
1242                               scale.y() * (maxBoundsNormal.y() - minBoundsNormal.y()),
1243                               scale.z() * (maxBoundsNormal.z() - minBoundsNormal.z()));
1244 
1245             item->setScaling(adjScaling);
1246 
1247             QVector3D adjPos = item->origPosition();
1248             QVector3D dataExtents = QVector3D(maxBounds.x() - minBounds.x(),
1249                                               maxBounds.y() - minBounds.y(),
1250                                               maxBounds.z() - minBounds.z()) / 2.0f;
1251             adjPos.setX(adjPos.x() + (dataExtents.x() * minBoundsNormal.x())
1252                         - (dataExtents.x() * (1.0f - maxBoundsNormal.x())));
1253             adjPos.setY(adjPos.y() + (dataExtents.y() * minBoundsNormal.y())
1254                         - (dataExtents.y() * (1.0f - maxBoundsNormal.y())));
1255             adjPos.setZ(adjPos.z() + (dataExtents.z() * minBoundsNormal.z())
1256                         - (dataExtents.z() * (1.0f - maxBoundsNormal.z())));
1257             item->setPosition(adjPos);
1258         } else {
1259             // Only scale for non-volume items, and do not readjust position
1260             item->setScaling(scale);
1261             item->setPosition(item->origPosition());
1262         }
1263     } else {
1264         item->setScaling(item->origScaling());
1265         item->setPosition(item->origPosition());
1266         if (item->isVolume()) {
1267             // Y and Z need to be flipped as shader flips those axes
1268             item->setMinBounds(QVector3D(-1.0f, 1.0f, 1.0f));
1269             item->setMaxBounds(QVector3D(1.0f, -1.0f, -1.0f));
1270         }
1271     }
1272     QVector3D translation = convertPositionToTranslation(item->position(),
1273                                                          item->isPositionAbsolute());
1274     item->setTranslation(translation);
1275 }
1276 
updateCustomItem(CustomRenderItem * renderItem)1277 void Abstract3DRenderer::updateCustomItem(CustomRenderItem *renderItem)
1278 {
1279     QCustom3DItem *item = renderItem->itemPointer();
1280     if (item->d_ptr->m_dirtyBits.meshDirty) {
1281         renderItem->setMesh(item->meshFile());
1282         item->d_ptr->m_dirtyBits.meshDirty = false;
1283     }
1284     if (item->d_ptr->m_dirtyBits.positionDirty) {
1285         renderItem->setOrigPosition(item->position());
1286         renderItem->setPositionAbsolute(item->isPositionAbsolute());
1287         if (!item->d_ptr->m_dirtyBits.scalingDirty)
1288             recalculateCustomItemScalingAndPos(renderItem);
1289         item->d_ptr->m_dirtyBits.positionDirty = false;
1290     }
1291     if (item->d_ptr->m_dirtyBits.scalingDirty) {
1292         QVector3D scaling = item->scaling();
1293         renderItem->setOrigScaling(scaling);
1294         renderItem->setScalingAbsolute(item->isScalingAbsolute());
1295         // In case we have label item, we need to recreate texture for scaling adjustment
1296         if (item->d_ptr->m_isLabelItem) {
1297             QCustom3DLabel *labelItem = static_cast<QCustom3DLabel *>(item);
1298             float pointSize = labelItem->font().pointSizeF();
1299             // Check do we have custom visuals or need to use theme
1300             if (labelItem->dptr()->m_customVisuals) {
1301                 // Recreate texture
1302                 labelItem->dptr()->createTextureImage();
1303             } else {
1304                 // Recreate texture using theme
1305                 labelItem->dptr()->createTextureImage(m_cachedTheme->labelBackgroundColor(),
1306                                                       m_cachedTheme->labelTextColor(),
1307                                                       m_cachedTheme->isLabelBackgroundEnabled(),
1308                                                       m_cachedTheme->isLabelBorderEnabled());
1309                 pointSize = m_cachedTheme->font().pointSizeF();
1310             }
1311             QImage textureImage = item->d_ptr->textureImage();
1312             // Calculate scaling based on text (texture size), font size and asked scaling
1313             float scaledFontSize = (0.05f + pointSize / 500.0f) / float(textureImage.height());
1314             scaling.setX(scaling.x() * textureImage.width() * scaledFontSize);
1315             scaling.setY(scaling.y() * textureImage.height() * scaledFontSize);
1316             item->d_ptr->clearTextureImage();
1317             renderItem->setOrigScaling(scaling);
1318         }
1319         recalculateCustomItemScalingAndPos(renderItem);
1320         item->d_ptr->m_dirtyBits.scalingDirty = false;
1321     }
1322     if (item->d_ptr->m_dirtyBits.rotationDirty) {
1323         renderItem->setRotation(item->rotation());
1324         item->d_ptr->m_dirtyBits.rotationDirty = false;
1325     }
1326     if (item->d_ptr->m_dirtyBits.textureDirty) {
1327         QImage textureImage = item->d_ptr->textureImage();
1328         if (item->d_ptr->m_isLabelItem) {
1329             QCustom3DLabel *labelItem = static_cast<QCustom3DLabel *>(item);
1330             // Check do we have custom visuals or need to use theme
1331             if (!labelItem->dptr()->m_customVisuals) {
1332                 // Recreate texture using theme
1333                 labelItem->dptr()->createTextureImage(m_cachedTheme->labelBackgroundColor(),
1334                                                       m_cachedTheme->labelTextColor(),
1335                                                       m_cachedTheme->isLabelBackgroundEnabled(),
1336                                                       m_cachedTheme->isLabelBorderEnabled());
1337                 textureImage = item->d_ptr->textureImage();
1338             }
1339         } else if (!item->d_ptr->m_isVolumeItem || m_isOpenGLES) {
1340             renderItem->setBlendNeeded(textureImage.hasAlphaChannel());
1341             GLuint oldTexture = renderItem->texture();
1342             m_textureHelper->deleteTexture(&oldTexture);
1343             GLuint texture = m_textureHelper->create2DTexture(textureImage, true, true, true);
1344             renderItem->setTexture(texture);
1345         }
1346         item->d_ptr->clearTextureImage();
1347         item->d_ptr->m_dirtyBits.textureDirty = false;
1348     }
1349     if (item->d_ptr->m_dirtyBits.visibleDirty) {
1350         renderItem->setVisible(item->isVisible());
1351         item->d_ptr->m_dirtyBits.visibleDirty = false;
1352     }
1353     if (item->d_ptr->m_dirtyBits.shadowCastingDirty) {
1354         renderItem->setShadowCasting(item->isShadowCasting());
1355         item->d_ptr->m_dirtyBits.shadowCastingDirty = false;
1356     }
1357     if (item->d_ptr->m_isLabelItem) {
1358         QCustom3DLabel *labelItem = static_cast<QCustom3DLabel *>(item);
1359         if (labelItem->dptr()->m_facingCameraDirty) {
1360             renderItem->setFacingCamera(labelItem->isFacingCamera());
1361             labelItem->dptr()->m_facingCameraDirty = false;
1362         }
1363     } else if (item->d_ptr->m_isVolumeItem && !m_isOpenGLES) {
1364         QCustom3DVolume *volumeItem = static_cast<QCustom3DVolume *>(item);
1365         if (volumeItem->dptr()->m_dirtyBitsVolume.colorTableDirty) {
1366             renderItem->setColorTable(volumeItem->colorTable());
1367             volumeItem->dptr()->m_dirtyBitsVolume.colorTableDirty = false;
1368         }
1369         if (volumeItem->dptr()->m_dirtyBitsVolume.textureDimensionsDirty
1370                 || volumeItem->dptr()->m_dirtyBitsVolume.textureDataDirty
1371                 || volumeItem->dptr()->m_dirtyBitsVolume.textureFormatDirty) {
1372             GLuint oldTexture = renderItem->texture();
1373             m_textureHelper->deleteTexture(&oldTexture);
1374             GLuint texture = m_textureHelper->create3DTexture(volumeItem->textureData(),
1375                                                               volumeItem->textureWidth(),
1376                                                               volumeItem->textureHeight(),
1377                                                               volumeItem->textureDepth(),
1378                                                               volumeItem->textureFormat());
1379             renderItem->setTexture(texture);
1380             renderItem->setTextureWidth(volumeItem->textureWidth());
1381             renderItem->setTextureHeight(volumeItem->textureHeight());
1382             renderItem->setTextureDepth(volumeItem->textureDepth());
1383             renderItem->setTextureFormat(volumeItem->textureFormat());
1384             volumeItem->dptr()->m_dirtyBitsVolume.textureDimensionsDirty = false;
1385             volumeItem->dptr()->m_dirtyBitsVolume.textureDataDirty = false;
1386             volumeItem->dptr()->m_dirtyBitsVolume.textureFormatDirty = false;
1387         }
1388         if (volumeItem->dptr()->m_dirtyBitsVolume.slicesDirty) {
1389             renderItem->setDrawSlices(volumeItem->drawSlices());
1390             renderItem->setDrawSliceFrames(volumeItem->drawSliceFrames());
1391             renderItem->setSliceFrameColor(volumeItem->sliceFrameColor());
1392             renderItem->setSliceFrameWidths(volumeItem->sliceFrameWidths());
1393             renderItem->setSliceFrameGaps(volumeItem->sliceFrameGaps());
1394             renderItem->setSliceFrameThicknesses(volumeItem->sliceFrameThicknesses());
1395             renderItem->setSliceIndexX(volumeItem->sliceIndexX());
1396             renderItem->setSliceIndexY(volumeItem->sliceIndexY());
1397             renderItem->setSliceIndexZ(volumeItem->sliceIndexZ());
1398             volumeItem->dptr()->m_dirtyBitsVolume.slicesDirty = false;
1399         }
1400         if (volumeItem->dptr()->m_dirtyBitsVolume.alphaDirty) {
1401             renderItem->setAlphaMultiplier(volumeItem->alphaMultiplier());
1402             renderItem->setPreserveOpacity(volumeItem->preserveOpacity());
1403             volumeItem->dptr()->m_dirtyBitsVolume.alphaDirty = false;
1404         }
1405         if (volumeItem->dptr()->m_dirtyBitsVolume.shaderDirty) {
1406             renderItem->setUseHighDefShader(volumeItem->useHighDefShader());
1407             volumeItem->dptr()->m_dirtyBitsVolume.shaderDirty = false;
1408         }
1409     }
1410 }
1411 
updateCustomItemPositions()1412 void Abstract3DRenderer::updateCustomItemPositions()
1413 {
1414     foreach (CustomRenderItem *renderItem, m_customRenderCache)
1415         recalculateCustomItemScalingAndPos(renderItem);
1416 }
1417 
drawCustomItems(RenderingState state,ShaderHelper * regularShader,const QMatrix4x4 & viewMatrix,const QMatrix4x4 & projectionViewMatrix,const QMatrix4x4 & depthProjectionViewMatrix,GLuint depthTexture,GLfloat shadowQuality,GLfloat reflection)1418 void Abstract3DRenderer::drawCustomItems(RenderingState state,
1419                                          ShaderHelper *regularShader,
1420                                          const QMatrix4x4 &viewMatrix,
1421                                          const QMatrix4x4 &projectionViewMatrix,
1422                                          const QMatrix4x4 &depthProjectionViewMatrix,
1423                                          GLuint depthTexture,
1424                                          GLfloat shadowQuality,
1425                                          GLfloat reflection)
1426 {
1427     if (m_customRenderCache.isEmpty())
1428         return;
1429 
1430     ShaderHelper *shader = regularShader;
1431     shader->bind();
1432 
1433     if (RenderingNormal == state) {
1434         shader->setUniformValue(shader->lightP(), m_cachedScene->activeLight()->position());
1435         shader->setUniformValue(shader->ambientS(), m_cachedTheme->ambientLightStrength());
1436         shader->setUniformValue(shader->lightColor(),
1437                                 Utils::vectorFromColor(m_cachedTheme->lightColor()));
1438         shader->setUniformValue(shader->view(), viewMatrix);
1439     }
1440 
1441     // Draw custom items - first regular and then volumes
1442     bool volumeDetected = false;
1443     int loopCount = 0;
1444     while (loopCount < 2) {
1445         for (QCustom3DItem *customItem : qAsConst(m_customItemDrawOrder)) {
1446             CustomRenderItem *item = m_customRenderCache.value(customItem);
1447             // Check that the render item is visible, and skip drawing if not
1448             // Also check if reflected item is on the "wrong" side, and skip drawing if it is
1449             if (!item->isVisible() || ((m_reflectionEnabled && reflection < 0.0f)
1450                     && (m_yFlipped == (item->translation().y() >= 0.0)))) {
1451                 continue;
1452             }
1453             if (loopCount == 0) {
1454                 if (item->isVolume()) {
1455                     volumeDetected = true;
1456                     continue;
1457                 }
1458             } else {
1459                 if (!item->isVolume())
1460                     continue;
1461             }
1462 
1463             // If the render item is in data coordinates and not within axis ranges, skip it
1464             if (!item->isPositionAbsolute()
1465                     && (item->position().x() < m_axisCacheX.min()
1466                         || item->position().x() > m_axisCacheX.max()
1467                         || item->position().z() < m_axisCacheZ.min()
1468                         || item->position().z() > m_axisCacheZ.max()
1469                         || item->position().y() < m_axisCacheY.min()
1470                         || item->position().y() > m_axisCacheY.max())) {
1471                 continue;
1472             }
1473 
1474             QMatrix4x4 modelMatrix;
1475             QMatrix4x4 itModelMatrix;
1476             QMatrix4x4 MVPMatrix;
1477 
1478             QQuaternion rotation = item->rotation();
1479             // Check if the (label) item should be facing camera, and adjust rotation accordingly
1480             if (item->isFacingCamera()) {
1481                 float camRotationX = m_cachedScene->activeCamera()->xRotation();
1482                 float camRotationY = m_cachedScene->activeCamera()->yRotation();
1483                 rotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, -camRotationX)
1484                         * QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, -camRotationY);
1485             }
1486 
1487             if (m_reflectionEnabled) {
1488                 if (reflection < 0.0f) {
1489                     if (item->itemPointer()->d_ptr->m_isLabelItem)
1490                         continue;
1491                     else
1492                         glCullFace(GL_FRONT);
1493                 } else {
1494                     glCullFace(GL_BACK);
1495                 }
1496                 QVector3D trans = item->translation();
1497                 trans.setY(reflection * trans.y());
1498                 modelMatrix.translate(trans);
1499                 if (reflection < 0.0f) {
1500                     QQuaternion mirror = QQuaternion(rotation.scalar(),
1501                                                      -rotation.x(), rotation.y(), -rotation.z());
1502                     modelMatrix.rotate(mirror);
1503                     itModelMatrix.rotate(mirror);
1504                 } else {
1505                     modelMatrix.rotate(rotation);
1506                     itModelMatrix.rotate(rotation);
1507                 }
1508                 QVector3D scale = item->scaling();
1509                 scale.setY(reflection * scale.y());
1510                 modelMatrix.scale(scale);
1511             } else {
1512                 modelMatrix.translate(item->translation());
1513                 modelMatrix.rotate(rotation);
1514                 modelMatrix.scale(item->scaling());
1515                 itModelMatrix.rotate(rotation);
1516             }
1517             if (!item->isFacingCamera())
1518                 itModelMatrix.scale(item->scaling());
1519             MVPMatrix = projectionViewMatrix * modelMatrix;
1520 
1521             if (RenderingNormal == state) {
1522                 // Normal render
1523                 ShaderHelper *prevShader = shader;
1524                 if (item->isVolume() && !m_isOpenGLES) {
1525                     if (item->drawSlices() &&
1526                             (item->sliceIndexX() >= 0
1527                              || item->sliceIndexY() >= 0
1528                              || item->sliceIndexZ() >= 0)) {
1529                         shader = m_volumeTextureSliceShader;
1530                     } else if (item->useHighDefShader()) {
1531                         shader = m_volumeTextureShader;
1532                     } else {
1533                         shader = m_volumeTextureLowDefShader;
1534                     }
1535                 } else if (item->isLabel()) {
1536                     shader = m_labelShader;
1537                 } else {
1538                     shader = regularShader;
1539                 }
1540                 if (shader != prevShader)
1541                     shader->bind();
1542                 shader->setUniformValue(shader->model(), modelMatrix);
1543                 shader->setUniformValue(shader->MVP(), MVPMatrix);
1544                 shader->setUniformValue(shader->nModel(), itModelMatrix.inverted().transposed());
1545 
1546                 if (item->isBlendNeeded()) {
1547                     glEnable(GL_BLEND);
1548                     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1549                     if (!item->isVolume() && !m_isOpenGLES)
1550                         glDisable(GL_CULL_FACE);
1551                 } else {
1552                     glDisable(GL_BLEND);
1553                     glEnable(GL_CULL_FACE);
1554                 }
1555 
1556                 if (!m_isOpenGLES && m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone
1557                         && !item->isVolume()) {
1558                     // Set shadow shader bindings
1559                     shader->setUniformValue(shader->shadowQ(), shadowQuality);
1560                     shader->setUniformValue(shader->depth(), depthProjectionViewMatrix * modelMatrix);
1561                     shader->setUniformValue(shader->lightS(), m_cachedTheme->lightStrength() / 10.0f);
1562                     m_drawer->drawObject(shader, item->mesh(), item->texture(), depthTexture);
1563                 } else {
1564                     // Set shadowless shader bindings
1565                     if (item->isVolume() && !m_isOpenGLES) {
1566                         QVector3D cameraPos = m_cachedScene->activeCamera()->position();
1567                         cameraPos = MVPMatrix.inverted().map(cameraPos);
1568                         // Adjust camera position according to min/max bounds
1569                         cameraPos = -(cameraPos
1570                                       + ((oneVector - cameraPos) * item->minBoundsNormal())
1571                                       - ((oneVector + cameraPos) * (oneVector - item->maxBoundsNormal())));
1572                         shader->setUniformValue(shader->cameraPositionRelativeToModel(), cameraPos);
1573                         GLint color8Bit = (item->textureFormat() == QImage::Format_Indexed8) ? 1 : 0;
1574                         if (color8Bit) {
1575                             shader->setUniformValueArray(shader->colorIndex(),
1576                                                          item->colorTable().constData(), 256);
1577                         }
1578                         shader->setUniformValue(shader->color8Bit(), color8Bit);
1579                         shader->setUniformValue(shader->alphaMultiplier(), item->alphaMultiplier());
1580                         shader->setUniformValue(shader->preserveOpacity(),
1581                                                 item->preserveOpacity() ? 1 : 0);
1582 
1583                         shader->setUniformValue(shader->minBounds(), item->minBounds());
1584                         shader->setUniformValue(shader->maxBounds(), item->maxBounds());
1585 
1586                         if (shader == m_volumeTextureSliceShader) {
1587                             shader->setUniformValue(shader->volumeSliceIndices(),
1588                                                     item->sliceFractions());
1589                         } else {
1590                             // Precalculate texture dimensions so we can optimize
1591                             // ray stepping to hit every texture layer.
1592                             QVector3D textureDimensions(1.0f / float(item->textureWidth()),
1593                                                         1.0f / float(item->textureHeight()),
1594                                                         1.0f / float(item->textureDepth()));
1595 
1596                             // Worst case scenario sample count
1597                             int sampleCount;
1598                             if (shader == m_volumeTextureLowDefShader) {
1599                                 sampleCount = qMax(item->textureWidth(),
1600                                                    qMax(item->textureDepth(), item->textureHeight()));
1601                                 // Further improve speed with big textures by simply dropping every
1602                                 // other sample:
1603                                 if (sampleCount > 256)
1604                                     sampleCount /= 2;
1605                             } else {
1606                                 sampleCount = item->textureWidth() + item->textureHeight()
1607                                         + item->textureDepth();
1608                             }
1609                             shader->setUniformValue(shader->textureDimensions(), textureDimensions);
1610                             shader->setUniformValue(shader->sampleCount(), sampleCount);
1611                         }
1612                         if (item->drawSliceFrames()) {
1613                             // Set up the slice frame shader
1614                             glDisable(GL_CULL_FACE);
1615                             m_volumeSliceFrameShader->bind();
1616                             m_volumeSliceFrameShader->setUniformValue(
1617                                         m_volumeSliceFrameShader->color(), item->sliceFrameColor());
1618 
1619                             // Draw individual slice frames.
1620                             if (item->sliceIndexX() >= 0)
1621                                 drawVolumeSliceFrame(item, Qt::XAxis, projectionViewMatrix);
1622                             if (item->sliceIndexY() >= 0)
1623                                 drawVolumeSliceFrame(item, Qt::YAxis, projectionViewMatrix);
1624                             if (item->sliceIndexZ() >= 0)
1625                                 drawVolumeSliceFrame(item, Qt::ZAxis, projectionViewMatrix);
1626 
1627                             glEnable(GL_CULL_FACE);
1628                             shader->bind();
1629                         }
1630                         m_drawer->drawObject(shader, item->mesh(), 0, 0, item->texture());
1631                     } else {
1632                         shader->setUniformValue(shader->lightS(), m_cachedTheme->lightStrength());
1633                         m_drawer->drawObject(shader, item->mesh(), item->texture());
1634                     }
1635                 }
1636             } else if (RenderingSelection == state) {
1637                 // Selection render
1638                 shader->setUniformValue(shader->MVP(), MVPMatrix);
1639                 QVector4D itemColor = indexToSelectionColor(item->index());
1640                 itemColor.setW(customItemAlpha);
1641                 itemColor /= 255.0f;
1642                 shader->setUniformValue(shader->color(), itemColor);
1643                 m_drawer->drawObject(shader, item->mesh());
1644             } else if (item->isShadowCasting()) {
1645                 // Depth render
1646                 shader->setUniformValue(shader->MVP(), depthProjectionViewMatrix * modelMatrix);
1647                 m_drawer->drawObject(shader, item->mesh());
1648             }
1649         }
1650         loopCount++;
1651         if (!volumeDetected)
1652             loopCount++; // Skip second run if no volumes detected
1653     }
1654 
1655     if (RenderingNormal == state) {
1656         glDisable(GL_BLEND);
1657         glEnable(GL_CULL_FACE);
1658     }
1659 }
1660 
drawVolumeSliceFrame(const CustomRenderItem * item,Qt::Axis axis,const QMatrix4x4 & projectionViewMatrix)1661 void Abstract3DRenderer::drawVolumeSliceFrame(const CustomRenderItem *item, Qt::Axis axis,
1662                                               const QMatrix4x4 &projectionViewMatrix)
1663 {
1664     QVector2D frameWidth;
1665     QVector3D frameScaling;
1666     QVector3D translation = item->translation();
1667     QQuaternion rotation = item->rotation();
1668     float fracTrans;
1669     bool needRotate = !rotation.isIdentity();
1670     QMatrix4x4 rotationMatrix;
1671     if (needRotate)
1672         rotationMatrix.rotate(rotation);
1673 
1674     if (axis == Qt::XAxis) {
1675         fracTrans = item->sliceFractions().x();
1676         float range = item->maxBoundsNormal().x() - item->minBoundsNormal().x();
1677         float minMult = item->minBoundsNormal().x() / range;
1678         float maxMult = (1.0f - item->maxBoundsNormal().x()) / range;
1679         fracTrans = fracTrans - ((1.0f - fracTrans) * minMult) + ((1.0f + fracTrans) * maxMult);
1680         if (needRotate)
1681             translation += rotationMatrix.map(QVector3D(fracTrans * item->scaling().x(), 0.0f, 0.0f));
1682         else
1683             translation.setX(translation.x() + fracTrans * item->scaling().x());
1684         frameScaling = QVector3D(item->scaling().z()
1685                                 + (item->scaling().z() * item->sliceFrameGaps().z())
1686                                 + (item->scaling().z() * item->sliceFrameWidths().z()),
1687                                 item->scaling().y()
1688                                 + (item->scaling().y() * item->sliceFrameGaps().y())
1689                                 + (item->scaling().y() * item->sliceFrameWidths().y()),
1690                                 item->scaling().x() * item->sliceFrameThicknesses().x());
1691         frameWidth = QVector2D(item->scaling().z() * item->sliceFrameWidths().z(),
1692                                item->scaling().y() * item->sliceFrameWidths().y());
1693         rotation *= m_yRightAngleRotation;
1694     } else if (axis == Qt::YAxis) {
1695         fracTrans = item->sliceFractions().y();
1696         float range = item->maxBoundsNormal().y() - item->minBoundsNormal().y();
1697         // Y axis is logically flipped, so we need to swam min and max bounds
1698         float minMult = (1.0f - item->maxBoundsNormal().y()) / range;
1699         float maxMult = item->minBoundsNormal().y() / range;
1700         fracTrans = fracTrans - ((1.0f - fracTrans) * minMult) + ((1.0f + fracTrans) * maxMult);
1701         if (needRotate)
1702             translation -= rotationMatrix.map(QVector3D(0.0f, fracTrans * item->scaling().y(), 0.0f));
1703         else
1704             translation.setY(translation.y() - fracTrans * item->scaling().y());
1705         frameScaling = QVector3D(item->scaling().x()
1706                                 + (item->scaling().x() * item->sliceFrameGaps().x())
1707                                 + (item->scaling().x() * item->sliceFrameWidths().x()),
1708                                 item->scaling().z()
1709                                 + (item->scaling().z() * item->sliceFrameGaps().z())
1710                                 + (item->scaling().z() * item->sliceFrameWidths().z()),
1711                                 item->scaling().y() * item->sliceFrameThicknesses().y());
1712         frameWidth = QVector2D(item->scaling().x() * item->sliceFrameWidths().x(),
1713                                item->scaling().z() * item->sliceFrameWidths().z());
1714         rotation *= m_xRightAngleRotation;
1715     } else { // Z axis
1716         fracTrans = item->sliceFractions().z();
1717         float range = item->maxBoundsNormal().z() - item->minBoundsNormal().z();
1718         // Z axis is logically flipped, so we need to swam min and max bounds
1719         float minMult = (1.0f - item->maxBoundsNormal().z()) / range;
1720         float maxMult = item->minBoundsNormal().z() / range;
1721         fracTrans = fracTrans - ((1.0f - fracTrans) * minMult) + ((1.0f + fracTrans) * maxMult);
1722         if (needRotate)
1723             translation -= rotationMatrix.map(QVector3D(0.0f, 0.0f, fracTrans * item->scaling().z()));
1724         else
1725             translation.setZ(translation.z() - fracTrans * item->scaling().z());
1726         frameScaling = QVector3D(item->scaling().x()
1727                                 + (item->scaling().x() * item->sliceFrameGaps().x())
1728                                 + (item->scaling().x() * item->sliceFrameWidths().x()),
1729                                 item->scaling().y()
1730                                 + (item->scaling().y() * item->sliceFrameGaps().y())
1731                                 + (item->scaling().y() * item->sliceFrameWidths().y()),
1732                                 item->scaling().z() * item->sliceFrameThicknesses().z());
1733         frameWidth = QVector2D(item->scaling().x() * item->sliceFrameWidths().x(),
1734                                item->scaling().y() * item->sliceFrameWidths().y());
1735     }
1736 
1737     // If the slice is outside the shown area, don't show the frame
1738     if (fracTrans < -1.0 || fracTrans > 1.0)
1739         return;
1740 
1741     // Shader needs the width of clear space in the middle.
1742     frameWidth.setX(1.0f - (frameWidth.x() / frameScaling.x()));
1743     frameWidth.setY(1.0f - (frameWidth.y() / frameScaling.y()));
1744 
1745     QMatrix4x4 modelMatrix;
1746     QMatrix4x4 mvpMatrix;
1747 
1748     modelMatrix.translate(translation);
1749     modelMatrix.rotate(rotation);
1750     modelMatrix.scale(frameScaling);
1751     mvpMatrix = projectionViewMatrix * modelMatrix;
1752     m_volumeSliceFrameShader->setUniformValue(m_volumeSliceFrameShader->MVP(), mvpMatrix);
1753     m_volumeSliceFrameShader->setUniformValue(m_volumeSliceFrameShader->sliceFrameWidth(),
1754                                               frameWidth);
1755 
1756     m_drawer->drawObject(m_volumeSliceFrameShader, item->mesh());
1757 
1758 }
1759 
queriedGraphPosition(const QMatrix4x4 & projectionViewMatrix,const QVector3D & scaling,GLuint defaultFboHandle)1760 void Abstract3DRenderer::queriedGraphPosition(const QMatrix4x4 &projectionViewMatrix,
1761                                               const QVector3D &scaling,
1762                                               GLuint defaultFboHandle)
1763 {
1764     m_cursorPositionShader->bind();
1765 
1766     // Set up mapper framebuffer
1767     glBindFramebuffer(GL_FRAMEBUFFER, m_cursorPositionFrameBuffer);
1768     glViewport(0, 0,
1769                m_primarySubViewport.width(),
1770                m_primarySubViewport.height());
1771     glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
1772     glClear(GL_COLOR_BUFFER_BIT);
1773     glDisable(GL_DITHER); // Dither may affect colors if enabled
1774     glEnable(GL_CULL_FACE);
1775     glCullFace(GL_FRONT);
1776 
1777     // Draw a cube scaled to the graph dimensions
1778     QMatrix4x4 modelMatrix;
1779     QMatrix4x4 MVPMatrix;
1780 
1781     modelMatrix.scale(scaling);
1782 
1783     MVPMatrix = projectionViewMatrix * modelMatrix;
1784     m_cursorPositionShader->setUniformValue(m_cursorPositionShader->MVP(), MVPMatrix);
1785     m_drawer->drawObject(m_cursorPositionShader, m_positionMapperObj);
1786 
1787     QVector4D dataColor = Utils::getSelection(m_graphPositionQuery,
1788                                               m_primarySubViewport.height());
1789     if (dataColor.w() > 0.0f) {
1790         // If position is outside the graph, set the position well outside the graph boundaries
1791         dataColor = QVector4D(-10000.0f, -10000.0f, -10000.0f, 0.0f);
1792     } else {
1793         // Normalize to range [0.0, 1.0]
1794         dataColor /= 255.0f;
1795     }
1796 
1797     // Restore state
1798     glEnable(GL_DITHER);
1799     glCullFace(GL_BACK);
1800 
1801     // Note: Zeroing the frame buffer before resetting it is a workaround for flickering that occurs
1802     // during zoom in some environments.
1803     glBindFramebuffer(GL_FRAMEBUFFER, 0);
1804 
1805     glBindFramebuffer(GL_FRAMEBUFFER, defaultFboHandle);
1806     glViewport(m_primarySubViewport.x(),
1807                m_primarySubViewport.y(),
1808                m_primarySubViewport.width(),
1809                m_primarySubViewport.height());
1810 
1811     QVector3D normalizedValues = dataColor.toVector3D() * 2.0f;
1812     normalizedValues -= oneVector;
1813     m_queriedGraphPosition = QVector3D(normalizedValues.x(),
1814                                        normalizedValues.y(),
1815                                        normalizedValues.z());
1816     m_graphPositionQueryResolved = true;
1817     m_graphPositionQueryPending = false;
1818 }
1819 
calculatePolarXZ(const QVector3D & dataPos,float & x,float & z) const1820 void Abstract3DRenderer::calculatePolarXZ(const QVector3D &dataPos, float &x, float &z) const
1821 {
1822     // x is angular, z is radial
1823     qreal angle = m_axisCacheX.formatter()->positionAt(dataPos.x()) * doublePi;
1824     qreal radius = m_axisCacheZ.formatter()->positionAt(dataPos.z());
1825 
1826     // Convert angle & radius to X and Z coords
1827     x = float(radius * qSin(angle)) * m_polarRadius;
1828     z = -float(radius * qCos(angle)) * m_polarRadius;
1829 }
1830 
drawRadialGrid(ShaderHelper * shader,float yFloorLinePos,const QMatrix4x4 & projectionViewMatrix,const QMatrix4x4 & depthMatrix)1831 void Abstract3DRenderer::drawRadialGrid(ShaderHelper *shader, float yFloorLinePos,
1832                                         const QMatrix4x4 &projectionViewMatrix,
1833                                         const QMatrix4x4 &depthMatrix)
1834 {
1835     static QVector<QQuaternion> lineRotations;
1836     if (!lineRotations.size()) {
1837         lineRotations.resize(polarGridRoundness);
1838         for (int j = 0; j < polarGridRoundness; j++) {
1839             lineRotations[j] = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f,
1840                                                              polarGridAngleDegrees * float(j));
1841         }
1842     }
1843     int gridLineCount = m_axisCacheZ.gridLineCount();
1844     const QVector<float> &gridPositions = m_axisCacheZ.formatter()->gridPositions();
1845     const QVector<float> &subGridPositions = m_axisCacheZ.formatter()->subGridPositions();
1846     int mainSize = gridPositions.size();
1847     QVector3D translateVector(0.0f, yFloorLinePos, 0.0f);
1848     QQuaternion finalRotation = m_xRightAngleRotationNeg;
1849     if (m_yFlippedForGrid)
1850         finalRotation *= m_xFlipRotation;
1851 
1852     for (int i = 0; i < gridLineCount; i++) {
1853         float gridPosition = (i >= mainSize)
1854                 ? subGridPositions.at(i - mainSize) : gridPositions.at(i);
1855         float radiusFraction = m_polarRadius * gridPosition;
1856         QVector3D gridLineScaler(radiusFraction * float(qSin(polarGridHalfAngle)),
1857                                  gridLineWidth, gridLineWidth);
1858         translateVector.setZ(gridPosition * m_polarRadius);
1859         for (int j = 0; j < polarGridRoundness; j++) {
1860             QMatrix4x4 modelMatrix;
1861             QMatrix4x4 itModelMatrix;
1862             modelMatrix.rotate(lineRotations.at(j));
1863             itModelMatrix.rotate(lineRotations.at(j));
1864             modelMatrix.translate(translateVector);
1865             modelMatrix.scale(gridLineScaler);
1866             itModelMatrix.scale(gridLineScaler);
1867             modelMatrix.rotate(finalRotation);
1868             itModelMatrix.rotate(finalRotation);
1869             QMatrix4x4 MVPMatrix = projectionViewMatrix * modelMatrix;
1870 
1871             shader->setUniformValue(shader->model(), modelMatrix);
1872             shader->setUniformValue(shader->nModel(), itModelMatrix.inverted().transposed());
1873             shader->setUniformValue(shader->MVP(), MVPMatrix);
1874             if (!m_isOpenGLES) {
1875                 if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
1876                     // Set shadow shader bindings
1877                     QMatrix4x4 depthMVPMatrix = depthMatrix * modelMatrix;
1878                     shader->setUniformValue(shader->depth(), depthMVPMatrix);
1879                     // Draw the object
1880                     m_drawer->drawObject(shader, m_gridLineObj, 0, m_depthTexture);
1881                 } else {
1882                     // Draw the object
1883                     m_drawer->drawObject(shader, m_gridLineObj);
1884                 }
1885             } else {
1886                 m_drawer->drawLine(shader);
1887             }
1888         }
1889     }
1890 }
1891 
drawAngularGrid(ShaderHelper * shader,float yFloorLinePos,const QMatrix4x4 & projectionViewMatrix,const QMatrix4x4 & depthMatrix)1892 void Abstract3DRenderer::drawAngularGrid(ShaderHelper *shader, float yFloorLinePos,
1893                                          const QMatrix4x4 &projectionViewMatrix,
1894                                          const QMatrix4x4 &depthMatrix)
1895 {
1896     float halfRatio((m_polarRadius + (labelMargin / 2.0f)) / 2.0f);
1897     QVector3D gridLineScaler(gridLineWidth, gridLineWidth, halfRatio);
1898     int gridLineCount = m_axisCacheX.gridLineCount();
1899     const QVector<float> &gridPositions = m_axisCacheX.formatter()->gridPositions();
1900     const QVector<float> &subGridPositions = m_axisCacheX.formatter()->subGridPositions();
1901     int mainSize = gridPositions.size();
1902     QVector3D translateVector(0.0f, yFloorLinePos, -halfRatio);
1903     QQuaternion finalRotation;
1904     if (m_isOpenGLES)
1905         finalRotation = m_yRightAngleRotationNeg;
1906     else
1907         finalRotation = m_xRightAngleRotationNeg;
1908     if (m_yFlippedForGrid)
1909         finalRotation *= m_xFlipRotation;
1910     for (int i = 0; i < gridLineCount; i++) {
1911         QMatrix4x4 modelMatrix;
1912         QMatrix4x4 itModelMatrix;
1913         float gridPosition = (i >= mainSize)
1914                 ? subGridPositions.at(i - mainSize) : gridPositions.at(i);
1915         QQuaternion lineRotation = QQuaternion::fromAxisAndAngle(upVector, gridPosition * 360.0f);
1916         modelMatrix.rotate(lineRotation);
1917         itModelMatrix.rotate(lineRotation);
1918         modelMatrix.translate(translateVector);
1919         modelMatrix.scale(gridLineScaler);
1920         itModelMatrix.scale(gridLineScaler);
1921         modelMatrix.rotate(finalRotation);
1922         itModelMatrix.rotate(finalRotation);
1923         QMatrix4x4 MVPMatrix = projectionViewMatrix * modelMatrix;
1924 
1925         shader->setUniformValue(shader->model(), modelMatrix);
1926         shader->setUniformValue(shader->nModel(), itModelMatrix.inverted().transposed());
1927         shader->setUniformValue(shader->MVP(), MVPMatrix);
1928         if (m_isOpenGLES) {
1929             m_drawer->drawLine(shader);
1930         } else {
1931             if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
1932                 // Set shadow shader bindings
1933                 QMatrix4x4 depthMVPMatrix = depthMatrix * modelMatrix;
1934                 shader->setUniformValue(shader->depth(), depthMVPMatrix);
1935                 // Draw the object
1936                 m_drawer->drawObject(shader, m_gridLineObj, 0, m_depthTexture);
1937             } else {
1938                 // Draw the object
1939                 m_drawer->drawObject(shader, m_gridLineObj);
1940             }
1941         }
1942     }
1943 }
1944 
calculatePolarBackgroundMargin()1945 float Abstract3DRenderer::calculatePolarBackgroundMargin()
1946 {
1947     // Check each extents of each angular label
1948     // Calculate angular position
1949     QVector<float> &labelPositions = m_axisCacheX.formatter()->labelPositions();
1950     float actualLabelHeight = m_drawer->scaledFontSize() * 2.0f; // All labels are same height
1951     float maxNeededMargin = 0.0f;
1952 
1953     // Axis title needs to be accounted for
1954     if (m_axisCacheX.isTitleVisible())
1955         maxNeededMargin = 2.0f * actualLabelHeight + 3.0f * labelMargin;
1956 
1957     for (int label = 0; label < labelPositions.size(); label++) {
1958         QSize labelSize = m_axisCacheX.labelItems().at(label)->size();
1959         float actualLabelWidth = actualLabelHeight / labelSize.height() * labelSize.width();
1960         float labelPosition = labelPositions.at(label);
1961         qreal angle = labelPosition * M_PI * 2.0;
1962         float x = qAbs((m_polarRadius + labelMargin) * float(qSin(angle)))
1963                 + actualLabelWidth - m_polarRadius + labelMargin;
1964         float z = qAbs(-(m_polarRadius + labelMargin) * float(qCos(angle)))
1965                 + actualLabelHeight - m_polarRadius + labelMargin;
1966         float neededMargin = qMax(x, z);
1967         maxNeededMargin = qMax(maxNeededMargin, neededMargin);
1968     }
1969 
1970     return maxNeededMargin;
1971 }
1972 
updateCameraViewport()1973 void Abstract3DRenderer::updateCameraViewport()
1974 {
1975     QVector3D adjustedTarget = m_cachedScene->activeCamera()->target();
1976     fixCameraTarget(adjustedTarget);
1977     if (m_oldCameraTarget != adjustedTarget) {
1978         QVector3D cameraBase = cameraDistanceVector + adjustedTarget;
1979 
1980         m_cachedScene->activeCamera()->d_ptr->setBaseOrientation(cameraBase,
1981                                                                  adjustedTarget,
1982                                                                  upVector);
1983         m_oldCameraTarget = adjustedTarget;
1984     }
1985     m_cachedScene->activeCamera()->d_ptr->updateViewMatrix(m_autoScaleAdjustment);
1986     // Set light position (i.e rotate light with activeCamera, a bit above it).
1987     // Check if we want to use automatic light positioning even without shadows
1988     if (m_cachedScene->activeLight()->isAutoPosition()
1989             || m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
1990         m_cachedScene->d_ptr->setLightPositionRelativeToCamera(defaultLightPos);
1991     }
1992 }
1993 
1994 QT_END_NAMESPACE_DATAVISUALIZATION
1995