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

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