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

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