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

source code of qtdatavis3d/src/datavisualization/engine/abstract3drenderer.cpp