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

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