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 "bars3drenderer_p.h"
7#include "q3dcamera_p.h"
8#include "shaderhelper_p.h"
9#include "texturehelper_p.h"
10#include "utils_p.h"
11#include "barseriesrendercache_p.h"
12
13#include <QtGui/qquaternion.h>
14
15#include <QtCore/qmath.h>
16
17// You can verify that depth buffer drawing works correctly by uncommenting this.
18// You should see the scene from where the light is
19//#define SHOW_DEPTH_TEXTURE_SCENE
20
21QT_BEGIN_NAMESPACE
22
23const bool sliceGridLabels = true;
24
25Bars3DRenderer::Bars3DRenderer(Bars3DController *controller)
26 : Abstract3DRenderer(controller),
27 m_cachedIsSlicingActivated(false),
28 m_cachedRowCount(0),
29 m_cachedColumnCount(0),
30 m_cachedBarSeriesMargin(0.0f, 0.0f),
31 m_selectedBar(0),
32 m_sliceCache(0),
33 m_sliceTitleItem(0),
34 m_updateLabels(false),
35 m_barShader(0),
36 m_barGradientShader(0),
37 m_depthShader(0),
38 m_selectionShader(0),
39 m_backgroundShader(0),
40 m_bgrTexture(0),
41 m_selectionTexture(0),
42 m_depthFrameBuffer(0),
43 m_selectionFrameBuffer(0),
44 m_selectionDepthBuffer(0),
45 m_shadowQualityToShader(100.0f),
46 m_shadowQualityMultiplier(3),
47 m_heightNormalizer(1.0f),
48 m_backgroundAdjustment(0.0f),
49 m_rowWidth(0),
50 m_columnDepth(0),
51 m_maxDimension(0),
52 m_scaleX(0),
53 m_scaleZ(0),
54 m_scaleFactor(0),
55 m_maxSceneSize(40.0f),
56 m_visualSelectedBarPos(Bars3DController::invalidSelectionPosition()),
57 m_selectedBarPos(Bars3DController::invalidSelectionPosition()),
58 m_selectedSeriesCache(0),
59 m_noZeroInRange(false),
60 m_seriesScaleX(0.0f),
61 m_seriesScaleZ(0.0f),
62 m_seriesStep(0.0f),
63 m_seriesStart(0.0f),
64 m_clickedPosition(Bars3DController::invalidSelectionPosition()),
65 m_keepSeriesUniform(false),
66 m_haveUniformColorSeries(false),
67 m_haveGradientSeries(false),
68 m_zeroPosition(0.0f),
69 m_xScaleFactor(1.0f),
70 m_zScaleFactor(1.0f),
71 m_floorLevel(0.0f),
72 m_actualFloorLevel(0.0f)
73{
74 m_axisCacheY.setScale(2.0f);
75 m_axisCacheY.setTranslate(-1.0f);
76
77 initializeOpenGL();
78}
79
80Bars3DRenderer::~Bars3DRenderer()
81{
82 contextCleanup();
83 delete m_barShader;
84 delete m_barGradientShader;
85 delete m_depthShader;
86 delete m_selectionShader;
87 delete m_backgroundShader;
88}
89
90void Bars3DRenderer::contextCleanup()
91{
92 if (QOpenGLContext::currentContext()) {
93 m_textureHelper->glDeleteFramebuffers(n: 1, framebuffers: &m_selectionFrameBuffer);
94 m_textureHelper->glDeleteRenderbuffers(n: 1, renderbuffers: &m_selectionDepthBuffer);
95 m_textureHelper->deleteTexture(texture: &m_selectionTexture);
96 m_textureHelper->glDeleteFramebuffers(n: 1, framebuffers: &m_depthFrameBuffer);
97 m_textureHelper->deleteTexture(texture: &m_bgrTexture);
98 }
99}
100
101void Bars3DRenderer::initializeOpenGL()
102{
103 Abstract3DRenderer::initializeOpenGL();
104
105 // Initialize shaders
106
107 // Init depth shader (for shadows). Init in any case, easier to handle shadow activation if done via api.
108 initDepthShader();
109
110 // Init selection shader
111 initSelectionShader();
112
113 // Load grid line mesh
114 loadGridLineMesh();
115
116 // Load background mesh (we need to be initialized first)
117 loadBackgroundMesh();
118}
119
120void Bars3DRenderer::fixCameraTarget(QVector3D &target)
121{
122 target.setX(target.x() * m_xScaleFactor);
123 target.setY(0.0f);
124 target.setZ(target.z() * -m_zScaleFactor);
125}
126
127void Bars3DRenderer::getVisibleItemBounds(QVector3D &minBounds, QVector3D &maxBounds)
128{
129 // The inputs are the item bounds in OpenGL coordinates.
130 // The outputs limit these bounds to visible ranges, normalized to range [-1, 1]
131 // Volume shader flips the Y and Z axes, so we need to set negatives of actual values to those
132 float itemRangeX = (maxBounds.x() - minBounds.x());
133 float itemRangeY = (maxBounds.y() - minBounds.y());
134 float itemRangeZ = (maxBounds.z() - minBounds.z());
135
136 if (minBounds.x() < -m_xScaleFactor)
137 minBounds.setX(-1.0f + (2.0f * qAbs(t: minBounds.x() + m_xScaleFactor) / itemRangeX));
138 else
139 minBounds.setX(-1.0f);
140
141 if (minBounds.y() < -1.0f + m_backgroundAdjustment)
142 minBounds.setY(-(-1.0f + (2.0f * qAbs(t: minBounds.y() + 1.0f - m_backgroundAdjustment) / itemRangeY)));
143 else
144 minBounds.setY(1.0f);
145
146 if (minBounds.z() < -m_zScaleFactor)
147 minBounds.setZ(-(-1.0f + (2.0f * qAbs(t: minBounds.z() + m_zScaleFactor) / itemRangeZ)));
148 else
149 minBounds.setZ(1.0f);
150
151 if (maxBounds.x() > m_xScaleFactor)
152 maxBounds.setX(1.0f - (2.0f * qAbs(t: maxBounds.x() - m_xScaleFactor) / itemRangeX));
153 else
154 maxBounds.setX(1.0f);
155
156 if (maxBounds.y() > 1.0f + m_backgroundAdjustment)
157 maxBounds.setY(-(1.0f - (2.0f * qAbs(t: maxBounds.y() - 1.0f - m_backgroundAdjustment) / itemRangeY)));
158 else
159 maxBounds.setY(-1.0f);
160
161 if (maxBounds.z() > m_zScaleFactor)
162 maxBounds.setZ(-(1.0f - (2.0f * qAbs(t: maxBounds.z() - m_zScaleFactor) / itemRangeZ)));
163 else
164 maxBounds.setZ(-1.0f);
165}
166
167void Bars3DRenderer::updateData()
168{
169 int minRow = m_axisCacheZ.min();
170 int maxRow = m_axisCacheZ.max();
171 int minCol = m_axisCacheX.min();
172 int maxCol = m_axisCacheX.max();
173 int newRows = maxRow - minRow + 1;
174 int newColumns = maxCol - minCol + 1;
175 int dataRowCount = 0;
176 int maxDataRowCount = 0;
177
178 m_seriesScaleX = 1.0f / float(m_visibleSeriesCount);
179 m_seriesStep = 1.0f / float(m_visibleSeriesCount);
180 m_seriesStart = -((float(m_visibleSeriesCount) - 1.0f) / 2.0f)
181 * (m_seriesStep - (m_seriesStep * m_cachedBarSeriesMargin.width()));
182
183 if (m_keepSeriesUniform)
184 m_seriesScaleZ = m_seriesScaleX;
185 else
186 m_seriesScaleZ = 1.0f;
187
188 if (m_cachedRowCount != newRows || m_cachedColumnCount != newColumns) {
189 // Force update for selection related items
190 m_sliceCache = 0;
191 m_sliceTitleItem = 0;
192
193 m_cachedColumnCount = newColumns;
194 m_cachedRowCount = newRows;
195 // Calculate max scene size
196 GLfloat sceneRatio = qMin(a: GLfloat(newColumns) / GLfloat(newRows),
197 b: GLfloat(newRows) / GLfloat(newColumns));
198 m_maxSceneSize = 2.0f * qSqrt(v: sceneRatio * newColumns * newRows);
199 }
200
201 calculateSceneScalingFactors();
202
203 m_zeroPosition = m_axisCacheY.formatter()->positionAt(value: m_actualFloorLevel);
204
205 foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
206 BarSeriesRenderCache *cache = static_cast<BarSeriesRenderCache *>(baseCache);
207 if (cache->isVisible()) {
208 const QBar3DSeries *currentSeries = cache->series();
209 BarRenderItemArray &renderArray = cache->renderArray();
210 bool dimensionsChanged = false;
211 if (newRows != renderArray.size()
212 || newColumns != renderArray.at(i: 0).size()) {
213 // Destroy old render items and reallocate new array
214 dimensionsChanged = true;
215 renderArray.resize(size: newRows);
216 for (int i = 0; i < newRows; i++)
217 renderArray[i].resize(size: newColumns);
218 cache->sliceArray().clear();
219 }
220
221 if (cache->dataDirty() || dimensionsChanged) {
222 QBarDataProxy *dataProxy = currentSeries->dataProxy();
223 dataRowCount = dataProxy->rowCount();
224 if (maxDataRowCount < dataRowCount)
225 maxDataRowCount = qMin(a: dataRowCount, b: newRows);
226 int dataRowIndex = minRow;
227 for (int i = 0; i < newRows; i++) {
228 BarRenderItemRow &renderRow = renderArray[i];
229 const QBarDataRow *dataRow = 0;
230 if (dataRowIndex < dataRowCount)
231 dataRow = dataProxy->rowAt(rowIndex: dataRowIndex);
232 updateRenderRow(dataRow, renderRow);
233 dataRowIndex++;
234 }
235 cache->setDataDirty(false);
236 }
237 }
238 }
239
240 // Reset selected bar to update selection
241 updateSelectedBar(position: m_selectedBarPos,
242 series: m_selectedSeriesCache ? m_selectedSeriesCache->series() : 0);
243}
244
245void Bars3DRenderer::updateRenderRow(const QBarDataRow *dataRow, BarRenderItemRow &renderRow)
246{
247 int j = 0;
248 int renderRowSize = renderRow.size();
249 int startIndex = m_axisCacheX.min();
250
251 if (dataRow) {
252 int updateSize = qMin(a: (dataRow->size() - startIndex), b: renderRowSize);
253 int dataColIndex = startIndex;
254 for (; j < updateSize ; j++) {
255 updateRenderItem(dataItem: dataRow->at(i: dataColIndex), renderItem&: renderRow[j]);
256 dataColIndex++;
257 }
258 }
259 for (; j < renderRowSize; j++) {
260 renderRow[j].setValue(0.0f);
261 renderRow[j].setHeight(0.0f);
262 renderRow[j].setRotation(identityQuaternion);
263 }
264}
265
266void Bars3DRenderer::updateRenderItem(const QBarDataItem &dataItem, BarRenderItem &renderItem)
267{
268 float value = dataItem.value();
269 float heightValue = m_axisCacheY.formatter()->positionAt(value);
270 if (m_noZeroInRange) {
271 if (m_hasNegativeValues) {
272 heightValue = -1.0f + heightValue;
273 if (heightValue > 0.0f)
274 heightValue = 0.0f;
275 } else {
276 if (heightValue < 0.0f)
277 heightValue = 0.0f;
278 }
279 } else {
280 heightValue -= m_zeroPosition;
281 }
282 if (m_axisCacheY.reversed())
283 heightValue = -heightValue;
284
285 renderItem.setValue(value);
286 renderItem.setHeight(heightValue);
287
288 float angle = dataItem.rotation();
289 if (angle) {
290 renderItem.setRotation(
291 QQuaternion::fromAxisAndAngle(
292 axis: upVector, angle));
293 } else {
294 renderItem.setRotation(identityQuaternion);
295 }
296}
297
298void Bars3DRenderer::updateSeries(const QList<QAbstract3DSeries *> &seriesList)
299{
300 Abstract3DRenderer::updateSeries(seriesList);
301
302 bool noSelection = true;
303 int seriesCount = seriesList.size();
304 int visualIndex = 0;
305 m_haveUniformColorSeries = false;
306 m_haveGradientSeries = false;
307 for (int i = 0; i < seriesCount; i++) {
308 QBar3DSeries *barSeries = static_cast<QBar3DSeries *>(seriesList[i]);
309 BarSeriesRenderCache *cache =
310 static_cast<BarSeriesRenderCache *>(m_renderCacheList.value(key: barSeries));
311 if (barSeries->isVisible()) {
312 if (noSelection
313 && barSeries->selectedBar() != QBar3DSeries::invalidSelectionPosition()) {
314 if (selectionLabel() != cache->itemLabel())
315 m_selectionLabelDirty = true;
316 noSelection = false;
317 }
318 cache->setVisualIndex(visualIndex++);
319 if (cache->colorStyle() == Q3DTheme::ColorStyleUniform)
320 m_haveUniformColorSeries = true;
321 else
322 m_haveGradientSeries = true;
323 } else {
324 cache->setVisualIndex(-1);
325 }
326
327 }
328 if (noSelection) {
329 if (!selectionLabel().isEmpty())
330 m_selectionLabelDirty = true;
331 m_selectedSeriesCache = 0;
332 }
333}
334
335SeriesRenderCache *Bars3DRenderer::createNewCache(QAbstract3DSeries *series)
336{
337 return new BarSeriesRenderCache(series, this);
338}
339
340void Bars3DRenderer::updateRows(const QList<Bars3DController::ChangeRow> &rows)
341{
342 int minRow = m_axisCacheZ.min();
343 int maxRow = m_axisCacheZ.max();
344 BarSeriesRenderCache *cache = 0;
345 const QBar3DSeries *prevSeries = 0;
346 const QBarDataArray *dataArray = 0;
347
348 foreach (Bars3DController::ChangeRow item, rows) {
349 const int row = item.row;
350 if (row < minRow || row > maxRow)
351 continue;
352 QBar3DSeries *currentSeries = item.series;
353 if (currentSeries != prevSeries) {
354 cache = static_cast<BarSeriesRenderCache *>(m_renderCacheList.value(key: currentSeries));
355 prevSeries = currentSeries;
356 dataArray = item.series->dataProxy()->array();
357 // Invisible series render caches are not updated, but instead just marked dirty, so that
358 // they can be completely recalculated when they are turned visible.
359 if (!cache->isVisible() && !cache->dataDirty())
360 cache->setDataDirty(true);
361 }
362 if (cache->isVisible()) {
363 updateRenderRow(dataRow: dataArray->at(i: row), renderRow&: cache->renderArray()[row - minRow]);
364 if (m_cachedIsSlicingActivated
365 && cache == m_selectedSeriesCache
366 && m_selectedBarPos.x() == row) {
367 m_selectionDirty = true; // Need to update slice view
368 }
369 }
370 }
371}
372
373void Bars3DRenderer::updateItems(const QList<Bars3DController::ChangeItem> &items)
374{
375 int minRow = m_axisCacheZ.min();
376 int maxRow = m_axisCacheZ.max();
377 int minCol = m_axisCacheX.min();
378 int maxCol = m_axisCacheX.max();
379 BarSeriesRenderCache *cache = 0;
380 const QBar3DSeries *prevSeries = 0;
381 const QBarDataArray *dataArray = 0;
382
383 foreach (Bars3DController::ChangeItem item, items) {
384 const int row = item.point.x();
385 const int col = item.point.y();
386 if (row < minRow || row > maxRow || col < minCol || col > maxCol)
387 continue;
388 QBar3DSeries *currentSeries = item.series;
389 if (currentSeries != prevSeries) {
390 cache = static_cast<BarSeriesRenderCache *>(m_renderCacheList.value(key: currentSeries));
391 prevSeries = currentSeries;
392 dataArray = item.series->dataProxy()->array();
393 // Invisible series render caches are not updated, but instead just marked dirty, so that
394 // they can be completely recalculated when they are turned visible.
395 if (!cache->isVisible() && !cache->dataDirty())
396 cache->setDataDirty(true);
397 }
398 if (cache->isVisible()) {
399 updateRenderItem(dataItem: dataArray->at(i: row)->at(i: col),
400 renderItem&: cache->renderArray()[row - minRow][col - minCol]);
401 if (m_cachedIsSlicingActivated
402 && cache == m_selectedSeriesCache
403 && m_selectedBarPos == QPoint(row, col)) {
404 m_selectionDirty = true; // Need to update slice view
405 }
406 }
407 }
408}
409
410void Bars3DRenderer::updateScene(Q3DScene *scene)
411{
412 if (!m_noZeroInRange) {
413 scene->activeCamera()->d_ptr->setMinYRotation(-90.0);
414 scene->activeCamera()->d_ptr->setMaxYRotation(90.0);
415 } else {
416 if ((m_hasNegativeValues && !m_axisCacheY.reversed())
417 || (!m_hasNegativeValues && m_axisCacheY.reversed())) {
418 scene->activeCamera()->d_ptr->setMinYRotation(-90.0f);
419 scene->activeCamera()->d_ptr->setMaxYRotation(0.0);
420 } else {
421 scene->activeCamera()->d_ptr->setMinYRotation(0.0f);
422 scene->activeCamera()->d_ptr->setMaxYRotation(90.0);
423 }
424 }
425
426 Abstract3DRenderer::updateScene(scene);
427
428 updateSlicingActive(isSlicing: scene->isSlicingActive());
429}
430
431void Bars3DRenderer::render(GLuint defaultFboHandle)
432{
433 // Handle GL state setup for FBO buffers and clearing of the render surface
434 Abstract3DRenderer::render(defaultFboHandle);
435
436 if (m_axisCacheY.positionsDirty())
437 m_axisCacheY.updateAllPositions();
438
439 drawScene(defaultFboHandle);
440 if (m_cachedIsSlicingActivated)
441 drawSlicedScene();
442}
443
444void Bars3DRenderer::drawSlicedScene()
445{
446 if (m_cachedSelectionMode.testFlag(flag: QAbstract3DGraph::SelectionRow)
447 == m_cachedSelectionMode.testFlag(flag: QAbstract3DGraph::SelectionColumn)) {
448 qWarning(msg: "Invalid selection mode. Either QAbstract3DGraph::SelectionRow or"
449 " QAbstract3DGraph::SelectionColumn must be set before calling"
450 " setSlicingActive(true).");
451 return;
452 }
453
454 GLfloat barPosX = 0;
455 QVector3D lightPos;
456 QVector4D lightColor = Utils::vectorFromColor(color: m_cachedTheme->lightColor());
457
458 // Specify viewport
459 glViewport(x: m_secondarySubViewport.x(),
460 y: m_secondarySubViewport.y(),
461 width: m_secondarySubViewport.width(),
462 height: m_secondarySubViewport.height());
463
464 // Set up projection matrix
465 QMatrix4x4 projectionMatrix;
466 GLfloat viewPortRatio = (GLfloat)m_primarySubViewport.width()
467 / (GLfloat)m_primarySubViewport.height();
468 if (m_useOrthoProjection) {
469 GLfloat orthoRatio = 2.0f / m_autoScaleAdjustment;
470 projectionMatrix.ortho(left: -viewPortRatio * orthoRatio, right: viewPortRatio * orthoRatio,
471 bottom: -orthoRatio, top: orthoRatio,
472 nearPlane: 0.0f, farPlane: 100.0f);
473 } else {
474 projectionMatrix.perspective(verticalAngle: 35.0f, aspectRatio: viewPortRatio, nearPlane: 0.1f, farPlane: 100.0f);
475 }
476
477 // Set view matrix
478 QMatrix4x4 viewMatrix;
479
480 // Adjust scaling (zoom rate based on aspect ratio)
481 GLfloat camZPosSliced = cameraDistance / m_autoScaleAdjustment;
482
483 viewMatrix.lookAt(eye: QVector3D(0.0f, 0.0f, camZPosSliced), center: zeroVector, up: upVector);
484
485 // Set light position
486 lightPos = QVector3D(0.0f, 0.0f, camZPosSliced * 2.0f);
487
488 const Q3DCamera *activeCamera = m_cachedScene->activeCamera();
489
490 // Draw the selected row / column
491 QMatrix4x4 projectionViewMatrix = projectionMatrix * viewMatrix;
492 bool rowMode = m_cachedSelectionMode.testFlag(flag: QAbstract3DGraph::SelectionRow);
493 bool itemMode = m_cachedSelectionMode.testFlag(flag: QAbstract3DGraph::SelectionItem);
494
495 GLfloat barPosYAdjustment = -0.8f; // Translate to -1.0 + 0.2 for row/column labels
496 GLfloat gridAdjustment = 1.0f + barPosYAdjustment - m_backgroundAdjustment;
497 GLfloat scaleFactor = 0.0f;
498 if (rowMode)
499 scaleFactor = (1.1f * m_rowWidth) / m_scaleFactor;
500 else
501 scaleFactor = (1.1f * m_columnDepth) / m_scaleFactor;
502 GLfloat barLabelYPos = barPosYAdjustment - labelMargin;
503 GLfloat zeroPosAdjustment = 0.0f;
504 GLfloat directionMultiplier = 2.0f;
505 GLfloat directionBase = 0.0f;
506 if (m_axisCacheY.reversed()) {
507 directionMultiplier = -2.0f;
508 directionBase = -2.0f;
509 }
510 zeroPosAdjustment = directionBase +
511 directionMultiplier * m_axisCacheY.min() / m_heightNormalizer;
512 zeroPosAdjustment = qBound(min: -2.0f, val: zeroPosAdjustment, max: 0.0f);
513
514 // Draw grid lines
515 if (m_cachedTheme->isGridEnabled()) {
516 glDisable(GL_DEPTH_TEST);
517 ShaderHelper *lineShader;
518 if (m_isOpenGLES)
519 lineShader = m_selectionShader; // Plain color shader for GL_LINES
520 else
521 lineShader = m_backgroundShader;
522
523 // Bind line shader
524 lineShader->bind();
525
526 // Set unchanging shader bindings
527 QVector4D lineColor = Utils::vectorFromColor(color: m_cachedTheme->gridLineColor());
528 lineShader->setUniformValue(uniform: lineShader->lightP(), value: lightPos);
529 lineShader->setUniformValue(uniform: lineShader->view(), value: viewMatrix);
530 lineShader->setUniformValue(uniform: lineShader->color(), value: lineColor);
531 lineShader->setUniformValue(uniform: lineShader->ambientS(),
532 value: m_cachedTheme->ambientLightStrength()
533 + m_cachedTheme->lightStrength() / 7.0f);
534 lineShader->setUniformValue(uniform: lineShader->lightS(), value: 0.0f);
535 lineShader->setUniformValue(uniform: lineShader->lightColor(), value: lightColor);
536
537 // Horizontal lines
538 if (m_axisCacheY.segmentCount() > 0) {
539 int gridLineCount = m_axisCacheY.gridLineCount();
540
541 QVector3D gridLineScale(scaleFactor, gridLineWidth, gridLineWidth);
542 bool noZero = true;
543 QMatrix4x4 MVPMatrix;
544 QMatrix4x4 itModelMatrix;
545
546 for (int line = 0; line < gridLineCount; line++) {
547 QMatrix4x4 modelMatrix;
548 GLfloat gridPos = m_axisCacheY.gridLinePosition(index: line) + gridAdjustment;
549 modelMatrix.translate(x: 0.0f, y: gridPos, z: 0.0f);
550 modelMatrix.scale(vector: gridLineScale);
551 itModelMatrix = modelMatrix;
552 MVPMatrix = projectionViewMatrix * modelMatrix;
553
554 // Set the rest of the shader bindings
555 lineShader->setUniformValue(uniform: lineShader->model(), value: modelMatrix);
556 lineShader->setUniformValue(uniform: lineShader->nModel(),
557 value: itModelMatrix.inverted().transposed());
558 lineShader->setUniformValue(uniform: lineShader->MVP(), value: MVPMatrix);
559
560 // Draw the object
561 if (m_isOpenGLES)
562 m_drawer->drawLine(shader: lineShader);
563 else
564 m_drawer->drawObject(shader: lineShader, object: m_gridLineObj);
565
566 // Check if we have a line at zero position already
567 if (gridPos == (barPosYAdjustment + zeroPosAdjustment))
568 noZero = false;
569 }
570
571 // Draw a line at zero, if none exists
572 if (!m_noZeroInRange && noZero) {
573 QMatrix4x4 modelMatrix;
574 modelMatrix.translate(x: 0.0f, y: barPosYAdjustment - zeroPosAdjustment, z: 0.0f);
575 modelMatrix.scale(vector: gridLineScale);
576 itModelMatrix = modelMatrix;
577 MVPMatrix = projectionViewMatrix * modelMatrix;
578
579 // Set the rest of the shader bindings
580 lineShader->setUniformValue(uniform: lineShader->model(), value: modelMatrix);
581 lineShader->setUniformValue(uniform: lineShader->nModel(),
582 value: itModelMatrix.inverted().transposed());
583 lineShader->setUniformValue(uniform: lineShader->MVP(), value: MVPMatrix);
584 lineShader->setUniformValue(uniform: lineShader->color(),
585 value: Utils::vectorFromColor(
586 color: m_cachedTheme->labelTextColor()));
587
588 // Draw the object
589 if (m_isOpenGLES)
590 m_drawer->drawLine(shader: lineShader);
591 else
592 m_drawer->drawObject(shader: lineShader, object: m_gridLineObj);
593 }
594 }
595
596 if (sliceGridLabels) {
597 // Bind label shader
598 m_labelShader->bind();
599 glCullFace(GL_BACK);
600 glEnable(GL_BLEND);
601 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
602
603 // Draw grid labels
604 int labelNbr = 0;
605 int labelCount = m_axisCacheY.labelCount();
606 QVector3D labelTrans = QVector3D(scaleFactor + labelMargin, 0.0f, 0.0f);
607
608 for (int i = 0; i < labelCount; i++) {
609 if (m_axisCacheY.labelItems().size() > labelNbr) {
610 const LabelItem &axisLabelItem = *m_axisCacheY.labelItems().at(i: labelNbr);
611 GLfloat gridPos = m_axisCacheY.labelPosition(index: i) + gridAdjustment;
612 labelTrans.setY(gridPos);
613 m_dummyBarRenderItem.setTranslation(labelTrans);
614 m_drawer->drawLabel(item: m_dummyBarRenderItem, labelItem: axisLabelItem, viewmatrix: viewMatrix,
615 projectionmatrix: projectionMatrix, positionComp: zeroVector, rotation: identityQuaternion, itemHeight: 0,
616 mode: m_cachedSelectionMode, shader: m_labelShader, object: m_labelObj,
617 camera: activeCamera, useDepth: true, rotateAlong: true, position: Drawer::LabelMid, alignment: Qt::AlignLeft);
618 }
619 labelNbr++;
620 }
621 glDisable(GL_BLEND);
622 glEnable(GL_DEPTH_TEST);
623 }
624 }
625
626 // Draw bars
627 QVector3D modelMatrixScaler(m_scaleX * m_seriesScaleX, 0.0f, m_scaleZ * m_seriesScaleZ);
628 if (!rowMode) {
629 modelMatrixScaler.setX(m_scaleZ * m_seriesScaleZ);
630 modelMatrixScaler.setZ(m_scaleX * m_seriesScaleX);
631 }
632
633 // Set common bar shader bindings
634 m_barShader->bind();
635 m_barShader->setUniformValue(uniform: m_barShader->lightP(), value: lightPos);
636 m_barShader->setUniformValue(uniform: m_barShader->view(), value: viewMatrix);
637 m_barShader->setUniformValue(uniform: m_barShader->lightS(), value: 0.15f);
638 m_barShader->setUniformValue(uniform: m_barShader->ambientS(),
639 value: m_cachedTheme->ambientLightStrength()
640 + m_cachedTheme->lightStrength() / 7.0f);
641 m_barShader->setUniformValue(uniform: m_barShader->lightColor(), value: lightColor);
642 m_barGradientShader->bind();
643 m_barGradientShader->setUniformValue(uniform: m_barGradientShader->lightP(), value: lightPos);
644 m_barGradientShader->setUniformValue(uniform: m_barGradientShader->view(), value: viewMatrix);
645 m_barGradientShader->setUniformValue(uniform: m_barGradientShader->lightS(), value: 0.15f);
646 m_barGradientShader->setUniformValue(uniform: m_barGradientShader->ambientS(),
647 value: m_cachedTheme->ambientLightStrength()
648 + m_cachedTheme->lightStrength() / 7.0f);
649 m_barGradientShader->setUniformValue(uniform: m_barGradientShader->gradientMin(), value: 0.0f);
650 m_barGradientShader->setUniformValue(uniform: m_barGradientShader->lightColor(), value: lightColor);
651
652 // Default to uniform shader
653 ShaderHelper *barShader = m_barShader;
654 barShader->bind();
655
656 Q3DTheme::ColorStyle previousColorStyle = Q3DTheme::ColorStyleUniform;
657 Q3DTheme::ColorStyle colorStyle = Q3DTheme::ColorStyleUniform;
658 ObjectHelper *barObj = 0;
659 QVector4D highlightColor;
660 QVector4D baseColor;
661 GLuint highlightGradientTexture = 0;
662 GLuint baseGradientTexture = 0;
663 bool colorStyleIsUniform = true;
664 int firstVisualIndex = m_renderCacheList.size();
665 QList<BarRenderSliceItem> *firstVisualSliceArray = 0;
666 BarRenderSliceItem *selectedItem = 0;
667
668 QQuaternion seriesRotation;
669 foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
670 if (baseCache->isVisible()
671 && (baseCache == m_selectedSeriesCache
672 || m_cachedSelectionMode.testFlag(flag: QAbstract3DGraph::SelectionMultiSeries))) {
673 BarSeriesRenderCache *cache = static_cast<BarSeriesRenderCache *>(baseCache);
674 QList<BarRenderSliceItem> &sliceArray = cache->sliceArray();
675 int sliceCount = sliceArray.size();
676 if (firstVisualIndex > cache->visualIndex()) {
677 firstVisualIndex = cache->visualIndex();
678 firstVisualSliceArray = &sliceArray;
679 }
680
681 barObj = cache->object();
682 colorStyle = cache->colorStyle();
683 colorStyleIsUniform = (colorStyle == Q3DTheme::ColorStyleUniform);
684 if (colorStyleIsUniform) {
685 highlightColor = cache->singleHighlightColor();
686 baseColor = cache->baseColor();
687 } else {
688 highlightGradientTexture = cache->singleHighlightGradientTexture();
689 baseGradientTexture = cache->baseGradientTexture();
690 }
691
692 // Rebind shader if it has changed
693 if (colorStyleIsUniform != (previousColorStyle == Q3DTheme::ColorStyleUniform)) {
694 if (colorStyleIsUniform)
695 barShader = m_barShader;
696 else
697 barShader = m_barGradientShader;
698 barShader->bind();
699 }
700
701 if (!colorStyleIsUniform && (previousColorStyle != colorStyle)
702 && (colorStyle == Q3DTheme::ColorStyleObjectGradient)) {
703 m_barGradientShader->setUniformValue(uniform: m_barGradientShader->gradientHeight(), value: 0.5f);
704 }
705
706 previousColorStyle = colorStyle;
707 seriesRotation = cache->meshRotation();
708 bool selectedSeries = (cache == m_selectedSeriesCache);
709
710 for (int bar = 0; bar < sliceCount; bar++) {
711 BarRenderSliceItem &item = cache->sliceArray()[bar];
712 if (selectedSeries && itemMode && sliceGridLabels
713 && m_visualSelectedBarPos.x() == item.position().x()
714 && m_visualSelectedBarPos.y() == item.position().y()) {
715 selectedItem = &item;
716 }
717 if (!item.value())
718 continue;
719
720 if (item.height() < 0)
721 glCullFace(GL_FRONT);
722 else
723 glCullFace(GL_BACK);
724
725 QMatrix4x4 MVPMatrix;
726 QMatrix4x4 modelMatrix;
727 QMatrix4x4 itModelMatrix;
728 QQuaternion barRotation = item.rotation();
729 GLfloat barPosY = item.translation().y() + barPosYAdjustment - zeroPosAdjustment;
730
731 if (rowMode) {
732 barPosX = item.translation().x();
733 } else {
734 barPosX = -(item.translation().z()); // flip z; frontmost bar to the left
735 barRotation *= m_yRightAngleRotation;
736 }
737
738 modelMatrix.translate(x: barPosX, y: barPosY, z: 0.0f);
739 modelMatrixScaler.setY(item.height());
740
741 if (!seriesRotation.isIdentity())
742 barRotation *= seriesRotation;
743
744 if (!barRotation.isIdentity()) {
745 modelMatrix.rotate(quaternion: barRotation);
746 itModelMatrix.rotate(quaternion: barRotation);
747 }
748
749 modelMatrix.scale(vector: modelMatrixScaler);
750 itModelMatrix.scale(vector: modelMatrixScaler);
751
752 MVPMatrix = projectionViewMatrix * modelMatrix;
753
754 QVector4D barColor;
755 GLuint gradientTexture = 0;
756
757 if (itemMode && m_visualSelectedBarPos.x() == item.position().x()
758 && m_visualSelectedBarPos.y() == item.position().y()) {
759 if (colorStyleIsUniform)
760 barColor = highlightColor;
761 else
762 gradientTexture = highlightGradientTexture;
763 } else {
764 if (colorStyleIsUniform)
765 barColor = baseColor;
766 else
767 gradientTexture = baseGradientTexture;
768 }
769
770 if (item.height() != 0) {
771 // Set shader bindings
772 barShader->setUniformValue(uniform: barShader->model(), value: modelMatrix);
773 barShader->setUniformValue(uniform: barShader->nModel(),
774 value: itModelMatrix.inverted().transposed());
775 barShader->setUniformValue(uniform: barShader->MVP(), value: MVPMatrix);
776 if (colorStyleIsUniform) {
777 barShader->setUniformValue(uniform: barShader->color(), value: barColor);
778 } else if (colorStyle == Q3DTheme::ColorStyleRangeGradient) {
779 barShader->setUniformValue(uniform: barShader->gradientHeight(),
780 value: (qAbs(t: item.height()) / m_gradientFraction));
781 }
782
783 // Draw the object
784 m_drawer->drawObject(shader: barShader,
785 object: barObj,
786 textureId: gradientTexture);
787 }
788 }
789 }
790 }
791
792 // Draw labels
793 m_labelShader->bind();
794 glDisable(GL_DEPTH_TEST);
795 glCullFace(GL_BACK);
796 glEnable(GL_BLEND);
797 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
798
799 const LabelItem &sliceSelectionLabel = *m_sliceTitleItem;
800 QVector3D positionComp(0.0f, m_autoScaleAdjustment, 0.0f);
801
802 // Draw labels for bars
803 QVector3D sliceValueRotation(0.0f, 0.0f, 90.0f);
804 QVector3D sliceLabelRotation(0.0f, 0.0f, -45.0f);
805 QQuaternion totalSliceValueRotation = Utils::calculateRotation(xyzRotations: sliceValueRotation);
806 QQuaternion totalSliceLabelRotation = Utils::calculateRotation(xyzRotations: sliceLabelRotation);
807
808 int labelCount = m_sliceCache->labelItems().size();
809
810 for (int labelNo = 0; labelNo < labelCount; labelNo++) {
811 // Check for invalid usage (no selection when setting slicing active)
812 if (!firstVisualSliceArray) {
813 qWarning(msg: "No slice data found. Make sure there is a valid selection.");
814 continue;
815 }
816
817 // Get labels from first series only
818 const BarRenderSliceItem &item = firstVisualSliceArray->at(i: labelNo);
819 m_dummyBarRenderItem.setTranslation(QVector3D(item.translation().x(),
820 barLabelYPos,
821 item.translation().z()));
822
823 // Draw labels
824 m_drawer->drawLabel(item: m_dummyBarRenderItem, labelItem: *m_sliceCache->labelItems().at(i: labelNo),
825 viewmatrix: viewMatrix, projectionmatrix: projectionMatrix, positionComp, rotation: totalSliceLabelRotation,
826 itemHeight: 0, mode: m_cachedSelectionMode, shader: m_labelShader,
827 object: m_labelObj, camera: activeCamera, useDepth: false, rotateAlong: false, position: Drawer::LabelMid,
828 alignment: Qt::AlignLeft | Qt::AlignTop, isSlicing: true);
829 }
830
831 if (!sliceGridLabels) {
832 foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
833 if (baseCache->isVisible()) {
834 BarSeriesRenderCache *cache = static_cast<BarSeriesRenderCache *>(baseCache);
835 QList<BarRenderSliceItem> &sliceArray = cache->sliceArray();
836 int sliceCount = sliceArray.size();
837 for (int col = 0; col < sliceCount; col++) {
838 BarRenderSliceItem &item = sliceArray[col];
839
840 // Draw values
841 if (item.height() != 0.0f || (!m_noZeroInRange && item.value() == 0.0f)) {
842 // Create label texture if we need it
843 if (item.sliceLabel().isNull() || m_updateLabels) {
844 QString valueLabelText = m_axisCacheY.formatter()->stringForValue(
845 value: qreal(item.value()), format: m_axisCacheY.labelFormat());
846 item.setSliceLabel(valueLabelText);
847 m_drawer->generateLabelItem(item&: item.sliceLabelItem(), text: item.sliceLabel());
848 m_updateLabels = false;
849 }
850 Qt::AlignmentFlag alignment =
851 (item.height() > 0) ? Qt::AlignLeft : Qt::AlignRight;
852 Drawer::LabelPosition labelPos =
853 (item.height() < 0) ? Drawer::LabelBelow : Drawer::LabelOver;
854 m_dummyBarRenderItem.setTranslation(QVector3D(item.translation().x(),
855 barPosYAdjustment
856 - zeroPosAdjustment
857 + item.height(),
858 item.translation().z()));
859
860 m_drawer->drawLabel(item: m_dummyBarRenderItem, labelItem: item.sliceLabelItem(), viewmatrix: viewMatrix,
861 projectionmatrix: projectionMatrix, positionComp: zeroVector, rotation: totalSliceValueRotation,
862 itemHeight: item.height(), mode: m_cachedSelectionMode, shader: m_labelShader,
863 object: m_labelObj, camera: activeCamera, useDepth: false, rotateAlong: false, position: labelPos,
864 alignment, isSlicing: true);
865 }
866 }
867 }
868 }
869 } else if (selectedItem) {
870 // Only draw value for selected item when grid labels are on
871 // Create label texture if we need it
872 if (selectedItem->sliceLabel().isNull() || m_updateLabels) {
873 QString valueLabelText = m_axisCacheY.formatter()->stringForValue(
874 value: qreal(selectedItem->value()), format: m_axisCacheY.labelFormat());
875 selectedItem->setSliceLabel(valueLabelText);
876 m_drawer->generateLabelItem(item&: selectedItem->sliceLabelItem(), text: selectedItem->sliceLabel());
877 m_updateLabels = false;
878 }
879 Qt::AlignmentFlag alignment = (selectedItem->height() > 0) ? Qt::AlignLeft : Qt::AlignRight;
880 Drawer::LabelPosition labelPos =
881 (selectedItem->height() < 0) ? Drawer::LabelBelow : Drawer::LabelOver;
882 m_dummyBarRenderItem.setTranslation(QVector3D(selectedItem->translation().x(),
883 barPosYAdjustment - zeroPosAdjustment
884 + selectedItem->height(),
885 selectedItem->translation().z()));
886
887 m_drawer->drawLabel(item: m_dummyBarRenderItem, labelItem: selectedItem->sliceLabelItem(), viewmatrix: viewMatrix,
888 projectionmatrix: projectionMatrix, positionComp: zeroVector, rotation: totalSliceValueRotation,
889 itemHeight: selectedItem->height(), mode: m_cachedSelectionMode, shader: m_labelShader,
890 object: m_labelObj, camera: activeCamera, useDepth: false, rotateAlong: false, position: labelPos,
891 alignment, isSlicing: true);
892 }
893
894 // Draw labels for axes
895 if (rowMode) {
896 if (m_sliceTitleItem) {
897 m_drawer->drawLabel(item: m_dummyBarRenderItem, labelItem: sliceSelectionLabel, viewmatrix: viewMatrix, projectionmatrix: projectionMatrix,
898 positionComp, rotation: identityQuaternion, itemHeight: 0, mode: m_cachedSelectionMode,
899 shader: m_labelShader, object: m_labelObj, camera: activeCamera, useDepth: false, rotateAlong: false,
900 position: Drawer::LabelTop, alignment: Qt::AlignCenter, isSlicing: true);
901 }
902 m_drawer->drawLabel(item: m_dummyBarRenderItem, labelItem: m_axisCacheX.titleItem(), viewmatrix: viewMatrix, projectionmatrix: projectionMatrix,
903 positionComp, rotation: identityQuaternion, itemHeight: 0, mode: m_cachedSelectionMode,
904 shader: m_labelShader, object: m_labelObj, camera: activeCamera, useDepth: false, rotateAlong: false,
905 position: Drawer::LabelBottom, alignment: Qt::AlignCenter, isSlicing: true);
906 } else {
907 m_drawer->drawLabel(item: m_dummyBarRenderItem, labelItem: m_axisCacheZ.titleItem(), viewmatrix: viewMatrix, projectionmatrix: projectionMatrix,
908 positionComp, rotation: identityQuaternion, itemHeight: 0, mode: m_cachedSelectionMode,
909 shader: m_labelShader,
910 object: m_labelObj, camera: activeCamera, useDepth: false, rotateAlong: false, position: Drawer::LabelBottom,
911 alignment: Qt::AlignCenter, isSlicing: true);
912 if (m_sliceTitleItem) {
913 m_drawer->drawLabel(item: m_dummyBarRenderItem, labelItem: sliceSelectionLabel, viewmatrix: viewMatrix, projectionmatrix: projectionMatrix,
914 positionComp, rotation: identityQuaternion, itemHeight: 0, mode: m_cachedSelectionMode,
915 shader: m_labelShader,
916 object: m_labelObj, camera: activeCamera, useDepth: false, rotateAlong: false, position: Drawer::LabelTop,
917 alignment: Qt::AlignCenter, isSlicing: true);
918 }
919 }
920 // Y-axis label
921 QVector3D labelTrans = QVector3D(-scaleFactor - labelMargin, 0.2f, 0.0f); // y = 0.2 for row/column labels (see barPosYAdjustment)
922 m_dummyBarRenderItem.setTranslation(labelTrans);
923 m_drawer->drawLabel(item: m_dummyBarRenderItem, labelItem: m_axisCacheY.titleItem(), viewmatrix: viewMatrix,
924 projectionmatrix: projectionMatrix, positionComp: zeroVector, rotation: totalSliceValueRotation, itemHeight: 0,
925 mode: m_cachedSelectionMode, shader: m_labelShader, object: m_labelObj, camera: activeCamera,
926 useDepth: false, rotateAlong: false, position: Drawer::LabelMid, alignment: Qt::AlignBottom);
927
928 glDisable(GL_BLEND);
929 glEnable(GL_DEPTH_TEST);
930
931 // Release shader
932 glUseProgram(program: 0);
933}
934
935void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
936{
937 GLint startBar = 0;
938 GLint stopBar = 0;
939 GLint stepBar = 0;
940
941 GLint startRow = 0;
942 GLint stopRow = 0;
943 GLint stepRow = 0;
944
945 GLfloat backgroundRotation = 0;
946
947 GLfloat colPos = 0;
948 GLfloat rowPos = 0;
949
950 const Q3DCamera *activeCamera = m_cachedScene->activeCamera();
951
952 glViewport(x: m_primarySubViewport.x(),
953 y: m_primarySubViewport.y(),
954 width: m_primarySubViewport.width(),
955 height: m_primarySubViewport.height());
956
957 // Set up projection matrix
958 QMatrix4x4 projectionMatrix;
959 GLfloat viewPortRatio = (GLfloat)m_primarySubViewport.width()
960 / (GLfloat)m_primarySubViewport.height();
961 if (m_useOrthoProjection) {
962 GLfloat orthoRatio = 2.0f;
963 projectionMatrix.ortho(left: -viewPortRatio * orthoRatio, right: viewPortRatio * orthoRatio,
964 bottom: -orthoRatio, top: orthoRatio,
965 nearPlane: 0.0f, farPlane: 100.0f);
966 } else {
967 projectionMatrix.perspective(verticalAngle: 45.0f, aspectRatio: viewPortRatio, nearPlane: 0.1f, farPlane: 100.0f);
968 }
969
970 // Get the view matrix
971 QMatrix4x4 viewMatrix = activeCamera->d_ptr->viewMatrix();
972
973 // Calculate drawing order
974 // Draw order is reversed to optimize amount of drawing (ie. draw front objects first,
975 // depth test handles not needing to draw objects behind them)
976 if (viewMatrix.row(index: 0).x() > 0) {
977 startRow = 0;
978 stopRow = m_cachedRowCount;
979 stepRow = 1;
980 m_zFlipped = false;
981 } else {
982 startRow = m_cachedRowCount - 1;
983 stopRow = -1;
984 stepRow = -1;
985 m_zFlipped = true;
986 }
987 if (viewMatrix.row(index: 0).z() <= 0) {
988 startBar = 0;
989 stopBar = m_cachedColumnCount;
990 stepBar = 1;
991 m_xFlipped = false;
992 } else {
993 startBar = m_cachedColumnCount - 1;
994 stopBar = -1;
995 stepBar = -1;
996 m_xFlipped = true;
997 }
998
999 // Check if we're viewing the scene from below
1000 if (viewMatrix.row(index: 2).y() < 0)
1001 m_yFlipped = true;
1002 else
1003 m_yFlipped = false;
1004
1005 // calculate background rotation based on view matrix rotation
1006 if (viewMatrix.row(index: 0).x() > 0 && viewMatrix.row(index: 0).z() <= 0)
1007 backgroundRotation = 270.0f;
1008 else if (viewMatrix.row(index: 0).x() > 0 && viewMatrix.row(index: 0).z() > 0)
1009 backgroundRotation = 180.0f;
1010 else if (viewMatrix.row(index: 0).x() <= 0 && viewMatrix.row(index: 0).z() > 0)
1011 backgroundRotation = 90.0f;
1012 else if (viewMatrix.row(index: 0).x() <= 0 && viewMatrix.row(index: 0).z() <= 0)
1013 backgroundRotation = 0.0f;
1014
1015 // Get light position from the scene
1016 QVector3D lightPos = m_cachedScene->activeLight()->position();
1017
1018 // Skip depth rendering if we're in slice mode
1019 // Introduce regardless of shadow quality to simplify logic
1020 QMatrix4x4 depthViewMatrix;
1021 QMatrix4x4 depthProjectionMatrix;
1022 QMatrix4x4 depthProjectionViewMatrix;
1023
1024 QMatrix4x4 projectionViewMatrix = projectionMatrix * viewMatrix;
1025
1026 BarRenderItem *selectedBar(0);
1027
1028 if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) {
1029 // Render scene into a depth texture for using with shadow mapping
1030 // Enable drawing to depth framebuffer
1031 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer: m_depthFrameBuffer);
1032 glClear(GL_DEPTH_BUFFER_BIT);
1033
1034 // Bind depth shader
1035 m_depthShader->bind();
1036
1037 // Set viewport for depth map rendering. Must match texture size. Larger values give smoother shadows.
1038 // Depth viewport must always start from 0, 0, as it is rendered into a texture, not screen
1039 glViewport(x: 0, y: 0,
1040 width: m_primarySubViewport.width() * m_shadowQualityMultiplier,
1041 height: m_primarySubViewport.height() * m_shadowQualityMultiplier);
1042
1043 // Get the depth view matrix
1044 // It may be possible to hack lightPos here if we want to make some tweaks to shadow
1045 QVector3D depthLightPos = activeCamera->d_ptr->calculatePositionRelativeToCamera(
1046 relativePosition: zeroVector, fixedRotation: 0.0f, distanceModifier: 3.5f / m_autoScaleAdjustment);
1047 depthViewMatrix.lookAt(eye: depthLightPos, center: zeroVector, up: upVector);
1048
1049 // Set the depth projection matrix
1050 depthProjectionMatrix.perspective(verticalAngle: 10.0f, aspectRatio: viewPortRatio, nearPlane: 3.0f, farPlane: 100.0f);
1051 depthProjectionViewMatrix = depthProjectionMatrix * depthViewMatrix;
1052
1053 // Draw bars to depth buffer
1054 QVector3D shadowScaler(m_scaleX * m_seriesScaleX * 0.9f, 0.0f,
1055 m_scaleZ * m_seriesScaleZ * 0.9f);
1056 foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
1057 if (baseCache->isVisible()) {
1058 BarSeriesRenderCache *cache = static_cast<BarSeriesRenderCache *>(baseCache);
1059 float seriesPos = m_seriesStart + m_seriesStep
1060 * (cache->visualIndex() - (cache->visualIndex()
1061 * m_cachedBarSeriesMargin.width())) + 0.5f;
1062 ObjectHelper *barObj = cache->object();
1063 QQuaternion seriesRotation(cache->meshRotation());
1064 const BarRenderItemArray &renderArray = cache->renderArray();
1065 for (int row = startRow; row != stopRow; row += stepRow) {
1066 const BarRenderItemRow &renderRow = renderArray.at(i: row);
1067 for (int bar = startBar; bar != stopBar; bar += stepBar) {
1068 const BarRenderItem &item = renderRow.at(i: bar);
1069 if (!item.value())
1070 continue;
1071 GLfloat shadowOffset = 0.0f;
1072 // Set front face culling for negative valued bars and back face culling
1073 // for positive valued bars to remove peter-panning issues
1074 if (item.height() > 0) {
1075 glCullFace(GL_BACK);
1076 if (m_yFlipped)
1077 shadowOffset = 0.015f;
1078 } else {
1079 glCullFace(GL_FRONT);
1080 if (!m_yFlipped)
1081 shadowOffset = -0.015f;
1082 }
1083
1084 if (m_cachedTheme->isBackgroundEnabled() && m_reflectionEnabled
1085 && ((m_yFlipped && item.height() > 0.0)
1086 || (!m_yFlipped && item.height() < 0.0))) {
1087 continue;
1088 }
1089
1090 QMatrix4x4 modelMatrix;
1091 QMatrix4x4 MVPMatrix;
1092
1093 colPos = (bar + seriesPos) * (m_cachedBarSpacing.width());
1094 rowPos = (row + 0.5f) * (m_cachedBarSpacing.height());
1095
1096 // Draw shadows for bars "on the other side" a bit off ground to avoid
1097 // seeing shadows through the ground
1098 modelMatrix.translate(x: (colPos - m_rowWidth) / m_scaleFactor,
1099 y: item.height() + shadowOffset,
1100 z: (m_columnDepth - rowPos) / m_scaleFactor);
1101 // Scale the bars down in X and Z to reduce self-shadowing issues
1102 shadowScaler.setY(item.height());
1103 if (!seriesRotation.isIdentity() || !item.rotation().isIdentity())
1104 modelMatrix.rotate(quaternion: seriesRotation * item.rotation());
1105 modelMatrix.scale(vector: shadowScaler);
1106
1107 MVPMatrix = depthProjectionViewMatrix * modelMatrix;
1108
1109 m_depthShader->setUniformValue(uniform: m_depthShader->MVP(), value: MVPMatrix);
1110
1111 // 1st attribute buffer : vertices
1112 glEnableVertexAttribArray(index: m_depthShader->posAtt());
1113 glBindBuffer(GL_ARRAY_BUFFER, buffer: barObj->vertexBuf());
1114 glVertexAttribPointer(indx: m_depthShader->posAtt(), size: 3, GL_FLOAT, GL_FALSE, stride: 0,
1115 ptr: (void *)0);
1116
1117 // Index buffer
1118 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer: barObj->elementBuf());
1119
1120 // Draw the triangles
1121 glDrawElements(GL_TRIANGLES, count: barObj->indexCount(), GL_UNSIGNED_INT,
1122 indices: (void *)0);
1123
1124 // Free buffers
1125 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer: 0);
1126 glBindBuffer(GL_ARRAY_BUFFER, buffer: 0);
1127
1128 glDisableVertexAttribArray(index: m_depthShader->posAtt());
1129 }
1130 }
1131 }
1132 }
1133
1134 Abstract3DRenderer::drawCustomItems(state: RenderingDepth, regularShader: m_depthShader, viewMatrix,
1135 projectionViewMatrix,
1136 depthProjectionViewMatrix, depthTexture: m_depthTexture,
1137 shadowQuality: m_shadowQualityToShader);
1138
1139 // Disable drawing to depth framebuffer (= enable drawing to screen)
1140 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer: defaultFboHandle);
1141
1142 // Reset culling to normal
1143 glCullFace(GL_BACK);
1144
1145 // Revert to original viewport
1146 glViewport(x: m_primarySubViewport.x(),
1147 y: m_primarySubViewport.y(),
1148 width: m_primarySubViewport.width(),
1149 height: m_primarySubViewport.height());
1150 }
1151
1152 // Do position mapping when necessary
1153 if (m_graphPositionQueryPending) {
1154 QVector3D graphDimensions(m_xScaleFactor, 0.0f, m_zScaleFactor);
1155 queriedGraphPosition(projectionViewMatrix, scaling: graphDimensions, defaultFboHandle);
1156
1157 // Y is always at floor level
1158 m_queriedGraphPosition.setY(0.0f);
1159 emit needRender();
1160 }
1161
1162 // Skip selection mode drawing if we're slicing or have no selection mode
1163 if (!m_cachedIsSlicingActivated && m_cachedSelectionMode > QAbstract3DGraph::SelectionNone
1164 && m_selectionState == SelectOnScene
1165 && (m_visibleSeriesCount > 0 || !m_customRenderCache.isEmpty())
1166 && m_selectionTexture) {
1167 // Bind selection shader
1168 m_selectionShader->bind();
1169
1170 // Draw bars to selection buffer
1171 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer: m_selectionFrameBuffer);
1172 glViewport(x: 0, y: 0,
1173 width: m_primarySubViewport.width(),
1174 height: m_primarySubViewport.height());
1175
1176 glEnable(GL_DEPTH_TEST); // Needed, otherwise the depth render buffer is not used
1177 glClearColor(red: 1.0f, green: 1.0f, blue: 1.0f, alpha: 1.0f); // Set clear color to white (= selectionSkipColor)
1178 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Needed for clearing the frame buffer
1179 glDisable(GL_DITHER); // disable dithering, it may affect colors if enabled
1180 foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
1181 if (baseCache->isVisible()) {
1182 BarSeriesRenderCache *cache = static_cast<BarSeriesRenderCache *>(baseCache);
1183 float seriesPos = m_seriesStart + m_seriesStep
1184 * (cache->visualIndex() - (cache->visualIndex()
1185 * m_cachedBarSeriesMargin.width())) + 0.5f;
1186 ObjectHelper *barObj = cache->object();
1187 QQuaternion seriesRotation(cache->meshRotation());
1188 const BarRenderItemArray &renderArray = cache->renderArray();
1189 for (int row = startRow; row != stopRow; row += stepRow) {
1190 const BarRenderItemRow &renderRow = renderArray.at(i: row);
1191 for (int bar = startBar; bar != stopBar; bar += stepBar) {
1192 const BarRenderItem &item = renderRow.at(i: bar);
1193 if (!item.value())
1194 continue;
1195
1196 if (item.height() < 0)
1197 glCullFace(GL_FRONT);
1198 else
1199 glCullFace(GL_BACK);
1200
1201 QMatrix4x4 modelMatrix;
1202 QMatrix4x4 MVPMatrix;
1203
1204 colPos = (bar + seriesPos) * (m_cachedBarSpacing.width());
1205 rowPos = (row + 0.5f) * (m_cachedBarSpacing.height());
1206
1207 modelMatrix.translate(x: (colPos - m_rowWidth) / m_scaleFactor,
1208 y: item.height(),
1209 z: (m_columnDepth - rowPos) / m_scaleFactor);
1210 if (!seriesRotation.isIdentity() || !item.rotation().isIdentity())
1211 modelMatrix.rotate(quaternion: seriesRotation * item.rotation());
1212 modelMatrix.scale(vector: QVector3D(m_scaleX * m_seriesScaleX,
1213 item.height(),
1214 m_scaleZ * m_seriesScaleZ));
1215
1216 MVPMatrix = projectionViewMatrix * modelMatrix;
1217
1218 QVector4D barColor = QVector4D(GLfloat(row) / 255.0f,
1219 GLfloat(bar) / 255.0f,
1220 GLfloat(cache->visualIndex()) / 255.0f,
1221 itemAlpha);
1222
1223 m_selectionShader->setUniformValue(uniform: m_selectionShader->MVP(), value: MVPMatrix);
1224 m_selectionShader->setUniformValue(uniform: m_selectionShader->color(), value: barColor);
1225
1226 m_drawer->drawSelectionObject(shader: m_selectionShader, object: barObj);
1227 }
1228 }
1229 }
1230 }
1231 glCullFace(GL_BACK);
1232 Abstract3DRenderer::drawCustomItems(state: RenderingSelection, regularShader: m_selectionShader,
1233 viewMatrix,
1234 projectionViewMatrix, depthProjectionViewMatrix,
1235 depthTexture: m_depthTexture, shadowQuality: m_shadowQualityToShader);
1236 drawLabels(drawSelection: true, activeCamera, viewMatrix, projectionMatrix);
1237 drawBackground(backgroundRotation, depthProjectionViewMatrix, projectionViewMatrix,
1238 viewMatrix, reflectingDraw: false, drawingSelectionBuffer: true);
1239 glEnable(GL_DITHER);
1240
1241 // Read color under cursor
1242 QVector4D clickedColor = Utils::getSelection(mousepos: m_inputPosition, height: m_viewport.height());
1243 m_clickedPosition = selectionColorToArrayPosition(selectionColor: clickedColor);
1244 m_clickedSeries = selectionColorToSeries(selectionColor: clickedColor);
1245 m_clickResolved = true;
1246
1247 emit needRender();
1248
1249 // Revert to original render target and viewport
1250 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer: defaultFboHandle);
1251 glViewport(x: m_primarySubViewport.x(),
1252 y: m_primarySubViewport.y(),
1253 width: m_primarySubViewport.width(),
1254 height: m_primarySubViewport.height());
1255 }
1256
1257 if (m_reflectionEnabled) {
1258 //
1259 // Draw reflections
1260 //
1261 glDisable(GL_DEPTH_TEST);
1262 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
1263 glEnable(GL_STENCIL_TEST);
1264 glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
1265 glStencilFunc(GL_ALWAYS, ref: 1, mask: 0xffffffff);
1266
1267 // Draw background stencil
1268 drawBackground(backgroundRotation, depthProjectionViewMatrix, projectionViewMatrix,
1269 viewMatrix);
1270
1271 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
1272 glEnable(GL_DEPTH_TEST);
1273
1274 glStencilFunc(GL_EQUAL, ref: 1, mask: 0xffffffff);
1275 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
1276
1277 // Set light
1278 QVector3D reflectionLightPos = lightPos;
1279 reflectionLightPos.setY(-(lightPos.y()));
1280 m_cachedScene->activeLight()->setPosition(reflectionLightPos);
1281
1282 // Draw bar reflections
1283 (void)drawBars(selectedBar: &selectedBar, depthProjectionViewMatrix,
1284 projectionViewMatrix, viewMatrix,
1285 startRow, stopRow, stepRow,
1286 startBar, stopBar, stepBar, reflection: -1.0f);
1287
1288 Abstract3DRenderer::drawCustomItems(state: RenderingNormal, regularShader: m_customItemShader,
1289 viewMatrix, projectionViewMatrix,
1290 depthProjectionViewMatrix, depthTexture: m_depthTexture,
1291 shadowQuality: m_shadowQualityToShader, reflection: -1.0f);
1292
1293 // Reset light
1294 m_cachedScene->activeLight()->setPosition(lightPos);
1295
1296 glDisable(GL_STENCIL_TEST);
1297
1298 glCullFace(GL_BACK);
1299 }
1300
1301 //
1302 // Draw the real scene
1303 //
1304 // Draw background
1305 if (m_reflectionEnabled) {
1306 glEnable(GL_BLEND);
1307 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1308 drawBackground(backgroundRotation, depthProjectionViewMatrix, projectionViewMatrix,
1309 viewMatrix, reflectingDraw: true);
1310 glDisable(GL_BLEND);
1311 } else {
1312 drawBackground(backgroundRotation, depthProjectionViewMatrix, projectionViewMatrix,
1313 viewMatrix);
1314 }
1315
1316 // Draw bars
1317 bool barSelectionFound = drawBars(selectedBar: &selectedBar, depthProjectionViewMatrix,
1318 projectionViewMatrix, viewMatrix,
1319 startRow, stopRow, stepRow,
1320 startBar, stopBar, stepBar);
1321
1322 // Draw grid lines
1323 drawGridLines(depthProjectionViewMatrix, projectionViewMatrix, viewMatrix);
1324
1325 // Draw custom items
1326 Abstract3DRenderer::drawCustomItems(state: RenderingNormal, regularShader: m_customItemShader, viewMatrix,
1327 projectionViewMatrix, depthProjectionViewMatrix,
1328 depthTexture: m_depthTexture, shadowQuality: m_shadowQualityToShader);
1329
1330 // Draw labels
1331 drawLabels(drawSelection: false, activeCamera, viewMatrix, projectionMatrix);
1332
1333 // Handle selected bar label generation
1334 if (barSelectionFound) {
1335 // Print value of selected bar
1336 glDisable(GL_DEPTH_TEST);
1337 // Draw the selection label
1338 LabelItem &labelItem = selectionLabelItem();
1339 if (m_selectedBar != selectedBar || m_updateLabels || !labelItem.textureId()
1340 || m_selectionLabelDirty) {
1341 QString labelText = selectionLabel();
1342 if (labelText.isNull() || m_selectionLabelDirty) {
1343 labelText = m_selectedSeriesCache->itemLabel();
1344 setSelectionLabel(labelText);
1345 m_selectionLabelDirty = false;
1346 }
1347 m_drawer->generateLabelItem(item&: labelItem, text: labelText);
1348 m_selectedBar = selectedBar;
1349 }
1350
1351 Drawer::LabelPosition position =
1352 m_selectedBar->height() >= 0 ? Drawer::LabelOver : Drawer::LabelBelow;
1353
1354 m_drawer->drawLabel(item: *selectedBar, labelItem, viewmatrix: viewMatrix, projectionmatrix: projectionMatrix,
1355 positionComp: zeroVector, rotation: identityQuaternion, itemHeight: selectedBar->height(),
1356 mode: m_cachedSelectionMode, shader: m_labelShader,
1357 object: m_labelObj, camera: activeCamera, useDepth: true, rotateAlong: false, position);
1358
1359 // Reset label update flag; they should have been updated when we get here
1360 m_updateLabels = false;
1361
1362 glEnable(GL_DEPTH_TEST);
1363 } else {
1364 m_selectedBar = 0;
1365 }
1366
1367 glDisable(GL_BLEND);
1368
1369 // Release shader
1370 glUseProgram(program: 0);
1371 m_selectionDirty = false;
1372}
1373
1374bool Bars3DRenderer::drawBars(BarRenderItem **selectedBar,
1375 const QMatrix4x4 &depthProjectionViewMatrix,
1376 const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &viewMatrix,
1377 GLint startRow, GLint stopRow, GLint stepRow,
1378 GLint startBar, GLint stopBar, GLint stepBar, GLfloat reflection)
1379{
1380 QVector3D lightPos = m_cachedScene->activeLight()->position();
1381 QVector4D lightColor = Utils::vectorFromColor(color: m_cachedTheme->lightColor());
1382
1383 bool rowMode = m_cachedSelectionMode.testFlag(flag: QAbstract3DGraph::SelectionRow);
1384
1385 ShaderHelper *barShader = 0;
1386 GLuint gradientTexture = 0;
1387 Q3DTheme::ColorStyle previousColorStyle = Q3DTheme::ColorStyleUniform;
1388
1389 // Set unchanging shader bindings
1390 if (m_haveGradientSeries) {
1391 m_barGradientShader->bind();
1392 m_barGradientShader->setUniformValue(uniform: m_barGradientShader->lightP(), value: lightPos);
1393 m_barGradientShader->setUniformValue(uniform: m_barGradientShader->view(), value: viewMatrix);
1394 m_barGradientShader->setUniformValue(uniform: m_barGradientShader->ambientS(),
1395 value: m_cachedTheme->ambientLightStrength());
1396 m_barGradientShader->setUniformValue(uniform: m_barGradientShader->gradientMin(), value: 0.0f);
1397 m_barGradientShader->setUniformValue(uniform: m_barGradientShader->lightColor(), value: lightColor);
1398 }
1399
1400 if (m_haveUniformColorSeries) {
1401 m_barShader->bind();
1402 m_barShader->setUniformValue(uniform: m_barShader->lightP(), value: lightPos);
1403 m_barShader->setUniformValue(uniform: m_barShader->view(), value: viewMatrix);
1404 m_barShader->setUniformValue(uniform: m_barShader->ambientS(),
1405 value: m_cachedTheme->ambientLightStrength());
1406 m_barShader->setUniformValue(uniform: m_barShader->lightColor(), value: lightColor);
1407 barShader = m_barShader;
1408 } else {
1409 barShader = m_barGradientShader;
1410 previousColorStyle = Q3DTheme::ColorStyleRangeGradient;
1411 }
1412
1413 int sliceReserveAmount = 0;
1414 if (m_selectionDirty && m_cachedIsSlicingActivated) {
1415 // Slice doesn't own its items, no need to delete them - just clear
1416 if (rowMode)
1417 sliceReserveAmount = m_cachedColumnCount;
1418 else
1419 sliceReserveAmount = m_cachedRowCount;
1420
1421 // Set slice cache, i.e. axis cache from where slice labels are taken
1422 if (rowMode)
1423 m_sliceCache = &m_axisCacheX;
1424 else
1425 m_sliceCache = &m_axisCacheZ;
1426 m_sliceTitleItem = 0;
1427 }
1428
1429 glEnable(GL_POLYGON_OFFSET_FILL);
1430 glPolygonOffset(factor: 0.5f, units: 1.0f);
1431
1432 GLfloat adjustedLightStrength = m_cachedTheme->lightStrength() / 10.0f;
1433 GLfloat adjustedHighlightStrength = m_cachedTheme->highlightLightStrength() / 10.0f;
1434
1435 bool barSelectionFound = false;
1436
1437 QVector4D baseColor;
1438 QVector4D barColor;
1439 QVector3D modelScaler(m_scaleX * m_seriesScaleX, 0.0f, m_scaleZ * m_seriesScaleZ);
1440 bool somethingSelected =
1441 (m_visualSelectedBarPos != Bars3DController::invalidSelectionPosition());
1442 foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
1443 if (baseCache->isVisible()) {
1444 BarSeriesRenderCache *cache = static_cast<BarSeriesRenderCache *>(baseCache);
1445 float seriesPos = m_seriesStart + m_seriesStep
1446 * (cache->visualIndex() - (cache->visualIndex()
1447 * m_cachedBarSeriesMargin.width())) + 0.5f;
1448 ObjectHelper *barObj = cache->object();
1449 QQuaternion seriesRotation(cache->meshRotation());
1450 Q3DTheme::ColorStyle colorStyle = cache->colorStyle();
1451 BarRenderItemArray &renderArray = cache->renderArray();
1452 bool colorStyleIsUniform = (colorStyle == Q3DTheme::ColorStyleUniform);
1453 if (sliceReserveAmount)
1454 cache->sliceArray().resize(size: sliceReserveAmount);
1455
1456 // Rebind shader if it has changed
1457 if (colorStyleIsUniform != (previousColorStyle == Q3DTheme::ColorStyleUniform)) {
1458 if (colorStyleIsUniform)
1459 barShader = m_barShader;
1460 else
1461 barShader = m_barGradientShader;
1462 barShader->bind();
1463 }
1464
1465 if (colorStyleIsUniform) {
1466 baseColor = cache->baseColor();
1467 } else if ((previousColorStyle != colorStyle)
1468 && (colorStyle == Q3DTheme::ColorStyleObjectGradient)) {
1469 m_barGradientShader->setUniformValue(uniform: m_barGradientShader->gradientHeight(), value: 0.5f);
1470 }
1471
1472 // Always use base color when no selection mode
1473 if (m_cachedSelectionMode == QAbstract3DGraph::SelectionNone) {
1474 if (colorStyleIsUniform)
1475 barColor = baseColor;
1476 else
1477 gradientTexture = cache->baseGradientTexture();
1478 }
1479
1480 previousColorStyle = colorStyle;
1481 for (int row = startRow; row != stopRow; row += stepRow) {
1482 BarRenderItemRow &renderRow = renderArray[row];
1483 for (int bar = startBar; bar != stopBar; bar += stepBar) {
1484 BarRenderItem &item = renderRow[bar];
1485 float adjustedHeight = reflection * item.height();
1486 if (adjustedHeight < 0)
1487 glCullFace(GL_FRONT);
1488 else
1489 glCullFace(GL_BACK);
1490
1491 QMatrix4x4 modelMatrix;
1492 QMatrix4x4 itModelMatrix;
1493 QMatrix4x4 MVPMatrix;
1494
1495 GLfloat colPos = (bar + seriesPos) * (m_cachedBarSpacing.width());
1496 GLfloat rowPos = (row + 0.5f) * (m_cachedBarSpacing.height());
1497
1498 modelMatrix.translate(x: (colPos - m_rowWidth) / m_scaleFactor,
1499 y: adjustedHeight,
1500 z: (m_columnDepth - rowPos) / m_scaleFactor);
1501 modelScaler.setY(adjustedHeight);
1502 if (!seriesRotation.isIdentity() || !item.rotation().isIdentity()) {
1503 QQuaternion totalRotation = seriesRotation * item.rotation();
1504 modelMatrix.rotate(quaternion: totalRotation);
1505 itModelMatrix.rotate(quaternion: totalRotation);
1506 }
1507 modelMatrix.scale(vector: modelScaler);
1508 itModelMatrix.scale(vector: modelScaler);
1509#ifdef SHOW_DEPTH_TEXTURE_SCENE
1510 MVPMatrix = depthProjectionViewMatrix * modelMatrix;
1511#else
1512 MVPMatrix = projectionViewMatrix * modelMatrix;
1513#endif
1514 GLfloat lightStrength = m_cachedTheme->lightStrength();
1515 GLfloat shadowLightStrength = adjustedLightStrength;
1516
1517 if (m_cachedSelectionMode > QAbstract3DGraph::SelectionNone) {
1518 Bars3DController::SelectionType selectionType =
1519 Bars3DController::SelectionNone;
1520 if (somethingSelected)
1521 selectionType = isSelected(row, bar, cache);
1522
1523 switch (selectionType) {
1524 case Bars3DController::SelectionItem: {
1525 if (colorStyleIsUniform)
1526 barColor = cache->singleHighlightColor();
1527 else
1528 gradientTexture = cache->singleHighlightGradientTexture();
1529
1530 lightStrength = m_cachedTheme->highlightLightStrength();
1531 shadowLightStrength = adjustedHighlightStrength;
1532 // Insert position data into render item
1533 // We have no ownership, don't delete the previous one
1534 if (!m_cachedIsSlicingActivated
1535 && m_selectedSeriesCache == cache) {
1536 *selectedBar = &item;
1537 (*selectedBar)->setPosition(QPoint(row, bar));
1538 item.setTranslation(modelMatrix.column(index: 3).toVector3D());
1539 barSelectionFound = true;
1540 }
1541 if (m_selectionDirty && m_cachedIsSlicingActivated) {
1542 QVector3D translation = modelMatrix.column(index: 3).toVector3D();
1543 if (m_cachedSelectionMode & QAbstract3DGraph::SelectionColumn
1544 && m_visibleSeriesCount > 1) {
1545 translation.setZ((m_columnDepth
1546 - ((row + seriesPos)
1547 * (m_cachedBarSpacing.height())))
1548 / m_scaleFactor);
1549 }
1550 item.setTranslation(translation);
1551 item.setPosition(QPoint(row, bar));
1552 if (rowMode)
1553 cache->sliceArray()[bar].setItem(item);
1554 else
1555 cache->sliceArray()[row].setItem(item);
1556 }
1557 break;
1558 }
1559 case Bars3DController::SelectionRow: {
1560 // Current bar is on the same row as the selected bar
1561 if (colorStyleIsUniform)
1562 barColor = cache->multiHighlightColor();
1563 else
1564 gradientTexture = cache->multiHighlightGradientTexture();
1565
1566 lightStrength = m_cachedTheme->highlightLightStrength();
1567 shadowLightStrength = adjustedHighlightStrength;
1568 if (m_cachedIsSlicingActivated) {
1569 item.setTranslation(modelMatrix.column(index: 3).toVector3D());
1570 item.setPosition(QPoint(row, bar));
1571 if (m_selectionDirty) {
1572 if (!m_sliceTitleItem && m_axisCacheZ.labelItems().size() > row)
1573 m_sliceTitleItem = m_axisCacheZ.labelItems().at(i: row);
1574 cache->sliceArray()[bar].setItem(item);
1575 }
1576 }
1577 break;
1578 }
1579 case Bars3DController::SelectionColumn: {
1580 // Current bar is on the same column as the selected bar
1581 if (colorStyleIsUniform)
1582 barColor = cache->multiHighlightColor();
1583 else
1584 gradientTexture = cache->multiHighlightGradientTexture();
1585
1586 lightStrength = m_cachedTheme->highlightLightStrength();
1587 shadowLightStrength = adjustedHighlightStrength;
1588 if (m_cachedIsSlicingActivated) {
1589 QVector3D translation = modelMatrix.column(index: 3).toVector3D();
1590 if (m_visibleSeriesCount > 1) {
1591 translation.setZ((m_columnDepth
1592 - ((row + seriesPos)
1593 * (m_cachedBarSpacing.height())))
1594 / m_scaleFactor);
1595 }
1596 item.setTranslation(translation);
1597 item.setPosition(QPoint(row, bar));
1598 if (m_selectionDirty) {
1599 if (!m_sliceTitleItem && m_axisCacheX.labelItems().size() > bar)
1600 m_sliceTitleItem = m_axisCacheX.labelItems().at(i: bar);
1601 cache->sliceArray()[row].setItem(item);
1602 }
1603 }
1604 break;
1605 }
1606 case Bars3DController::SelectionNone: {
1607 // Current bar is not selected, nor on a row or column
1608 if (colorStyleIsUniform) {
1609 QList<QColor> rowColors = cache->series()->rowColors();
1610 if (rowColors.size() == 0) {
1611 barColor = baseColor;
1612 } else {
1613 int rowColorIndex = row % rowColors.size();
1614 barColor = Utils::vectorFromColor(color: rowColors[rowColorIndex]);
1615 }
1616 } else {
1617 gradientTexture = cache->baseGradientTexture();
1618 }
1619 break;
1620 }
1621 }
1622 }
1623
1624 if (item.height() == 0) {
1625 continue;
1626 } else if ((m_reflectionEnabled
1627 && (reflection == 1.0f
1628 || (reflection != 1.0f
1629 && ((m_yFlipped && item.height() < 0.0)
1630 || (!m_yFlipped && item.height() > 0.0)))))
1631 || !m_reflectionEnabled) {
1632 // Skip drawing of 0-height bars and reflections of bars on the "wrong side"
1633 // Set shader bindings
1634 barShader->setUniformValue(uniform: barShader->model(), value: modelMatrix);
1635 barShader->setUniformValue(uniform: barShader->nModel(),
1636 value: itModelMatrix.transposed().inverted());
1637 barShader->setUniformValue(uniform: barShader->MVP(), value: MVPMatrix);
1638 if (colorStyleIsUniform) {
1639 barShader->setUniformValue(uniform: barShader->color(), value: barColor);
1640 } else if (colorStyle == Q3DTheme::ColorStyleRangeGradient) {
1641 barShader->setUniformValue(uniform: barShader->gradientHeight(),
1642 value: qAbs(t: item.height()) / m_gradientFraction);
1643 }
1644
1645 if (((m_reflectionEnabled && reflection == 1.0f
1646 && m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone)
1647 || m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone)
1648 && !m_isOpenGLES) {
1649 // Set shadow shader bindings
1650 QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
1651 barShader->setUniformValue(uniform: barShader->shadowQ(),
1652 value: m_shadowQualityToShader);
1653 barShader->setUniformValue(uniform: barShader->depth(), value: depthMVPMatrix);
1654 barShader->setUniformValue(uniform: barShader->lightS(), value: shadowLightStrength);
1655 barShader->setUniformValue(uniform: barShader->lightColor(), value: lightColor);
1656
1657 // Draw the object
1658 m_drawer->drawObject(shader: barShader, object: barObj, textureId: gradientTexture,
1659 depthTextureId: m_depthTexture);
1660 } else {
1661 // Set shadowless shader bindings
1662 if (m_reflectionEnabled && reflection != 1.0f
1663 && m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
1664 barShader->setUniformValue(uniform: barShader->lightS(),
1665 value: adjustedLightStrength);
1666 } else {
1667 barShader->setUniformValue(uniform: barShader->lightS(), value: lightStrength);
1668 }
1669
1670 // Draw the object
1671 m_drawer->drawObject(shader: barShader, object: barObj, textureId: gradientTexture);
1672 }
1673 }
1674 }
1675 }
1676 }
1677 }
1678 glDisable(GL_POLYGON_OFFSET_FILL);
1679
1680 // Reset culling
1681 glCullFace(GL_BACK);
1682
1683 return barSelectionFound;
1684}
1685
1686void Bars3DRenderer::drawBackground(GLfloat backgroundRotation,
1687 const QMatrix4x4 &depthProjectionViewMatrix,
1688 const QMatrix4x4 &projectionViewMatrix,
1689 const QMatrix4x4 &viewMatrix, bool reflectingDraw,
1690 bool drawingSelectionBuffer)
1691{
1692 // Draw background
1693 if (m_cachedTheme->isBackgroundEnabled() && m_backgroundObj) {
1694 QVector3D lightPos = m_cachedScene->activeLight()->position();
1695 QVector4D lightColor = Utils::vectorFromColor(color: m_cachedTheme->lightColor());
1696 GLfloat adjustedLightStrength = m_cachedTheme->lightStrength() / 10.0f;
1697 ShaderHelper *shader = 0;
1698
1699 // Bind background shader
1700 if (drawingSelectionBuffer)
1701 shader = m_selectionShader; // Use single color shader when drawing to selection buffer
1702 else
1703 shader = m_backgroundShader;
1704 shader->bind();
1705
1706 QMatrix4x4 modelMatrix;
1707 QMatrix4x4 MVPMatrix;
1708 QMatrix4x4 itModelMatrix;
1709
1710 QVector3D backgroundScaler(m_scaleXWithBackground, m_scaleYWithBackground,
1711 m_scaleZWithBackground);
1712 QVector4D backgroundColor = Utils::vectorFromColor(color: m_cachedTheme->backgroundColor());
1713 if (m_reflectionEnabled)
1714 backgroundColor.setW(backgroundColor.w() * m_reflectivity);
1715
1716 // Set shader bindings
1717 shader->setUniformValue(uniform: shader->lightP(), value: lightPos);
1718 shader->setUniformValue(uniform: shader->view(), value: viewMatrix);
1719 if (drawingSelectionBuffer) {
1720 // Use selectionSkipColor for background when drawing to selection buffer
1721 shader->setUniformValue(uniform: shader->color(), value: selectionSkipColor);
1722 } else {
1723 shader->setUniformValue(uniform: shader->color(), value: backgroundColor);
1724 }
1725 shader->setUniformValue(uniform: shader->ambientS(),
1726 value: m_cachedTheme->ambientLightStrength() * 2.0f);
1727 shader->setUniformValue(uniform: shader->lightColor(), value: lightColor);
1728
1729 // Draw floor
1730 modelMatrix.scale(vector: backgroundScaler);
1731
1732 if (m_yFlipped)
1733 modelMatrix.rotate(quaternion: m_xRightAngleRotation);
1734 else
1735 modelMatrix.rotate(quaternion: m_xRightAngleRotationNeg);
1736
1737 itModelMatrix = modelMatrix;
1738
1739#ifdef SHOW_DEPTH_TEXTURE_SCENE
1740 MVPMatrix = depthProjectionViewMatrix * modelMatrix;
1741#else
1742 MVPMatrix = projectionViewMatrix * modelMatrix;
1743#endif
1744 // Set changed shader bindings
1745 shader->setUniformValue(uniform: shader->model(), value: modelMatrix);
1746 shader->setUniformValue(uniform: shader->nModel(), value: itModelMatrix.inverted().transposed());
1747 shader->setUniformValue(uniform: shader->MVP(), value: MVPMatrix);
1748
1749 if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) {
1750 // Set shadow shader bindings
1751 QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
1752 shader->setUniformValue(uniform: shader->depth(), value: depthMVPMatrix);
1753 // Draw the object
1754 m_drawer->drawObject(shader, object: m_gridLineObj, textureId: 0, depthTextureId: m_depthTexture);
1755 } else {
1756 // Draw the object
1757 m_drawer->drawObject(shader, object: m_gridLineObj);
1758 }
1759
1760 // Draw walls
1761 modelMatrix = QMatrix4x4();
1762 itModelMatrix = QMatrix4x4();
1763 modelMatrix.translate(x: 0.0f, y: m_backgroundAdjustment, z: 0.0f);
1764
1765 modelMatrix.scale(vector: backgroundScaler);
1766 itModelMatrix.scale(vector: backgroundScaler);
1767 modelMatrix.rotate(angle: backgroundRotation, x: 0.0f, y: 1.0f, z: 0.0f);
1768 itModelMatrix.rotate(angle: backgroundRotation, x: 0.0f, y: 1.0f, z: 0.0f);
1769
1770#ifdef SHOW_DEPTH_TEXTURE_SCENE
1771 MVPMatrix = depthProjectionViewMatrix * modelMatrix;
1772#else
1773 MVPMatrix = projectionViewMatrix * modelMatrix;
1774#endif
1775
1776 // Set changed shader bindings
1777 shader->setUniformValue(uniform: shader->model(), value: modelMatrix);
1778 shader->setUniformValue(uniform: shader->nModel(), value: itModelMatrix.inverted().transposed());
1779 shader->setUniformValue(uniform: shader->MVP(), value: MVPMatrix);
1780 if (!m_reflectionEnabled || (m_reflectionEnabled && reflectingDraw)) {
1781 if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) {
1782 // Set shadow shader bindings
1783 QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
1784 shader->setUniformValue(uniform: shader->shadowQ(), value: m_shadowQualityToShader);
1785 shader->setUniformValue(uniform: shader->depth(), value: depthMVPMatrix);
1786 shader->setUniformValue(uniform: shader->lightS(), value: adjustedLightStrength);
1787
1788 // Draw the object
1789 m_drawer->drawObject(shader, object: m_backgroundObj, textureId: 0, depthTextureId: m_depthTexture);
1790 } else {
1791 // Set shadowless shader bindings
1792 shader->setUniformValue(uniform: shader->lightS(), value: m_cachedTheme->lightStrength());
1793
1794 // Draw the object
1795 m_drawer->drawObject(shader, object: m_backgroundObj);
1796 }
1797 }
1798 }
1799}
1800
1801void Bars3DRenderer::drawGridLines(const QMatrix4x4 &depthProjectionViewMatrix,
1802 const QMatrix4x4 &projectionViewMatrix,
1803 const QMatrix4x4 &viewMatrix)
1804{
1805 if (m_cachedTheme->isGridEnabled()) {
1806 ShaderHelper *lineShader;
1807 if (m_isOpenGLES)
1808 lineShader = m_selectionShader; // Plain color shader for GL_LINES
1809 else
1810 lineShader = m_backgroundShader;
1811
1812 QQuaternion lineRotation;
1813
1814 QVector3D lightPos = m_cachedScene->activeLight()->position();
1815 QVector4D lightColor = Utils::vectorFromColor(color: m_cachedTheme->lightColor());
1816
1817 // Bind bar shader
1818 lineShader->bind();
1819
1820 // Set unchanging shader bindings
1821 QVector4D barColor = Utils::vectorFromColor(color: m_cachedTheme->gridLineColor());
1822 lineShader->setUniformValue(uniform: lineShader->lightP(), value: lightPos);
1823 lineShader->setUniformValue(uniform: lineShader->view(), value: viewMatrix);
1824 lineShader->setUniformValue(uniform: lineShader->color(), value: barColor);
1825 lineShader->setUniformValue(uniform: lineShader->ambientS(), value: m_cachedTheme->ambientLightStrength());
1826 lineShader->setUniformValue(uniform: lineShader->lightColor(), value: lightColor);
1827 if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) {
1828 // Set shadowed shader bindings
1829 lineShader->setUniformValue(uniform: lineShader->shadowQ(), value: m_shadowQualityToShader);
1830 lineShader->setUniformValue(uniform: lineShader->lightS(),
1831 value: m_cachedTheme->lightStrength() / 20.0f);
1832 } else {
1833 // Set shadowless shader bindings
1834 lineShader->setUniformValue(uniform: lineShader->lightS(),
1835 value: m_cachedTheme->lightStrength() / 2.5f);
1836 }
1837
1838 GLfloat yFloorLinePosition = gridLineOffset;
1839 if (m_yFlipped)
1840 yFloorLinePosition = -yFloorLinePosition;
1841
1842 QVector3D gridLineScaler(m_scaleXWithBackground, gridLineWidth, gridLineWidth);
1843
1844 if (m_yFlipped)
1845 lineRotation = m_xRightAngleRotation;
1846 else
1847 lineRotation = m_xRightAngleRotationNeg;
1848
1849 // Floor lines: rows
1850 for (GLfloat row = 0.0f; row <= m_cachedRowCount; row++) {
1851 QMatrix4x4 modelMatrix;
1852 QMatrix4x4 MVPMatrix;
1853 QMatrix4x4 itModelMatrix;
1854
1855 GLfloat rowPos = row * m_cachedBarSpacing.height();
1856 modelMatrix.translate(x: 0.0f, y: yFloorLinePosition,
1857 z: (m_columnDepth - rowPos) / m_scaleFactor);
1858 modelMatrix.scale(vector: gridLineScaler);
1859 itModelMatrix.scale(vector: gridLineScaler);
1860 modelMatrix.rotate(quaternion: lineRotation);
1861 itModelMatrix.rotate(quaternion: lineRotation);
1862
1863 MVPMatrix = projectionViewMatrix * modelMatrix;
1864
1865 // Set the rest of the shader bindings
1866 lineShader->setUniformValue(uniform: lineShader->model(), value: modelMatrix);
1867 lineShader->setUniformValue(uniform: lineShader->nModel(),
1868 value: itModelMatrix.inverted().transposed());
1869 lineShader->setUniformValue(uniform: lineShader->MVP(), value: MVPMatrix);
1870
1871 if (m_isOpenGLES) {
1872 m_drawer->drawLine(shader: lineShader);
1873 } else {
1874 if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
1875 // Set shadow shader bindings
1876 QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
1877 lineShader->setUniformValue(uniform: lineShader->depth(), value: depthMVPMatrix);
1878 // Draw the object
1879 m_drawer->drawObject(shader: lineShader, object: m_gridLineObj, textureId: 0, depthTextureId: m_depthTexture);
1880 } else {
1881 // Draw the object
1882 m_drawer->drawObject(shader: lineShader, object: m_gridLineObj);
1883 }
1884 }
1885 }
1886
1887 // Floor lines: columns
1888 if (m_isOpenGLES)
1889 lineRotation = m_yRightAngleRotation;
1890 gridLineScaler = QVector3D(gridLineWidth, gridLineWidth, m_scaleZWithBackground);
1891 for (GLfloat bar = 0.0f; bar <= m_cachedColumnCount; bar++) {
1892 QMatrix4x4 modelMatrix;
1893 QMatrix4x4 MVPMatrix;
1894 QMatrix4x4 itModelMatrix;
1895
1896 GLfloat colPos = bar * m_cachedBarSpacing.width();
1897 modelMatrix.translate(x: (m_rowWidth - colPos) / m_scaleFactor,
1898 y: yFloorLinePosition, z: 0.0f);
1899 modelMatrix.scale(vector: gridLineScaler);
1900 itModelMatrix.scale(vector: gridLineScaler);
1901 modelMatrix.rotate(quaternion: lineRotation);
1902 itModelMatrix.rotate(quaternion: lineRotation);
1903
1904 MVPMatrix = projectionViewMatrix * modelMatrix;
1905
1906 // Set the rest of the shader bindings
1907 lineShader->setUniformValue(uniform: lineShader->model(), value: modelMatrix);
1908 lineShader->setUniformValue(uniform: lineShader->nModel(),
1909 value: itModelMatrix.inverted().transposed());
1910 lineShader->setUniformValue(uniform: lineShader->MVP(), value: MVPMatrix);
1911
1912 if (m_isOpenGLES) {
1913 m_drawer->drawLine(shader: lineShader);
1914 } else {
1915 if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
1916 // Set shadow shader bindings
1917 QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
1918 lineShader->setUniformValue(uniform: lineShader->depth(), value: depthMVPMatrix);
1919 // Draw the object
1920 m_drawer->drawObject(shader: lineShader, object: m_gridLineObj, textureId: 0, depthTextureId: m_depthTexture);
1921 } else {
1922 // Draw the object
1923 m_drawer->drawObject(shader: lineShader, object: m_gridLineObj);
1924 }
1925 }
1926 }
1927
1928 if (m_axisCacheY.segmentCount() > 0) {
1929 // Wall lines: back wall
1930 int gridLineCount = m_axisCacheY.gridLineCount();
1931
1932 GLfloat zWallLinePosition = -m_scaleZWithBackground + gridLineOffset;
1933 if (m_zFlipped)
1934 zWallLinePosition = -zWallLinePosition;
1935
1936 gridLineScaler = QVector3D(m_scaleXWithBackground, gridLineWidth, gridLineWidth);
1937 for (int line = 0; line < gridLineCount; line++) {
1938 QMatrix4x4 modelMatrix;
1939 QMatrix4x4 MVPMatrix;
1940 QMatrix4x4 itModelMatrix;
1941
1942 modelMatrix.translate(x: 0.0f,
1943 y: m_axisCacheY.gridLinePosition(index: line),
1944 z: zWallLinePosition);
1945 modelMatrix.scale(vector: gridLineScaler);
1946 itModelMatrix.scale(vector: gridLineScaler);
1947 if (m_zFlipped) {
1948 modelMatrix.rotate(quaternion: m_xFlipRotation);
1949 itModelMatrix.rotate(quaternion: m_xFlipRotation);
1950 }
1951
1952 MVPMatrix = projectionViewMatrix * modelMatrix;
1953
1954 // Set the rest of the shader bindings
1955 lineShader->setUniformValue(uniform: lineShader->model(), value: modelMatrix);
1956 lineShader->setUniformValue(uniform: lineShader->nModel(),
1957 value: itModelMatrix.inverted().transposed());
1958 lineShader->setUniformValue(uniform: lineShader->MVP(), value: MVPMatrix);
1959
1960 if (m_isOpenGLES) {
1961 m_drawer->drawLine(shader: lineShader);
1962 } else {
1963 if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
1964 // Set shadow shader bindings
1965 QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
1966 lineShader->setUniformValue(uniform: lineShader->depth(), value: depthMVPMatrix);
1967 // Draw the object
1968 m_drawer->drawObject(shader: lineShader, object: m_gridLineObj, textureId: 0, depthTextureId: m_depthTexture);
1969 } else {
1970 // Draw the object
1971 m_drawer->drawObject(shader: lineShader, object: m_gridLineObj);
1972 }
1973 }
1974 }
1975
1976 // Wall lines: side wall
1977 GLfloat xWallLinePosition = -m_scaleXWithBackground + gridLineOffset;
1978 if (m_xFlipped)
1979 xWallLinePosition = -xWallLinePosition;
1980
1981 if (m_xFlipped)
1982 lineRotation = m_yRightAngleRotationNeg;
1983 else
1984 lineRotation = m_yRightAngleRotation;
1985
1986 gridLineScaler = QVector3D(gridLineWidth, gridLineWidth, m_scaleZWithBackground);
1987 for (int line = 0; line < gridLineCount; line++) {
1988 QMatrix4x4 modelMatrix;
1989 QMatrix4x4 MVPMatrix;
1990 QMatrix4x4 itModelMatrix;
1991
1992 modelMatrix.translate(x: xWallLinePosition,
1993 y: m_axisCacheY.gridLinePosition(index: line),
1994 z: 0.0f);
1995 modelMatrix.scale(vector: gridLineScaler);
1996 itModelMatrix.scale(vector: gridLineScaler);
1997 modelMatrix.rotate(quaternion: lineRotation);
1998 itModelMatrix.rotate(quaternion: lineRotation);
1999
2000 MVPMatrix = projectionViewMatrix * modelMatrix;
2001
2002 // Set the rest of the shader bindings
2003 lineShader->setUniformValue(uniform: lineShader->model(), value: modelMatrix);
2004 lineShader->setUniformValue(uniform: lineShader->nModel(),
2005 value: itModelMatrix.inverted().transposed());
2006 lineShader->setUniformValue(uniform: lineShader->MVP(), value: MVPMatrix);
2007
2008 if (m_isOpenGLES) {
2009 m_drawer->drawLine(shader: lineShader);
2010 } else {
2011 if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
2012 // Set shadow shader bindings
2013 QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
2014 lineShader->setUniformValue(uniform: lineShader->depth(), value: depthMVPMatrix);
2015 // Draw the object
2016 m_drawer->drawObject(shader: lineShader, object: m_gridLineObj, textureId: 0, depthTextureId: m_depthTexture);
2017 } else {
2018 // Draw the object
2019 m_drawer->drawObject(shader: lineShader, object: m_gridLineObj);
2020 }
2021 }
2022 }
2023 }
2024 }
2025}
2026
2027void Bars3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCamera,
2028 const QMatrix4x4 &viewMatrix, const QMatrix4x4 &projectionMatrix) {
2029 ShaderHelper *shader = 0;
2030 GLfloat alphaForValueSelection = labelValueAlpha / 255.0f;
2031 GLfloat alphaForRowSelection = labelRowAlpha / 255.0f;
2032 GLfloat alphaForColumnSelection = labelColumnAlpha / 255.0f;
2033 if (drawSelection) {
2034 shader = m_selectionShader;
2035 // m_selectionShader is already bound
2036 } else {
2037 shader = m_labelShader;
2038 shader->bind();
2039
2040 glEnable(GL_BLEND);
2041 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2042 }
2043
2044 glEnable(GL_POLYGON_OFFSET_FILL);
2045
2046 // If camera x rotation is 180, side labels face wrong direction
2047 float activeCameraXRotation = (activeCamera->xRotation() >= 180.0f) ? -180.0f
2048 : activeCamera->xRotation();
2049
2050 float labelAutoAngle = m_axisCacheY.labelAutoRotation();
2051 float labelAngleFraction = labelAutoAngle / 90.0f;
2052 float fractionCamY = activeCamera->yRotation() * labelAngleFraction;
2053 float fractionCamX = activeCameraXRotation * labelAngleFraction;
2054 float labelsMaxWidth = 0.0f;
2055
2056 int startIndex;
2057 int endIndex;
2058 int indexStep;
2059
2060 // Y Labels
2061 int labelCount = m_axisCacheY.labelCount();
2062 GLfloat labelMarginXTrans = labelMargin;
2063 GLfloat labelMarginZTrans = labelMargin;
2064 GLfloat labelXTrans = m_scaleXWithBackground;
2065 GLfloat labelZTrans = m_scaleZWithBackground;
2066 QVector3D backLabelRotation(0.0f, -90.0f, 0.0f);
2067 QVector3D sideLabelRotation(0.0f, 0.0f, 0.0f);
2068 Qt::AlignmentFlag backAlignment = (m_xFlipped != m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight;
2069 Qt::AlignmentFlag sideAlignment = (m_xFlipped == m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight;
2070
2071 if (!m_xFlipped) {
2072 labelXTrans = -labelXTrans;
2073 labelMarginXTrans = -labelMargin;
2074 }
2075 if (m_zFlipped) {
2076 labelZTrans = -labelZTrans;
2077 labelMarginZTrans = -labelMargin;
2078 }
2079
2080 if (labelAutoAngle == 0.0f) {
2081 if (!m_xFlipped)
2082 backLabelRotation.setY(90.0f);
2083 if (m_zFlipped)
2084 sideLabelRotation.setY(180.f);
2085 } else {
2086 // Orient side labels somewhat towards the camera
2087 if (m_xFlipped) {
2088 if (m_zFlipped)
2089 sideLabelRotation.setY(180.0f + (2.0f * labelAutoAngle) - fractionCamX);
2090 else
2091 sideLabelRotation.setY(-fractionCamX);
2092 backLabelRotation.setY(-90.0f + labelAutoAngle - fractionCamX);
2093 } else {
2094 if (m_zFlipped)
2095 sideLabelRotation.setY(180.0f - (2.0f * labelAutoAngle) - fractionCamX);
2096 else
2097 sideLabelRotation.setY(-fractionCamX);
2098 backLabelRotation.setY(90.0f - labelAutoAngle - fractionCamX);
2099 }
2100 }
2101 sideLabelRotation.setX(-fractionCamY);
2102 backLabelRotation.setX(-fractionCamY);
2103
2104 QQuaternion totalSideRotation = Utils::calculateRotation(xyzRotations: sideLabelRotation);
2105 QQuaternion totalBackRotation = Utils::calculateRotation(xyzRotations: backLabelRotation);
2106
2107 QVector3D backLabelTrans = QVector3D(labelXTrans, 0.0f,
2108 labelZTrans + labelMarginZTrans);
2109 QVector3D sideLabelTrans = QVector3D(-labelXTrans - labelMarginXTrans,
2110 0.0f, -labelZTrans);
2111
2112 if (m_yFlipped) {
2113 startIndex = labelCount - 1;
2114 endIndex = -1;
2115 indexStep = -1;
2116 } else {
2117 startIndex = 0;
2118 endIndex = labelCount;
2119 indexStep = 1;
2120 }
2121 float offsetValue = 0.0f;
2122 for (int i = startIndex; i != endIndex; i = i + indexStep) {
2123 backLabelTrans.setY(m_axisCacheY.labelPosition(index: i));
2124 sideLabelTrans.setY(backLabelTrans.y());
2125
2126 glPolygonOffset(factor: offsetValue++ / -10.0f, units: 1.0f);
2127
2128 const LabelItem &axisLabelItem = *m_axisCacheY.labelItems().at(i);
2129
2130 if (drawSelection) {
2131 QVector4D labelColor = QVector4D(0.0f, 0.0f, i / 255.0f,
2132 alphaForValueSelection);
2133 shader->setUniformValue(uniform: shader->color(), value: labelColor);
2134 }
2135
2136 // Back wall
2137 m_dummyBarRenderItem.setTranslation(backLabelTrans);
2138 m_drawer->drawLabel(item: m_dummyBarRenderItem, labelItem: axisLabelItem, viewmatrix: viewMatrix, projectionmatrix: projectionMatrix,
2139 positionComp: zeroVector, rotation: totalBackRotation, itemHeight: 0, mode: m_cachedSelectionMode,
2140 shader, object: m_labelObj, camera: activeCamera,
2141 useDepth: true, rotateAlong: true, position: Drawer::LabelMid, alignment: backAlignment, isSlicing: false, isSelecting: drawSelection);
2142
2143 // Side wall
2144 m_dummyBarRenderItem.setTranslation(sideLabelTrans);
2145 m_drawer->drawLabel(item: m_dummyBarRenderItem, labelItem: axisLabelItem, viewmatrix: viewMatrix, projectionmatrix: projectionMatrix,
2146 positionComp: zeroVector, rotation: totalSideRotation, itemHeight: 0, mode: m_cachedSelectionMode,
2147 shader, object: m_labelObj, camera: activeCamera,
2148 useDepth: true, rotateAlong: true, position: Drawer::LabelMid, alignment: sideAlignment, isSlicing: false, isSelecting: drawSelection);
2149
2150 labelsMaxWidth = qMax(a: labelsMaxWidth, b: float(axisLabelItem.size().width()));
2151 }
2152
2153 if (!drawSelection && m_axisCacheY.isTitleVisible()) {
2154 sideLabelTrans.setY(m_backgroundAdjustment);
2155 backLabelTrans.setY(m_backgroundAdjustment);
2156 drawAxisTitleY(sideLabelRotation, backLabelRotation, sideLabelTrans, backLabelTrans,
2157 totalSideRotation, totalBackRotation, dummyItem&: m_dummyBarRenderItem, activeCamera,
2158 labelsMaxWidth, viewMatrix, projectionMatrix, shader);
2159 }
2160
2161 // Z labels
2162 // Calculate the positions for row and column labels and store them
2163 labelsMaxWidth = 0.0f;
2164 labelAutoAngle = m_axisCacheZ.labelAutoRotation();
2165 labelAngleFraction = labelAutoAngle / 90.0f;
2166 fractionCamY = activeCamera->yRotation() * labelAngleFraction;
2167 fractionCamX = activeCameraXRotation * labelAngleFraction;
2168 GLfloat labelYAdjustment = 0.005f;
2169 GLfloat colPosValue = m_scaleXWithBackground + labelMargin;
2170 GLfloat rowPosValue = m_scaleZWithBackground + labelMargin;
2171 GLfloat rowPos = 0.0f;
2172 GLfloat colPos = 0.0f;
2173 Qt::AlignmentFlag alignment = (m_xFlipped == m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight;
2174 QVector3D labelRotation;
2175
2176 if (labelAutoAngle == 0.0f) {
2177 if (m_zFlipped)
2178 labelRotation.setY(180.0f);
2179 if (m_yFlipped) {
2180 if (m_zFlipped)
2181 labelRotation.setY(180.0f);
2182 else
2183 labelRotation.setY(0.0f);
2184 labelRotation.setX(90.0f);
2185 } else {
2186 labelRotation.setX(-90.0f);
2187 }
2188 } else {
2189 if (m_zFlipped)
2190 labelRotation.setY(180.0f);
2191 if (m_yFlipped) {
2192 if (m_zFlipped) {
2193 if (m_xFlipped) {
2194 labelRotation.setX(90.0f - (labelAutoAngle - fractionCamX)
2195 * (-labelAutoAngle - fractionCamY) / labelAutoAngle);
2196 labelRotation.setZ(labelAutoAngle + fractionCamY);
2197 } else {
2198 labelRotation.setX(90.0f + (labelAutoAngle + fractionCamX)
2199 * (labelAutoAngle + fractionCamY) / labelAutoAngle);
2200 labelRotation.setZ(-labelAutoAngle - fractionCamY);
2201 }
2202 } else {
2203 if (m_xFlipped) {
2204 labelRotation.setX(90.0f + (labelAutoAngle - fractionCamX)
2205 * -(labelAutoAngle + fractionCamY) / labelAutoAngle);
2206 labelRotation.setZ(-labelAutoAngle - fractionCamY);
2207 } else {
2208 labelRotation.setX(90.0f - (labelAutoAngle + fractionCamX)
2209 * (labelAutoAngle + fractionCamY) / labelAutoAngle);
2210 labelRotation.setZ(labelAutoAngle + fractionCamY);
2211 }
2212 }
2213 } else {
2214 if (m_zFlipped) {
2215 if (m_xFlipped) {
2216 labelRotation.setX(-90.0f + (labelAutoAngle - fractionCamX)
2217 * (-labelAutoAngle + fractionCamY) / labelAutoAngle);
2218 labelRotation.setZ(-labelAutoAngle + fractionCamY);
2219 } else {
2220 labelRotation.setX(-90.0f - (labelAutoAngle + fractionCamX)
2221 * (labelAutoAngle - fractionCamY) / labelAutoAngle);
2222 labelRotation.setZ(labelAutoAngle - fractionCamY);
2223 }
2224 } else {
2225 if (m_xFlipped) {
2226 labelRotation.setX(-90.0f - (labelAutoAngle - fractionCamX)
2227 * (-labelAutoAngle + fractionCamY) / labelAutoAngle);
2228 labelRotation.setZ(labelAutoAngle - fractionCamY);
2229 } else {
2230 labelRotation.setX(-90.0f + (labelAutoAngle + fractionCamX)
2231 * (labelAutoAngle - fractionCamY) / labelAutoAngle);
2232 labelRotation.setZ(-labelAutoAngle + fractionCamY);
2233 }
2234 }
2235 }
2236 }
2237
2238 QQuaternion totalRotation = Utils::calculateRotation(xyzRotations: labelRotation);
2239 labelCount = qMin(a: m_axisCacheZ.labelCount(), b: m_cachedRowCount);
2240 if (m_zFlipped) {
2241 startIndex = 0;
2242 endIndex = labelCount;
2243 indexStep = 1;
2244 } else {
2245 startIndex = labelCount - 1;
2246 endIndex = -1;
2247 indexStep = -1;
2248 }
2249 offsetValue = 0.0f;
2250 for (int row = startIndex; row != endIndex; row = row + indexStep) {
2251 // Go through all rows and get position of max+1 or min-1 column, depending on x flip
2252 // We need only positions for them, labels have already been generated
2253 rowPos = (row + 0.5f) * m_cachedBarSpacing.height();
2254 if (m_xFlipped)
2255 colPos = -colPosValue;
2256 else
2257 colPos = colPosValue;
2258
2259 glPolygonOffset(factor: offsetValue++ / -10.0f, units: 1.0f);
2260
2261 QVector3D labelPos = QVector3D(colPos,
2262 labelYAdjustment, // raise a bit over background to avoid depth "glimmering"
2263 (m_columnDepth - rowPos) / m_scaleFactor);
2264
2265 m_dummyBarRenderItem.setTranslation(labelPos);
2266 const LabelItem &axisLabelItem = *m_axisCacheZ.labelItems().at(i: row);
2267
2268 if (drawSelection) {
2269 QVector4D labelColor = QVector4D(row / 255.0f, 0.0f, 0.0f, alphaForRowSelection);
2270 shader->setUniformValue(uniform: shader->color(), value: labelColor);
2271 }
2272
2273 m_drawer->drawLabel(item: m_dummyBarRenderItem, labelItem: axisLabelItem, viewmatrix: viewMatrix, projectionmatrix: projectionMatrix,
2274 positionComp: zeroVector, rotation: totalRotation, itemHeight: 0, mode: m_cachedSelectionMode,
2275 shader, object: m_labelObj, camera: activeCamera,
2276 useDepth: true, rotateAlong: true, position: Drawer::LabelMid, alignment,
2277 isSlicing: false, isSelecting: drawSelection);
2278 labelsMaxWidth = qMax(a: labelsMaxWidth, b: float(axisLabelItem.size().width()));
2279 }
2280
2281 if (!drawSelection && m_axisCacheZ.isTitleVisible()) {
2282 QVector3D titleTrans(colPos, 0.0f, 0.0f);
2283 drawAxisTitleZ(labelRotation, labelTrans: titleTrans, totalRotation, dummyItem&: m_dummyBarRenderItem,
2284 activeCamera, labelsMaxWidth, viewMatrix, projectionMatrix, shader);
2285 }
2286
2287 // X labels
2288 labelsMaxWidth = 0.0f;
2289 labelAutoAngle = m_axisCacheX.labelAutoRotation();
2290 labelAngleFraction = labelAutoAngle / 90.0f;
2291 fractionCamY = activeCamera->yRotation() * labelAngleFraction;
2292 fractionCamX = activeCameraXRotation * labelAngleFraction;
2293 alignment = (m_xFlipped != m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight;
2294 if (labelAutoAngle == 0.0f) {
2295 labelRotation = QVector3D(-90.0f, 90.0f, 0.0f);
2296 if (m_xFlipped)
2297 labelRotation.setY(-90.0f);
2298 if (m_yFlipped) {
2299 if (m_xFlipped)
2300 labelRotation.setY(-90.0f);
2301 else
2302 labelRotation.setY(90.0f);
2303 labelRotation.setX(90.0f);
2304 }
2305 } else {
2306 if (m_xFlipped)
2307 labelRotation.setY(-90.0f);
2308 else
2309 labelRotation.setY(90.0f);
2310 if (m_yFlipped) {
2311 if (m_zFlipped) {
2312 if (m_xFlipped) {
2313 labelRotation.setX(90.0f - (2.0f * labelAutoAngle - fractionCamX)
2314 * (labelAutoAngle + fractionCamY) / labelAutoAngle);
2315 labelRotation.setZ(-labelAutoAngle - fractionCamY);
2316 } else {
2317 labelRotation.setX(90.0f - (2.0f * labelAutoAngle + fractionCamX)
2318 * (labelAutoAngle + fractionCamY) / labelAutoAngle);
2319 labelRotation.setZ(labelAutoAngle + fractionCamY);
2320 }
2321 } else {
2322 if (m_xFlipped) {
2323 labelRotation.setX(90.0f + fractionCamX
2324 * -(labelAutoAngle + fractionCamY) / labelAutoAngle);
2325 labelRotation.setZ(labelAutoAngle + fractionCamY);
2326 } else {
2327 labelRotation.setX(90.0f - fractionCamX
2328 * (-labelAutoAngle - fractionCamY) / labelAutoAngle);
2329 labelRotation.setZ(-labelAutoAngle - fractionCamY);
2330 }
2331 }
2332 } else {
2333 if (m_zFlipped) {
2334 if (m_xFlipped) {
2335 labelRotation.setX(-90.0f + (2.0f * labelAutoAngle - fractionCamX)
2336 * (labelAutoAngle - fractionCamY) / labelAutoAngle);
2337 labelRotation.setZ(labelAutoAngle - fractionCamY);
2338 } else {
2339 labelRotation.setX(-90.0f + (2.0f * labelAutoAngle + fractionCamX)
2340 * (labelAutoAngle - fractionCamY) / labelAutoAngle);
2341 labelRotation.setZ(-labelAutoAngle + fractionCamY);
2342 }
2343 } else {
2344 if (m_xFlipped) {
2345 labelRotation.setX(-90.0f - fractionCamX
2346 * (-labelAutoAngle + fractionCamY) / labelAutoAngle);
2347 labelRotation.setZ(-labelAutoAngle + fractionCamY);
2348 } else {
2349 labelRotation.setX(-90.0f + fractionCamX
2350 * -(labelAutoAngle - fractionCamY) / labelAutoAngle);
2351 labelRotation.setZ(labelAutoAngle - fractionCamY);
2352 }
2353 }
2354 }
2355 }
2356
2357 totalRotation = Utils::calculateRotation(xyzRotations: labelRotation);
2358 labelCount = qMin(a: m_axisCacheX.labelCount(), b: m_cachedColumnCount);
2359 if (m_xFlipped) {
2360 startIndex = labelCount - 1;
2361 endIndex = -1;
2362 indexStep = -1;
2363 } else {
2364 startIndex = 0;
2365 endIndex = labelCount;
2366 indexStep = 1;
2367 }
2368 offsetValue = 0.0f;
2369 for (int column = startIndex; column != endIndex; column = column + indexStep) {
2370 // Go through all columns and get position of max+1 or min-1 row, depending on z flip
2371 // We need only positions for them, labels have already been generated
2372 colPos = (column + 0.5f) * m_cachedBarSpacing.width();
2373 if (m_zFlipped)
2374 rowPos = -rowPosValue;
2375 else
2376 rowPos = rowPosValue;
2377
2378 glPolygonOffset(factor: offsetValue++ / -10.0f, units: 1.0f);
2379
2380 QVector3D labelPos = QVector3D((colPos - m_rowWidth) / m_scaleFactor,
2381 labelYAdjustment, // raise a bit over background to avoid depth "glimmering"
2382 rowPos);
2383
2384 m_dummyBarRenderItem.setTranslation(labelPos);
2385 const LabelItem &axisLabelItem = *m_axisCacheX.labelItems().at(i: column);
2386
2387 if (drawSelection) {
2388 QVector4D labelColor = QVector4D(0.0f, column / 255.0f, 0.0f,
2389 alphaForColumnSelection);
2390 shader->setUniformValue(uniform: shader->color(), value: labelColor);
2391 }
2392
2393 m_drawer->drawLabel(item: m_dummyBarRenderItem, labelItem: axisLabelItem, viewmatrix: viewMatrix, projectionmatrix: projectionMatrix,
2394 positionComp: zeroVector, rotation: totalRotation, itemHeight: 0, mode: m_cachedSelectionMode,
2395 shader, object: m_labelObj, camera: activeCamera,
2396 useDepth: true, rotateAlong: true, position: Drawer::LabelMid, alignment, isSlicing: false, isSelecting: drawSelection);
2397 labelsMaxWidth = qMax(a: labelsMaxWidth, b: float(axisLabelItem.size().width()));
2398 }
2399
2400 if (!drawSelection && m_axisCacheX.isTitleVisible()) {
2401 QVector3D titleTrans(0.0f, 0.0f, rowPos);
2402 drawAxisTitleX(labelRotation, labelTrans: titleTrans, totalRotation, dummyItem&: m_dummyBarRenderItem,
2403 activeCamera, labelsMaxWidth, viewMatrix, projectionMatrix, shader);
2404 }
2405
2406#if 0 // Debug label
2407 static LabelItem debugLabelItem;
2408 QString debugLabelString(QStringLiteral("Flips: x:%1 y:%2 z:%3 xr:%4 yr:%5"));
2409 QString finalDebugString = debugLabelString.arg(m_xFlipped).arg(m_yFlipped).arg(m_zFlipped)
2410 .arg(activeCamera->xRotation()).arg(activeCamera->yRotation());
2411 m_dummyBarRenderItem.setTranslation(QVector3D(m_xFlipped ? -1.5f : 1.5f,
2412 m_yFlipped ? 1.5f : -1.5f,
2413 m_zFlipped ? -1.5f : 1.5f));
2414
2415 m_drawer->generateLabelItem(debugLabelItem, finalDebugString);
2416 m_drawer->drawLabel(m_dummyBarRenderItem, debugLabelItem, viewMatrix, projectionMatrix,
2417 zeroVector, identityQuaternion, 0, m_cachedSelectionMode,
2418 shader, m_labelObj, activeCamera,
2419 true, false, Drawer::LabelMid, Qt::AlignHCenter, false, drawSelection);
2420#endif
2421 glDisable(GL_POLYGON_OFFSET_FILL);
2422}
2423
2424void Bars3DRenderer::updateMultiSeriesScaling(bool uniform)
2425{
2426 m_keepSeriesUniform = uniform;
2427
2428 // Recalculate scale factors
2429 m_seriesScaleX = 1.0f / float(m_visibleSeriesCount);
2430 if (m_keepSeriesUniform)
2431 m_seriesScaleZ = m_seriesScaleX;
2432 else
2433 m_seriesScaleZ = 1.0f;
2434}
2435
2436void Bars3DRenderer::updateBarSpecs(GLfloat thicknessRatio, const QSizeF &spacing, bool relative)
2437{
2438 // Convert ratio to QSizeF, as we need it in that format for autoscaling calculations
2439 m_cachedBarThickness.setWidth(1.0f);
2440 m_cachedBarThickness.setHeight(1.0f / thicknessRatio);
2441
2442 if (relative) {
2443 m_cachedBarSpacing.setWidth((m_cachedBarThickness.width() * 2)
2444 * (spacing.width() + 1.0f));
2445 m_cachedBarSpacing.setHeight((m_cachedBarThickness.height() * 2)
2446 * (spacing.height() + 1.0f));
2447 } else {
2448 m_cachedBarSpacing = m_cachedBarThickness * 2 + spacing * 2;
2449 }
2450
2451 // Slice mode doesn't update correctly without this
2452 if (m_cachedIsSlicingActivated)
2453 m_selectionDirty = true;
2454
2455 // Calculate here and at setting sample space
2456 calculateSceneScalingFactors();
2457}
2458
2459void Bars3DRenderer::updateBarSeriesMargin(const QSizeF &margin)
2460{
2461 m_cachedBarSeriesMargin = margin;
2462 calculateSeriesStartPosition();
2463 calculateSceneScalingFactors();
2464}
2465
2466void Bars3DRenderer::updateAxisRange(QAbstract3DAxis::AxisOrientation orientation, float min,
2467 float max)
2468{
2469 Abstract3DRenderer::updateAxisRange(orientation, min, max);
2470
2471 if (orientation == QAbstract3DAxis::AxisOrientationY)
2472 calculateHeightAdjustment();
2473}
2474
2475void Bars3DRenderer::updateAxisReversed(QAbstract3DAxis::AxisOrientation orientation, bool enable)
2476{
2477 Abstract3DRenderer::updateAxisReversed(orientation, enable);
2478 if (orientation == QAbstract3DAxis::AxisOrientationY)
2479 calculateHeightAdjustment();
2480}
2481
2482
2483void Bars3DRenderer::updateSelectedBar(const QPoint &position, QBar3DSeries *series)
2484{
2485 m_selectedBarPos = position;
2486 m_selectedSeriesCache = static_cast<BarSeriesRenderCache *>(m_renderCacheList.value(key: series, defaultValue: 0));
2487 m_selectionDirty = true;
2488 m_selectionLabelDirty = true;
2489
2490 if (!m_selectedSeriesCache
2491 || !m_selectedSeriesCache->isVisible()
2492 || m_selectedSeriesCache->renderArray().isEmpty()) {
2493 m_visualSelectedBarPos = Bars3DController::invalidSelectionPosition();
2494 return;
2495 }
2496
2497 int adjustedZ = m_selectedBarPos.x() - int(m_axisCacheZ.min());
2498 int adjustedX = m_selectedBarPos.y() - int(m_axisCacheX.min());
2499 int maxZ = m_selectedSeriesCache->renderArray().size() - 1;
2500 int maxX = maxZ >= 0 ? m_selectedSeriesCache->renderArray().at(i: 0).size() - 1 : -1;
2501
2502 if (m_selectedBarPos == Bars3DController::invalidSelectionPosition()
2503 || adjustedZ < 0 || adjustedZ > maxZ
2504 || adjustedX < 0 || adjustedX > maxX) {
2505 m_visualSelectedBarPos = Bars3DController::invalidSelectionPosition();
2506 } else {
2507 m_visualSelectedBarPos = QPoint(adjustedZ, adjustedX);
2508 }
2509}
2510
2511void Bars3DRenderer::resetClickedStatus()
2512{
2513 m_clickedPosition = Bars3DController::invalidSelectionPosition();
2514 m_clickedSeries = 0;
2515}
2516
2517void Bars3DRenderer::updateShadowQuality(QAbstract3DGraph::ShadowQuality quality)
2518{
2519 m_cachedShadowQuality = quality;
2520 switch (quality) {
2521 case QAbstract3DGraph::ShadowQualityLow:
2522 m_shadowQualityToShader = 33.3f;
2523 m_shadowQualityMultiplier = 1;
2524 break;
2525 case QAbstract3DGraph::ShadowQualityMedium:
2526 m_shadowQualityToShader = 100.0f;
2527 m_shadowQualityMultiplier = 3;
2528 break;
2529 case QAbstract3DGraph::ShadowQualityHigh:
2530 m_shadowQualityToShader = 200.0f;
2531 m_shadowQualityMultiplier = 5;
2532 break;
2533 case QAbstract3DGraph::ShadowQualitySoftLow:
2534 m_shadowQualityToShader = 7.5f;
2535 m_shadowQualityMultiplier = 1;
2536 break;
2537 case QAbstract3DGraph::ShadowQualitySoftMedium:
2538 m_shadowQualityToShader = 10.0f;
2539 m_shadowQualityMultiplier = 3;
2540 break;
2541 case QAbstract3DGraph::ShadowQualitySoftHigh:
2542 m_shadowQualityToShader = 15.0f;
2543 m_shadowQualityMultiplier = 4;
2544 break;
2545 default:
2546 m_shadowQualityToShader = 0.0f;
2547 m_shadowQualityMultiplier = 1;
2548 break;
2549 }
2550
2551 handleShadowQualityChange();
2552
2553 // Re-init depth buffer
2554 updateDepthBuffer();
2555
2556 // Redraw to handle both reflections and shadows on background
2557 if (m_reflectionEnabled)
2558 needRender();
2559}
2560
2561void Bars3DRenderer::loadBackgroundMesh()
2562{
2563 ObjectHelper::resetObjectHelper(cacheId: this, obj&: m_backgroundObj,
2564 QStringLiteral(":/defaultMeshes/backgroundNoFloor"));
2565}
2566
2567void Bars3DRenderer::updateTextures()
2568{
2569 Abstract3DRenderer::updateTextures();
2570
2571 // Drawer has changed; this flag needs to be checked when checking if we need to update labels
2572 m_updateLabels = true;
2573}
2574
2575void Bars3DRenderer::fixMeshFileName(QString &fileName, QAbstract3DSeries::Mesh mesh)
2576{
2577 if (!m_cachedTheme->isBackgroundEnabled()) {
2578 // Load full version of meshes that have it available
2579 // Note: Minimal, Point, and Arrow not supported in bar charts
2580 if (mesh != QAbstract3DSeries::MeshSphere)
2581 fileName.append(QStringLiteral("Full"));
2582 }
2583}
2584
2585void Bars3DRenderer::calculateSceneScalingFactors()
2586{
2587 // Calculate scene scaling and translation factors
2588 m_rowWidth = (m_cachedColumnCount * m_cachedBarSpacing.width()) / 2.0f;
2589 m_columnDepth = (m_cachedRowCount * m_cachedBarSpacing.height()) / 2.0f;
2590 m_maxDimension = qMax(a: m_rowWidth, b: m_columnDepth);
2591 m_scaleFactor = qMin(a: (m_cachedColumnCount * (m_maxDimension / m_maxSceneSize)),
2592 b: (m_cachedRowCount * (m_maxDimension / m_maxSceneSize)));
2593
2594 // Single bar scaling
2595 m_scaleX = m_cachedBarThickness.width() / m_scaleFactor;
2596 m_scaleZ = m_cachedBarThickness.height() / m_scaleFactor;
2597
2598 // Adjust scaling according to margin
2599 m_scaleX -= m_scaleX * m_cachedBarSeriesMargin.width();
2600 m_scaleZ -= m_scaleZ * m_cachedBarSeriesMargin.height();
2601
2602 // Whole graph scale factors
2603 m_xScaleFactor = m_rowWidth / m_scaleFactor;
2604 m_zScaleFactor = m_columnDepth / m_scaleFactor;
2605
2606 if (m_requestedMargin < 0.0f) {
2607 m_hBackgroundMargin = 0.0f;
2608 m_vBackgroundMargin = 0.0f;
2609 } else {
2610 m_hBackgroundMargin = m_requestedMargin;
2611 m_vBackgroundMargin = m_requestedMargin;
2612 }
2613
2614 m_scaleXWithBackground = m_xScaleFactor + m_hBackgroundMargin;
2615 m_scaleYWithBackground = 1.0f + m_vBackgroundMargin;
2616 m_scaleZWithBackground = m_zScaleFactor + m_hBackgroundMargin;
2617
2618 updateCameraViewport();
2619 updateCustomItemPositions();
2620}
2621
2622void Bars3DRenderer::calculateHeightAdjustment()
2623{
2624 float min = m_axisCacheY.min();
2625 float max = m_axisCacheY.max();
2626 GLfloat newAdjustment = 1.0f;
2627 m_actualFloorLevel = qBound(min, val: m_floorLevel, max);
2628 GLfloat maxAbs = qFabs(v: max - m_actualFloorLevel);
2629
2630 // Check if we have negative values
2631 if (min < m_actualFloorLevel)
2632 m_hasNegativeValues = true;
2633 else if (min >= m_actualFloorLevel)
2634 m_hasNegativeValues = false;
2635
2636 if (max < m_actualFloorLevel) {
2637 m_heightNormalizer = GLfloat(qFabs(v: min) - qFabs(v: max));
2638 maxAbs = qFabs(v: max) - qFabs(v: min);
2639 } else {
2640 m_heightNormalizer = GLfloat(max - min);
2641 }
2642
2643 // Height fractions are used in gradient calculations and are therefore doubled
2644 // Note that if max or min is exactly zero, we still consider it outside the range
2645 if (max <= m_actualFloorLevel || min >= m_actualFloorLevel) {
2646 m_noZeroInRange = true;
2647 m_gradientFraction = 2.0f;
2648 } else {
2649 m_noZeroInRange = false;
2650 GLfloat minAbs = qFabs(v: min - m_actualFloorLevel);
2651 m_gradientFraction = qMax(a: minAbs, b: maxAbs) / m_heightNormalizer * 2.0f;
2652 }
2653
2654 // Calculate translation adjustment for background floor
2655 newAdjustment = (qBound(min: 0.0f, val: (maxAbs / m_heightNormalizer), max: 1.0f) - 0.5f) * 2.0f;
2656 if (m_axisCacheY.reversed())
2657 newAdjustment = -newAdjustment;
2658
2659 if (newAdjustment != m_backgroundAdjustment) {
2660 m_backgroundAdjustment = newAdjustment;
2661 m_axisCacheY.setTranslate(m_backgroundAdjustment - 1.0f);
2662 }
2663}
2664
2665void Bars3DRenderer::calculateSeriesStartPosition()
2666{
2667 m_seriesStart = -((float(m_visibleSeriesCount) - 1.0f) / 2.0f)
2668 * (m_seriesStep - (m_seriesStep * m_cachedBarSeriesMargin.width()));
2669}
2670
2671Bars3DController::SelectionType Bars3DRenderer::isSelected(int row, int bar,
2672 const BarSeriesRenderCache *cache)
2673{
2674 Bars3DController::SelectionType isSelectedType = Bars3DController::SelectionNone;
2675
2676 if ((m_cachedSelectionMode.testFlag(flag: QAbstract3DGraph::SelectionMultiSeries)
2677 && m_selectedSeriesCache) || cache == m_selectedSeriesCache) {
2678 if (row == m_visualSelectedBarPos.x() && bar == m_visualSelectedBarPos.y()
2679 && (m_cachedSelectionMode.testFlag(flag: QAbstract3DGraph::SelectionItem))) {
2680 isSelectedType = Bars3DController::SelectionItem;
2681 } else if (row == m_visualSelectedBarPos.x()
2682 && (m_cachedSelectionMode.testFlag(flag: QAbstract3DGraph::SelectionRow))) {
2683 isSelectedType = Bars3DController::SelectionRow;
2684 } else if (bar == m_visualSelectedBarPos.y()
2685 && (m_cachedSelectionMode.testFlag(flag: QAbstract3DGraph::SelectionColumn))) {
2686 isSelectedType = Bars3DController::SelectionColumn;
2687 }
2688 }
2689
2690 return isSelectedType;
2691}
2692
2693QPoint Bars3DRenderer::selectionColorToArrayPosition(const QVector4D &selectionColor)
2694{
2695 QPoint position = Bars3DController::invalidSelectionPosition();
2696 m_clickedType = QAbstract3DGraph::ElementNone;
2697 m_selectedLabelIndex = -1;
2698 m_selectedCustomItemIndex = -1;
2699 if (selectionColor.w() == itemAlpha) {
2700 // Normal selection item
2701 position = QPoint(int(selectionColor.x() + int(m_axisCacheZ.min())),
2702 int(selectionColor.y()) + int(m_axisCacheX.min()));
2703 // Pass item clicked info to input handler
2704 m_clickedType = QAbstract3DGraph::ElementSeries;
2705 } else if (selectionColor.w() == labelRowAlpha) {
2706 // Row selection
2707 if (m_cachedSelectionMode.testFlag(flag: QAbstract3DGraph::SelectionRow)) {
2708 // Use column from previous selection in case we have row + column mode
2709 GLint previousCol = qMax(a: 0, b: m_selectedBarPos.y()); // Use 0 if previous is invalid
2710 position = QPoint(int(selectionColor.x() + int(m_axisCacheZ.min())), previousCol);
2711 }
2712 m_selectedLabelIndex = selectionColor.x();
2713 // Pass label clicked info to input handler
2714 m_clickedType = QAbstract3DGraph::ElementAxisZLabel;
2715 } else if (selectionColor.w() == labelColumnAlpha) {
2716 // Column selection
2717 if (m_cachedSelectionMode.testFlag(flag: QAbstract3DGraph::SelectionColumn)) {
2718 // Use row from previous selection in case we have row + column mode
2719 GLint previousRow = qMax(a: 0, b: m_selectedBarPos.x()); // Use 0 if previous is invalid
2720 position = QPoint(previousRow, int(selectionColor.y()) + int(m_axisCacheX.min()));
2721 }
2722 m_selectedLabelIndex = selectionColor.y();
2723 // Pass label clicked info to input handler
2724 m_clickedType = QAbstract3DGraph::ElementAxisXLabel;
2725 } else if (selectionColor.w() == labelValueAlpha) {
2726 // Value selection
2727 position = Bars3DController::invalidSelectionPosition();
2728 m_selectedLabelIndex = selectionColor.z();
2729 // Pass label clicked info to input handler
2730 m_clickedType = QAbstract3DGraph::ElementAxisYLabel;
2731 } else if (selectionColor.w() == customItemAlpha) {
2732 // Custom item selection
2733 position = Bars3DController::invalidSelectionPosition();
2734 m_selectedCustomItemIndex = int(selectionColor.x())
2735 + (int(selectionColor.y()) << 8)
2736 + (int(selectionColor.z()) << 16);
2737 m_clickedType = QAbstract3DGraph::ElementCustomItem;
2738 }
2739 return position;
2740}
2741
2742QBar3DSeries *Bars3DRenderer::selectionColorToSeries(const QVector4D &selectionColor)
2743{
2744 if (selectionColor == selectionSkipColor) {
2745 return 0;
2746 } else {
2747 int seriesIndexFromColor(selectionColor.z());
2748 foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
2749 BarSeriesRenderCache *cache = static_cast<BarSeriesRenderCache *>(baseCache);
2750 if (cache->visualIndex() == seriesIndexFromColor)
2751 return cache->series();
2752 }
2753 }
2754 return 0;
2755}
2756
2757void Bars3DRenderer::updateSlicingActive(bool isSlicing)
2758{
2759 if (isSlicing == m_cachedIsSlicingActivated)
2760 return;
2761
2762 m_cachedIsSlicingActivated = isSlicing;
2763
2764 if (!m_cachedIsSlicingActivated) {
2765 // We need to re-init selection buffer in case there has been a resize
2766 initSelectionBuffer();
2767 initCursorPositionBuffer();
2768 }
2769
2770 updateDepthBuffer(); // Re-init depth buffer as well
2771 m_selectionDirty = true;
2772}
2773
2774void Bars3DRenderer::initShaders(const QString &vertexShader, const QString &fragmentShader)
2775{
2776 if (m_barShader)
2777 delete m_barShader;
2778 m_barShader = new ShaderHelper(this, vertexShader, fragmentShader);
2779 m_barShader->initialize();
2780}
2781
2782void Bars3DRenderer::initGradientShaders(const QString &vertexShader, const QString &fragmentShader)
2783{
2784 if (m_barGradientShader)
2785 delete m_barGradientShader;
2786 m_barGradientShader = new ShaderHelper(this, vertexShader, fragmentShader);
2787 m_barGradientShader->initialize();
2788}
2789
2790void Bars3DRenderer::initSelectionShader()
2791{
2792 if (m_selectionShader)
2793 delete m_selectionShader;
2794 m_selectionShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexPlainColor"),
2795 QStringLiteral(":/shaders/fragmentPlainColor"));
2796 m_selectionShader->initialize();
2797}
2798
2799void Bars3DRenderer::initSelectionBuffer()
2800{
2801 m_textureHelper->deleteTexture(texture: &m_selectionTexture);
2802
2803 if (m_cachedIsSlicingActivated || m_primarySubViewport.size().isEmpty())
2804 return;
2805
2806 m_selectionTexture = m_textureHelper->createSelectionTexture(size: m_primarySubViewport.size(),
2807 frameBuffer&: m_selectionFrameBuffer,
2808 depthBuffer&: m_selectionDepthBuffer);
2809}
2810
2811void Bars3DRenderer::initDepthShader()
2812{
2813 if (!m_isOpenGLES) {
2814 if (m_depthShader)
2815 delete m_depthShader;
2816 m_depthShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexDepth"),
2817 QStringLiteral(":/shaders/fragmentDepth"));
2818 m_depthShader->initialize();
2819 }
2820}
2821
2822void Bars3DRenderer::updateDepthBuffer()
2823{
2824 if (!m_isOpenGLES) {
2825 m_textureHelper->deleteTexture(texture: &m_depthTexture);
2826
2827 if (m_primarySubViewport.size().isEmpty())
2828 return;
2829
2830 if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
2831 m_depthTexture =
2832 m_textureHelper->createDepthTextureFrameBuffer(size: m_primarySubViewport.size(),
2833 frameBuffer&: m_depthFrameBuffer,
2834 textureSize: m_shadowQualityMultiplier);
2835 if (!m_depthTexture)
2836 lowerShadowQuality();
2837 }
2838 }
2839}
2840
2841void Bars3DRenderer::initBackgroundShaders(const QString &vertexShader,
2842 const QString &fragmentShader)
2843{
2844 if (m_backgroundShader)
2845 delete m_backgroundShader;
2846 m_backgroundShader = new ShaderHelper(this, vertexShader, fragmentShader);
2847 m_backgroundShader->initialize();
2848}
2849
2850QVector3D Bars3DRenderer::convertPositionToTranslation(const QVector3D &position, bool isAbsolute)
2851{
2852 float xTrans = 0.0f;
2853 float yTrans = 0.0f;
2854 float zTrans = 0.0f;
2855 if (!isAbsolute) {
2856 // Convert row and column to translation on graph
2857 xTrans = (((position.x() - m_axisCacheX.min() + 0.5f) * m_cachedBarSpacing.width())
2858 - m_rowWidth) / m_scaleFactor;
2859 zTrans = (m_columnDepth - ((position.z() - m_axisCacheZ.min() + 0.5f)
2860 * m_cachedBarSpacing.height())) / m_scaleFactor;
2861 yTrans = m_axisCacheY.positionAt(value: position.y());
2862 } else {
2863 xTrans = position.x() * m_xScaleFactor;
2864 yTrans = position.y() + m_backgroundAdjustment;
2865 zTrans = position.z() * -m_zScaleFactor;
2866 }
2867 return QVector3D(xTrans, yTrans, zTrans);
2868}
2869
2870void Bars3DRenderer::updateAspectRatio(float ratio)
2871{
2872 Q_UNUSED(ratio);
2873}
2874
2875void Bars3DRenderer::updateFloorLevel(float level)
2876{
2877 foreach (SeriesRenderCache *cache, m_renderCacheList)
2878 cache->setDataDirty(true);
2879 m_floorLevel = level;
2880 calculateHeightAdjustment();
2881}
2882
2883void Bars3DRenderer::updateMargin(float margin)
2884{
2885 Abstract3DRenderer::updateMargin(margin);
2886 calculateSceneScalingFactors();
2887}
2888
2889QT_END_NAMESPACE
2890

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