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

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