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 "scatter3drenderer_p.h"
7#include "q3dcamera_p.h"
8#include "shaderhelper_p.h"
9#include "texturehelper_p.h"
10#include "utils_p.h"
11#include "scatterseriesrendercache_p.h"
12#include "scatterobjectbufferhelper_p.h"
13#include "scatterpointbufferhelper_p.h"
14
15#include <QtCore/qmath.h>
16
17// You can verify that depth buffer drawing works correctly by uncommenting this.
18// You should see the scene from where the light is
19//#define SHOW_DEPTH_TEXTURE_SCENE
20
21QT_BEGIN_NAMESPACE
22
23const GLfloat defaultMinSize = 0.01f;
24const GLfloat defaultMaxSize = 0.1f;
25const GLfloat itemScaler = 3.0f;
26
27Scatter3DRenderer::Scatter3DRenderer(Scatter3DController *controller)
28 : Abstract3DRenderer(controller),
29 m_selectedItem(0),
30 m_updateLabels(false),
31 m_dotShader(0),
32 m_dotGradientShader(0),
33 m_staticSelectedItemGradientShader(0),
34 m_staticSelectedItemShader(0),
35 m_pointShader(0),
36 m_depthShader(0),
37 m_selectionShader(0),
38 m_backgroundShader(0),
39 m_staticGradientPointShader(0),
40 m_bgrTexture(0),
41 m_selectionTexture(0),
42 m_depthFrameBuffer(0),
43 m_selectionFrameBuffer(0),
44 m_selectionDepthBuffer(0),
45 m_shadowQualityToShader(100.0f),
46 m_shadowQualityMultiplier(3),
47 m_scaleX(0.0f),
48 m_scaleY(0.0f),
49 m_scaleZ(0.0f),
50 m_selectedItemIndex(Scatter3DController::invalidSelectionIndex()),
51 m_selectedSeriesCache(0),
52 m_oldSelectedSeriesCache(0),
53 m_dotSizeScale(1.0f),
54 m_maxItemSize(0.0f),
55 m_clickedIndex(Scatter3DController::invalidSelectionIndex()),
56 m_havePointSeries(false),
57 m_haveMeshSeries(false),
58 m_haveUniformColorMeshSeries(false),
59 m_haveGradientMeshSeries(false)
60{
61 initializeOpenGL();
62}
63
64Scatter3DRenderer::~Scatter3DRenderer()
65{
66 contextCleanup();
67 delete m_dotShader;
68 delete m_staticSelectedItemGradientShader;
69 delete m_staticSelectedItemShader;
70 delete m_dotGradientShader;
71 delete m_depthShader;
72 delete m_selectionShader;
73 delete m_backgroundShader;
74 delete m_staticGradientPointShader;
75}
76
77void Scatter3DRenderer::contextCleanup()
78{
79 if (QOpenGLContext::currentContext()) {
80 m_textureHelper->glDeleteFramebuffers(n: 1, framebuffers: &m_selectionFrameBuffer);
81 m_textureHelper->glDeleteRenderbuffers(n: 1, renderbuffers: &m_selectionDepthBuffer);
82 m_textureHelper->deleteTexture(texture: &m_selectionTexture);
83 m_textureHelper->glDeleteFramebuffers(n: 1, framebuffers: &m_depthFrameBuffer);
84 m_textureHelper->deleteTexture(texture: &m_bgrTexture);
85 }
86}
87
88void Scatter3DRenderer::initializeOpenGL()
89{
90 Abstract3DRenderer::initializeOpenGL();
91
92 // Initialize shaders
93
94 if (!m_isOpenGLES) {
95 initDepthShader(); // For shadows
96 loadGridLineMesh();
97 } else {
98 initPointShader();
99 }
100
101 // Init selection shader
102 initSelectionShader();
103
104 // Set view port
105 glViewport(x: m_primarySubViewport.x(),
106 y: m_primarySubViewport.y(),
107 width: m_primarySubViewport.width(),
108 height: m_primarySubViewport.height());
109
110 // Load background mesh (we need to be initialized first)
111 loadBackgroundMesh();
112}
113
114void Scatter3DRenderer::fixCameraTarget(QVector3D &target)
115{
116 target.setX(target.x() * m_scaleX);
117 target.setY(target.y() * m_scaleY);
118 target.setZ(target.z() * -m_scaleZ);
119}
120
121void Scatter3DRenderer::getVisibleItemBounds(QVector3D &minBounds, QVector3D &maxBounds)
122{
123 // The inputs are the item bounds in OpenGL coordinates.
124 // The outputs limit these bounds to visible ranges, normalized to range [-1, 1]
125 // Volume shader flips the Y and Z axes, so we need to set negatives of actual values to those
126 float itemRangeX = (maxBounds.x() - minBounds.x());
127 float itemRangeY = (maxBounds.y() - minBounds.y());
128 float itemRangeZ = (maxBounds.z() - minBounds.z());
129
130 if (minBounds.x() < -m_scaleX)
131 minBounds.setX(-1.0f + (2.0f * qAbs(t: minBounds.x() + m_scaleX) / itemRangeX));
132 else
133 minBounds.setX(-1.0f);
134
135 if (minBounds.y() < -m_scaleY)
136 minBounds.setY(-(-1.0f + (2.0f * qAbs(t: minBounds.y() + m_scaleY) / itemRangeY)));
137 else
138 minBounds.setY(1.0f);
139
140 if (minBounds.z() < -m_scaleZ)
141 minBounds.setZ(-(-1.0f + (2.0f * qAbs(t: minBounds.z() + m_scaleZ) / itemRangeZ)));
142 else
143 minBounds.setZ(1.0f);
144
145 if (maxBounds.x() > m_scaleX)
146 maxBounds.setX(1.0f - (2.0f * qAbs(t: maxBounds.x() - m_scaleX) / itemRangeX));
147 else
148 maxBounds.setX(1.0f);
149
150 if (maxBounds.y() > m_scaleY)
151 maxBounds.setY(-(1.0f - (2.0f * qAbs(t: maxBounds.y() - m_scaleY) / itemRangeY)));
152 else
153 maxBounds.setY(-1.0f);
154
155 if (maxBounds.z() > m_scaleZ)
156 maxBounds.setZ(-(1.0f - (2.0f * qAbs(t: maxBounds.z() - m_scaleZ) / itemRangeZ)));
157 else
158 maxBounds.setZ(-1.0f);
159}
160
161void Scatter3DRenderer::updateData()
162{
163 calculateSceneScalingFactors();
164 int totalDataSize = 0;
165
166 foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
167 ScatterSeriesRenderCache *cache = static_cast<ScatterSeriesRenderCache *>(baseCache);
168 if (cache->isVisible()) {
169 const QScatter3DSeries *currentSeries = cache->series();
170 ScatterRenderItemArray &renderArray = cache->renderArray();
171 QScatterDataProxy *dataProxy = currentSeries->dataProxy();
172 const QScatterDataArray &dataArray = *dataProxy->array();
173 int dataSize = dataArray.size();
174 totalDataSize += dataSize;
175 if (cache->dataDirty()) {
176 if (dataSize != renderArray.size())
177 renderArray.resize(size: dataSize);
178
179 for (int i = 0; i < dataSize; i++)
180 updateRenderItem(dataItem: dataArray.at(i), renderItem&: renderArray[i]);
181
182 if (m_cachedOptimizationHint.testFlag(flag: QAbstract3DGraph::OptimizationStatic))
183 cache->setStaticBufferDirty(true);
184
185 cache->setDataDirty(false);
186 }
187 }
188 }
189
190 if (totalDataSize) {
191 m_dotSizeScale = GLfloat(qBound(min: defaultMinSize,
192 val: 2.0f / float(qSqrt(v: qreal(totalDataSize))),
193 max: defaultMaxSize));
194 }
195
196 if (m_cachedOptimizationHint.testFlag(flag: QAbstract3DGraph::OptimizationStatic)) {
197 foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
198 ScatterSeriesRenderCache *cache = static_cast<ScatterSeriesRenderCache *>(baseCache);
199 if (cache->isVisible()) {
200 ScatterRenderItemArray &renderArray = cache->renderArray();
201 const int renderArraySize = renderArray.size();
202
203 if (cache->mesh() == QAbstract3DSeries::MeshPoint) {
204 ScatterPointBufferHelper *points = cache->bufferPoints();
205 if (!points) {
206 points = new ScatterPointBufferHelper();
207 cache->setBufferPoints(points);
208 }
209 points->setScaleY(m_scaleY);
210 points->load(cache);
211 } else {
212 ScatterObjectBufferHelper *object = cache->bufferObject();
213 if (!object) {
214 object = new ScatterObjectBufferHelper();
215 cache->setBufferObject(object);
216 }
217 if (renderArraySize != cache->oldArraySize()
218 || cache->object()->objectFile() != cache->oldMeshFileName()
219 || cache->staticBufferDirty()) {
220 object->setScaleY(m_scaleY);
221 object->fullLoad(cache, dotScale: m_dotSizeScale);
222 cache->setOldArraySize(renderArraySize);
223 cache->setOldMeshFileName(cache->object()->objectFile());
224 } else {
225 object->update(cache, dotScale: m_dotSizeScale);
226 }
227 }
228
229 cache->setStaticBufferDirty(false);
230 }
231 }
232 }
233
234 updateSelectedItem(index: m_selectedItemIndex,
235 series: m_selectedSeriesCache ? m_selectedSeriesCache->series() : 0);
236}
237
238void Scatter3DRenderer::updateSeries(const QList<QAbstract3DSeries *> &seriesList)
239{
240 int seriesCount = seriesList.size();
241
242 // Check OptimizationStatic specific issues before populate marks changeTracker done
243 if (m_cachedOptimizationHint.testFlag(flag: QAbstract3DGraph::OptimizationStatic)) {
244 for (int i = 0; i < seriesCount; i++) {
245 QScatter3DSeries *scatterSeries = static_cast<QScatter3DSeries *>(seriesList[i]);
246 if (scatterSeries->isVisible()) {
247 QAbstract3DSeriesChangeBitField &changeTracker = scatterSeries->d_ptr->m_changeTracker;
248 ScatterSeriesRenderCache *cache =
249 static_cast<ScatterSeriesRenderCache *>(m_renderCacheList.value(key: scatterSeries));
250 if (cache) {
251 if (changeTracker.baseGradientChanged || changeTracker.colorStyleChanged)
252 cache->setStaticObjectUVDirty(true);
253 if (cache->itemSize() != scatterSeries->itemSize())
254 cache->setStaticBufferDirty(true);
255 }
256 }
257 }
258 }
259
260 Abstract3DRenderer::updateSeries(seriesList);
261
262 float maxItemSize = 0.0f;
263 float itemSize = 0.0f;
264 bool noSelection = true;
265
266 m_havePointSeries = false;
267 m_haveMeshSeries = false;
268 m_haveUniformColorMeshSeries = false;
269 m_haveGradientMeshSeries = false;
270
271 for (int i = 0; i < seriesCount; i++) {
272 QScatter3DSeries *scatterSeries = static_cast<QScatter3DSeries *>(seriesList[i]);
273 if (scatterSeries->isVisible()) {
274 ScatterSeriesRenderCache *cache =
275 static_cast<ScatterSeriesRenderCache *>(m_renderCacheList.value(key: scatterSeries));
276 itemSize = scatterSeries->itemSize();
277 if (maxItemSize < itemSize)
278 maxItemSize = itemSize;
279 if (cache->itemSize() != itemSize)
280 cache->setItemSize(itemSize);
281 if (noSelection
282 && scatterSeries->selectedItem() != QScatter3DSeries::invalidSelectionIndex()) {
283 if (m_selectionLabel != cache->itemLabel())
284 m_selectionLabelDirty = true;
285 noSelection = false;
286 }
287
288 if (cache->mesh() == QAbstract3DSeries::MeshPoint) {
289 m_havePointSeries = true;
290 } else {
291 m_haveMeshSeries = true;
292 if (cache->colorStyle() == Q3DTheme::ColorStyleUniform)
293 m_haveUniformColorMeshSeries = true;
294 else
295 m_haveGradientMeshSeries = true;
296 }
297
298 if (cache->staticBufferDirty()) {
299 if (cache->mesh() != QAbstract3DSeries::MeshPoint) {
300 ScatterObjectBufferHelper *object = cache->bufferObject();
301 object->update(cache, dotScale: m_dotSizeScale);
302 }
303 cache->setStaticBufferDirty(false);
304 }
305 if (cache->staticObjectUVDirty()) {
306 if (cache->mesh() == QAbstract3DSeries::MeshPoint) {
307 ScatterPointBufferHelper *object = cache->bufferPoints();
308 object->updateUVs(cache);
309 } else {
310 ScatterObjectBufferHelper *object = cache->bufferObject();
311 object->updateUVs(cache);
312 }
313 cache->setStaticObjectUVDirty(false);
314 }
315 }
316 }
317 m_maxItemSize = maxItemSize;
318 calculateSceneScalingFactors();
319
320 if (noSelection) {
321 if (!selectionLabel().isEmpty())
322 m_selectionLabelDirty = true;
323 m_selectedSeriesCache = 0;
324 }
325}
326
327SeriesRenderCache *Scatter3DRenderer::createNewCache(QAbstract3DSeries *series)
328{
329 return new ScatterSeriesRenderCache(series, this);
330}
331
332void Scatter3DRenderer::updateItems(const QList<Scatter3DController::ChangeItem> &items)
333{
334 ScatterSeriesRenderCache *cache = 0;
335 const QScatter3DSeries *prevSeries = 0;
336 const QScatterDataArray *dataArray = 0;
337 const bool optimizationStatic = m_cachedOptimizationHint.testFlag(
338 flag: QAbstract3DGraph::OptimizationStatic);
339
340 foreach (Scatter3DController::ChangeItem item, items) {
341 QScatter3DSeries *currentSeries = item.series;
342 if (currentSeries != prevSeries) {
343 cache = static_cast<ScatterSeriesRenderCache *>(m_renderCacheList.value(key: currentSeries));
344 prevSeries = currentSeries;
345 dataArray = item.series->dataProxy()->array();
346 // Invisible series render caches are not updated, but instead just marked dirty, so that
347 // they can be completely recalculated when they are turned visible.
348 if (!cache->isVisible() && !cache->dataDirty())
349 cache->setDataDirty(true);
350 }
351 if (cache->isVisible()) {
352 const int index = item.index;
353 if (index >= cache->renderArray().size())
354 continue; // Items removed from array for same render
355 bool oldVisibility = false;
356 ScatterRenderItem &item = cache->renderArray()[index];
357 if (optimizationStatic)
358 oldVisibility = item.isVisible();
359 updateRenderItem(dataItem: dataArray->at(i: index), renderItem&: item);
360 if (optimizationStatic) {
361 if (!cache->visibilityChanged() && oldVisibility != item.isVisible())
362 cache->setVisibilityChanged(true);
363 cache->updateIndices().append(t: index);
364 }
365 }
366 }
367 if (optimizationStatic) {
368 foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
369 ScatterSeriesRenderCache *cache = static_cast<ScatterSeriesRenderCache *>(baseCache);
370 if (cache->isVisible() && cache->updateIndices().size()) {
371 if (cache->mesh() == QAbstract3DSeries::MeshPoint) {
372 cache->bufferPoints()->update(cache);
373 if (cache->colorStyle() == Q3DTheme::ColorStyleRangeGradient)
374 cache->bufferPoints()->updateUVs(cache);
375 } else {
376 if (cache->visibilityChanged()) {
377 // If any change changes item visibility, full load is needed to
378 // resize the buffers.
379 cache->updateIndices().clear();
380 cache->bufferObject()->fullLoad(cache, dotScale: m_dotSizeScale);
381 } else {
382 cache->bufferObject()->update(cache, dotScale: m_dotSizeScale);
383 if (cache->colorStyle() == Q3DTheme::ColorStyleRangeGradient)
384 cache->bufferObject()->updateUVs(cache);
385 }
386 }
387 cache->updateIndices().clear();
388 }
389 cache->setVisibilityChanged(false);
390 }
391 }
392}
393
394void Scatter3DRenderer::updateScene(Q3DScene *scene)
395{
396 scene->activeCamera()->d_ptr->setMinYRotation(-90.0f);
397
398 Abstract3DRenderer::updateScene(scene);
399}
400
401void Scatter3DRenderer::updateAxisLabels(QAbstract3DAxis::AxisOrientation orientation,
402 const QStringList &labels)
403{
404 Abstract3DRenderer::updateAxisLabels(orientation, labels);
405
406 // Angular axis label dimensions affect the chart dimensions
407 if (m_polarGraph && orientation == QAbstract3DAxis::AxisOrientationX)
408 calculateSceneScalingFactors();
409}
410
411void Scatter3DRenderer::updateAxisTitleVisibility(QAbstract3DAxis::AxisOrientation orientation,
412 bool visible)
413{
414 Abstract3DRenderer::updateAxisTitleVisibility(orientation, visible);
415
416 // Angular axis title existence affects the chart dimensions
417 if (m_polarGraph && orientation == QAbstract3DAxis::AxisOrientationX)
418 calculateSceneScalingFactors();
419}
420
421void Scatter3DRenderer::updateOptimizationHint(QAbstract3DGraph::OptimizationHints hint)
422{
423 Abstract3DRenderer::updateOptimizationHint(hint);
424
425 Abstract3DRenderer::reInitShaders();
426
427 if (m_isOpenGLES && hint.testFlag(flag: QAbstract3DGraph::OptimizationStatic)
428 && !m_staticGradientPointShader) {
429 initStaticPointShaders(QStringLiteral(":/shaders/vertexPointES2_UV"),
430 QStringLiteral(":/shaders/fragmentLabel"));
431 }
432}
433
434void Scatter3DRenderer::updateMargin(float margin)
435{
436 Abstract3DRenderer::updateMargin(margin);
437 calculateSceneScalingFactors();
438}
439
440void Scatter3DRenderer::resetClickedStatus()
441{
442 m_clickedIndex = Scatter3DController::invalidSelectionIndex();
443 m_clickedSeries = 0;
444}
445
446void Scatter3DRenderer::render(GLuint defaultFboHandle)
447{
448 // Handle GL state setup for FBO buffers and clearing of the render surface
449 Abstract3DRenderer::render(defaultFboHandle);
450
451 if (m_axisCacheX.positionsDirty())
452 m_axisCacheX.updateAllPositions();
453 if (m_axisCacheY.positionsDirty())
454 m_axisCacheY.updateAllPositions();
455 if (m_axisCacheZ.positionsDirty())
456 m_axisCacheZ.updateAllPositions();
457
458 // Draw dots scene
459 drawScene(defaultFboHandle);
460}
461
462void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
463{
464 GLfloat backgroundRotation = 0;
465 GLfloat selectedItemSize = 0.0f;
466
467 // Get the optimization flag
468 const bool optimizationDefault =
469 !m_cachedOptimizationHint.testFlag(flag: QAbstract3DGraph::OptimizationStatic);
470
471 const Q3DCamera *activeCamera = m_cachedScene->activeCamera();
472
473 QVector4D lightColor = Utils::vectorFromColor(color: m_cachedTheme->lightColor());
474
475 // Specify viewport
476 glViewport(x: m_primarySubViewport.x(),
477 y: m_primarySubViewport.y(),
478 width: m_primarySubViewport.width(),
479 height: m_primarySubViewport.height());
480
481 // Set up projection matrix
482 QMatrix4x4 projectionMatrix;
483 GLfloat viewPortRatio = (GLfloat)m_primarySubViewport.width()
484 / (GLfloat)m_primarySubViewport.height();
485 if (m_useOrthoProjection) {
486 GLfloat orthoRatio = 2.0f;
487 projectionMatrix.ortho(left: -viewPortRatio * orthoRatio, right: viewPortRatio * orthoRatio,
488 bottom: -orthoRatio, top: orthoRatio,
489 nearPlane: 0.0f, farPlane: 100.0f);
490 } else {
491 projectionMatrix.perspective(verticalAngle: 45.0f, aspectRatio: viewPortRatio, nearPlane: 0.1f, farPlane: 100.0f);
492 }
493
494 // Calculate view matrix
495 QMatrix4x4 viewMatrix = activeCamera->d_ptr->viewMatrix();
496 QMatrix4x4 projectionViewMatrix = projectionMatrix * viewMatrix;
497
498 // Calculate label flipping
499 if (viewMatrix.row(index: 0).x() > 0)
500 m_zFlipped = false;
501 else
502 m_zFlipped = true;
503 if (viewMatrix.row(index: 0).z() <= 0)
504 m_xFlipped = false;
505 else
506 m_xFlipped = true;
507
508 // Check if we're viewing the scene from below
509 if (viewMatrix.row(index: 2).y() < 0)
510 m_yFlipped = true;
511 else
512 m_yFlipped = false;
513 m_yFlippedForGrid = m_yFlipped; // Polar axis grid drawing in abstract needs this
514
515 // Calculate background rotation
516 if (!m_zFlipped && !m_xFlipped)
517 backgroundRotation = 270.0f;
518 else if (!m_zFlipped && m_xFlipped)
519 backgroundRotation = 180.0f;
520 else if (m_zFlipped && m_xFlipped)
521 backgroundRotation = 90.0f;
522 else if (m_zFlipped && !m_xFlipped)
523 backgroundRotation = 0.0f;
524
525 // Get light position from the scene
526 QVector3D lightPos = m_cachedScene->activeLight()->position();
527
528 // Introduce regardless of shadow quality to simplify logic
529 QMatrix4x4 depthProjectionViewMatrix;
530
531 ShaderHelper *pointSelectionShader;
532 if (!m_isOpenGLES) {
533#if !QT_CONFIG(opengles2)
534 if (m_havePointSeries) {
535 glEnable(GL_POINT_SMOOTH);
536 glEnable(GL_PROGRAM_POINT_SIZE);
537 }
538
539 if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
540 // Render scene into a depth texture for using with shadow mapping
541 // Bind depth shader
542 m_depthShader->bind();
543
544 // Set viewport for depth map rendering. Must match texture size. Larger values give smoother shadows.
545 glViewport(x: 0, y: 0,
546 width: m_primarySubViewport.width() * m_shadowQualityMultiplier,
547 height: m_primarySubViewport.height() * m_shadowQualityMultiplier);
548
549 // Enable drawing to framebuffer
550 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer: m_depthFrameBuffer);
551 glClear(GL_DEPTH_BUFFER_BIT);
552
553 // Set front face culling to reduce self-shadowing issues
554 glCullFace(GL_FRONT);
555
556 QMatrix4x4 depthViewMatrix;
557 QMatrix4x4 depthProjectionMatrix;
558
559 // Get the depth view matrix
560 // It may be possible to hack lightPos here if we want to make some tweaks to shadow
561 QVector3D depthLightPos = activeCamera->d_ptr->calculatePositionRelativeToCamera(
562 relativePosition: zeroVector, fixedRotation: 0.0f, distanceModifier: 2.5f / m_autoScaleAdjustment);
563 depthViewMatrix.lookAt(eye: depthLightPos, center: zeroVector, up: upVector);
564 // Set the depth projection matrix
565 depthProjectionMatrix.perspective(verticalAngle: 15.0f, aspectRatio: viewPortRatio, nearPlane: 3.0f, farPlane: 100.0f);
566 depthProjectionViewMatrix = depthProjectionMatrix * depthViewMatrix;
567
568 // Draw dots to depth buffer
569 foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
570 if (baseCache->isVisible()) {
571 ScatterSeriesRenderCache *cache =
572 static_cast<ScatterSeriesRenderCache *>(baseCache);
573 ObjectHelper *dotObj = cache->object();
574 QQuaternion seriesRotation(cache->meshRotation());
575 const ScatterRenderItemArray &renderArray = cache->renderArray();
576 const int renderArraySize = renderArray.size();
577 bool drawingPoints = (cache->mesh() == QAbstract3DSeries::MeshPoint);
578 float itemSize = cache->itemSize() / itemScaler;
579 if (itemSize == 0.0f)
580 itemSize = m_dotSizeScale;
581 if (drawingPoints) {
582 // Scale points based on shadow quality for shadows, not by zoom level
583 m_funcs_2_1->glPointSize(size: itemSize * 100.0f * m_shadowQualityMultiplier);
584 }
585 QVector3D modelScaler(itemSize, itemSize, itemSize);
586
587 if (!optimizationDefault
588 && ((drawingPoints && cache->bufferPoints()->indexCount() == 0)
589 || (!drawingPoints && cache->bufferObject()->indexCount() == 0))) {
590 continue;
591 }
592
593 int loopCount = 1;
594 if (optimizationDefault)
595 loopCount = renderArraySize;
596 for (int dot = 0; dot < loopCount; dot++) {
597 const ScatterRenderItem &item = renderArray.at(i: dot);
598 if (!item.isVisible() && optimizationDefault)
599 continue;
600
601 QMatrix4x4 modelMatrix;
602 QMatrix4x4 MVPMatrix;
603
604 if (optimizationDefault) {
605 modelMatrix.translate(vector: item.translation());
606 if (!drawingPoints) {
607 if (!seriesRotation.isIdentity() || !item.rotation().isIdentity())
608 modelMatrix.rotate(quaternion: seriesRotation * item.rotation());
609 modelMatrix.scale(vector: modelScaler);
610 }
611 }
612
613 MVPMatrix = depthProjectionViewMatrix * modelMatrix;
614
615 m_depthShader->setUniformValue(uniform: m_depthShader->MVP(), value: MVPMatrix);
616
617 if (drawingPoints) {
618 if (optimizationDefault)
619 m_drawer->drawPoint(shader: m_depthShader);
620 else
621 m_drawer->drawPoints(shader: m_depthShader, object: cache->bufferPoints(), textureId: 0);
622 } else {
623 if (optimizationDefault) {
624 // 1st attribute buffer : vertices
625 glEnableVertexAttribArray(index: m_depthShader->posAtt());
626 glBindBuffer(GL_ARRAY_BUFFER, buffer: dotObj->vertexBuf());
627 glVertexAttribPointer(indx: m_depthShader->posAtt(), size: 3, GL_FLOAT, GL_FALSE, stride: 0,
628 ptr: (void *)0);
629
630 // Index buffer
631 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer: dotObj->elementBuf());
632
633 // Draw the triangles
634 glDrawElements(GL_TRIANGLES, count: dotObj->indexCount(),
635 GL_UNSIGNED_INT, indices: (void *)0);
636
637 // Free buffers
638 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer: 0);
639 glBindBuffer(GL_ARRAY_BUFFER, buffer: 0);
640
641 glDisableVertexAttribArray(index: m_depthShader->posAtt());
642 } else {
643 ScatterObjectBufferHelper *object = cache->bufferObject();
644 // 1st attribute buffer : vertices
645 glEnableVertexAttribArray(index: m_depthShader->posAtt());
646 glBindBuffer(GL_ARRAY_BUFFER, buffer: object->vertexBuf());
647 glVertexAttribPointer(indx: m_depthShader->posAtt(), size: 3, GL_FLOAT, GL_FALSE, stride: 0,
648 ptr: (void *)0);
649
650 // Index buffer
651 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer: object->elementBuf());
652
653 // Draw the triangles
654 glDrawElements(GL_TRIANGLES, count: object->indexCount(),
655 GL_UNSIGNED_INT, indices: (void *)0);
656
657 // Free buffers
658 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer: 0);
659 glBindBuffer(GL_ARRAY_BUFFER, buffer: 0);
660
661 glDisableVertexAttribArray(index: m_depthShader->posAtt());
662 }
663 }
664 }
665 }
666 }
667
668 Abstract3DRenderer::drawCustomItems(state: RenderingDepth, regularShader: m_depthShader, viewMatrix,
669 projectionViewMatrix,
670 depthProjectionViewMatrix, depthTexture: m_depthTexture,
671 shadowQuality: m_shadowQualityToShader);
672
673 // Disable drawing to framebuffer (= enable drawing to screen)
674 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer: defaultFboHandle);
675
676 // Reset culling to normal
677 glCullFace(GL_BACK);
678
679 // Revert to original viewport
680 glViewport(x: m_primarySubViewport.x(),
681 y: m_primarySubViewport.y(),
682 width: m_primarySubViewport.width(),
683 height: m_primarySubViewport.height());
684 }
685#endif
686 pointSelectionShader = m_selectionShader;
687 } else {
688 pointSelectionShader = m_pointShader;
689 }
690
691 ShaderHelper *selectionShader = m_selectionShader;
692
693 // Do position mapping when necessary
694 if (m_graphPositionQueryPending) {
695 QVector3D graphDimensions(m_scaleX, m_scaleY, m_scaleZ);
696 queriedGraphPosition(projectionViewMatrix, scaling: graphDimensions, defaultFboHandle);
697 emit needRender();
698 }
699
700 // Skip selection mode drawing if we have no selection mode
701 if (m_cachedSelectionMode > QAbstract3DGraph::SelectionNone
702 && SelectOnScene == m_selectionState
703 && (m_visibleSeriesCount > 0 || !m_customRenderCache.isEmpty())
704 && m_selectionTexture) {
705 // Draw dots to selection buffer
706 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer: m_selectionFrameBuffer);
707 glViewport(x: 0, y: 0,
708 width: m_primarySubViewport.width(),
709 height: m_primarySubViewport.height());
710
711 glEnable(GL_DEPTH_TEST); // Needed, otherwise the depth render buffer is not used
712 glClearColor(red: 1.0f, green: 1.0f, blue: 1.0f, alpha: 1.0f); // Set clear color to white (= skipColor)
713 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Needed for clearing the frame buffer
714 glDisable(GL_DITHER); // disable dithering, it may affect colors if enabled
715
716 bool previousDrawingPoints = false;
717 int totalIndex = 0;
718 foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
719 if (baseCache->isVisible()) {
720 ScatterSeriesRenderCache *cache =
721 static_cast<ScatterSeriesRenderCache *>(baseCache);
722 ObjectHelper *dotObj = cache->object();
723 QQuaternion seriesRotation(cache->meshRotation());
724 const ScatterRenderItemArray &renderArray = cache->renderArray();
725 const int renderArraySize = renderArray.size();
726 bool drawingPoints = (cache->mesh() == QAbstract3DSeries::MeshPoint);
727 float itemSize = cache->itemSize() / itemScaler;
728 if (itemSize == 0.0f)
729 itemSize = m_dotSizeScale;
730#if !QT_CONFIG(opengles2)
731 if (drawingPoints && !m_isOpenGLES)
732 m_funcs_2_1->glPointSize(size: itemSize * activeCamera->zoomLevel());
733#endif
734 QVector3D modelScaler(itemSize, itemSize, itemSize);
735
736 // Rebind selection shader if it has changed
737 if (!totalIndex || drawingPoints != previousDrawingPoints) {
738 previousDrawingPoints = drawingPoints;
739 if (drawingPoints)
740 selectionShader = pointSelectionShader;
741 else
742 selectionShader = m_selectionShader;
743
744 selectionShader->bind();
745 }
746 cache->setSelectionIndexOffset(totalIndex);
747 for (int dot = 0; dot < renderArraySize; dot++) {
748 const ScatterRenderItem &item = renderArray.at(i: dot);
749 if (!item.isVisible()) {
750 totalIndex++;
751 continue;
752 }
753
754 QMatrix4x4 modelMatrix;
755 QMatrix4x4 MVPMatrix;
756
757 modelMatrix.translate(vector: item.translation());
758 if (!drawingPoints) {
759 if (!seriesRotation.isIdentity() || !item.rotation().isIdentity())
760 modelMatrix.rotate(quaternion: seriesRotation * item.rotation());
761 modelMatrix.scale(vector: modelScaler);
762 }
763
764 MVPMatrix = projectionViewMatrix * modelMatrix;
765
766 QVector4D dotColor = indexToSelectionColor(index: totalIndex++);
767 dotColor /= 255.0f;
768
769 selectionShader->setUniformValue(uniform: selectionShader->MVP(), value: MVPMatrix);
770 selectionShader->setUniformValue(uniform: selectionShader->color(), value: dotColor);
771
772 if (drawingPoints)
773 m_drawer->drawPoint(shader: selectionShader);
774 else
775 m_drawer->drawSelectionObject(shader: selectionShader, object: dotObj);
776 }
777 }
778 }
779
780 Abstract3DRenderer::drawCustomItems(state: RenderingSelection, regularShader: m_selectionShader,
781 viewMatrix, projectionViewMatrix,
782 depthProjectionViewMatrix, depthTexture: m_depthTexture,
783 shadowQuality: m_shadowQualityToShader);
784
785 drawLabels(drawSelection: true, activeCamera, viewMatrix, projectionMatrix);
786
787 glEnable(GL_DITHER);
788
789 // Read color under cursor
790 QVector4D clickedColor = Utils::getSelection(mousepos: m_inputPosition,
791 height: m_viewport.height());
792 selectionColorToSeriesAndIndex(color: clickedColor, index&: m_clickedIndex, series&: m_clickedSeries);
793 m_clickResolved = true;
794
795 emit needRender();
796
797 // Revert to original fbo and viewport
798 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer: defaultFboHandle);
799 glViewport(x: m_primarySubViewport.x(),
800 y: m_primarySubViewport.y(),
801 width: m_primarySubViewport.width(),
802 height: m_primarySubViewport.height());
803 }
804
805 // Draw dots
806 ShaderHelper *dotShader = 0;
807 GLuint gradientTexture = 0;
808 bool dotSelectionFound = false;
809 ScatterRenderItem *selectedItem(0);
810 QVector4D baseColor;
811 QVector4D dotColor;
812
813 bool previousDrawingPoints = false;
814 Q3DTheme::ColorStyle previousMeshColorStyle = Q3DTheme::ColorStyleUniform;
815 if (m_haveMeshSeries) {
816 // Set unchanging shader bindings
817 if (m_haveGradientMeshSeries) {
818 m_dotGradientShader->bind();
819 m_dotGradientShader->setUniformValue(uniform: m_dotGradientShader->lightP(), value: lightPos);
820 m_dotGradientShader->setUniformValue(uniform: m_dotGradientShader->view(), value: viewMatrix);
821 m_dotGradientShader->setUniformValue(uniform: m_dotGradientShader->ambientS(),
822 value: m_cachedTheme->ambientLightStrength());
823 m_dotGradientShader->setUniformValue(uniform: m_dotGradientShader->lightColor(), value: lightColor);
824 }
825 if (m_haveUniformColorMeshSeries) {
826 m_dotShader->bind();
827 m_dotShader->setUniformValue(uniform: m_dotShader->lightP(), value: lightPos);
828 m_dotShader->setUniformValue(uniform: m_dotShader->view(), value: viewMatrix);
829 m_dotShader->setUniformValue(uniform: m_dotShader->ambientS(),
830 value: m_cachedTheme->ambientLightStrength());
831 m_dotShader->setUniformValue(uniform: m_dotShader->lightColor(), value: lightColor);
832 dotShader = m_dotShader;
833 } else {
834 dotShader = m_dotGradientShader;
835 previousMeshColorStyle = Q3DTheme::ColorStyleRangeGradient;
836 m_dotGradientShader->setUniformValue(uniform: m_dotGradientShader->gradientHeight(), value: 0.0f);
837 }
838 } else {
839 dotShader = pointSelectionShader;
840 }
841
842 float rangeGradientYScaler = 0.5f / m_scaleY;
843
844 foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
845 if (baseCache->isVisible()) {
846 ScatterSeriesRenderCache *cache =
847 static_cast<ScatterSeriesRenderCache *>(baseCache);
848 ObjectHelper *dotObj = cache->object();
849 QQuaternion seriesRotation(cache->meshRotation());
850 ScatterRenderItemArray &renderArray = cache->renderArray();
851 const int renderArraySize = renderArray.size();
852 bool selectedSeries = m_cachedSelectionMode > QAbstract3DGraph::SelectionNone
853 && (m_selectedSeriesCache == cache);
854 bool drawingPoints = (cache->mesh() == QAbstract3DSeries::MeshPoint);
855 Q3DTheme::ColorStyle colorStyle = cache->colorStyle();
856 bool colorStyleIsUniform = (colorStyle == Q3DTheme::ColorStyleUniform);
857 bool useColor = colorStyleIsUniform || drawingPoints;
858 bool rangeGradientPoints = drawingPoints
859 && (colorStyle == Q3DTheme::ColorStyleRangeGradient);
860 float itemSize = cache->itemSize() / itemScaler;
861 if (itemSize == 0.0f)
862 itemSize = m_dotSizeScale;
863#if !QT_CONFIG(opengles2)
864 if (drawingPoints && !m_isOpenGLES)
865 m_funcs_2_1->glPointSize(size: itemSize * activeCamera->zoomLevel());
866#endif
867 QVector3D modelScaler(itemSize, itemSize, itemSize);
868 int gradientImageHeight = cache->gradientImage().height();
869 int maxGradientPositition = gradientImageHeight - 1;
870
871 if (!optimizationDefault
872 && ((drawingPoints && cache->bufferPoints()->indexCount() == 0)
873 || (!drawingPoints && cache->bufferObject()->indexCount() == 0))) {
874 continue;
875 }
876
877 // Rebind shader if it has changed
878 if (drawingPoints != previousDrawingPoints
879 || (!drawingPoints &&
880 (colorStyleIsUniform != (previousMeshColorStyle
881 == Q3DTheme::ColorStyleUniform)))
882 || (!optimizationDefault && drawingPoints)) {
883 previousDrawingPoints = drawingPoints;
884 if (drawingPoints) {
885 if (!optimizationDefault && rangeGradientPoints) {
886 if (m_isOpenGLES)
887 dotShader = m_staticGradientPointShader;
888 else
889 dotShader = m_labelShader;
890 } else {
891 dotShader = pointSelectionShader;
892 }
893 } else {
894 if (colorStyleIsUniform)
895 dotShader = m_dotShader;
896 else
897 dotShader = m_dotGradientShader;
898 }
899 dotShader->bind();
900 }
901
902 if (!drawingPoints && !colorStyleIsUniform && previousMeshColorStyle != colorStyle) {
903 if (colorStyle == Q3DTheme::ColorStyleObjectGradient) {
904 dotShader->setUniformValue(uniform: dotShader->gradientMin(), value: 0.0f);
905 dotShader->setUniformValue(uniform: dotShader->gradientHeight(),
906 value: 0.5f);
907 } else {
908 // Each dot is of uniform color according to its Y-coordinate
909 dotShader->setUniformValue(uniform: dotShader->gradientHeight(),
910 value: 0.0f);
911 }
912 }
913
914 if (!drawingPoints)
915 previousMeshColorStyle = colorStyle;
916
917 if (useColor) {
918 baseColor = cache->baseColor();
919 dotColor = baseColor;
920 }
921 int loopCount = 1;
922 if (optimizationDefault)
923 loopCount = renderArraySize;
924
925 for (int i = 0; i < loopCount; i++) {
926 ScatterRenderItem &item = renderArray[i];
927 if (!item.isVisible() && optimizationDefault)
928 continue;
929
930 QMatrix4x4 modelMatrix;
931 QMatrix4x4 MVPMatrix;
932 QMatrix4x4 itModelMatrix;
933
934 if (optimizationDefault) {
935 modelMatrix.translate(vector: item.translation());
936 if (!drawingPoints) {
937 if (!seriesRotation.isIdentity() || !item.rotation().isIdentity()) {
938 QQuaternion totalRotation = seriesRotation * item.rotation();
939 modelMatrix.rotate(quaternion: totalRotation);
940 itModelMatrix.rotate(quaternion: totalRotation);
941 }
942 modelMatrix.scale(vector: modelScaler);
943 itModelMatrix.scale(vector: modelScaler);
944 }
945 }
946#ifdef SHOW_DEPTH_TEXTURE_SCENE
947 MVPMatrix = depthProjectionViewMatrix * modelMatrix;
948#else
949 MVPMatrix = projectionViewMatrix * modelMatrix;
950#endif
951
952 if (useColor) {
953 if (rangeGradientPoints) {
954 // Drawing points with range gradient
955 // Get color from gradient based on items y position converted to percent
956 int position = ((item.translation().y() + m_scaleY) * rangeGradientYScaler) * gradientImageHeight;
957 position = qMin(a: maxGradientPositition, b: position); // clamp to edge
958 dotColor = Utils::vectorFromColor(
959 color: cache->gradientImage().pixel(x: 0, y: position));
960 } else {
961 dotColor = baseColor;
962 }
963 } else {
964 gradientTexture = cache->baseGradientTexture();
965 }
966
967 if (!optimizationDefault && rangeGradientPoints)
968 gradientTexture = cache->baseGradientTexture();
969
970 GLfloat lightStrength = m_cachedTheme->lightStrength();
971 if (optimizationDefault && selectedSeries && (m_selectedItemIndex == i)) {
972 if (useColor)
973 dotColor = cache->singleHighlightColor();
974 else
975 gradientTexture = cache->singleHighlightGradientTexture();
976 lightStrength = m_cachedTheme->highlightLightStrength();
977 // Save the reference to the item to be used in label drawing
978 selectedItem = &item;
979 dotSelectionFound = true;
980 // Save selected item size (adjusted with font size) for selection label
981 // positioning
982 selectedItemSize = itemSize + m_drawer->scaledFontSize() - 0.05f;
983 }
984
985 if (!drawingPoints) {
986 // Set shader bindings
987 dotShader->setUniformValue(uniform: dotShader->model(), value: modelMatrix);
988 dotShader->setUniformValue(uniform: dotShader->nModel(),
989 value: itModelMatrix.inverted().transposed());
990 }
991
992 dotShader->setUniformValue(uniform: dotShader->MVP(), value: MVPMatrix);
993 if (useColor) {
994 dotShader->setUniformValue(uniform: dotShader->color(), value: dotColor);
995 } else if (colorStyle == Q3DTheme::ColorStyleRangeGradient) {
996 dotShader->setUniformValue(uniform: dotShader->gradientMin(),
997 value: (item.translation().y() + m_scaleY)
998 * rangeGradientYScaler);
999 }
1000 if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) {
1001 if (!drawingPoints) {
1002 // Set shadow shader bindings
1003 QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
1004 dotShader->setUniformValue(uniform: dotShader->shadowQ(), value: m_shadowQualityToShader);
1005 dotShader->setUniformValue(uniform: dotShader->depth(), value: depthMVPMatrix);
1006 dotShader->setUniformValue(uniform: dotShader->lightS(), value: lightStrength / 10.0f);
1007
1008 // Draw the object
1009 if (optimizationDefault) {
1010 m_drawer->drawObject(shader: dotShader, object: dotObj, textureId: gradientTexture,
1011 depthTextureId: m_depthTexture);
1012 } else {
1013 m_drawer->drawObject(shader: dotShader, object: cache->bufferObject(), textureId: gradientTexture,
1014 depthTextureId: m_depthTexture);
1015 }
1016 } else {
1017 // Draw the object
1018 if (optimizationDefault)
1019 m_drawer->drawPoint(shader: dotShader);
1020 else
1021 m_drawer->drawPoints(shader: dotShader, object: cache->bufferPoints(), textureId: gradientTexture);
1022 }
1023 } else {
1024 if (!drawingPoints) {
1025 // Set shadowless shader bindings
1026 dotShader->setUniformValue(uniform: dotShader->lightS(), value: lightStrength);
1027 // Draw the object
1028 if (optimizationDefault)
1029 m_drawer->drawObject(shader: dotShader, object: dotObj, textureId: gradientTexture);
1030 else
1031 m_drawer->drawObject(shader: dotShader, object: cache->bufferObject(), textureId: gradientTexture);
1032 } else {
1033 // Draw the object
1034 if (optimizationDefault)
1035 m_drawer->drawPoint(shader: dotShader);
1036 else
1037 m_drawer->drawPoints(shader: dotShader, object: cache->bufferPoints(), textureId: gradientTexture);
1038 }
1039 }
1040 }
1041
1042
1043 // Draw the selected item on static optimization
1044 if (!optimizationDefault && selectedSeries
1045 && m_selectedItemIndex != Scatter3DController::invalidSelectionIndex()) {
1046 ScatterRenderItem &item = renderArray[m_selectedItemIndex];
1047 if (item.isVisible()) {
1048 ShaderHelper *selectionShader;
1049 if (drawingPoints) {
1050 selectionShader = pointSelectionShader;
1051 } else {
1052 if (colorStyleIsUniform)
1053 selectionShader = m_staticSelectedItemShader;
1054 else
1055 selectionShader = m_staticSelectedItemGradientShader;
1056 }
1057 selectionShader->bind();
1058
1059 ObjectHelper *dotObj = cache->object();
1060
1061 QMatrix4x4 modelMatrix;
1062 QMatrix4x4 itModelMatrix;
1063
1064 modelMatrix.translate(vector: item.translation());
1065 if (!drawingPoints) {
1066 if (!seriesRotation.isIdentity() || !item.rotation().isIdentity()) {
1067 QQuaternion totalRotation = seriesRotation * item.rotation();
1068 modelMatrix.rotate(quaternion: totalRotation);
1069 itModelMatrix.rotate(quaternion: totalRotation);
1070 }
1071 modelMatrix.scale(vector: modelScaler);
1072 itModelMatrix.scale(vector: modelScaler);
1073
1074 selectionShader->setUniformValue(uniform: selectionShader->lightP(),
1075 value: lightPos);
1076 selectionShader->setUniformValue(uniform: selectionShader->view(),
1077 value: viewMatrix);
1078 selectionShader->setUniformValue(uniform: selectionShader->ambientS(),
1079 value: m_cachedTheme->ambientLightStrength());
1080 selectionShader->setUniformValue(uniform: selectionShader->lightColor(),
1081 value: lightColor);
1082 }
1083
1084 QMatrix4x4 MVPMatrix;
1085#ifdef SHOW_DEPTH_TEXTURE_SCENE
1086 MVPMatrix = depthProjectionViewMatrix * modelMatrix;
1087#else
1088 MVPMatrix = projectionViewMatrix * modelMatrix;
1089#endif
1090
1091 if (useColor)
1092 dotColor = cache->singleHighlightColor();
1093 else
1094 gradientTexture = cache->singleHighlightGradientTexture();
1095 GLfloat lightStrength = m_cachedTheme->highlightLightStrength();
1096 // Save the reference to the item to be used in label drawing
1097 selectedItem = &item;
1098 dotSelectionFound = true;
1099 // Save selected item size (adjusted with font size) for selection label
1100 // positioning
1101 selectedItemSize = itemSize + m_drawer->scaledFontSize() - 0.05f;
1102
1103 if (!drawingPoints) {
1104 // Set shader bindings
1105 selectionShader->setUniformValue(uniform: selectionShader->model(), value: modelMatrix);
1106 selectionShader->setUniformValue(uniform: selectionShader->nModel(),
1107 value: itModelMatrix.inverted().transposed());
1108 if (!colorStyleIsUniform) {
1109 if (colorStyle == Q3DTheme::ColorStyleObjectGradient) {
1110 selectionShader->setUniformValue(uniform: selectionShader->gradientMin(),
1111 value: 0.0f);
1112 selectionShader->setUniformValue(uniform: selectionShader->gradientHeight(),
1113 value: 0.5f);
1114 } else {
1115 // Each dot is of uniform color according to its Y-coordinate
1116 selectionShader->setUniformValue(uniform: selectionShader->gradientHeight(),
1117 value: 0.0f);
1118 selectionShader->setUniformValue(uniform: selectionShader->gradientMin(),
1119 value: (item.translation().y() + m_scaleY)
1120 * rangeGradientYScaler);
1121 }
1122 }
1123 }
1124
1125 selectionShader->setUniformValue(uniform: selectionShader->MVP(), value: MVPMatrix);
1126 if (useColor)
1127 selectionShader->setUniformValue(uniform: selectionShader->color(), value: dotColor);
1128
1129 if (!drawingPoints) {
1130 glEnable(GL_POLYGON_OFFSET_FILL);
1131 glPolygonOffset(factor: -1.0f, units: 1.0f);
1132 }
1133
1134 if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone
1135 && !m_isOpenGLES) {
1136 if (!drawingPoints) {
1137 // Set shadow shader bindings
1138 QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
1139 selectionShader->setUniformValue(uniform: selectionShader->shadowQ(),
1140 value: m_shadowQualityToShader);
1141 selectionShader->setUniformValue(uniform: selectionShader->depth(),
1142 value: depthMVPMatrix);
1143 selectionShader->setUniformValue(uniform: selectionShader->lightS(),
1144 value: lightStrength / 10.0f);
1145
1146 // Draw the object
1147 m_drawer->drawObject(shader: selectionShader, object: dotObj, textureId: gradientTexture,
1148 depthTextureId: m_depthTexture);
1149 } else {
1150 // Draw the object
1151 m_drawer->drawPoint(shader: selectionShader);
1152 }
1153 } else {
1154 if (!drawingPoints) {
1155 // Set shadowless shader bindings
1156 selectionShader->setUniformValue(uniform: selectionShader->lightS(),
1157 value: lightStrength);
1158 // Draw the object
1159 m_drawer->drawObject(shader: selectionShader, object: dotObj, textureId: gradientTexture);
1160 } else {
1161 // Draw the object
1162 m_drawer->drawPoint(shader: selectionShader);
1163 }
1164 }
1165
1166 if (!drawingPoints)
1167 glDisable(GL_POLYGON_OFFSET_FILL);
1168 }
1169 dotShader->bind();
1170 }
1171 }
1172 }
1173
1174#if !QT_CONFIG(opengles2)
1175 if (m_havePointSeries) {
1176 glDisable(GL_POINT_SMOOTH);
1177 glDisable(GL_PROGRAM_POINT_SIZE);
1178 }
1179#endif
1180
1181 // Bind background shader
1182 m_backgroundShader->bind();
1183
1184 glCullFace(GL_BACK);
1185
1186 // Draw background
1187 if (m_cachedTheme->isBackgroundEnabled() && m_backgroundObj) {
1188 QMatrix4x4 modelMatrix;
1189 QMatrix4x4 MVPMatrix;
1190 QMatrix4x4 itModelMatrix;
1191
1192 QVector3D bgScale(m_scaleXWithBackground, m_scaleYWithBackground,
1193 m_scaleZWithBackground);
1194 modelMatrix.scale(vector: bgScale);
1195 // If we're viewing from below, background object must be flipped
1196 if (m_yFlipped) {
1197 modelMatrix.rotate(quaternion: m_xFlipRotation);
1198 modelMatrix.rotate(angle: 270.0f - backgroundRotation, x: 0.0f, y: 1.0f, z: 0.0f);
1199 } else {
1200 modelMatrix.rotate(angle: backgroundRotation, x: 0.0f, y: 1.0f, z: 0.0f);
1201 }
1202 itModelMatrix = modelMatrix; // Only scaling and rotations, can be used directly
1203
1204#ifdef SHOW_DEPTH_TEXTURE_SCENE
1205 MVPMatrix = depthProjectionViewMatrix * modelMatrix;
1206#else
1207 MVPMatrix = projectionViewMatrix * modelMatrix;
1208#endif
1209 QVector4D backgroundColor = Utils::vectorFromColor(color: m_cachedTheme->backgroundColor());
1210
1211 // Set shader bindings
1212 m_backgroundShader->setUniformValue(uniform: m_backgroundShader->lightP(), value: lightPos);
1213 m_backgroundShader->setUniformValue(uniform: m_backgroundShader->view(), value: viewMatrix);
1214 m_backgroundShader->setUniformValue(uniform: m_backgroundShader->model(), value: modelMatrix);
1215 m_backgroundShader->setUniformValue(uniform: m_backgroundShader->nModel(),
1216 value: itModelMatrix.inverted().transposed());
1217 m_backgroundShader->setUniformValue(uniform: m_backgroundShader->MVP(), value: MVPMatrix);
1218 m_backgroundShader->setUniformValue(uniform: m_backgroundShader->color(), value: backgroundColor);
1219 m_backgroundShader->setUniformValue(uniform: m_backgroundShader->ambientS(),
1220 value: m_cachedTheme->ambientLightStrength() * 2.0f);
1221 m_backgroundShader->setUniformValue(uniform: m_backgroundShader->lightColor(), value: lightColor);
1222
1223 if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) {
1224 // Set shadow shader bindings
1225 QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
1226 m_backgroundShader->setUniformValue(uniform: m_backgroundShader->shadowQ(),
1227 value: m_shadowQualityToShader);
1228 m_backgroundShader->setUniformValue(uniform: m_backgroundShader->depth(), value: depthMVPMatrix);
1229 m_backgroundShader->setUniformValue(uniform: m_backgroundShader->lightS(),
1230 value: m_cachedTheme->lightStrength() / 10.0f);
1231
1232 // Draw the object
1233 m_drawer->drawObject(shader: m_backgroundShader, object: m_backgroundObj, textureId: 0, depthTextureId: m_depthTexture);
1234 } else {
1235 // Set shadowless shader bindings
1236 m_backgroundShader->setUniformValue(uniform: m_backgroundShader->lightS(),
1237 value: m_cachedTheme->lightStrength());
1238
1239 // Draw the object
1240 m_drawer->drawObject(shader: m_backgroundShader, object: m_backgroundObj);
1241 }
1242 }
1243
1244 // Draw grid lines
1245 QVector3D gridLineScaleX(m_scaleXWithBackground, gridLineWidth, gridLineWidth);
1246 QVector3D gridLineScaleZ(gridLineWidth, gridLineWidth, m_scaleZWithBackground);
1247 QVector3D gridLineScaleY(gridLineWidth, m_scaleYWithBackground, gridLineWidth);
1248
1249 if (m_cachedTheme->isGridEnabled()) {
1250 ShaderHelper *lineShader;
1251 if (m_isOpenGLES)
1252 lineShader = m_selectionShader; // Plain color shader for GL_LINES
1253 else
1254 lineShader = m_backgroundShader;
1255
1256 // Bind line shader
1257 lineShader->bind();
1258
1259 // Set unchanging shader bindings
1260 QVector4D lineColor = Utils::vectorFromColor(color: m_cachedTheme->gridLineColor());
1261 lineShader->setUniformValue(uniform: lineShader->lightP(), value: lightPos);
1262 lineShader->setUniformValue(uniform: lineShader->view(), value: viewMatrix);
1263 lineShader->setUniformValue(uniform: lineShader->color(), value: lineColor);
1264 lineShader->setUniformValue(uniform: lineShader->ambientS(), value: m_cachedTheme->ambientLightStrength());
1265 lineShader->setUniformValue(uniform: lineShader->lightColor(), value: lightColor);
1266 if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) {
1267 // Set shadowed shader bindings
1268 lineShader->setUniformValue(uniform: lineShader->shadowQ(), value: m_shadowQualityToShader);
1269 lineShader->setUniformValue(uniform: lineShader->lightS(),
1270 value: m_cachedTheme->lightStrength() / 20.0f);
1271 } else {
1272 // Set shadowless shader bindings
1273 lineShader->setUniformValue(uniform: lineShader->lightS(),
1274 value: m_cachedTheme->lightStrength() / 2.5f);
1275 }
1276
1277 QQuaternion lineYRotation;
1278 QQuaternion lineXRotation;
1279
1280 if (m_xFlipped)
1281 lineYRotation = m_yRightAngleRotationNeg;
1282 else
1283 lineYRotation = m_yRightAngleRotation;
1284
1285 if (m_yFlippedForGrid)
1286 lineXRotation = m_xRightAngleRotation;
1287 else
1288 lineXRotation = m_xRightAngleRotationNeg;
1289
1290 GLfloat yFloorLinePosition = -m_scaleYWithBackground + gridLineOffset;
1291 if (m_yFlippedForGrid)
1292 yFloorLinePosition = -yFloorLinePosition;
1293
1294 // Rows (= Z)
1295 if (m_axisCacheZ.segmentCount() > 0) {
1296 // Floor lines
1297 int gridLineCount = m_axisCacheZ.gridLineCount();
1298 if (m_polarGraph) {
1299 drawRadialGrid(shader: lineShader, yFloorLinePos: yFloorLinePosition, projectionViewMatrix,
1300 depthMatrix: depthProjectionViewMatrix);
1301 } else {
1302 for (int line = 0; line < gridLineCount; line++) {
1303 QMatrix4x4 modelMatrix;
1304 QMatrix4x4 MVPMatrix;
1305 QMatrix4x4 itModelMatrix;
1306
1307 modelMatrix.translate(x: 0.0f, y: yFloorLinePosition,
1308 z: m_axisCacheZ.gridLinePosition(index: line));
1309
1310 modelMatrix.scale(vector: gridLineScaleX);
1311 itModelMatrix.scale(vector: gridLineScaleX);
1312
1313 modelMatrix.rotate(quaternion: lineXRotation);
1314 itModelMatrix.rotate(quaternion: lineXRotation);
1315
1316 MVPMatrix = projectionViewMatrix * modelMatrix;
1317
1318 // Set the rest of the shader bindings
1319 lineShader->setUniformValue(uniform: lineShader->model(), value: modelMatrix);
1320 lineShader->setUniformValue(uniform: lineShader->nModel(),
1321 value: itModelMatrix.inverted().transposed());
1322 lineShader->setUniformValue(uniform: lineShader->MVP(), value: MVPMatrix);
1323
1324 if (m_isOpenGLES) {
1325 m_drawer->drawLine(shader: lineShader);
1326 } else {
1327 if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
1328 QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
1329 // Set shadow shader bindings
1330 lineShader->setUniformValue(uniform: lineShader->depth(), value: depthMVPMatrix);
1331 // Draw the object
1332 m_drawer->drawObject(shader: lineShader, object: m_gridLineObj, textureId: 0, depthTextureId: m_depthTexture);
1333 } else {
1334 // Draw the object
1335 m_drawer->drawObject(shader: lineShader, object: m_gridLineObj);
1336 }
1337 }
1338 }
1339
1340 // Side wall lines
1341 GLfloat lineXTrans = m_scaleXWithBackground - gridLineOffset;
1342
1343 if (!m_xFlipped)
1344 lineXTrans = -lineXTrans;
1345
1346 for (int line = 0; line < gridLineCount; line++) {
1347 QMatrix4x4 modelMatrix;
1348 QMatrix4x4 MVPMatrix;
1349 QMatrix4x4 itModelMatrix;
1350
1351 modelMatrix.translate(x: lineXTrans, y: 0.0f, z: m_axisCacheZ.gridLinePosition(index: line));
1352
1353 modelMatrix.scale(vector: gridLineScaleY);
1354 itModelMatrix.scale(vector: gridLineScaleY);
1355
1356 if (m_isOpenGLES) {
1357 modelMatrix.rotate(quaternion: m_zRightAngleRotation);
1358 itModelMatrix.rotate(quaternion: m_zRightAngleRotation);
1359 } else {
1360 modelMatrix.rotate(quaternion: lineYRotation);
1361 itModelMatrix.rotate(quaternion: lineYRotation);
1362 }
1363
1364 MVPMatrix = projectionViewMatrix * modelMatrix;
1365
1366 // Set the rest of the shader bindings
1367 lineShader->setUniformValue(uniform: lineShader->model(), value: modelMatrix);
1368 lineShader->setUniformValue(uniform: lineShader->nModel(),
1369 value: itModelMatrix.inverted().transposed());
1370 lineShader->setUniformValue(uniform: lineShader->MVP(), value: MVPMatrix);
1371
1372 if (!m_isOpenGLES) {
1373 if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
1374 // Set shadow shader bindings
1375 QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
1376 lineShader->setUniformValue(uniform: lineShader->depth(), value: depthMVPMatrix);
1377 // Draw the object
1378 m_drawer->drawObject(shader: lineShader, object: m_gridLineObj, textureId: 0, depthTextureId: m_depthTexture);
1379 } else {
1380 // Draw the object
1381 m_drawer->drawObject(shader: lineShader, object: m_gridLineObj);
1382 }
1383 } else {
1384 m_drawer->drawLine(shader: lineShader);
1385 }
1386 }
1387 }
1388 }
1389
1390 // Columns (= X)
1391 if (m_axisCacheX.segmentCount() > 0) {
1392 if (m_isOpenGLES)
1393 lineXRotation = m_yRightAngleRotation;
1394 // Floor lines
1395 int gridLineCount = m_axisCacheX.gridLineCount();
1396
1397 if (m_polarGraph) {
1398 drawAngularGrid(shader: lineShader, yFloorLinePos: yFloorLinePosition, projectionViewMatrix,
1399 depthMatrix: depthProjectionViewMatrix);
1400 } else {
1401 for (int line = 0; line < gridLineCount; line++) {
1402 QMatrix4x4 modelMatrix;
1403 QMatrix4x4 MVPMatrix;
1404 QMatrix4x4 itModelMatrix;
1405
1406 modelMatrix.translate(x: m_axisCacheX.gridLinePosition(index: line), y: yFloorLinePosition,
1407 z: 0.0f);
1408
1409 modelMatrix.scale(vector: gridLineScaleZ);
1410 itModelMatrix.scale(vector: gridLineScaleZ);
1411
1412 modelMatrix.rotate(quaternion: lineXRotation);
1413 itModelMatrix.rotate(quaternion: lineXRotation);
1414
1415 MVPMatrix = projectionViewMatrix * modelMatrix;
1416
1417 // Set the rest of the shader bindings
1418 lineShader->setUniformValue(uniform: lineShader->model(), value: modelMatrix);
1419 lineShader->setUniformValue(uniform: lineShader->nModel(),
1420 value: itModelMatrix.inverted().transposed());
1421 lineShader->setUniformValue(uniform: lineShader->MVP(), value: MVPMatrix);
1422
1423 if (!m_isOpenGLES) {
1424 if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
1425 // Set shadow shader bindings
1426 QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
1427 lineShader->setUniformValue(uniform: lineShader->depth(), value: depthMVPMatrix);
1428 // Draw the object
1429 m_drawer->drawObject(shader: lineShader, object: m_gridLineObj, textureId: 0, depthTextureId: m_depthTexture);
1430 } else {
1431 // Draw the object
1432 m_drawer->drawObject(shader: lineShader, object: m_gridLineObj);
1433 }
1434 } else {
1435 m_drawer->drawLine(shader: lineShader);
1436 }
1437 }
1438
1439 // Back wall lines
1440 GLfloat lineZTrans = m_scaleZWithBackground - gridLineOffset;
1441
1442 if (!m_zFlipped)
1443 lineZTrans = -lineZTrans;
1444
1445 for (int line = 0; line < gridLineCount; line++) {
1446 QMatrix4x4 modelMatrix;
1447 QMatrix4x4 MVPMatrix;
1448 QMatrix4x4 itModelMatrix;
1449
1450 modelMatrix.translate(x: m_axisCacheX.gridLinePosition(index: line), y: 0.0f, z: lineZTrans);
1451
1452 modelMatrix.scale(vector: gridLineScaleY);
1453 itModelMatrix.scale(vector: gridLineScaleY);
1454
1455 if (m_isOpenGLES) {
1456 modelMatrix.rotate(quaternion: m_zRightAngleRotation);
1457 itModelMatrix.rotate(quaternion: m_zRightAngleRotation);
1458 } else {
1459 if (m_zFlipped) {
1460 modelMatrix.rotate(quaternion: m_xFlipRotation);
1461 itModelMatrix.rotate(quaternion: m_xFlipRotation);
1462 }
1463 }
1464
1465 MVPMatrix = projectionViewMatrix * modelMatrix;
1466
1467 // Set the rest of the shader bindings
1468 lineShader->setUniformValue(uniform: lineShader->model(), value: modelMatrix);
1469 lineShader->setUniformValue(uniform: lineShader->nModel(),
1470 value: itModelMatrix.inverted().transposed());
1471 lineShader->setUniformValue(uniform: lineShader->MVP(), value: MVPMatrix);
1472
1473 if (!m_isOpenGLES) {
1474 if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
1475 // Set shadow shader bindings
1476 QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
1477 lineShader->setUniformValue(uniform: lineShader->depth(), value: depthMVPMatrix);
1478 // Draw the object
1479 m_drawer->drawObject(shader: lineShader, object: m_gridLineObj, textureId: 0, depthTextureId: m_depthTexture);
1480 } else {
1481 // Draw the object
1482 m_drawer->drawObject(shader: lineShader, object: m_gridLineObj);
1483 }
1484 } else {
1485 m_drawer->drawLine(shader: lineShader);
1486 }
1487 }
1488 }
1489 }
1490
1491 // Horizontal wall lines
1492 if (m_axisCacheY.segmentCount() > 0) {
1493 // Back wall
1494 int gridLineCount = m_axisCacheY.gridLineCount();
1495
1496 GLfloat lineZTrans = m_scaleZWithBackground - gridLineOffset;
1497
1498 if (!m_zFlipped)
1499 lineZTrans = -lineZTrans;
1500
1501 for (int line = 0; line < gridLineCount; line++) {
1502 QMatrix4x4 modelMatrix;
1503 QMatrix4x4 MVPMatrix;
1504 QMatrix4x4 itModelMatrix;
1505
1506 modelMatrix.translate(x: 0.0f, y: m_axisCacheY.gridLinePosition(index: line), z: lineZTrans);
1507
1508 modelMatrix.scale(vector: gridLineScaleX);
1509 itModelMatrix.scale(vector: gridLineScaleX);
1510
1511 if (m_zFlipped) {
1512 modelMatrix.rotate(quaternion: m_xFlipRotation);
1513 itModelMatrix.rotate(quaternion: m_xFlipRotation);
1514 }
1515
1516 MVPMatrix = projectionViewMatrix * modelMatrix;
1517
1518 // Set the rest of the shader bindings
1519 lineShader->setUniformValue(uniform: lineShader->model(), value: modelMatrix);
1520 lineShader->setUniformValue(uniform: lineShader->nModel(),
1521 value: itModelMatrix.inverted().transposed());
1522 lineShader->setUniformValue(uniform: lineShader->MVP(), value: MVPMatrix);
1523
1524 if (!m_isOpenGLES) {
1525 if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
1526 // Set shadow shader bindings
1527 QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
1528 lineShader->setUniformValue(uniform: lineShader->depth(), value: depthMVPMatrix);
1529 // Draw the object
1530 m_drawer->drawObject(shader: lineShader, object: m_gridLineObj, textureId: 0, depthTextureId: m_depthTexture);
1531 } else {
1532 // Draw the object
1533 m_drawer->drawObject(shader: lineShader, object: m_gridLineObj);
1534 }
1535 } else {
1536 m_drawer->drawLine(shader: lineShader);
1537 }
1538 }
1539
1540 // Side wall
1541 GLfloat lineXTrans = m_scaleXWithBackground - gridLineOffset;
1542
1543 if (!m_xFlipped)
1544 lineXTrans = -lineXTrans;
1545
1546 for (int line = 0; line < gridLineCount; line++) {
1547 QMatrix4x4 modelMatrix;
1548 QMatrix4x4 MVPMatrix;
1549 QMatrix4x4 itModelMatrix;
1550
1551 modelMatrix.translate(x: lineXTrans, y: m_axisCacheY.gridLinePosition(index: line), z: 0.0f);
1552
1553 modelMatrix.scale(vector: gridLineScaleZ);
1554 itModelMatrix.scale(vector: gridLineScaleZ);
1555
1556 modelMatrix.rotate(quaternion: lineYRotation);
1557 itModelMatrix.rotate(quaternion: lineYRotation);
1558
1559 MVPMatrix = projectionViewMatrix * modelMatrix;
1560
1561 // Set the rest of the shader bindings
1562 lineShader->setUniformValue(uniform: lineShader->model(), value: modelMatrix);
1563 lineShader->setUniformValue(uniform: lineShader->nModel(),
1564 value: itModelMatrix.inverted().transposed());
1565 lineShader->setUniformValue(uniform: lineShader->MVP(), value: MVPMatrix);
1566
1567 if (!m_isOpenGLES) {
1568 if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
1569 // Set shadow shader bindings
1570 QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
1571 lineShader->setUniformValue(uniform: lineShader->depth(), value: depthMVPMatrix);
1572 // Draw the object
1573 m_drawer->drawObject(shader: lineShader, object: m_gridLineObj, textureId: 0, depthTextureId: m_depthTexture);
1574 } else {
1575 // Draw the object
1576 m_drawer->drawObject(shader: lineShader, object: m_gridLineObj);
1577 }
1578 } else {
1579 m_drawer->drawLine(shader: lineShader);
1580 }
1581 }
1582 }
1583 }
1584
1585 Abstract3DRenderer::drawCustomItems(state: RenderingNormal, regularShader: m_customItemShader, viewMatrix,
1586 projectionViewMatrix, depthProjectionViewMatrix,
1587 depthTexture: m_depthTexture, shadowQuality: m_shadowQualityToShader);
1588
1589 drawLabels(drawSelection: false, activeCamera, viewMatrix, projectionMatrix);
1590
1591 // Handle selection clearing and selection label drawing
1592 if (!dotSelectionFound) {
1593 // We have no ownership, don't delete. Just NULL the pointer.
1594 m_selectedItem = NULL;
1595 } else {
1596 glDisable(GL_DEPTH_TEST);
1597 // Draw the selection label
1598 LabelItem &labelItem = selectionLabelItem();
1599 if (m_selectedItem != selectedItem || m_updateLabels
1600 || !labelItem.textureId() || m_selectionLabelDirty) {
1601 QString labelText = selectionLabel();
1602 if (labelText.isNull() || m_selectionLabelDirty) {
1603 labelText = m_selectedSeriesCache->itemLabel();
1604 setSelectionLabel(labelText);
1605 m_selectionLabelDirty = false;
1606 }
1607 m_drawer->generateLabelItem(item&: labelItem, text: labelText);
1608 m_selectedItem = selectedItem;
1609 }
1610
1611 m_drawer->drawLabel(item: *selectedItem, labelItem, viewmatrix: viewMatrix, projectionmatrix: projectionMatrix,
1612 positionComp: zeroVector, rotation: identityQuaternion, itemHeight: selectedItemSize, mode: m_cachedSelectionMode,
1613 shader: m_labelShader, object: m_labelObj, camera: activeCamera, useDepth: true, rotateAlong: false,
1614 position: Drawer::LabelOver);
1615
1616 // Reset label update flag; they should have been updated when we get here
1617 m_updateLabels = false;
1618 glEnable(GL_DEPTH_TEST);
1619 }
1620
1621 glDisable(GL_BLEND);
1622
1623 // Release shader
1624 glUseProgram(program: 0);
1625
1626 m_selectionDirty = false;
1627}
1628
1629void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCamera,
1630 const QMatrix4x4 &viewMatrix,
1631 const QMatrix4x4 &projectionMatrix) {
1632 ShaderHelper *shader = 0;
1633 GLfloat alphaForValueSelection = labelValueAlpha / 255.0f;
1634 GLfloat alphaForRowSelection = labelRowAlpha / 255.0f;
1635 GLfloat alphaForColumnSelection = labelColumnAlpha / 255.0f;
1636 if (drawSelection) {
1637 shader = m_selectionShader;
1638 // m_selectionShader is already bound
1639 } else {
1640 shader = m_labelShader;
1641 shader->bind();
1642
1643 glEnable(GL_BLEND);
1644 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1645 }
1646
1647 glEnable(GL_POLYGON_OFFSET_FILL);
1648
1649 float labelAutoAngle = m_axisCacheZ.labelAutoRotation();
1650 float labelAngleFraction = labelAutoAngle / 90.0f;
1651 float fractionCamY = activeCamera->yRotation() * labelAngleFraction;
1652 float fractionCamX = activeCamera->xRotation() * labelAngleFraction;
1653 float labelsMaxWidth = 0.0f;
1654
1655 int startIndex;
1656 int endIndex;
1657 int indexStep;
1658
1659 // Z Labels
1660 if (m_axisCacheZ.segmentCount() > 0) {
1661 int labelCount = m_axisCacheZ.labelCount();
1662 float labelXTrans = m_scaleXWithBackground + labelMargin;
1663 float labelYTrans = -m_scaleYWithBackground;
1664 if (m_polarGraph) {
1665 labelXTrans *= m_radialLabelOffset;
1666 // YTrans up only if over background
1667 if (m_radialLabelOffset < 1.0f)
1668 labelYTrans += gridLineOffset + gridLineWidth;
1669 }
1670 Qt::AlignmentFlag alignment = (m_xFlipped == m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight;
1671 QVector3D labelRotation;
1672 if (m_xFlipped)
1673 labelXTrans = -labelXTrans;
1674 if (m_yFlipped)
1675 labelYTrans = -labelYTrans;
1676 if (labelAutoAngle == 0.0f) {
1677 if (m_zFlipped)
1678 labelRotation.setY(180.0f);
1679 if (m_yFlippedForGrid) {
1680 if (m_zFlipped)
1681 labelRotation.setY(180.0f);
1682 else
1683 labelRotation.setY(0.0f);
1684 labelRotation.setX(90.0f);
1685 } else {
1686 labelRotation.setX(-90.0f);
1687 }
1688 } else {
1689 if (m_zFlipped)
1690 labelRotation.setY(180.0f);
1691 if (m_yFlippedForGrid) {
1692 if (m_zFlipped) {
1693 if (m_xFlipped) {
1694 labelRotation.setX(90.0f - (labelAutoAngle - fractionCamX)
1695 * (-labelAutoAngle - fractionCamY) / labelAutoAngle);
1696 labelRotation.setZ(labelAutoAngle + fractionCamY);
1697 } else {
1698 labelRotation.setX(90.0f + (labelAutoAngle + fractionCamX)
1699 * (labelAutoAngle + fractionCamY) / labelAutoAngle);
1700 labelRotation.setZ(-labelAutoAngle - fractionCamY);
1701 }
1702 } else {
1703 if (m_xFlipped) {
1704 labelRotation.setX(90.0f + (labelAutoAngle - fractionCamX)
1705 * -(labelAutoAngle + fractionCamY) / labelAutoAngle);
1706 labelRotation.setZ(-labelAutoAngle - fractionCamY);
1707 } else {
1708 labelRotation.setX(90.0f - (labelAutoAngle + fractionCamX)
1709 * (labelAutoAngle + fractionCamY) / labelAutoAngle);
1710 labelRotation.setZ(labelAutoAngle + fractionCamY);
1711 }
1712 }
1713 } else {
1714 if (m_zFlipped) {
1715 if (m_xFlipped) {
1716 labelRotation.setX(-90.0f + (labelAutoAngle - fractionCamX)
1717 * (-labelAutoAngle + fractionCamY) / labelAutoAngle);
1718 labelRotation.setZ(-labelAutoAngle + fractionCamY);
1719 } else {
1720 labelRotation.setX(-90.0f - (labelAutoAngle + fractionCamX)
1721 * (labelAutoAngle - fractionCamY) / labelAutoAngle);
1722 labelRotation.setZ(labelAutoAngle - fractionCamY);
1723 }
1724 } else {
1725 if (m_xFlipped) {
1726 labelRotation.setX(-90.0f - (labelAutoAngle - fractionCamX)
1727 * (-labelAutoAngle + fractionCamY) / labelAutoAngle);
1728 labelRotation.setZ(labelAutoAngle - fractionCamY);
1729 } else {
1730 labelRotation.setX(-90.0f + (labelAutoAngle + fractionCamX)
1731 * (labelAutoAngle - fractionCamY) / labelAutoAngle);
1732 labelRotation.setZ(-labelAutoAngle + fractionCamY);
1733 }
1734 }
1735 }
1736 }
1737 QQuaternion totalRotation = Utils::calculateRotation(xyzRotations: labelRotation);
1738 QVector3D labelTrans = QVector3D(labelXTrans, labelYTrans, 0.0f);
1739 if (m_zFlipped) {
1740 startIndex = 0;
1741 endIndex = labelCount;
1742 indexStep = 1;
1743 } else {
1744 startIndex = labelCount - 1;
1745 endIndex = -1;
1746 indexStep = -1;
1747 }
1748 float offsetValue = 0.0f;
1749 for (int label = startIndex; label != endIndex; label = label + indexStep) {
1750 glPolygonOffset(factor: offsetValue++ / -10.0f, units: 1.0f);
1751 const LabelItem &axisLabelItem = *m_axisCacheZ.labelItems().at(i: label);
1752 // Draw the label here
1753 if (m_polarGraph) {
1754 float direction = m_zFlipped ? -1.0f : 1.0f;
1755 labelTrans.setZ((m_axisCacheZ.formatter()->labelPositions().at(i: label)
1756 * -m_polarRadius
1757 + m_drawer->scaledFontSize() + gridLineWidth) * direction);
1758 } else {
1759 labelTrans.setZ(m_axisCacheZ.labelPosition(index: label));
1760 }
1761 if (label == 0 || label == (labelCount - 1)) {
1762 // If the margin is small, adjust the position of the edge labels to avoid overlapping
1763 // with labels of the other axes.
1764 float scaleFactor = m_drawer->scaledFontSize() / axisLabelItem.size().height();
1765 float labelOverlap = qAbs(t: labelTrans.z())
1766 + (scaleFactor * axisLabelItem.size().height() / 2.0f)
1767 - m_scaleZWithBackground + labelMargin;
1768 // No need to adjust quite as much on the front edges
1769 if (label != startIndex)
1770 labelOverlap /= 2.0f;
1771 if (labelOverlap > 0.0f) {
1772 if (label == 0)
1773 labelTrans.setZ(labelTrans.z() - labelOverlap);
1774 else
1775 labelTrans.setZ(labelTrans.z() + labelOverlap);
1776 }
1777 }
1778 m_dummyRenderItem.setTranslation(labelTrans);
1779
1780 if (drawSelection) {
1781 QVector4D labelColor = QVector4D(label / 255.0f, 0.0f, 0.0f,
1782 alphaForRowSelection);
1783 shader->setUniformValue(uniform: shader->color(), value: labelColor);
1784 }
1785
1786 m_drawer->drawLabel(item: m_dummyRenderItem, labelItem: axisLabelItem, viewmatrix: viewMatrix, projectionmatrix: projectionMatrix,
1787 positionComp: zeroVector, rotation: totalRotation, itemHeight: 0, mode: m_cachedSelectionMode,
1788 shader, object: m_labelObj, camera: activeCamera, useDepth: true, rotateAlong: true,
1789 position: Drawer::LabelMid, alignment, isSlicing: false, isSelecting: drawSelection);
1790 labelsMaxWidth = qMax(a: labelsMaxWidth, b: float(axisLabelItem.size().width()));
1791 }
1792 if (!drawSelection && m_axisCacheZ.isTitleVisible()) {
1793 if (m_polarGraph) {
1794 float titleZ = -m_polarRadius / 2.0f;
1795 if (m_zFlipped)
1796 titleZ = -titleZ;
1797 labelTrans.setZ(titleZ);
1798 } else {
1799 labelTrans.setZ(0.0f);
1800 }
1801 drawAxisTitleZ(labelRotation, labelTrans, totalRotation, dummyItem&: m_dummyRenderItem,
1802 activeCamera, labelsMaxWidth, viewMatrix, projectionMatrix, shader);
1803 }
1804 }
1805
1806 // X Labels
1807 if (m_axisCacheX.segmentCount() > 0) {
1808 labelsMaxWidth = 0.0f;
1809 labelAutoAngle = m_axisCacheX.labelAutoRotation();
1810 labelAngleFraction = labelAutoAngle / 90.0f;
1811 fractionCamY = activeCamera->yRotation() * labelAngleFraction;
1812 fractionCamX = activeCamera->xRotation() * labelAngleFraction;
1813 int labelCount = m_axisCacheX.labelCount();
1814 float labelZTrans = 0.0f;
1815 float labelYTrans = -m_scaleYWithBackground;
1816 if (m_polarGraph)
1817 labelYTrans += gridLineOffset + gridLineWidth;
1818 else
1819 labelZTrans = m_scaleZWithBackground + labelMargin;
1820
1821 Qt::Alignment alignment = (m_xFlipped != m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight;
1822 QVector3D labelRotation;
1823 if (m_zFlipped)
1824 labelZTrans = -labelZTrans;
1825 if (m_yFlipped)
1826 labelYTrans = -labelYTrans;
1827 if (labelAutoAngle == 0.0f) {
1828 labelRotation = QVector3D(-90.0f, 90.0f, 0.0f);
1829 if (m_xFlipped)
1830 labelRotation.setY(-90.0f);
1831 if (m_yFlippedForGrid) {
1832 if (m_xFlipped)
1833 labelRotation.setY(-90.0f);
1834 else
1835 labelRotation.setY(90.0f);
1836 labelRotation.setX(90.0f);
1837 }
1838 } else {
1839 if (m_xFlipped)
1840 labelRotation.setY(-90.0f);
1841 else
1842 labelRotation.setY(90.0f);
1843 if (m_yFlippedForGrid) {
1844 if (m_zFlipped) {
1845 if (m_xFlipped) {
1846 labelRotation.setX(90.0f - (2.0f * labelAutoAngle - fractionCamX)
1847 * (labelAutoAngle + fractionCamY) / labelAutoAngle);
1848 labelRotation.setZ(-labelAutoAngle - fractionCamY);
1849 } else {
1850 labelRotation.setX(90.0f - (2.0f * labelAutoAngle + fractionCamX)
1851 * (labelAutoAngle + fractionCamY) / labelAutoAngle);
1852 labelRotation.setZ(labelAutoAngle + fractionCamY);
1853 }
1854 } else {
1855 if (m_xFlipped) {
1856 labelRotation.setX(90.0f + fractionCamX
1857 * -(labelAutoAngle + fractionCamY) / labelAutoAngle);
1858 labelRotation.setZ(labelAutoAngle + fractionCamY);
1859 } else {
1860 labelRotation.setX(90.0f - fractionCamX
1861 * (-labelAutoAngle - fractionCamY) / labelAutoAngle);
1862 labelRotation.setZ(-labelAutoAngle - fractionCamY);
1863 }
1864 }
1865 } else {
1866 if (m_zFlipped) {
1867 if (m_xFlipped) {
1868 labelRotation.setX(-90.0f + (2.0f * labelAutoAngle - fractionCamX)
1869 * (labelAutoAngle - fractionCamY) / labelAutoAngle);
1870 labelRotation.setZ(labelAutoAngle - fractionCamY);
1871 } else {
1872 labelRotation.setX(-90.0f + (2.0f * labelAutoAngle + fractionCamX)
1873 * (labelAutoAngle - fractionCamY) / labelAutoAngle);
1874 labelRotation.setZ(-labelAutoAngle + fractionCamY);
1875 }
1876 } else {
1877 if (m_xFlipped) {
1878 labelRotation.setX(-90.0f - fractionCamX
1879 * (-labelAutoAngle + fractionCamY) / labelAutoAngle);
1880 labelRotation.setZ(-labelAutoAngle + fractionCamY);
1881 } else {
1882 labelRotation.setX(-90.0f + fractionCamX
1883 * -(labelAutoAngle - fractionCamY) / labelAutoAngle);
1884 labelRotation.setZ(labelAutoAngle - fractionCamY);
1885 }
1886 }
1887 }
1888 }
1889
1890 QQuaternion totalRotation = Utils::calculateRotation(xyzRotations: labelRotation);
1891 if (m_polarGraph) {
1892 if ((!m_yFlippedForGrid && (m_zFlipped != m_xFlipped))
1893 || (m_yFlippedForGrid && (m_zFlipped == m_xFlipped))) {
1894 totalRotation *= m_zRightAngleRotation;
1895 } else {
1896 totalRotation *= m_zRightAngleRotationNeg;
1897 }
1898 }
1899 QVector3D labelTrans = QVector3D(0.0f, labelYTrans, labelZTrans);
1900 if (m_xFlipped) {
1901 startIndex = labelCount - 1;
1902 endIndex = -1;
1903 indexStep = -1;
1904 } else {
1905 startIndex = 0;
1906 endIndex = labelCount;
1907 indexStep = 1;
1908 }
1909 float offsetValue = 0.0f;
1910 bool showLastLabel = false;
1911 QList<float> &labelPositions = m_axisCacheX.formatter()->labelPositions();
1912 int lastLabelPosIndex = labelPositions.size() - 1;
1913 if (labelPositions.size()
1914 && (labelPositions.at(i: lastLabelPosIndex) != 1.0f || labelPositions.at(i: 0) != 0.0f)) {
1915 // Avoid overlapping first and last label if they would get on same position
1916 showLastLabel = true;
1917 }
1918
1919 for (int label = startIndex; label != endIndex; label = label + indexStep) {
1920 glPolygonOffset(factor: offsetValue++ / -10.0f, units: 1.0f);
1921 // Draw the label here
1922 if (m_polarGraph) {
1923 // Calculate angular position
1924 if (label == lastLabelPosIndex && !showLastLabel)
1925 continue;
1926 float labelPosition = labelPositions.at(i: label);
1927 qreal angle = labelPosition * M_PI * 2.0;
1928 labelTrans.setX((m_polarRadius + labelMargin) * float(qSin(v: angle)));
1929 labelTrans.setZ(-(m_polarRadius + labelMargin) * float(qCos(v: angle)));
1930 // Alignment depends on label angular position, as well as flips
1931 Qt::AlignmentFlag vAlignment = Qt::AlignCenter;
1932 Qt::AlignmentFlag hAlignment = Qt::AlignCenter;
1933 const float centerMargin = 0.005f;
1934 if (labelPosition < 0.25f - centerMargin || labelPosition > 0.75f + centerMargin)
1935 vAlignment = m_zFlipped ? Qt::AlignTop : Qt::AlignBottom;
1936 else if (labelPosition > 0.25f + centerMargin && labelPosition < 0.75f - centerMargin)
1937 vAlignment = m_zFlipped ? Qt::AlignBottom : Qt::AlignTop;
1938
1939 if (labelPosition < 0.50f - centerMargin && labelPosition > centerMargin)
1940 hAlignment = m_zFlipped ? Qt::AlignRight : Qt::AlignLeft;
1941 else if (labelPosition < 1.0f - centerMargin && labelPosition > 0.5f + centerMargin)
1942 hAlignment = m_zFlipped ? Qt::AlignLeft : Qt::AlignRight;
1943 if (m_yFlippedForGrid && vAlignment != Qt::AlignCenter)
1944 vAlignment = (vAlignment == Qt::AlignTop) ? Qt::AlignBottom : Qt::AlignTop;
1945 alignment = vAlignment | hAlignment;
1946 } else {
1947 labelTrans.setX(m_axisCacheX.labelPosition(index: label));
1948 }
1949 const LabelItem &axisLabelItem = *m_axisCacheX.labelItems().at(i: label);
1950 if (label == 0 || label == (labelCount - 1)) {
1951 // If the margin is small, adjust the position of the edge labels to avoid overlapping
1952 // with labels of the other axes.
1953 float scaleFactor = m_drawer->scaledFontSize() / axisLabelItem.size().height();
1954 float labelOverlap = qAbs(t: labelTrans.x())
1955 + (scaleFactor * axisLabelItem.size().height() / 2.0f)
1956 - m_scaleXWithBackground + labelMargin;
1957 // No need to adjust quite as much on the front edges
1958 if (label != startIndex)
1959 labelOverlap /= 2.0f;
1960 if (labelOverlap > 0.0f) {
1961 if (label == 0)
1962 labelTrans.setX(labelTrans.x() + labelOverlap);
1963 else
1964 labelTrans.setX(labelTrans.x() - labelOverlap);
1965 }
1966 }
1967 m_dummyRenderItem.setTranslation(labelTrans);
1968
1969 if (drawSelection) {
1970 QVector4D labelColor = QVector4D(0.0f, label / 255.0f, 0.0f,
1971 alphaForColumnSelection);
1972 shader->setUniformValue(uniform: shader->color(), value: labelColor);
1973 }
1974
1975 m_drawer->drawLabel(item: m_dummyRenderItem, labelItem: axisLabelItem, viewmatrix: viewMatrix, projectionmatrix: projectionMatrix,
1976 positionComp: zeroVector, rotation: totalRotation, itemHeight: 0, mode: m_cachedSelectionMode,
1977 shader, object: m_labelObj, camera: activeCamera, useDepth: true, rotateAlong: true,
1978 position: Drawer::LabelMid, alignment, isSlicing: false, isSelecting: drawSelection);
1979 labelsMaxWidth = qMax(a: labelsMaxWidth, b: float(axisLabelItem.size().width()));
1980 }
1981 if (!drawSelection && m_axisCacheX.isTitleVisible()) {
1982 labelTrans.setX(0.0f);
1983 bool radial = false;
1984 if (m_polarGraph) {
1985 if (m_xFlipped == m_zFlipped)
1986 totalRotation *= m_zRightAngleRotation;
1987 else
1988 totalRotation *= m_zRightAngleRotationNeg;
1989 if (m_yFlippedForGrid)
1990 totalRotation *= QQuaternion::fromAxisAndAngle(x: 0.0f, y: 0.0f, z: 1.0f, angle: -180.0f);
1991 labelTrans.setZ(-m_polarRadius);
1992 radial = true;
1993 }
1994 drawAxisTitleX(labelRotation, labelTrans, totalRotation, dummyItem&: m_dummyRenderItem,
1995 activeCamera, labelsMaxWidth, viewMatrix, projectionMatrix, shader,
1996 radial);
1997 }
1998 }
1999
2000 // Y Labels
2001 if (m_axisCacheY.segmentCount() > 0) {
2002 labelsMaxWidth = 0.0f;
2003 labelAutoAngle = m_axisCacheY.labelAutoRotation();
2004 labelAngleFraction = labelAutoAngle / 90.0f;
2005 fractionCamY = activeCamera->yRotation() * labelAngleFraction;
2006 fractionCamX = activeCamera->xRotation() * labelAngleFraction;
2007 int labelCount = m_axisCacheY.labelCount();
2008
2009 float labelXTrans = m_scaleXWithBackground;
2010 float labelZTrans = m_scaleZWithBackground;
2011
2012 // Back & side wall
2013 float labelMarginXTrans = labelMargin;
2014 float labelMarginZTrans = labelMargin;
2015 QVector3D backLabelRotation(0.0f, -90.0f, 0.0f);
2016 QVector3D sideLabelRotation(0.0f, 0.0f, 0.0f);
2017 Qt::AlignmentFlag backAlignment =
2018 (m_xFlipped != m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight;
2019 Qt::AlignmentFlag sideAlignment =
2020 (m_xFlipped == m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight;
2021 if (!m_xFlipped) {
2022 labelXTrans = -labelXTrans;
2023 labelMarginXTrans = -labelMargin;
2024 }
2025 if (m_zFlipped) {
2026 labelZTrans = -labelZTrans;
2027 labelMarginZTrans = -labelMargin;
2028 }
2029 if (labelAutoAngle == 0.0f) {
2030 if (!m_xFlipped)
2031 backLabelRotation.setY(90.0f);
2032 if (m_zFlipped)
2033 sideLabelRotation.setY(180.f);
2034 } else {
2035 // Orient side labels somewhat towards the camera
2036 if (m_xFlipped) {
2037 if (m_zFlipped)
2038 sideLabelRotation.setY(180.0f + (2.0f * labelAutoAngle) - fractionCamX);
2039 else
2040 sideLabelRotation.setY(-fractionCamX);
2041 backLabelRotation.setY(-90.0f + labelAutoAngle - fractionCamX);
2042 } else {
2043 if (m_zFlipped)
2044 sideLabelRotation.setY(180.0f - (2.0f * labelAutoAngle) - fractionCamX);
2045 else
2046 sideLabelRotation.setY(-fractionCamX);
2047 backLabelRotation.setY(90.0f - labelAutoAngle - fractionCamX);
2048 }
2049 }
2050 sideLabelRotation.setX(-fractionCamY);
2051 backLabelRotation.setX(-fractionCamY);
2052
2053 QQuaternion totalSideRotation = Utils::calculateRotation(xyzRotations: sideLabelRotation);
2054 QQuaternion totalBackRotation = Utils::calculateRotation(xyzRotations: backLabelRotation);
2055
2056 QVector3D labelTransBack = QVector3D(labelXTrans, 0.0f, labelZTrans + labelMarginZTrans);
2057 QVector3D labelTransSide(-labelXTrans - labelMarginXTrans, 0.0f, -labelZTrans);
2058
2059 if (m_yFlipped) {
2060 startIndex = labelCount - 1;
2061 endIndex = -1;
2062 indexStep = -1;
2063 } else {
2064 startIndex = 0;
2065 endIndex = labelCount;
2066 indexStep = 1;
2067 }
2068 float offsetValue = 0.0f;
2069 for (int label = startIndex; label != endIndex; label = label + indexStep) {
2070 const LabelItem &axisLabelItem = *m_axisCacheY.labelItems().at(i: label);
2071 float labelYTrans = m_axisCacheY.labelPosition(index: label);
2072
2073 glPolygonOffset(factor: offsetValue++ / -10.0f, units: 1.0f);
2074
2075 if (drawSelection) {
2076 QVector4D labelColor = QVector4D(0.0f, 0.0f, label / 255.0f,
2077 alphaForValueSelection);
2078 shader->setUniformValue(uniform: shader->color(), value: labelColor);
2079 }
2080
2081 if (label == startIndex) {
2082 // If the margin is small, adjust the position of the edge label to avoid
2083 // overlapping with labels of the other axes.
2084 float scaleFactor = m_drawer->scaledFontSize() / axisLabelItem.size().height();
2085 float labelOverlap = qAbs(t: labelYTrans)
2086 + (scaleFactor * axisLabelItem.size().height() / 2.0f)
2087 - m_scaleYWithBackground + labelMargin;
2088 if (labelOverlap > 0.0f) {
2089 if (label == 0)
2090 labelYTrans += labelOverlap;
2091 else
2092 labelYTrans -= labelOverlap;
2093 }
2094 }
2095
2096 // Back wall
2097 labelTransBack.setY(labelYTrans);
2098 m_dummyRenderItem.setTranslation(labelTransBack);
2099 m_drawer->drawLabel(item: m_dummyRenderItem, labelItem: axisLabelItem, viewmatrix: viewMatrix, projectionmatrix: projectionMatrix,
2100 positionComp: zeroVector, rotation: totalBackRotation, itemHeight: 0, mode: m_cachedSelectionMode,
2101 shader, object: m_labelObj, camera: activeCamera, useDepth: true, rotateAlong: true,
2102 position: Drawer::LabelMid, alignment: backAlignment, isSlicing: false, isSelecting: drawSelection);
2103
2104 // Side wall
2105 labelTransSide.setY(labelYTrans);
2106 m_dummyRenderItem.setTranslation(labelTransSide);
2107 m_drawer->drawLabel(item: m_dummyRenderItem, labelItem: axisLabelItem, viewmatrix: viewMatrix, projectionmatrix: projectionMatrix,
2108 positionComp: zeroVector, rotation: totalSideRotation, itemHeight: 0, mode: m_cachedSelectionMode,
2109 shader, object: m_labelObj, camera: activeCamera, useDepth: true, rotateAlong: true,
2110 position: Drawer::LabelMid, alignment: sideAlignment, isSlicing: false, isSelecting: drawSelection);
2111 labelsMaxWidth = qMax(a: labelsMaxWidth, b: float(axisLabelItem.size().width()));
2112 }
2113 if (!drawSelection && m_axisCacheY.isTitleVisible()) {
2114 labelTransSide.setY(0.0f);
2115 labelTransBack.setY(0.0f);
2116 drawAxisTitleY(sideLabelRotation, backLabelRotation, sideLabelTrans: labelTransSide, backLabelTrans: labelTransBack,
2117 totalSideRotation, totalBackRotation, dummyItem&: m_dummyRenderItem, activeCamera,
2118 labelsMaxWidth, viewMatrix, projectionMatrix,
2119 shader);
2120 }
2121 }
2122 glDisable(GL_POLYGON_OFFSET_FILL);
2123}
2124
2125void Scatter3DRenderer::updateSelectedItem(int index, QScatter3DSeries *series)
2126{
2127 m_selectionDirty = true;
2128 m_selectionLabelDirty = true;
2129 m_selectedSeriesCache =
2130 static_cast<ScatterSeriesRenderCache *>(m_renderCacheList.value(key: series, defaultValue: 0));
2131 m_selectedItemIndex = Scatter3DController::invalidSelectionIndex();
2132
2133 if (m_cachedOptimizationHint.testFlag(flag: QAbstract3DGraph::OptimizationStatic)
2134 && m_oldSelectedSeriesCache
2135 && m_oldSelectedSeriesCache->mesh() == QAbstract3DSeries::MeshPoint) {
2136 m_oldSelectedSeriesCache->bufferPoints()->popPoint();
2137 m_oldSelectedSeriesCache = 0;
2138 }
2139
2140 if (m_selectedSeriesCache) {
2141 const ScatterRenderItemArray &renderArray = m_selectedSeriesCache->renderArray();
2142 if (index < renderArray.size() && index >= 0) {
2143 m_selectedItemIndex = index;
2144
2145 if (m_cachedOptimizationHint.testFlag(flag: QAbstract3DGraph::OptimizationStatic)
2146 && m_selectedSeriesCache->mesh() == QAbstract3DSeries::MeshPoint) {
2147 m_selectedSeriesCache->bufferPoints()->pushPoint(pointIndex: m_selectedItemIndex);
2148 m_oldSelectedSeriesCache = m_selectedSeriesCache;
2149 }
2150 }
2151 }
2152}
2153
2154void Scatter3DRenderer::updateShadowQuality(QAbstract3DGraph::ShadowQuality quality)
2155{
2156 m_cachedShadowQuality = quality;
2157 switch (quality) {
2158 case QAbstract3DGraph::ShadowQualityLow:
2159 m_shadowQualityToShader = 33.3f;
2160 m_shadowQualityMultiplier = 1;
2161 break;
2162 case QAbstract3DGraph::ShadowQualityMedium:
2163 m_shadowQualityToShader = 100.0f;
2164 m_shadowQualityMultiplier = 3;
2165 break;
2166 case QAbstract3DGraph::ShadowQualityHigh:
2167 m_shadowQualityToShader = 200.0f;
2168 m_shadowQualityMultiplier = 5;
2169 break;
2170 case QAbstract3DGraph::ShadowQualitySoftLow:
2171 m_shadowQualityToShader = 5.0f;
2172 m_shadowQualityMultiplier = 1;
2173 break;
2174 case QAbstract3DGraph::ShadowQualitySoftMedium:
2175 m_shadowQualityToShader = 10.0f;
2176 m_shadowQualityMultiplier = 3;
2177 break;
2178 case QAbstract3DGraph::ShadowQualitySoftHigh:
2179 m_shadowQualityToShader = 15.0f;
2180 m_shadowQualityMultiplier = 4;
2181 break;
2182 default:
2183 m_shadowQualityToShader = 0.0f;
2184 m_shadowQualityMultiplier = 1;
2185 break;
2186 }
2187
2188 handleShadowQualityChange();
2189
2190 // Re-init depth buffer
2191 updateDepthBuffer();
2192}
2193
2194void Scatter3DRenderer::loadBackgroundMesh()
2195{
2196 ObjectHelper::resetObjectHelper(cacheId: this, obj&: m_backgroundObj,
2197 QStringLiteral(":/defaultMeshes/background"));
2198}
2199
2200void Scatter3DRenderer::updateTextures()
2201{
2202 Abstract3DRenderer::updateTextures();
2203
2204 // Drawer has changed; this flag needs to be checked when checking if we need to update labels
2205 m_updateLabels = true;
2206
2207 if (m_polarGraph)
2208 calculateSceneScalingFactors();
2209}
2210
2211void Scatter3DRenderer::fixMeshFileName(QString &fileName, QAbstract3DSeries::Mesh mesh)
2212{
2213 // Load full version of meshes that have it available
2214 if (mesh != QAbstract3DSeries::MeshSphere
2215 && mesh != QAbstract3DSeries::MeshMinimal
2216 && mesh != QAbstract3DSeries::MeshPoint
2217 && mesh != QAbstract3DSeries::MeshArrow) {
2218 fileName.append(QStringLiteral("Full"));
2219 }
2220}
2221
2222void Scatter3DRenderer::calculateTranslation(ScatterRenderItem &item)
2223{
2224 // We need to normalize translations
2225 const QVector3D &pos = item.position();
2226 float xTrans;
2227 float yTrans = m_axisCacheY.positionAt(value: pos.y());
2228 float zTrans;
2229 if (m_polarGraph) {
2230 calculatePolarXZ(dataPos: pos, x&: xTrans, z&: zTrans);
2231 } else {
2232 xTrans = m_axisCacheX.positionAt(value: pos.x());
2233 zTrans = m_axisCacheZ.positionAt(value: pos.z());
2234 }
2235 item.setTranslation(QVector3D(xTrans, yTrans, zTrans));
2236}
2237
2238void Scatter3DRenderer::calculateSceneScalingFactors()
2239{
2240 if (m_requestedMargin < 0.0f) {
2241 if (m_maxItemSize > defaultMaxSize)
2242 m_hBackgroundMargin = m_maxItemSize / itemScaler;
2243 else
2244 m_hBackgroundMargin = defaultMaxSize;
2245 m_vBackgroundMargin = m_hBackgroundMargin;
2246 } else {
2247 m_hBackgroundMargin = m_requestedMargin;
2248 m_vBackgroundMargin = m_requestedMargin;
2249 }
2250 if (m_polarGraph) {
2251 float polarMargin = calculatePolarBackgroundMargin();
2252 m_hBackgroundMargin = qMax(a: m_hBackgroundMargin, b: polarMargin);
2253 }
2254
2255 float horizontalAspectRatio;
2256 if (m_polarGraph)
2257 horizontalAspectRatio = 1.0f;
2258 else
2259 horizontalAspectRatio = m_graphHorizontalAspectRatio;
2260
2261 QSizeF areaSize;
2262 if (horizontalAspectRatio == 0.0f) {
2263 areaSize.setHeight(m_axisCacheZ.max() - m_axisCacheZ.min());
2264 areaSize.setWidth(m_axisCacheX.max() - m_axisCacheX.min());
2265 } else {
2266 areaSize.setHeight(1.0f);
2267 areaSize.setWidth(horizontalAspectRatio);
2268 }
2269
2270 float horizontalMaxDimension;
2271 if (m_graphAspectRatio > 2.0f) {
2272 horizontalMaxDimension = 2.0f;
2273 m_scaleY = 2.0f / m_graphAspectRatio;
2274 } else {
2275 horizontalMaxDimension = m_graphAspectRatio;
2276 m_scaleY = 1.0f;
2277 }
2278 if (m_polarGraph)
2279 m_polarRadius = horizontalMaxDimension;
2280
2281 float scaleFactor = qMax(a: areaSize.width(), b: areaSize.height());
2282 m_scaleX = horizontalMaxDimension * areaSize.width() / scaleFactor;
2283 m_scaleZ = horizontalMaxDimension * areaSize.height() / scaleFactor;
2284
2285 m_scaleXWithBackground = m_scaleX + m_hBackgroundMargin;
2286 m_scaleYWithBackground = m_scaleY + m_vBackgroundMargin;
2287 m_scaleZWithBackground = m_scaleZ + m_hBackgroundMargin;
2288
2289 m_axisCacheX.setScale(m_scaleX * 2.0f);
2290 m_axisCacheY.setScale(m_scaleY * 2.0f);
2291 m_axisCacheZ.setScale(-m_scaleZ * 2.0f);
2292 m_axisCacheX.setTranslate(-m_scaleX);
2293 m_axisCacheY.setTranslate(-m_scaleY);
2294 m_axisCacheZ.setTranslate(m_scaleZ);
2295
2296 updateCameraViewport();
2297 updateCustomItemPositions();
2298}
2299
2300void Scatter3DRenderer::initShaders(const QString &vertexShader, const QString &fragmentShader)
2301{
2302 delete m_dotShader;
2303 m_dotShader = new ShaderHelper(this, vertexShader, fragmentShader);
2304 m_dotShader->initialize();
2305}
2306
2307void Scatter3DRenderer::initGradientShaders(const QString &vertexShader,
2308 const QString &fragmentShader)
2309{
2310 delete m_dotGradientShader;
2311 m_dotGradientShader = new ShaderHelper(this, vertexShader, fragmentShader);
2312 m_dotGradientShader->initialize();
2313
2314}
2315
2316void Scatter3DRenderer::initStaticSelectedItemShaders(const QString &vertexShader,
2317 const QString &fragmentShader,
2318 const QString &gradientVertexShader,
2319 const QString &gradientFragmentShader)
2320{
2321 delete m_staticSelectedItemShader;
2322 m_staticSelectedItemShader = new ShaderHelper(this, vertexShader, fragmentShader);
2323 m_staticSelectedItemShader->initialize();
2324
2325 delete m_staticSelectedItemGradientShader;
2326 m_staticSelectedItemGradientShader = new ShaderHelper(this, gradientVertexShader,
2327 gradientFragmentShader);
2328 m_staticSelectedItemGradientShader->initialize();
2329}
2330
2331void Scatter3DRenderer::initSelectionShader()
2332{
2333 delete m_selectionShader;
2334 m_selectionShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexPlainColor"),
2335 QStringLiteral(":/shaders/fragmentPlainColor"));
2336 m_selectionShader->initialize();
2337}
2338
2339void Scatter3DRenderer::initSelectionBuffer()
2340{
2341 m_textureHelper->deleteTexture(texture: &m_selectionTexture);
2342
2343 if (m_primarySubViewport.size().isEmpty())
2344 return;
2345
2346 m_selectionTexture = m_textureHelper->createSelectionTexture(size: m_primarySubViewport.size(),
2347 frameBuffer&: m_selectionFrameBuffer,
2348 depthBuffer&: m_selectionDepthBuffer);
2349}
2350
2351void Scatter3DRenderer::initDepthShader()
2352{
2353 if (!m_isOpenGLES) {
2354 if (m_depthShader)
2355 delete m_depthShader;
2356 m_depthShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexDepth"),
2357 QStringLiteral(":/shaders/fragmentDepth"));
2358 m_depthShader->initialize();
2359 }
2360}
2361
2362void Scatter3DRenderer::updateDepthBuffer()
2363{
2364 if (!m_isOpenGLES) {
2365 m_textureHelper->deleteTexture(texture: &m_depthTexture);
2366
2367 if (m_primarySubViewport.size().isEmpty())
2368 return;
2369
2370 if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
2371 m_depthTexture = m_textureHelper->createDepthTextureFrameBuffer(size: m_primarySubViewport.size(),
2372 frameBuffer&: m_depthFrameBuffer,
2373 textureSize: m_shadowQualityMultiplier);
2374 if (!m_depthTexture)
2375 lowerShadowQuality();
2376 }
2377 }
2378}
2379
2380void Scatter3DRenderer::initPointShader()
2381{
2382 if (m_isOpenGLES) {
2383 if (m_pointShader)
2384 delete m_pointShader;
2385 m_pointShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexPointES2"),
2386 QStringLiteral(":/shaders/fragmentPlainColor"));
2387 m_pointShader->initialize();
2388 }
2389}
2390
2391void Scatter3DRenderer::initBackgroundShaders(const QString &vertexShader,
2392 const QString &fragmentShader)
2393{
2394 if (m_backgroundShader)
2395 delete m_backgroundShader;
2396 m_backgroundShader = new ShaderHelper(this, vertexShader, fragmentShader);
2397 m_backgroundShader->initialize();
2398}
2399
2400void Scatter3DRenderer::initStaticPointShaders(const QString &vertexShader,
2401 const QString &fragmentShader)
2402{
2403 if (m_staticGradientPointShader)
2404 delete m_staticGradientPointShader;
2405 m_staticGradientPointShader = new ShaderHelper(this, vertexShader, fragmentShader);
2406 m_staticGradientPointShader->initialize();
2407}
2408
2409void Scatter3DRenderer::selectionColorToSeriesAndIndex(const QVector4D &color,
2410 int &index,
2411 QAbstract3DSeries *&series)
2412{
2413 m_clickedType = QAbstract3DGraph::ElementNone;
2414 m_selectedLabelIndex = -1;
2415 m_selectedCustomItemIndex = -1;
2416 if (color != selectionSkipColor) {
2417 if (color.w() == labelRowAlpha) {
2418 // Row selection
2419 index = Scatter3DController::invalidSelectionIndex();
2420 m_selectedLabelIndex = color.x();
2421 m_clickedType = QAbstract3DGraph::ElementAxisZLabel;
2422 } else if (color.w() == labelColumnAlpha) {
2423 // Column selection
2424 index = Scatter3DController::invalidSelectionIndex();
2425 m_selectedLabelIndex = color.y();
2426 m_clickedType = QAbstract3DGraph::ElementAxisXLabel;
2427 } else if (color.w() == labelValueAlpha) {
2428 // Value selection
2429 index = Scatter3DController::invalidSelectionIndex();
2430 m_selectedLabelIndex = color.z();
2431 m_clickedType = QAbstract3DGraph::ElementAxisYLabel;
2432 } else if (color.w() == customItemAlpha) {
2433 // Custom item selection
2434 index = Scatter3DController::invalidSelectionIndex();
2435 m_selectedCustomItemIndex = int(color.x())
2436 + (int(color.y()) << 8)
2437 + (int(color.z()) << 16);
2438 m_clickedType = QAbstract3DGraph::ElementCustomItem;
2439 } else {
2440 int totalIndex = int(color.x())
2441 + (int(color.y()) << 8)
2442 + (int(color.z()) << 16);
2443 // Find the series and adjust the index accordingly
2444 foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
2445 if (baseCache->isVisible()) {
2446 ScatterSeriesRenderCache *cache =
2447 static_cast<ScatterSeriesRenderCache *>(baseCache);
2448 int offset = cache->selectionIndexOffset();
2449 if (totalIndex >= offset
2450 && totalIndex < (offset + cache->renderArray().size())) {
2451 index = totalIndex - offset;
2452 series = cache->series();
2453 m_clickedType = QAbstract3DGraph::ElementSeries;
2454 return;
2455 }
2456 }
2457 }
2458 }
2459 }
2460
2461 // No valid match found
2462 index = Scatter3DController::invalidSelectionIndex();
2463 series = 0;
2464}
2465
2466void Scatter3DRenderer::updateRenderItem(const QScatterDataItem &dataItem,
2467 ScatterRenderItem &renderItem)
2468{
2469 QVector3D dotPos = dataItem.position();
2470 if ((dotPos.x() >= m_axisCacheX.min() && dotPos.x() <= m_axisCacheX.max() )
2471 && (dotPos.y() >= m_axisCacheY.min() && dotPos.y() <= m_axisCacheY.max())
2472 && (dotPos.z() >= m_axisCacheZ.min() && dotPos.z() <= m_axisCacheZ.max())) {
2473 renderItem.setPosition(dotPos);
2474 renderItem.setVisible(true);
2475 if (!dataItem.rotation().isIdentity())
2476 renderItem.setRotation(dataItem.rotation().normalized());
2477 else
2478 renderItem.setRotation(identityQuaternion);
2479 calculateTranslation(item&: renderItem);
2480 } else {
2481 renderItem.setVisible(false);
2482 }
2483}
2484
2485QVector3D Scatter3DRenderer::convertPositionToTranslation(const QVector3D &position,
2486 bool isAbsolute)
2487{
2488 float xTrans = 0.0f;
2489 float yTrans = 0.0f;
2490 float zTrans = 0.0f;
2491 if (!isAbsolute) {
2492 if (m_polarGraph) {
2493 calculatePolarXZ(dataPos: position, x&: xTrans, z&: zTrans);
2494 } else {
2495 xTrans = m_axisCacheX.positionAt(value: position.x());
2496 zTrans = m_axisCacheZ.positionAt(value: position.z());
2497 }
2498 yTrans = m_axisCacheY.positionAt(value: position.y());
2499 } else {
2500 xTrans = position.x() * m_scaleX;
2501 yTrans = position.y() * m_scaleY;
2502 zTrans = position.z() * -m_scaleZ;
2503 }
2504 return QVector3D(xTrans, yTrans, zTrans);
2505}
2506
2507QT_END_NAMESPACE
2508

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