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

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