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