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

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