Revision control
++ // Copyright (C) 2016 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
++
++ #undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
++
++ #include "bars3drenderer_p.h"
++ #include "q3dcamera_p.h"
++ #include "shaderhelper_p.h"
++ #include "texturehelper_p.h"
++ #include "utils_p.h"
++ #include "barseriesrendercache_p.h"
++
++ #include <QtGui/qquaternion.h>
++
++ #include <QtCore/qmath.h>
++
++ // You can verify that depth buffer drawing works correctly by uncommenting this.
++ // You should see the scene from where the light is
++ //#define SHOW_DEPTH_TEXTURE_SCENE
++
++ QT_BEGIN_NAMESPACE
++
++ const bool sliceGridLabels = true;
++
++ Bars3DRenderer::Bars3DRenderer(Bars3DController *controller)
++ : Abstract3DRenderer(controller),
++ m_cachedIsSlicingActivated(false),
++ m_cachedRowCount(0),
++ m_cachedColumnCount(0),
++ m_cachedBarSeriesMargin(0.0f, 0.0f),
++ m_selectedBar(0),
++ m_sliceCache(0),
++ m_sliceTitleItem(0),
++ m_updateLabels(false),
++ m_barShader(0),
++ m_barGradientShader(0),
++ m_depthShader(0),
++ m_selectionShader(0),
++ m_backgroundShader(0),
++ m_bgrTexture(0),
++ m_selectionTexture(0),
++ m_depthFrameBuffer(0),
++ m_selectionFrameBuffer(0),
++ m_selectionDepthBuffer(0),
++ m_shadowQualityToShader(100.0f),
++ m_shadowQualityMultiplier(3),
++ m_heightNormalizer(1.0f),
++ m_backgroundAdjustment(0.0f),
++ m_rowWidth(0),
++ m_columnDepth(0),
++ m_maxDimension(0),
++ m_scaleX(0),
++ m_scaleZ(0),
++ m_scaleFactor(0),
++ m_maxSceneSize(40.0f),
++ m_visualSelectedBarPos(Bars3DController::invalidSelectionPosition()),
++ m_selectedBarPos(Bars3DController::invalidSelectionPosition()),
++ m_selectedSeriesCache(0),
++ m_noZeroInRange(false),
++ m_seriesScaleX(0.0f),
++ m_seriesScaleZ(0.0f),
++ m_seriesStep(0.0f),
++ m_seriesStart(0.0f),
++ m_clickedPosition(Bars3DController::invalidSelectionPosition()),
++ m_keepSeriesUniform(false),
++ m_haveUniformColorSeries(false),
++ m_haveGradientSeries(false),
++ m_zeroPosition(0.0f),
++ m_xScaleFactor(1.0f),
++ m_zScaleFactor(1.0f),
++ m_floorLevel(0.0f),
++ m_actualFloorLevel(0.0f)
++ {
++ m_axisCacheY.setScale(2.0f);
++ m_axisCacheY.setTranslate(-1.0f);
++
++ initializeOpenGL();
++ }
++
++ Bars3DRenderer::~Bars3DRenderer()
++ {
++ contextCleanup();
++ delete m_barShader;
++ delete m_barGradientShader;
++ delete m_depthShader;
++ delete m_selectionShader;
++ delete m_backgroundShader;
++ }
++
++ void Bars3DRenderer::contextCleanup()
++ {
++ if (QOpenGLContext::currentContext()) {
++ m_textureHelper->glDeleteFramebuffers(1, &m_selectionFrameBuffer);
++ m_textureHelper->glDeleteRenderbuffers(1, &m_selectionDepthBuffer);
++ m_textureHelper->deleteTexture(&m_selectionTexture);
++ m_textureHelper->glDeleteFramebuffers(1, &m_depthFrameBuffer);
++ m_textureHelper->deleteTexture(&m_bgrTexture);
++ }
++ }
++
++ void Bars3DRenderer::initializeOpenGL()
++ {
++ Abstract3DRenderer::initializeOpenGL();
++
++ // Initialize shaders
++
++ // Init depth shader (for shadows). Init in any case, easier to handle shadow activation if done via api.
++ initDepthShader();
++
++ // Init selection shader
++ initSelectionShader();
++
++ // Load grid line mesh
++ loadGridLineMesh();
++
++ // Load background mesh (we need to be initialized first)
++ loadBackgroundMesh();
++ }
++
++ void Bars3DRenderer::fixCameraTarget(QVector3D &target)
++ {
++ target.setX(target.x() * m_xScaleFactor);
++ target.setY(0.0f);
++ target.setZ(target.z() * -m_zScaleFactor);
++ }
++
++ void Bars3DRenderer::getVisibleItemBounds(QVector3D &minBounds, QVector3D &maxBounds)
++ {
++ // The inputs are the item bounds in OpenGL coordinates.
++ // The outputs limit these bounds to visible ranges, normalized to range [-1, 1]
++ // Volume shader flips the Y and Z axes, so we need to set negatives of actual values to those
++ float itemRangeX = (maxBounds.x() - minBounds.x());
++ float itemRangeY = (maxBounds.y() - minBounds.y());
++ float itemRangeZ = (maxBounds.z() - minBounds.z());
++
++ if (minBounds.x() < -m_xScaleFactor)
++ minBounds.setX(-1.0f + (2.0f * qAbs(minBounds.x() + m_xScaleFactor) / itemRangeX));
++ else
++ minBounds.setX(-1.0f);
++
++ if (minBounds.y() < -1.0f + m_backgroundAdjustment)
++ minBounds.setY(-(-1.0f + (2.0f * qAbs(minBounds.y() + 1.0f - m_backgroundAdjustment) / itemRangeY)));
++ else
++ minBounds.setY(1.0f);
++
++ if (minBounds.z() < -m_zScaleFactor)
++ minBounds.setZ(-(-1.0f + (2.0f * qAbs(minBounds.z() + m_zScaleFactor) / itemRangeZ)));
++ else
++ minBounds.setZ(1.0f);
++
++ if (maxBounds.x() > m_xScaleFactor)
++ maxBounds.setX(1.0f - (2.0f * qAbs(maxBounds.x() - m_xScaleFactor) / itemRangeX));
++ else
++ maxBounds.setX(1.0f);
++
++ if (maxBounds.y() > 1.0f + m_backgroundAdjustment)
++ maxBounds.setY(-(1.0f - (2.0f * qAbs(maxBounds.y() - 1.0f - m_backgroundAdjustment) / itemRangeY)));
++ else
++ maxBounds.setY(-1.0f);
++
++ if (maxBounds.z() > m_zScaleFactor)
++ maxBounds.setZ(-(1.0f - (2.0f * qAbs(maxBounds.z() - m_zScaleFactor) / itemRangeZ)));
++ else
++ maxBounds.setZ(-1.0f);
++ }
++
++ void Bars3DRenderer::updateData()
++ {
++ int minRow = m_axisCacheZ.min();
++ int maxRow = m_axisCacheZ.max();
++ int minCol = m_axisCacheX.min();
++ int maxCol = m_axisCacheX.max();
++ int newRows = maxRow - minRow + 1;
++ int newColumns = maxCol - minCol + 1;
++ int dataRowCount = 0;
++ int maxDataRowCount = 0;
++
++ m_seriesScaleX = 1.0f / float(m_visibleSeriesCount);
++ m_seriesStep = 1.0f / float(m_visibleSeriesCount);
++ m_seriesStart = -((float(m_visibleSeriesCount) - 1.0f) / 2.0f)
++ * (m_seriesStep - (m_seriesStep * m_cachedBarSeriesMargin.width()));
++
++ if (m_keepSeriesUniform)
++ m_seriesScaleZ = m_seriesScaleX;
++ else
++ m_seriesScaleZ = 1.0f;
++
++ if (m_cachedRowCount != newRows || m_cachedColumnCount != newColumns) {
++ // Force update for selection related items
++ m_sliceCache = 0;
++ m_sliceTitleItem = 0;
++
++ m_cachedColumnCount = newColumns;
++ m_cachedRowCount = newRows;
++ // Calculate max scene size
++ GLfloat sceneRatio = qMin(GLfloat(newColumns) / GLfloat(newRows),
++ GLfloat(newRows) / GLfloat(newColumns));
++ m_maxSceneSize = 2.0f * qSqrt(sceneRatio * newColumns * newRows);
++ }
++
++ calculateSceneScalingFactors();
++
++ m_zeroPosition = m_axisCacheY.formatter()->positionAt(m_actualFloorLevel);
++
++ for (auto baseCache: m_renderCacheList) {
++ BarSeriesRenderCache *cache = static_cast<BarSeriesRenderCache *>(baseCache);
++ if (cache->isVisible()) {
++ const QBar3DSeries *currentSeries = cache->series();
++ BarRenderItemArray &renderArray = cache->renderArray();
++ bool dimensionsChanged = false;
++ if (newRows != renderArray.size()
++ || newColumns != renderArray.at(0).size()) {
++ // Destroy old render items and reallocate new array
++ dimensionsChanged = true;
++ renderArray.resize(newRows);
++ for (int i = 0; i < newRows; i++)
++ renderArray[i].resize(newColumns);
++ cache->sliceArray().clear();
++ }
++
++ if (cache->dataDirty() || dimensionsChanged) {
++ QBarDataProxy *dataProxy = currentSeries->dataProxy();
++ dataRowCount = dataProxy->rowCount();
++ if (maxDataRowCount < dataRowCount)
++ maxDataRowCount = qMin(dataRowCount, newRows);
++ int dataRowIndex = minRow;
++ for (int i = 0; i < newRows; i++) {
++ BarRenderItemRow &renderRow = renderArray[i];
++ const QBarDataRow *dataRow = 0;
++ if (dataRowIndex < dataRowCount)
++ dataRow = dataProxy->rowAt(dataRowIndex);
++ updateRenderRow(dataRow, renderRow);
++ dataRowIndex++;
++ }
++ cache->setDataDirty(false);
++ }
++ }
++ }
++
++ // Reset selected bar to update selection
++ updateSelectedBar(m_selectedBarPos,
++ m_selectedSeriesCache ? m_selectedSeriesCache->series() : 0);
++ }
++
++ void Bars3DRenderer::updateRenderRow(const QBarDataRow *dataRow, BarRenderItemRow &renderRow)
++ {
++ int j = 0;
++ int renderRowSize = renderRow.size();
++ int startIndex = m_axisCacheX.min();
++
++ if (dataRow) {
++ int updateSize = qMin((dataRow->size() - startIndex), renderRowSize);
++ int dataColIndex = startIndex;
++ for (; j < updateSize ; j++) {
++ updateRenderItem(dataRow->at(dataColIndex), renderRow[j]);
++ dataColIndex++;
++ }
++ }
++ for (; j < renderRowSize; j++) {
++ renderRow[j].setValue(0.0f);
++ renderRow[j].setHeight(0.0f);
++ renderRow[j].setRotation(identityQuaternion);
++ }
++ }
++
++ void Bars3DRenderer::updateRenderItem(const QBarDataItem &dataItem, BarRenderItem &renderItem)
++ {
++ float value = dataItem.value();
++ float heightValue = m_axisCacheY.formatter()->positionAt(value);
++ if (m_noZeroInRange) {
++ if (m_hasNegativeValues) {
++ heightValue = -1.0f + heightValue;
++ if (heightValue > 0.0f)
++ heightValue = 0.0f;
++ } else {
++ if (heightValue < 0.0f)
++ heightValue = 0.0f;
++ }
++ } else {
++ heightValue -= m_zeroPosition;
++ }
++ if (m_axisCacheY.reversed())
++ heightValue = -heightValue;
++
++ renderItem.setValue(value);
++ renderItem.setHeight(heightValue);
++
++ float angle = dataItem.rotation();
++ if (angle) {
++ renderItem.setRotation(
++ QQuaternion::fromAxisAndAngle(
++ upVector, angle));
++ } else {
++ renderItem.setRotation(identityQuaternion);
++ }
++ }
++
++ void Bars3DRenderer::updateSeries(const QList<QAbstract3DSeries *> &seriesList)
++ {
++ Abstract3DRenderer::updateSeries(seriesList);
++
++ bool noSelection = true;
++ int seriesCount = seriesList.size();
++ int visualIndex = 0;
++ m_haveUniformColorSeries = false;
++ m_haveGradientSeries = false;
++ for (int i = 0; i < seriesCount; i++) {
++ QBar3DSeries *barSeries = static_cast<QBar3DSeries *>(seriesList[i]);
++ BarSeriesRenderCache *cache =
++ static_cast<BarSeriesRenderCache *>(m_renderCacheList.value(barSeries));
++ if (barSeries->isVisible()) {
++ if (noSelection
++ && barSeries->selectedBar() != QBar3DSeries::invalidSelectionPosition()) {
++ if (selectionLabel() != cache->itemLabel())
++ m_selectionLabelDirty = true;
++ noSelection = false;
++ }
++ cache->setVisualIndex(visualIndex++);
++ if (cache->colorStyle() == Q3DTheme::ColorStyleUniform)
++ m_haveUniformColorSeries = true;
++ else
++ m_haveGradientSeries = true;
++ } else {
++ cache->setVisualIndex(-1);
++ }
++
++ }
++ if (noSelection) {
++ if (!selectionLabel().isEmpty())
++ m_selectionLabelDirty = true;
++ m_selectedSeriesCache = 0;
++ }
++ }
++
++ SeriesRenderCache *Bars3DRenderer::createNewCache(QAbstract3DSeries *series)
++ {
++ return new BarSeriesRenderCache(series, this);
++ }
++
++ void Bars3DRenderer::updateRows(const QList<Bars3DController::ChangeRow> &rows)
++ {
++ int minRow = m_axisCacheZ.min();
++ int maxRow = m_axisCacheZ.max();
++ BarSeriesRenderCache *cache = 0;
++ const QBar3DSeries *prevSeries = 0;
++ const QBarDataArray *dataArray = 0;
++
++ for (auto item: rows) {
++ const int row = item.row;
++ if (row < minRow || row > maxRow)
++ continue;
++ QBar3DSeries *currentSeries = item.series;
++ if (currentSeries != prevSeries) {
++ cache = static_cast<BarSeriesRenderCache *>(m_renderCacheList.value(currentSeries));
++ prevSeries = currentSeries;
++ dataArray = item.series->dataProxy()->array();
++ // Invisible series render caches are not updated, but instead just marked dirty, so that
++ // they can be completely recalculated when they are turned visible.
++ if (!cache->isVisible() && !cache->dataDirty())
++ cache->setDataDirty(true);
++ }
++ if (cache->isVisible()) {
++ updateRenderRow(dataArray->at(row), cache->renderArray()[row - minRow]);
++ if (m_cachedIsSlicingActivated
++ && cache == m_selectedSeriesCache
++ && m_selectedBarPos.x() == row) {
++ m_selectionDirty = true; // Need to update slice view
++ }
++ }
++ }
++ }
++
++ void Bars3DRenderer::updateItems(const QList<Bars3DController::ChangeItem> &items)
++ {
++ int minRow = m_axisCacheZ.min();
++ int maxRow = m_axisCacheZ.max();
++ int minCol = m_axisCacheX.min();
++ int maxCol = m_axisCacheX.max();
++ BarSeriesRenderCache *cache = 0;
++ const QBar3DSeries *prevSeries = 0;
++ const QBarDataArray *dataArray = 0;
++
++ for (auto item: items) {
++ const int row = item.point.x();
++ const int col = item.point.y();
++ if (row < minRow || row > maxRow || col < minCol || col > maxCol)
++ continue;
++ QBar3DSeries *currentSeries = item.series;
++ if (currentSeries != prevSeries) {
++ cache = static_cast<BarSeriesRenderCache *>(m_renderCacheList.value(currentSeries));
++ prevSeries = currentSeries;
++ dataArray = item.series->dataProxy()->array();
++ // Invisible series render caches are not updated, but instead just marked dirty, so that
++ // they can be completely recalculated when they are turned visible.
++ if (!cache->isVisible() && !cache->dataDirty())
++ cache->setDataDirty(true);
++ }
++ if (cache->isVisible()) {
++ updateRenderItem(dataArray->at(row)->at(col),
++ cache->renderArray()[row - minRow][col - minCol]);
++ if (m_cachedIsSlicingActivated
++ && cache == m_selectedSeriesCache
++ && m_selectedBarPos == QPoint(row, col)) {
++ m_selectionDirty = true; // Need to update slice view
++ }
++ }
++ }
++ }
++
++ void Bars3DRenderer::updateScene(Q3DScene *scene)
++ {
++ if (!m_noZeroInRange) {
++ scene->activeCamera()->d_ptr->setMinYRotation(-90.0);
++ scene->activeCamera()->d_ptr->setMaxYRotation(90.0);
++ } else {
++ if ((m_hasNegativeValues && !m_axisCacheY.reversed())
++ || (!m_hasNegativeValues && m_axisCacheY.reversed())) {
++ scene->activeCamera()->d_ptr->setMinYRotation(-90.0f);
++ scene->activeCamera()->d_ptr->setMaxYRotation(0.0);
++ } else {
++ scene->activeCamera()->d_ptr->setMinYRotation(0.0f);
++ scene->activeCamera()->d_ptr->setMaxYRotation(90.0);
++ }
++ }
++
++ Abstract3DRenderer::updateScene(scene);
++
++ updateSlicingActive(scene->isSlicingActive());
++ }
++
++ void Bars3DRenderer::render(GLuint defaultFboHandle)
++ {
++ // Handle GL state setup for FBO buffers and clearing of the render surface
++ Abstract3DRenderer::render(defaultFboHandle);
++
++ if (m_axisCacheY.positionsDirty())
++ m_axisCacheY.updateAllPositions();
++
++ drawScene(defaultFboHandle);
++ if (m_cachedIsSlicingActivated)
++ drawSlicedScene();
++ }
++
++ void Bars3DRenderer::drawSlicedScene()
++ {
++ if (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionRow)
++ == m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionColumn)) {
++ qWarning("Invalid selection mode. Either QAbstract3DGraph::SelectionRow or"
++ " QAbstract3DGraph::SelectionColumn must be set before calling"
++ " setSlicingActive(true).");
++ return;
++ }
++
++ GLfloat barPosX = 0;
++ QVector3D lightPos;
++ QVector4D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor());
++
++ // Specify viewport
++ glViewport(m_secondarySubViewport.x(),
++ m_secondarySubViewport.y(),
++ m_secondarySubViewport.width(),
++ m_secondarySubViewport.height());
++
++ // Set up projection matrix
++ QMatrix4x4 projectionMatrix;
++ GLfloat viewPortRatio = (GLfloat)m_primarySubViewport.width()
++ / (GLfloat)m_primarySubViewport.height();
++ if (m_useOrthoProjection) {
++ GLfloat orthoRatio = 2.0f / m_autoScaleAdjustment;
++ projectionMatrix.ortho(-viewPortRatio * orthoRatio, viewPortRatio * orthoRatio,
++ -orthoRatio, orthoRatio,
++ 0.0f, 100.0f);
++ } else {
++ projectionMatrix.perspective(35.0f, viewPortRatio, 0.1f, 100.0f);
++ }
++
++ // Set view matrix
++ QMatrix4x4 viewMatrix;
++
++ // Adjust scaling (zoom rate based on aspect ratio)
++ GLfloat camZPosSliced = cameraDistance / m_autoScaleAdjustment;
++
++ viewMatrix.lookAt(QVector3D(0.0f, 0.0f, camZPosSliced), zeroVector, upVector);
++
++ // Set light position
++ lightPos = QVector3D(0.0f, 0.0f, camZPosSliced * 2.0f);
++
++ const Q3DCamera *activeCamera = m_cachedScene->activeCamera();
++
++ // Draw the selected row / column
++ QMatrix4x4 projectionViewMatrix = projectionMatrix * viewMatrix;
++ bool rowMode = m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionRow);
++ bool itemMode = m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionItem);
++
++ GLfloat barPosYAdjustment = -0.8f; // Translate to -1.0 + 0.2 for row/column labels
++ GLfloat gridAdjustment = 1.0f + barPosYAdjustment - m_backgroundAdjustment;
++ GLfloat scaleFactor = 0.0f;
++ if (rowMode)
++ scaleFactor = (1.1f * m_rowWidth) / m_scaleFactor;
++ else
++ scaleFactor = (1.1f * m_columnDepth) / m_scaleFactor;
++ GLfloat barLabelYPos = barPosYAdjustment - labelMargin;
++ GLfloat zeroPosAdjustment = 0.0f;
++ GLfloat directionMultiplier = 2.0f;
++ GLfloat directionBase = 0.0f;
++ if (m_axisCacheY.reversed()) {
++ directionMultiplier = -2.0f;
++ directionBase = -2.0f;
++ }
++ zeroPosAdjustment = directionBase +
++ directionMultiplier * m_axisCacheY.min() / m_heightNormalizer;
++ zeroPosAdjustment = qBound(-2.0f, zeroPosAdjustment, 0.0f);
++
++ // Draw grid lines
++ if (m_cachedTheme->isGridEnabled()) {
++ glDisable(GL_DEPTH_TEST);
++ ShaderHelper *lineShader;
++ if (m_isOpenGLES)
++ lineShader = m_selectionShader; // Plain color shader for GL_LINES
++ else
++ lineShader = m_backgroundShader;
++
++ // Bind line shader
++ lineShader->bind();
++
++ // Set unchanging shader bindings
++ QVector4D lineColor = Utils::vectorFromColor(m_cachedTheme->gridLineColor());
++ lineShader->setUniformValue(lineShader->lightP(), lightPos);
++ lineShader->setUniformValue(lineShader->view(), viewMatrix);
++ lineShader->setUniformValue(lineShader->color(), lineColor);
++ lineShader->setUniformValue(lineShader->ambientS(),
++ m_cachedTheme->ambientLightStrength()
++ + m_cachedTheme->lightStrength() / 7.0f);
++ lineShader->setUniformValue(lineShader->lightS(), 0.0f);
++ lineShader->setUniformValue(lineShader->lightColor(), lightColor);
++
++ // Horizontal lines
++ if (m_axisCacheY.segmentCount() > 0) {
++ int gridLineCount = m_axisCacheY.gridLineCount();
++
++ QVector3D gridLineScale(scaleFactor, gridLineWidth, gridLineWidth);
++ bool noZero = true;
++ QMatrix4x4 MVPMatrix;
++ QMatrix4x4 itModelMatrix;
++
++ for (int line = 0; line < gridLineCount; line++) {
++ QMatrix4x4 modelMatrix;
++ GLfloat gridPos = m_axisCacheY.gridLinePosition(line) + gridAdjustment;
++ modelMatrix.translate(0.0f, gridPos, 0.0f);
++ modelMatrix.scale(gridLineScale);
++ itModelMatrix = modelMatrix;
++ MVPMatrix = projectionViewMatrix * modelMatrix;
++
++ // Set the rest of the shader bindings
++ lineShader->setUniformValue(lineShader->model(), modelMatrix);
++ lineShader->setUniformValue(lineShader->nModel(),
++ itModelMatrix.inverted().transposed());
++ lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
++
++ // Draw the object
++ if (m_isOpenGLES)
++ m_drawer->drawLine(lineShader);
++ else
++ m_drawer->drawObject(lineShader, m_gridLineObj);
++
++ // Check if we have a line at zero position already
++ if (gridPos == (barPosYAdjustment + zeroPosAdjustment))
++ noZero = false;
++ }
++
++ // Draw a line at zero, if none exists
++ if (!m_noZeroInRange && noZero) {
++ QMatrix4x4 modelMatrix;
++ modelMatrix.translate(0.0f, barPosYAdjustment - zeroPosAdjustment, 0.0f);
++ modelMatrix.scale(gridLineScale);
++ itModelMatrix = modelMatrix;
++ MVPMatrix = projectionViewMatrix * modelMatrix;
++
++ // Set the rest of the shader bindings
++ lineShader->setUniformValue(lineShader->model(), modelMatrix);
++ lineShader->setUniformValue(lineShader->nModel(),
++ itModelMatrix.inverted().transposed());
++ lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
++ lineShader->setUniformValue(lineShader->color(),
++ Utils::vectorFromColor(
++ m_cachedTheme->labelTextColor()));
++
++ // Draw the object
++ if (m_isOpenGLES)
++ m_drawer->drawLine(lineShader);
++ else
++ m_drawer->drawObject(lineShader, m_gridLineObj);
++ }
++ }
++
++ if (sliceGridLabels) {
++ // Bind label shader
++ m_labelShader->bind();
++ glCullFace(GL_BACK);
++ glEnable(GL_BLEND);
++ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
++
++ // Draw grid labels
++ int labelNbr = 0;
++ int labelCount = m_axisCacheY.labelCount();
++ QVector3D labelTrans = QVector3D(scaleFactor + labelMargin, 0.0f, 0.0f);
++
++ for (int i = 0; i < labelCount; i++) {
++ if (m_axisCacheY.labelItems().size() > labelNbr) {
++ const LabelItem &axisLabelItem = *m_axisCacheY.labelItems().at(labelNbr);
++ GLfloat gridPos = m_axisCacheY.labelPosition(i) + gridAdjustment;
++ labelTrans.setY(gridPos);
++ m_dummyBarRenderItem.setTranslation(labelTrans);
++ m_drawer->drawLabel(m_dummyBarRenderItem, axisLabelItem, viewMatrix,
++ projectionMatrix, zeroVector, identityQuaternion, 0,
++ m_cachedSelectionMode, m_labelShader, m_labelObj,
++ activeCamera, true, true, Drawer::LabelMid, Qt::AlignLeft);
++ }
++ labelNbr++;
++ }
++ glDisable(GL_BLEND);
++ glEnable(GL_DEPTH_TEST);
++ }
++ }
++
++ // Draw bars
++ QVector3D modelMatrixScaler(m_scaleX * m_seriesScaleX, 0.0f, m_scaleZ * m_seriesScaleZ);
++ if (!rowMode) {
++ modelMatrixScaler.setX(m_scaleZ * m_seriesScaleZ);
++ modelMatrixScaler.setZ(m_scaleX * m_seriesScaleX);
++ }
++
++ // Set common bar shader bindings
++ m_barShader->bind();
++ m_barShader->setUniformValue(m_barShader->lightP(), lightPos);
++ m_barShader->setUniformValue(m_barShader->view(), viewMatrix);
++ m_barShader->setUniformValue(m_barShader->lightS(), 0.15f);
++ m_barShader->setUniformValue(m_barShader->ambientS(),
++ m_cachedTheme->ambientLightStrength()
++ + m_cachedTheme->lightStrength() / 7.0f);
++ m_barShader->setUniformValue(m_barShader->lightColor(), lightColor);
++ m_barGradientShader->bind();
++ m_barGradientShader->setUniformValue(m_barGradientShader->lightP(), lightPos);
++ m_barGradientShader->setUniformValue(m_barGradientShader->view(), viewMatrix);
++ m_barGradientShader->setUniformValue(m_barGradientShader->lightS(), 0.15f);
++ m_barGradientShader->setUniformValue(m_barGradientShader->ambientS(),
++ m_cachedTheme->ambientLightStrength()
++ + m_cachedTheme->lightStrength() / 7.0f);
++ m_barGradientShader->setUniformValue(m_barGradientShader->gradientMin(), 0.0f);
++ m_barGradientShader->setUniformValue(m_barGradientShader->lightColor(), lightColor);
++
++ // Default to uniform shader
++ ShaderHelper *barShader = m_barShader;
++ barShader->bind();
++
++ Q3DTheme::ColorStyle previousColorStyle = Q3DTheme::ColorStyleUniform;
++ Q3DTheme::ColorStyle colorStyle = Q3DTheme::ColorStyleUniform;
++ ObjectHelper *barObj = 0;
++ QVector4D highlightColor;
++ QVector4D baseColor;
++ GLuint highlightGradientTexture = 0;
++ GLuint baseGradientTexture = 0;
++ bool colorStyleIsUniform = true;
++ int firstVisualIndex = m_renderCacheList.size();
++ QList<BarRenderSliceItem> *firstVisualSliceArray = 0;
++ BarRenderSliceItem *selectedItem = 0;
++
++ QQuaternion seriesRotation;
++ for (auto baseCache: m_renderCacheList) {
++ if (baseCache->isVisible()
++ && (baseCache == m_selectedSeriesCache
++ || m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionMultiSeries))) {
++ BarSeriesRenderCache *cache = static_cast<BarSeriesRenderCache *>(baseCache);
++ QList<BarRenderSliceItem> &sliceArray = cache->sliceArray();
++ int sliceCount = sliceArray.size();
++ if (firstVisualIndex > cache->visualIndex()) {
++ firstVisualIndex = cache->visualIndex();
++ firstVisualSliceArray = &sliceArray;
++ }
++
++ barObj = cache->object();
++ colorStyle = cache->colorStyle();
++ colorStyleIsUniform = (colorStyle == Q3DTheme::ColorStyleUniform);
++ if (colorStyleIsUniform) {
++ highlightColor = cache->singleHighlightColor();
++ baseColor = cache->baseColor();
++ } else {
++ highlightGradientTexture = cache->singleHighlightGradientTexture();
++ baseGradientTexture = cache->baseGradientTexture();
++ }
++
++ // Rebind shader if it has changed
++ if (colorStyleIsUniform != (previousColorStyle == Q3DTheme::ColorStyleUniform)) {
++ if (colorStyleIsUniform)
++ barShader = m_barShader;
++ else
++ barShader = m_barGradientShader;
++ barShader->bind();
++ }
++
++ if (!colorStyleIsUniform && (previousColorStyle != colorStyle)
++ && (colorStyle == Q3DTheme::ColorStyleObjectGradient)) {
++ m_barGradientShader->setUniformValue(m_barGradientShader->gradientHeight(), 0.5f);
++ }
++
++ previousColorStyle = colorStyle;
++ seriesRotation = cache->meshRotation();
++ bool selectedSeries = (cache == m_selectedSeriesCache);
++
++ for (int bar = 0; bar < sliceCount; bar++) {
++ BarRenderSliceItem &item = cache->sliceArray()[bar];
++ if (selectedSeries && itemMode && sliceGridLabels
++ && m_visualSelectedBarPos.x() == item.position().x()
++ && m_visualSelectedBarPos.y() == item.position().y()) {
++ selectedItem = &item;
++ }
++ if (!item.value())
++ continue;
++
++ if (item.height() < 0)
++ glCullFace(GL_FRONT);
++ else
++ glCullFace(GL_BACK);
++
++ QMatrix4x4 MVPMatrix;
++ QMatrix4x4 modelMatrix;
++ QMatrix4x4 itModelMatrix;
++ QQuaternion barRotation = item.rotation();
++ GLfloat barPosY = item.translation().y() + barPosYAdjustment - zeroPosAdjustment;
++
++ if (rowMode) {
++ barPosX = item.translation().x();
++ } else {
++ barPosX = -(item.translation().z()); // flip z; frontmost bar to the left
++ barRotation *= m_yRightAngleRotation;
++ }
++
++ modelMatrix.translate(barPosX, barPosY, 0.0f);
++ modelMatrixScaler.setY(item.height());
++
++ if (!seriesRotation.isIdentity())
++ barRotation *= seriesRotation;
++
++ if (!barRotation.isIdentity()) {
++ modelMatrix.rotate(barRotation);
++ itModelMatrix.rotate(barRotation);
++ }
++
++ modelMatrix.scale(modelMatrixScaler);
++ itModelMatrix.scale(modelMatrixScaler);
++
++ MVPMatrix = projectionViewMatrix * modelMatrix;
++
++ QVector4D barColor;
++ GLuint gradientTexture = 0;
++
++ if (itemMode && m_visualSelectedBarPos.x() == item.position().x()
++ && m_visualSelectedBarPos.y() == item.position().y()) {
++ if (colorStyleIsUniform)
++ barColor = highlightColor;
++ else
++ gradientTexture = highlightGradientTexture;
++ } else {
++ if (colorStyleIsUniform)
++ barColor = baseColor;
++ else
++ gradientTexture = baseGradientTexture;
++ }
++
++ if (item.height() != 0) {
++ // Set shader bindings
++ barShader->setUniformValue(barShader->model(), modelMatrix);
++ barShader->setUniformValue(barShader->nModel(),
++ itModelMatrix.inverted().transposed());
++ barShader->setUniformValue(barShader->MVP(), MVPMatrix);
++ if (colorStyleIsUniform) {
++ barShader->setUniformValue(barShader->color(), barColor);
++ } else if (colorStyle == Q3DTheme::ColorStyleRangeGradient) {
++ barShader->setUniformValue(barShader->gradientHeight(),
++ (qAbs(item.height()) / m_gradientFraction));
++ }
++
++ // Draw the object
++ m_drawer->drawObject(barShader,
++ barObj,
++ gradientTexture);
++ }
++ }
++ }
++ }
++
++ // Draw labels
++ m_labelShader->bind();
++ glDisable(GL_DEPTH_TEST);
++ glCullFace(GL_BACK);
++ glEnable(GL_BLEND);
++ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
++
++ const LabelItem &sliceSelectionLabel = *m_sliceTitleItem;
++ QVector3D positionComp(0.0f, m_autoScaleAdjustment, 0.0f);
++
++ // Draw labels for bars
++ QVector3D sliceValueRotation(0.0f, 0.0f, 90.0f);
++ QVector3D sliceLabelRotation(0.0f, 0.0f, -45.0f);
++ QQuaternion totalSliceValueRotation = Utils::calculateRotation(sliceValueRotation);
++ QQuaternion totalSliceLabelRotation = Utils::calculateRotation(sliceLabelRotation);
++
++ int labelCount = m_sliceCache->labelItems().size();
++
++ for (int labelNo = 0; labelNo < labelCount; labelNo++) {
++ // Check for invalid usage (no selection when setting slicing active)
++ if (!firstVisualSliceArray) {
++ qWarning("No slice data found. Make sure there is a valid selection.");
++ continue;
++ }
++
++ // Get labels from first series only
++ const BarRenderSliceItem &item = firstVisualSliceArray->at(labelNo);
++ m_dummyBarRenderItem.setTranslation(QVector3D(item.translation().x(),
++ barLabelYPos,
++ item.translation().z()));
++
++ // Draw labels
++ m_drawer->drawLabel(m_dummyBarRenderItem, *m_sliceCache->labelItems().at(labelNo),
++ viewMatrix, projectionMatrix, positionComp, totalSliceLabelRotation,
++ 0, m_cachedSelectionMode, m_labelShader,
++ m_labelObj, activeCamera, false, false, Drawer::LabelMid,
++ Qt::AlignLeft | Qt::AlignTop, true);
++ }
++
++ if (!sliceGridLabels) {
++ for (auto baseCache: m_renderCacheList) {
++ if (baseCache->isVisible()) {
++ BarSeriesRenderCache *cache = static_cast<BarSeriesRenderCache *>(baseCache);
++ QList<BarRenderSliceItem> &sliceArray = cache->sliceArray();
++ int sliceCount = sliceArray.size();
++ for (int col = 0; col < sliceCount; col++) {
++ BarRenderSliceItem &item = sliceArray[col];
++
++ // Draw values
++ if (item.height() != 0.0f || (!m_noZeroInRange && item.value() == 0.0f)) {
++ // Create label texture if we need it
++ if (item.sliceLabel().isNull() || m_updateLabels) {
++ QString valueLabelText = m_axisCacheY.formatter()->stringForValue(
++ qreal(item.value()), m_axisCacheY.labelFormat());
++ item.setSliceLabel(valueLabelText);
++ m_drawer->generateLabelItem(item.sliceLabelItem(), item.sliceLabel());
++ m_updateLabels = false;
++ }
++ Qt::AlignmentFlag alignment =
++ (item.height() > 0) ? Qt::AlignLeft : Qt::AlignRight;
++ Drawer::LabelPosition labelPos =
++ (item.height() < 0) ? Drawer::LabelBelow : Drawer::LabelOver;
++ m_dummyBarRenderItem.setTranslation(QVector3D(item.translation().x(),
++ barPosYAdjustment
++ - zeroPosAdjustment
++ + item.height(),
++ item.translation().z()));
++
++ m_drawer->drawLabel(m_dummyBarRenderItem, item.sliceLabelItem(), viewMatrix,
++ projectionMatrix, zeroVector, totalSliceValueRotation,
++ item.height(), m_cachedSelectionMode, m_labelShader,
++ m_labelObj, activeCamera, false, false, labelPos,
++ alignment, true);
++ }
++ }
++ }
++ }
++ } else if (selectedItem) {
++ // Only draw value for selected item when grid labels are on
++ // Create label texture if we need it
++ if (selectedItem->sliceLabel().isNull() || m_updateLabels) {
++ QString valueLabelText = m_axisCacheY.formatter()->stringForValue(
++ qreal(selectedItem->value()), m_axisCacheY.labelFormat());
++ selectedItem->setSliceLabel(valueLabelText);
++ m_drawer->generateLabelItem(selectedItem->sliceLabelItem(), selectedItem->sliceLabel());
++ m_updateLabels = false;
++ }
++ Qt::AlignmentFlag alignment = (selectedItem->height() > 0) ? Qt::AlignLeft : Qt::AlignRight;
++ Drawer::LabelPosition labelPos =
++ (selectedItem->height() < 0) ? Drawer::LabelBelow : Drawer::LabelOver;
++ m_dummyBarRenderItem.setTranslation(QVector3D(selectedItem->translation().x(),
++ barPosYAdjustment - zeroPosAdjustment
++ + selectedItem->height(),
++ selectedItem->translation().z()));
++
++ m_drawer->drawLabel(m_dummyBarRenderItem, selectedItem->sliceLabelItem(), viewMatrix,
++ projectionMatrix, zeroVector, totalSliceValueRotation,
++ selectedItem->height(), m_cachedSelectionMode, m_labelShader,
++ m_labelObj, activeCamera, false, false, labelPos,
++ alignment, true);
++ }
++
++ // Draw labels for axes
++ if (rowMode) {
++ if (m_sliceTitleItem) {
++ m_drawer->drawLabel(m_dummyBarRenderItem, sliceSelectionLabel, viewMatrix, projectionMatrix,
++ positionComp, identityQuaternion, 0, m_cachedSelectionMode,
++ m_labelShader, m_labelObj, activeCamera, false, false,
++ Drawer::LabelTop, Qt::AlignCenter, true);
++ }
++ m_drawer->drawLabel(m_dummyBarRenderItem, m_axisCacheX.titleItem(), viewMatrix, projectionMatrix,
++ positionComp, identityQuaternion, 0, m_cachedSelectionMode,
++ m_labelShader, m_labelObj, activeCamera, false, false,
++ Drawer::LabelBottom, Qt::AlignCenter, true);
++ } else {
++ m_drawer->drawLabel(m_dummyBarRenderItem, m_axisCacheZ.titleItem(), viewMatrix, projectionMatrix,
++ positionComp, identityQuaternion, 0, m_cachedSelectionMode,
++ m_labelShader,
++ m_labelObj, activeCamera, false, false, Drawer::LabelBottom,
++ Qt::AlignCenter, true);
++ if (m_sliceTitleItem) {
++ m_drawer->drawLabel(m_dummyBarRenderItem, sliceSelectionLabel, viewMatrix, projectionMatrix,
++ positionComp, identityQuaternion, 0, m_cachedSelectionMode,
++ m_labelShader,
++ m_labelObj, activeCamera, false, false, Drawer::LabelTop,
++ Qt::AlignCenter, true);
++ }
++ }
++ // Y-axis label
++ QVector3D labelTrans = QVector3D(-scaleFactor - labelMargin, 0.2f, 0.0f); // y = 0.2 for row/column labels (see barPosYAdjustment)
++ m_dummyBarRenderItem.setTranslation(labelTrans);
++ m_drawer->drawLabel(m_dummyBarRenderItem, m_axisCacheY.titleItem(), viewMatrix,
++ projectionMatrix, zeroVector, totalSliceValueRotation, 0,
++ m_cachedSelectionMode, m_labelShader, m_labelObj, activeCamera,
++ false, false, Drawer::LabelMid, Qt::AlignBottom);
++
++ glDisable(GL_BLEND);
++ glEnable(GL_DEPTH_TEST);
++
++ // Release shader
++ glUseProgram(0);
++ }
++
++ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
++ {
++ GLint startBar = 0;
++ GLint stopBar = 0;
++ GLint stepBar = 0;
++
++ GLint startRow = 0;
++ GLint stopRow = 0;
++ GLint stepRow = 0;
++
++ GLfloat backgroundRotation = 0;
++
++ GLfloat colPos = 0;
++ GLfloat rowPos = 0;
++
++ const Q3DCamera *activeCamera = m_cachedScene->activeCamera();
++
++ glViewport(m_primarySubViewport.x(),
++ m_primarySubViewport.y(),
++ m_primarySubViewport.width(),
++ m_primarySubViewport.height());
++
++ // Set up projection matrix
++ QMatrix4x4 projectionMatrix;
++ GLfloat viewPortRatio = (GLfloat)m_primarySubViewport.width()
++ / (GLfloat)m_primarySubViewport.height();
++ if (m_useOrthoProjection) {
++ GLfloat orthoRatio = 2.0f;
++ projectionMatrix.ortho(-viewPortRatio * orthoRatio, viewPortRatio * orthoRatio,
++ -orthoRatio, orthoRatio,
++ 0.0f, 100.0f);
++ } else {
++ projectionMatrix.perspective(45.0f, viewPortRatio, 0.1f, 100.0f);
++ }
++
++ // Get the view matrix
++ QMatrix4x4 viewMatrix = activeCamera->d_ptr->viewMatrix();
++
++ // Calculate drawing order
++ // Draw order is reversed to optimize amount of drawing (ie. draw front objects first,
++ // depth test handles not needing to draw objects behind them)
++ if (viewMatrix.row(0).x() > 0) {
++ startRow = 0;
++ stopRow = m_cachedRowCount;
++ stepRow = 1;
++ m_zFlipped = false;
++ } else {
++ startRow = m_cachedRowCount - 1;
++ stopRow = -1;
++ stepRow = -1;
++ m_zFlipped = true;
++ }
++ if (viewMatrix.row(0).z() <= 0) {
++ startBar = 0;
++ stopBar = m_cachedColumnCount;
++ stepBar = 1;
++ m_xFlipped = false;
++ } else {
++ startBar = m_cachedColumnCount - 1;
++ stopBar = -1;
++ stepBar = -1;
++ m_xFlipped = true;
++ }
++
++ // Check if we're viewing the scene from below
++ if (viewMatrix.row(2).y() < 0)
++ m_yFlipped = true;
++ else
++ m_yFlipped = false;
++
++ // calculate background rotation based on view matrix rotation
++ if (viewMatrix.row(0).x() > 0 && viewMatrix.row(0).z() <= 0)
++ backgroundRotation = 270.0f;
++ else if (viewMatrix.row(0).x() > 0 && viewMatrix.row(0).z() > 0)
++ backgroundRotation = 180.0f;
++ else if (viewMatrix.row(0).x() <= 0 && viewMatrix.row(0).z() > 0)
++ backgroundRotation = 90.0f;
++ else if (viewMatrix.row(0).x() <= 0 && viewMatrix.row(0).z() <= 0)
++ backgroundRotation = 0.0f;
++
++ // Get light position from the scene
++ QVector3D lightPos = m_cachedScene->activeLight()->position();
++
++ // Skip depth rendering if we're in slice mode
++ // Introduce regardless of shadow quality to simplify logic
++ QMatrix4x4 depthViewMatrix;
++ QMatrix4x4 depthProjectionMatrix;
++ QMatrix4x4 depthProjectionViewMatrix;
++
++ QMatrix4x4 projectionViewMatrix = projectionMatrix * viewMatrix;
++
++ BarRenderItem *selectedBar(0);
++
++ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) {
++ // Render scene into a depth texture for using with shadow mapping
++ // Enable drawing to depth framebuffer
++ glBindFramebuffer(GL_FRAMEBUFFER, m_depthFrameBuffer);
++ glClear(GL_DEPTH_BUFFER_BIT);
++
++ // Bind depth shader
++ m_depthShader->bind();
++
++ // Set viewport for depth map rendering. Must match texture size. Larger values give smoother shadows.
++ // Depth viewport must always start from 0, 0, as it is rendered into a texture, not screen
++ glViewport(0, 0,
++ m_primarySubViewport.width() * m_shadowQualityMultiplier,
++ m_primarySubViewport.height() * m_shadowQualityMultiplier);
++
++ // Get the depth view matrix
++ // It may be possible to hack lightPos here if we want to make some tweaks to shadow
++ QVector3D depthLightPos = activeCamera->d_ptr->calculatePositionRelativeToCamera(
++ zeroVector, 0.0f, 3.5f / m_autoScaleAdjustment);
++ depthViewMatrix.lookAt(depthLightPos, zeroVector, upVector);
++
++ // Set the depth projection matrix
++ depthProjectionMatrix.perspective(10.0f, viewPortRatio, 3.0f, 100.0f);
++ depthProjectionViewMatrix = depthProjectionMatrix * depthViewMatrix;
++
++ // Draw bars to depth buffer
++ QVector3D shadowScaler(m_scaleX * m_seriesScaleX * 0.9f, 0.0f,
++ m_scaleZ * m_seriesScaleZ * 0.9f);
++ for (auto baseCache: m_renderCacheList) {
++ if (baseCache->isVisible()) {
++ BarSeriesRenderCache *cache = static_cast<BarSeriesRenderCache *>(baseCache);
++ float seriesPos = m_seriesStart + m_seriesStep
++ * (cache->visualIndex() - (cache->visualIndex()
++ * m_cachedBarSeriesMargin.width())) + 0.5f;
++ ObjectHelper *barObj = cache->object();
++ QQuaternion seriesRotation(cache->meshRotation());
++ const BarRenderItemArray &renderArray = cache->renderArray();
++ for (int row = startRow; row != stopRow; row += stepRow) {
++ const BarRenderItemRow &renderRow = renderArray.at(row);
++ for (int bar = startBar; bar != stopBar; bar += stepBar) {
++ const BarRenderItem &item = renderRow.at(bar);
++ if (!item.value())
++ continue;
++ GLfloat shadowOffset = 0.0f;
++ // Set front face culling for negative valued bars and back face culling
++ // for positive valued bars to remove peter-panning issues
++ if (item.height() > 0) {
++ glCullFace(GL_BACK);
++ if (m_yFlipped)
++ shadowOffset = 0.015f;
++ } else {
++ glCullFace(GL_FRONT);
++ if (!m_yFlipped)
++ shadowOffset = -0.015f;
++ }
++
++ if (m_cachedTheme->isBackgroundEnabled() && m_reflectionEnabled
++ && ((m_yFlipped && item.height() > 0.0)
++ || (!m_yFlipped && item.height() < 0.0))) {
++ continue;
++ }
++
++ QMatrix4x4 modelMatrix;
++ QMatrix4x4 MVPMatrix;
++
++ colPos = (bar + seriesPos) * (m_cachedBarSpacing.width());
++ rowPos = (row + 0.5f) * (m_cachedBarSpacing.height());
++
++ // Draw shadows for bars "on the other side" a bit off ground to avoid
++ // seeing shadows through the ground
++ modelMatrix.translate((colPos - m_rowWidth) / m_scaleFactor,
++ item.height() + shadowOffset,
++ (m_columnDepth - rowPos) / m_scaleFactor);
++ // Scale the bars down in X and Z to reduce self-shadowing issues
++ shadowScaler.setY(item.height());
++ if (!seriesRotation.isIdentity() || !item.rotation().isIdentity())
++ modelMatrix.rotate(seriesRotation * item.rotation());
++ modelMatrix.scale(shadowScaler);
++
++ MVPMatrix = depthProjectionViewMatrix * modelMatrix;
++
++ m_depthShader->setUniformValue(m_depthShader->MVP(), MVPMatrix);
++
++ // 1st attribute buffer : vertices
++ glEnableVertexAttribArray(m_depthShader->posAtt());
++ glBindBuffer(GL_ARRAY_BUFFER, barObj->vertexBuf());
++ glVertexAttribPointer(m_depthShader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0,
++ (void *)0);
++
++ // Index buffer
++ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, barObj->elementBuf());
++
++ // Draw the triangles
++ glDrawElements(GL_TRIANGLES, barObj->indexCount(), GL_UNSIGNED_INT,
++ (void *)0);
++
++ // Free buffers
++ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
++ glBindBuffer(GL_ARRAY_BUFFER, 0);
++
++ glDisableVertexAttribArray(m_depthShader->posAtt());
++ }
++ }
++ }
++ }
++
++ Abstract3DRenderer::drawCustomItems(RenderingDepth, m_depthShader, viewMatrix,
++ projectionViewMatrix,
++ depthProjectionViewMatrix, m_depthTexture,
++ m_shadowQualityToShader);
++
++ // Disable drawing to depth framebuffer (= enable drawing to screen)
++ glBindFramebuffer(GL_FRAMEBUFFER, defaultFboHandle);
++
++ // Reset culling to normal
++ glCullFace(GL_BACK);
++
++ // Revert to original viewport
++ glViewport(m_primarySubViewport.x(),
++ m_primarySubViewport.y(),
++ m_primarySubViewport.width(),
++ m_primarySubViewport.height());
++ }
++
++ // Do position mapping when necessary
++ if (m_graphPositionQueryPending) {
++ QVector3D graphDimensions(m_xScaleFactor, 0.0f, m_zScaleFactor);
++ queriedGraphPosition(projectionViewMatrix, graphDimensions, defaultFboHandle);
++
++ // Y is always at floor level
++ m_queriedGraphPosition.setY(0.0f);
++ emit needRender();
++ }
++
++ // Skip selection mode drawing if we're slicing or have no selection mode
++ if (!m_cachedIsSlicingActivated && m_cachedSelectionMode > QAbstract3DGraph::SelectionNone
++ && m_selectionState == SelectOnScene
++ && (m_visibleSeriesCount > 0 || !m_customRenderCache.isEmpty())
++ && m_selectionTexture) {
++ // Bind selection shader
++ m_selectionShader->bind();
++
++ // Draw bars to selection buffer
++ glBindFramebuffer(GL_FRAMEBUFFER, m_selectionFrameBuffer);
++ glViewport(0, 0,
++ m_primarySubViewport.width(),
++ m_primarySubViewport.height());
++
++ glEnable(GL_DEPTH_TEST); // Needed, otherwise the depth render buffer is not used
++ glClearColor(1.0f, 1.0f, 1.0f, 1.0f); // Set clear color to white (= selectionSkipColor)
++ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Needed for clearing the frame buffer
++ glDisable(GL_DITHER); // disable dithering, it may affect colors if enabled
++ for (auto baseCache: m_renderCacheList) {
++ if (baseCache->isVisible()) {
++ BarSeriesRenderCache *cache = static_cast<BarSeriesRenderCache *>(baseCache);
++ float seriesPos = m_seriesStart + m_seriesStep
++ * (cache->visualIndex() - (cache->visualIndex()
++ * m_cachedBarSeriesMargin.width())) + 0.5f;
++ ObjectHelper *barObj = cache->object();
++ QQuaternion seriesRotation(cache->meshRotation());
++ const BarRenderItemArray &renderArray = cache->renderArray();
++ for (int row = startRow; row != stopRow; row += stepRow) {
++ const BarRenderItemRow &renderRow = renderArray.at(row);
++ for (int bar = startBar; bar != stopBar; bar += stepBar) {
++ const BarRenderItem &item = renderRow.at(bar);
++ if (!item.value())
++ continue;
++
++ if (item.height() < 0)
++ glCullFace(GL_FRONT);
++ else
++ glCullFace(GL_BACK);
++
++ QMatrix4x4 modelMatrix;
++ QMatrix4x4 MVPMatrix;
++
++ colPos = (bar + seriesPos) * (m_cachedBarSpacing.width());
++ rowPos = (row + 0.5f) * (m_cachedBarSpacing.height());
++
++ modelMatrix.translate((colPos - m_rowWidth) / m_scaleFactor,
++ item.height(),
++ (m_columnDepth - rowPos) / m_scaleFactor);
++ if (!seriesRotation.isIdentity() || !item.rotation().isIdentity())
++ modelMatrix.rotate(seriesRotation * item.rotation());
++ modelMatrix.scale(QVector3D(m_scaleX * m_seriesScaleX,
++ item.height(),
++ m_scaleZ * m_seriesScaleZ));
++
++ MVPMatrix = projectionViewMatrix * modelMatrix;
++
++ QVector4D barColor = QVector4D(GLfloat(row) / 255.0f,
++ GLfloat(bar) / 255.0f,
++ GLfloat(cache->visualIndex()) / 255.0f,
++ itemAlpha);
++
++ m_selectionShader->setUniformValue(m_selectionShader->MVP(), MVPMatrix);
++ m_selectionShader->setUniformValue(m_selectionShader->color(), barColor);
++
++ m_drawer->drawSelectionObject(m_selectionShader, barObj);
++ }
++ }
++ }
++ }
++ glCullFace(GL_BACK);
++ Abstract3DRenderer::drawCustomItems(RenderingSelection, m_selectionShader,
++ viewMatrix,
++ projectionViewMatrix, depthProjectionViewMatrix,
++ m_depthTexture, m_shadowQualityToShader);
++ drawLabels(true, activeCamera, viewMatrix, projectionMatrix);
++ drawBackground(backgroundRotation, depthProjectionViewMatrix, projectionViewMatrix,
++ viewMatrix, false, true);
++ glEnable(GL_DITHER);
++
++ // Read color under cursor
++ QVector4D clickedColor = Utils::getSelection(m_inputPosition, m_viewport.height());
++ m_clickedPosition = selectionColorToArrayPosition(clickedColor);
++ m_clickedSeries = selectionColorToSeries(clickedColor);
++ m_clickResolved = true;
++
++ emit needRender();
++
++ // Revert to original render target and viewport
++ glBindFramebuffer(GL_FRAMEBUFFER, defaultFboHandle);
++ glViewport(m_primarySubViewport.x(),
++ m_primarySubViewport.y(),
++ m_primarySubViewport.width(),
++ m_primarySubViewport.height());
++ }
++
++ if (m_reflectionEnabled) {
++ //
++ // Draw reflections
++ //
++ glDisable(GL_DEPTH_TEST);
++ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
++ glEnable(GL_STENCIL_TEST);
++ glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
++ glStencilFunc(GL_ALWAYS, 1, 0xffffffff);
++
++ // Draw background stencil
++ drawBackground(backgroundRotation, depthProjectionViewMatrix, projectionViewMatrix,
++ viewMatrix);
++
++ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
++ glEnable(GL_DEPTH_TEST);
++
++ glStencilFunc(GL_EQUAL, 1, 0xffffffff);
++ glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
++
++ // Set light
++ QVector3D reflectionLightPos = lightPos;
++ reflectionLightPos.setY(-(lightPos.y()));
++ m_cachedScene->activeLight()->setPosition(reflectionLightPos);
++
++ // Draw bar reflections
++ (void)drawBars(&selectedBar, depthProjectionViewMatrix,
++ projectionViewMatrix, viewMatrix,
++ startRow, stopRow, stepRow,
++ startBar, stopBar, stepBar, -1.0f);
++
++ Abstract3DRenderer::drawCustomItems(RenderingNormal, m_customItemShader,
++ viewMatrix, projectionViewMatrix,
++ depthProjectionViewMatrix, m_depthTexture,
++ m_shadowQualityToShader, -1.0f);
++
++ // Reset light
++ m_cachedScene->activeLight()->setPosition(lightPos);
++
++ glDisable(GL_STENCIL_TEST);
++
++ glCullFace(GL_BACK);
++ }
++
++ //
++ // Draw the real scene
++ //
++ // Draw background
++ if (m_reflectionEnabled) {
++ glEnable(GL_BLEND);
++ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
++ drawBackground(backgroundRotation, depthProjectionViewMatrix, projectionViewMatrix,
++ viewMatrix, true);
++ glDisable(GL_BLEND);
++ } else {
++ drawBackground(backgroundRotation, depthProjectionViewMatrix, projectionViewMatrix,
++ viewMatrix);
++ }
++
++ // Draw bars
++ bool barSelectionFound = drawBars(&selectedBar, depthProjectionViewMatrix,
++ projectionViewMatrix, viewMatrix,
++ startRow, stopRow, stepRow,
++ startBar, stopBar, stepBar);
++
++ // Draw grid lines
++ drawGridLines(depthProjectionViewMatrix, projectionViewMatrix, viewMatrix);
++
++ // Draw custom items
++ Abstract3DRenderer::drawCustomItems(RenderingNormal, m_customItemShader, viewMatrix,
++ projectionViewMatrix, depthProjectionViewMatrix,
++ m_depthTexture, m_shadowQualityToShader);
++
++ // Draw labels
++ drawLabels(false, activeCamera, viewMatrix, projectionMatrix);
++
++ // Handle selected bar label generation
++ if (barSelectionFound) {
++ // Print value of selected bar
++ glDisable(GL_DEPTH_TEST);
++ // Draw the selection label
++ LabelItem &labelItem = selectionLabelItem();
++ if (m_selectedBar != selectedBar || m_updateLabels || !labelItem.textureId()
++ || m_selectionLabelDirty) {
++ QString labelText = selectionLabel();
++ if (labelText.isNull() || m_selectionLabelDirty) {
++ labelText = m_selectedSeriesCache->itemLabel();
++ setSelectionLabel(labelText);
++ m_selectionLabelDirty = false;
++ }
++ m_drawer->generateLabelItem(labelItem, labelText);
++ m_selectedBar = selectedBar;
++ }
++
++ Drawer::LabelPosition position =
++ m_selectedBar->height() >= 0 ? Drawer::LabelOver : Drawer::LabelBelow;
++
++ m_drawer->drawLabel(*selectedBar, labelItem, viewMatrix, projectionMatrix,
++ zeroVector, identityQuaternion, selectedBar->height(),
++ m_cachedSelectionMode, m_labelShader,
++ m_labelObj, activeCamera, true, false, position);
++
++ // Reset label update flag; they should have been updated when we get here
++ m_updateLabels = false;
++
++ glEnable(GL_DEPTH_TEST);
++ } else {
++ m_selectedBar = 0;
++ }
++
++ glDisable(GL_BLEND);
++
++ // Release shader
++ glUseProgram(0);
++ m_selectionDirty = false;
++ }
++
++ bool Bars3DRenderer::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)
++ {
++ QVector3D lightPos = m_cachedScene->activeLight()->position();
++ QVector4D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor());
++
++ bool rowMode = m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionRow);
++
++ ShaderHelper *barShader = 0;
++ GLuint gradientTexture = 0;
++ Q3DTheme::ColorStyle previousColorStyle = Q3DTheme::ColorStyleUniform;
++
++ // Set unchanging shader bindings
++ if (m_haveGradientSeries) {
++ m_barGradientShader->bind();
++ m_barGradientShader->setUniformValue(m_barGradientShader->lightP(), lightPos);
++ m_barGradientShader->setUniformValue(m_barGradientShader->view(), viewMatrix);
++ m_barGradientShader->setUniformValue(m_barGradientShader->ambientS(),
++ m_cachedTheme->ambientLightStrength());
++ m_barGradientShader->setUniformValue(m_barGradientShader->gradientMin(), 0.0f);
++ m_barGradientShader->setUniformValue(m_barGradientShader->lightColor(), lightColor);
++ }
++
++ if (m_haveUniformColorSeries) {
++ m_barShader->bind();
++ m_barShader->setUniformValue(m_barShader->lightP(), lightPos);
++ m_barShader->setUniformValue(m_barShader->view(), viewMatrix);
++ m_barShader->setUniformValue(m_barShader->ambientS(),
++ m_cachedTheme->ambientLightStrength());
++ m_barShader->setUniformValue(m_barShader->lightColor(), lightColor);
++ barShader = m_barShader;
++ } else {
++ barShader = m_barGradientShader;
++ previousColorStyle = Q3DTheme::ColorStyleRangeGradient;
++ }
++
++ int sliceReserveAmount = 0;
++ if (m_selectionDirty && m_cachedIsSlicingActivated) {
++ // Slice doesn't own its items, no need to delete them - just clear
++ if (rowMode)
++ sliceReserveAmount = m_cachedColumnCount;
++ else
++ sliceReserveAmount = m_cachedRowCount;
++
++ // Set slice cache, i.e. axis cache from where slice labels are taken
++ if (rowMode)
++ m_sliceCache = &m_axisCacheX;
++ else
++ m_sliceCache = &m_axisCacheZ;
++ m_sliceTitleItem = 0;
++ }
++
++ glEnable(GL_POLYGON_OFFSET_FILL);
++ glPolygonOffset(0.5f, 1.0f);
++
++ GLfloat adjustedLightStrength = m_cachedTheme->lightStrength() / 10.0f;
++ GLfloat adjustedHighlightStrength = m_cachedTheme->highlightLightStrength() / 10.0f;
++
++ bool barSelectionFound = false;
++
++ QVector4D baseColor;
++ QVector4D barColor;
++ QVector3D modelScaler(m_scaleX * m_seriesScaleX, 0.0f, m_scaleZ * m_seriesScaleZ);
++ bool somethingSelected =
++ (m_visualSelectedBarPos != Bars3DController::invalidSelectionPosition());
++ for (auto baseCache: m_renderCacheList) {
++ if (baseCache->isVisible()) {
++ BarSeriesRenderCache *cache = static_cast<BarSeriesRenderCache *>(baseCache);
++ float seriesPos = m_seriesStart + m_seriesStep
++ * (cache->visualIndex() - (cache->visualIndex()
++ * m_cachedBarSeriesMargin.width())) + 0.5f;
++ ObjectHelper *barObj = cache->object();
++ QQuaternion seriesRotation(cache->meshRotation());
++ Q3DTheme::ColorStyle colorStyle = cache->colorStyle();
++ BarRenderItemArray &renderArray = cache->renderArray();
++ bool colorStyleIsUniform = (colorStyle == Q3DTheme::ColorStyleUniform);
++ if (sliceReserveAmount)
++ cache->sliceArray().resize(sliceReserveAmount);
++
++ // Rebind shader if it has changed
++ if (colorStyleIsUniform != (previousColorStyle == Q3DTheme::ColorStyleUniform)) {
++ if (colorStyleIsUniform)
++ barShader = m_barShader;
++ else
++ barShader = m_barGradientShader;
++ barShader->bind();
++ }
++
++ if (colorStyleIsUniform) {
++ baseColor = cache->baseColor();
++ } else if ((previousColorStyle != colorStyle)
++ && (colorStyle == Q3DTheme::ColorStyleObjectGradient)) {
++ m_barGradientShader->setUniformValue(m_barGradientShader->gradientHeight(), 0.5f);
++ }
++
++ // Always use base color when no selection mode
++ if (m_cachedSelectionMode == QAbstract3DGraph::SelectionNone) {
++ if (colorStyleIsUniform)
++ barColor = baseColor;
++ else
++ gradientTexture = cache->baseGradientTexture();
++ }
++
++ previousColorStyle = colorStyle;
++ for (int row = startRow; row != stopRow; row += stepRow) {
++ BarRenderItemRow &renderRow = renderArray[row];
++ for (int bar = startBar; bar != stopBar; bar += stepBar) {
++ BarRenderItem &item = renderRow[bar];
++ float adjustedHeight = reflection * item.height();
++ if (adjustedHeight < 0)
++ glCullFace(GL_FRONT);
++ else
++ glCullFace(GL_BACK);
++
++ QMatrix4x4 modelMatrix;
++ QMatrix4x4 itModelMatrix;
++ QMatrix4x4 MVPMatrix;
++
++ GLfloat colPos = (bar + seriesPos) * (m_cachedBarSpacing.width());
++ GLfloat rowPos = (row + 0.5f) * (m_cachedBarSpacing.height());
++
++ modelMatrix.translate((colPos - m_rowWidth) / m_scaleFactor,
++ adjustedHeight,
++ (m_columnDepth - rowPos) / m_scaleFactor);
++ modelScaler.setY(adjustedHeight);
++ if (!seriesRotation.isIdentity() || !item.rotation().isIdentity()) {
++ QQuaternion totalRotation = seriesRotation * item.rotation();
++ modelMatrix.rotate(totalRotation);
++ itModelMatrix.rotate(totalRotation);
++ }
++ modelMatrix.scale(modelScaler);
++ itModelMatrix.scale(modelScaler);
++ #ifdef SHOW_DEPTH_TEXTURE_SCENE
++ MVPMatrix = depthProjectionViewMatrix * modelMatrix;
++ #else
++ MVPMatrix = projectionViewMatrix * modelMatrix;
++ #endif
++ GLfloat lightStrength = m_cachedTheme->lightStrength();
++ GLfloat shadowLightStrength = adjustedLightStrength;
++
++ if (m_cachedSelectionMode > QAbstract3DGraph::SelectionNone) {
++ Bars3DController::SelectionType selectionType =
++ Bars3DController::SelectionNone;
++ if (somethingSelected)
++ selectionType = isSelected(row, bar, cache);
++
++ switch (selectionType) {
++ case Bars3DController::SelectionItem: {
++ if (colorStyleIsUniform)
++ barColor = cache->singleHighlightColor();
++ else
++ gradientTexture = cache->singleHighlightGradientTexture();
++
++ lightStrength = m_cachedTheme->highlightLightStrength();
++ shadowLightStrength = adjustedHighlightStrength;
++ // Insert position data into render item
++ // We have no ownership, don't delete the previous one
++ if (!m_cachedIsSlicingActivated
++ && m_selectedSeriesCache == cache) {
++ *selectedBar = &item;
++ (*selectedBar)->setPosition(QPoint(row, bar));
++ item.setTranslation(modelMatrix.column(3).toVector3D());
++ barSelectionFound = true;
++ }
++ if (m_selectionDirty && m_cachedIsSlicingActivated) {
++ QVector3D translation = modelMatrix.column(3).toVector3D();
++ if (m_cachedSelectionMode & QAbstract3DGraph::SelectionColumn
++ && m_visibleSeriesCount > 1) {
++ translation.setZ((m_columnDepth
++ - ((row + seriesPos)
++ * (m_cachedBarSpacing.height())))
++ / m_scaleFactor);
++ }
++ item.setTranslation(translation);
++ item.setPosition(QPoint(row, bar));
++ if (rowMode)
++ cache->sliceArray()[bar].setItem(item);
++ else
++ cache->sliceArray()[row].setItem(item);
++ }
++ break;
++ }
++ case Bars3DController::SelectionRow: {
++ // Current bar is on the same row as the selected bar
++ if (colorStyleIsUniform)
++ barColor = cache->multiHighlightColor();
++ else
++ gradientTexture = cache->multiHighlightGradientTexture();
++
++ lightStrength = m_cachedTheme->highlightLightStrength();
++ shadowLightStrength = adjustedHighlightStrength;
++ if (m_cachedIsSlicingActivated) {
++ item.setTranslation(modelMatrix.column(3).toVector3D());
++ item.setPosition(QPoint(row, bar));
++ if (m_selectionDirty) {
++ if (!m_sliceTitleItem && m_axisCacheZ.labelItems().size() > row)
++ m_sliceTitleItem = m_axisCacheZ.labelItems().at(row);
++ cache->sliceArray()[bar].setItem(item);
++ }
++ }
++ break;
++ }
++ case Bars3DController::SelectionColumn: {
++ // Current bar is on the same column as the selected bar
++ if (colorStyleIsUniform)
++ barColor = cache->multiHighlightColor();
++ else
++ gradientTexture = cache->multiHighlightGradientTexture();
++
++ lightStrength = m_cachedTheme->highlightLightStrength();
++ shadowLightStrength = adjustedHighlightStrength;
++ if (m_cachedIsSlicingActivated) {
++ QVector3D translation = modelMatrix.column(3).toVector3D();
++ if (m_visibleSeriesCount > 1) {
++ translation.setZ((m_columnDepth
++ - ((row + seriesPos)
++ * (m_cachedBarSpacing.height())))
++ / m_scaleFactor);
++ }
++ item.setTranslation(translation);
++ item.setPosition(QPoint(row, bar));
++ if (m_selectionDirty) {
++ if (!m_sliceTitleItem && m_axisCacheX.labelItems().size() > bar)
++ m_sliceTitleItem = m_axisCacheX.labelItems().at(bar);
++ cache->sliceArray()[row].setItem(item);
++ }
++ }
++ break;
++ }
++ case Bars3DController::SelectionNone: {
++ // Current bar is not selected, nor on a row or column
++ if (colorStyleIsUniform) {
++ QList<QColor> rowColors = cache->series()->rowColors();
++ if (rowColors.size() == 0) {
++ barColor = baseColor;
++ } else {
++ int rowColorIndex = row % rowColors.size();
++ barColor = Utils::vectorFromColor(rowColors[rowColorIndex]);
++ }
++ } else {
++ gradientTexture = cache->baseGradientTexture();
++ }
++ break;
++ }
++ }
++ }
++
++ if (item.height() == 0) {
++ continue;
++ } else if ((m_reflectionEnabled
++ && (reflection == 1.0f
++ || (reflection != 1.0f
++ && ((m_yFlipped && item.height() < 0.0)
++ || (!m_yFlipped && item.height() > 0.0)))))
++ || !m_reflectionEnabled) {
++ // Skip drawing of 0-height bars and reflections of bars on the "wrong side"
++ // Set shader bindings
++ barShader->setUniformValue(barShader->model(), modelMatrix);
++ barShader->setUniformValue(barShader->nModel(),
++ itModelMatrix.transposed().inverted());
++ barShader->setUniformValue(barShader->MVP(), MVPMatrix);
++ if (colorStyleIsUniform) {
++ barShader->setUniformValue(barShader->color(), barColor);
++ } else if (colorStyle == Q3DTheme::ColorStyleRangeGradient) {
++ barShader->setUniformValue(barShader->gradientHeight(),
++ qAbs(item.height()) / m_gradientFraction);
++ }
++
++ if (((m_reflectionEnabled && reflection == 1.0f
++ && m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone)
++ || m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone)
++ && !m_isOpenGLES) {
++ // Set shadow shader bindings
++ QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
++ barShader->setUniformValue(barShader->shadowQ(),
++ m_shadowQualityToShader);
++ barShader->setUniformValue(barShader->depth(), depthMVPMatrix);
++ barShader->setUniformValue(barShader->lightS(), shadowLightStrength);
++ barShader->setUniformValue(barShader->lightColor(), lightColor);
++
++ // Draw the object
++ m_drawer->drawObject(barShader, barObj, gradientTexture,
++ m_depthTexture);
++ } else {
++ // Set shadowless shader bindings
++ if (m_reflectionEnabled && reflection != 1.0f
++ && m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
++ barShader->setUniformValue(barShader->lightS(),
++ adjustedLightStrength);
++ } else {
++ barShader->setUniformValue(barShader->lightS(), lightStrength);
++ }
++
++ // Draw the object
++ m_drawer->drawObject(barShader, barObj, gradientTexture);
++ }
++ }
++ }
++ }
++ }
++ }
++ glDisable(GL_POLYGON_OFFSET_FILL);
++
++ // Reset culling
++ glCullFace(GL_BACK);
++
++ return barSelectionFound;
++ }
++
++ void Bars3DRenderer::drawBackground(GLfloat backgroundRotation,
++ const QMatrix4x4 &depthProjectionViewMatrix,
++ const QMatrix4x4 &projectionViewMatrix,
++ const QMatrix4x4 &viewMatrix, bool reflectingDraw,
++ bool drawingSelectionBuffer)
++ {
++ // Draw background
++ if (m_cachedTheme->isBackgroundEnabled() && m_backgroundObj) {
++ QVector3D lightPos = m_cachedScene->activeLight()->position();
++ QVector4D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor());
++ GLfloat adjustedLightStrength = m_cachedTheme->lightStrength() / 10.0f;
++ ShaderHelper *shader = 0;
++
++ // Bind background shader
++ if (drawingSelectionBuffer)
++ shader = m_selectionShader; // Use single color shader when drawing to selection buffer
++ else
++ shader = m_backgroundShader;
++ shader->bind();
++
++ QMatrix4x4 modelMatrix;
++ QMatrix4x4 MVPMatrix;
++ QMatrix4x4 itModelMatrix;
++
++ QVector3D backgroundScaler(m_scaleXWithBackground, m_scaleYWithBackground,
++ m_scaleZWithBackground);
++ QVector4D backgroundColor = Utils::vectorFromColor(m_cachedTheme->backgroundColor());
++ if (m_reflectionEnabled)
++ backgroundColor.setW(backgroundColor.w() * m_reflectivity);
++
++ // Set shader bindings
++ shader->setUniformValue(shader->lightP(), lightPos);
++ shader->setUniformValue(shader->view(), viewMatrix);
++ if (drawingSelectionBuffer) {
++ // Use selectionSkipColor for background when drawing to selection buffer
++ shader->setUniformValue(shader->color(), selectionSkipColor);
++ } else {
++ shader->setUniformValue(shader->color(), backgroundColor);
++ }
++ shader->setUniformValue(shader->ambientS(),
++ m_cachedTheme->ambientLightStrength() * 2.0f);
++ shader->setUniformValue(shader->lightColor(), lightColor);
++
++ // Draw floor
++ modelMatrix.scale(backgroundScaler);
++
++ if (m_yFlipped)
++ modelMatrix.rotate(m_xRightAngleRotation);
++ else
++ modelMatrix.rotate(m_xRightAngleRotationNeg);
++
++ itModelMatrix = modelMatrix;
++
++ #ifdef SHOW_DEPTH_TEXTURE_SCENE
++ MVPMatrix = depthProjectionViewMatrix * modelMatrix;
++ #else
++ MVPMatrix = projectionViewMatrix * modelMatrix;
++ #endif
++ // Set changed shader bindings
++ shader->setUniformValue(shader->model(), modelMatrix);
++ shader->setUniformValue(shader->nModel(), itModelMatrix.inverted().transposed());
++ shader->setUniformValue(shader->MVP(), MVPMatrix);
++
++ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) {
++ // Set shadow shader bindings
++ QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
++ shader->setUniformValue(shader->depth(), depthMVPMatrix);
++ // Draw the object
++ m_drawer->drawObject(shader, m_gridLineObj, 0, m_depthTexture);
++ } else {
++ // Draw the object
++ m_drawer->drawObject(shader, m_gridLineObj);
++ }
++
++ // Draw walls
++ modelMatrix = QMatrix4x4();
++ itModelMatrix = QMatrix4x4();
++ modelMatrix.translate(0.0f, m_backgroundAdjustment, 0.0f);
++
++ modelMatrix.scale(backgroundScaler);
++ itModelMatrix.scale(backgroundScaler);
++ modelMatrix.rotate(backgroundRotation, 0.0f, 1.0f, 0.0f);
++ itModelMatrix.rotate(backgroundRotation, 0.0f, 1.0f, 0.0f);
++
++ #ifdef SHOW_DEPTH_TEXTURE_SCENE
++ MVPMatrix = depthProjectionViewMatrix * modelMatrix;
++ #else
++ MVPMatrix = projectionViewMatrix * modelMatrix;
++ #endif
++
++ // Set changed shader bindings
++ shader->setUniformValue(shader->model(), modelMatrix);
++ shader->setUniformValue(shader->nModel(), itModelMatrix.inverted().transposed());
++ shader->setUniformValue(shader->MVP(), MVPMatrix);
++ if (!m_reflectionEnabled || (m_reflectionEnabled && reflectingDraw)) {
++ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) {
++ // Set shadow shader bindings
++ QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
++ shader->setUniformValue(shader->shadowQ(), m_shadowQualityToShader);
++ shader->setUniformValue(shader->depth(), depthMVPMatrix);
++ shader->setUniformValue(shader->lightS(), adjustedLightStrength);
++
++ // Draw the object
++ m_drawer->drawObject(shader, m_backgroundObj, 0, m_depthTexture);
++ } else {
++ // Set shadowless shader bindings
++ shader->setUniformValue(shader->lightS(), m_cachedTheme->lightStrength());
++
++ // Draw the object
++ m_drawer->drawObject(shader, m_backgroundObj);
++ }
++ }
++ }
++ }
++
++ void Bars3DRenderer::drawGridLines(const QMatrix4x4 &depthProjectionViewMatrix,
++ const QMatrix4x4 &projectionViewMatrix,
++ const QMatrix4x4 &viewMatrix)
++ {
++ if (m_cachedTheme->isGridEnabled()) {
++ ShaderHelper *lineShader;
++ if (m_isOpenGLES)
++ lineShader = m_selectionShader; // Plain color shader for GL_LINES
++ else
++ lineShader = m_backgroundShader;
++
++ QQuaternion lineRotation;
++
++ QVector3D lightPos = m_cachedScene->activeLight()->position();
++ QVector4D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor());
++
++ // Bind bar shader
++ lineShader->bind();
++
++ // Set unchanging shader bindings
++ QVector4D barColor = Utils::vectorFromColor(m_cachedTheme->gridLineColor());
++ lineShader->setUniformValue(lineShader->lightP(), lightPos);
++ lineShader->setUniformValue(lineShader->view(), viewMatrix);
++ lineShader->setUniformValue(lineShader->color(), barColor);
++ lineShader->setUniformValue(lineShader->ambientS(), m_cachedTheme->ambientLightStrength());
++ lineShader->setUniformValue(lineShader->lightColor(), lightColor);
++ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) {
++ // Set shadowed shader bindings
++ lineShader->setUniformValue(lineShader->shadowQ(), m_shadowQualityToShader);
++ lineShader->setUniformValue(lineShader->lightS(),
++ m_cachedTheme->lightStrength() / 20.0f);
++ } else {
++ // Set shadowless shader bindings
++ lineShader->setUniformValue(lineShader->lightS(),
++ m_cachedTheme->lightStrength() / 2.5f);
++ }
++
++ GLfloat yFloorLinePosition = gridLineOffset;
++ if (m_yFlipped)
++ yFloorLinePosition = -yFloorLinePosition;
++
++ QVector3D gridLineScaler(m_scaleXWithBackground, gridLineWidth, gridLineWidth);
++
++ if (m_yFlipped)
++ lineRotation = m_xRightAngleRotation;
++ else
++ lineRotation = m_xRightAngleRotationNeg;
++
++ // Floor lines: rows
++ for (GLfloat row = 0.0f; row <= m_cachedRowCount; row++) {
++ QMatrix4x4 modelMatrix;
++ QMatrix4x4 MVPMatrix;
++ QMatrix4x4 itModelMatrix;
++
++ GLfloat rowPos = row * m_cachedBarSpacing.height();
++ modelMatrix.translate(0.0f, yFloorLinePosition,
++ (m_columnDepth - rowPos) / m_scaleFactor);
++ modelMatrix.scale(gridLineScaler);
++ itModelMatrix.scale(gridLineScaler);
++ modelMatrix.rotate(lineRotation);
++ itModelMatrix.rotate(lineRotation);
++
++ MVPMatrix = projectionViewMatrix * modelMatrix;
++
++ // Set the rest of the shader bindings
++ lineShader->setUniformValue(lineShader->model(), modelMatrix);
++ lineShader->setUniformValue(lineShader->nModel(),
++ itModelMatrix.inverted().transposed());
++ lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
++
++ if (m_isOpenGLES) {
++ m_drawer->drawLine(lineShader);
++ } else {
++ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
++ // Set shadow shader bindings
++ QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
++ lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
++ // Draw the object
++ m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
++ } else {
++ // Draw the object
++ m_drawer->drawObject(lineShader, m_gridLineObj);
++ }
++ }
++ }
++
++ // Floor lines: columns
++ if (m_isOpenGLES)
++ lineRotation = m_yRightAngleRotation;
++ gridLineScaler = QVector3D(gridLineWidth, gridLineWidth, m_scaleZWithBackground);
++ for (GLfloat bar = 0.0f; bar <= m_cachedColumnCount; bar++) {
++ QMatrix4x4 modelMatrix;
++ QMatrix4x4 MVPMatrix;
++ QMatrix4x4 itModelMatrix;
++
++ GLfloat colPos = bar * m_cachedBarSpacing.width();
++ modelMatrix.translate((m_rowWidth - colPos) / m_scaleFactor,
++ yFloorLinePosition, 0.0f);
++ modelMatrix.scale(gridLineScaler);
++ itModelMatrix.scale(gridLineScaler);
++ modelMatrix.rotate(lineRotation);
++ itModelMatrix.rotate(lineRotation);
++
++ MVPMatrix = projectionViewMatrix * modelMatrix;
++
++ // Set the rest of the shader bindings
++ lineShader->setUniformValue(lineShader->model(), modelMatrix);
++ lineShader->setUniformValue(lineShader->nModel(),
++ itModelMatrix.inverted().transposed());
++ lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
++
++ if (m_isOpenGLES) {
++ m_drawer->drawLine(lineShader);
++ } else {
++ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
++ // Set shadow shader bindings
++ QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
++ lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
++ // Draw the object
++ m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
++ } else {
++ // Draw the object
++ m_drawer->drawObject(lineShader, m_gridLineObj);
++ }
++ }
++ }
++
++ if (m_axisCacheY.segmentCount() > 0) {
++ // Wall lines: back wall
++ int gridLineCount = m_axisCacheY.gridLineCount();
++
++ GLfloat zWallLinePosition = -m_scaleZWithBackground + gridLineOffset;
++ if (m_zFlipped)
++ zWallLinePosition = -zWallLinePosition;
++
++ gridLineScaler = QVector3D(m_scaleXWithBackground, gridLineWidth, gridLineWidth);
++ for (int line = 0; line < gridLineCount; line++) {
++ QMatrix4x4 modelMatrix;
++ QMatrix4x4 MVPMatrix;
++ QMatrix4x4 itModelMatrix;
++
++ modelMatrix.translate(0.0f,
++ m_axisCacheY.gridLinePosition(line),
++ zWallLinePosition);
++ modelMatrix.scale(gridLineScaler);
++ itModelMatrix.scale(gridLineScaler);
++ if (m_zFlipped) {
++ modelMatrix.rotate(m_xFlipRotation);
++ itModelMatrix.rotate(m_xFlipRotation);
++ }
++
++ MVPMatrix = projectionViewMatrix * modelMatrix;
++
++ // Set the rest of the shader bindings
++ lineShader->setUniformValue(lineShader->model(), modelMatrix);
++ lineShader->setUniformValue(lineShader->nModel(),
++ itModelMatrix.inverted().transposed());
++ lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
++
++ if (m_isOpenGLES) {
++ m_drawer->drawLine(lineShader);
++ } else {
++ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
++ // Set shadow shader bindings
++ QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
++ lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
++ // Draw the object
++ m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
++ } else {
++ // Draw the object
++ m_drawer->drawObject(lineShader, m_gridLineObj);
++ }
++ }
++ }
++
++ // Wall lines: side wall
++ GLfloat xWallLinePosition = -m_scaleXWithBackground + gridLineOffset;
++ if (m_xFlipped)
++ xWallLinePosition = -xWallLinePosition;
++
++ if (m_xFlipped)
++ lineRotation = m_yRightAngleRotationNeg;
++ else
++ lineRotation = m_yRightAngleRotation;
++
++ gridLineScaler = QVector3D(gridLineWidth, gridLineWidth, m_scaleZWithBackground);
++ for (int line = 0; line < gridLineCount; line++) {
++ QMatrix4x4 modelMatrix;
++ QMatrix4x4 MVPMatrix;
++ QMatrix4x4 itModelMatrix;
++
++ modelMatrix.translate(xWallLinePosition,
++ m_axisCacheY.gridLinePosition(line),
++ 0.0f);
++ modelMatrix.scale(gridLineScaler);
++ itModelMatrix.scale(gridLineScaler);
++ modelMatrix.rotate(lineRotation);
++ itModelMatrix.rotate(lineRotation);
++
++ MVPMatrix = projectionViewMatrix * modelMatrix;
++
++ // Set the rest of the shader bindings
++ lineShader->setUniformValue(lineShader->model(), modelMatrix);
++ lineShader->setUniformValue(lineShader->nModel(),
++ itModelMatrix.inverted().transposed());
++ lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
++
++ if (m_isOpenGLES) {
++ m_drawer->drawLine(lineShader);
++ } else {
++ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
++ // Set shadow shader bindings
++ QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
++ lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
++ // Draw the object
++ m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
++ } else {
++ // Draw the object
++ m_drawer->drawObject(lineShader, m_gridLineObj);
++ }
++ }
++ }
++ }
++ }
++ }
++
++ void Bars3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCamera,
++ const QMatrix4x4 &viewMatrix, const QMatrix4x4 &projectionMatrix) {
++ ShaderHelper *shader = 0;
++ GLfloat alphaForValueSelection = labelValueAlpha / 255.0f;
++ GLfloat alphaForRowSelection = labelRowAlpha / 255.0f;
++ GLfloat alphaForColumnSelection = labelColumnAlpha / 255.0f;
++ if (drawSelection) {
++ shader = m_selectionShader;
++ // m_selectionShader is already bound
++ } else {
++ shader = m_labelShader;
++ shader->bind();
++
++ glEnable(GL_BLEND);
++ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
++ }
++
++ glEnable(GL_POLYGON_OFFSET_FILL);
++
++ // If camera x rotation is 180, side labels face wrong direction
++ float activeCameraXRotation = (activeCamera->xRotation() >= 180.0f) ? -180.0f
++ : activeCamera->xRotation();
++
++ float labelAutoAngle = m_axisCacheY.labelAutoRotation();
++ float labelAngleFraction = labelAutoAngle / 90.0f;
++ float fractionCamY = activeCamera->yRotation() * labelAngleFraction;
++ float fractionCamX = activeCameraXRotation * labelAngleFraction;
++ float labelsMaxWidth = 0.0f;
++
++ int startIndex;
++ int endIndex;
++ int indexStep;
++
++ // Y Labels
++ int labelCount = m_axisCacheY.labelCount();
++ GLfloat labelMarginXTrans = labelMargin;
++ GLfloat labelMarginZTrans = labelMargin;
++ GLfloat labelXTrans = m_scaleXWithBackground;
++ GLfloat labelZTrans = m_scaleZWithBackground;
++ QVector3D backLabelRotation(0.0f, -90.0f, 0.0f);
++ QVector3D sideLabelRotation(0.0f, 0.0f, 0.0f);
++ Qt::AlignmentFlag backAlignment = (m_xFlipped != m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight;
++ Qt::AlignmentFlag sideAlignment = (m_xFlipped == m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight;
++
++ if (!m_xFlipped) {
++ labelXTrans = -labelXTrans;
++ labelMarginXTrans = -labelMargin;
++ }
++ if (m_zFlipped) {
++ labelZTrans = -labelZTrans;
++ labelMarginZTrans = -labelMargin;
++ }
++
++ if (labelAutoAngle == 0.0f) {
++ if (!m_xFlipped)
++ backLabelRotation.setY(90.0f);
++ if (m_zFlipped)
++ sideLabelRotation.setY(180.f);
++ } else {
++ // Orient side labels somewhat towards the camera
++ if (m_xFlipped) {
++ if (m_zFlipped)
++ sideLabelRotation.setY(180.0f + (2.0f * labelAutoAngle) - fractionCamX);
++ else
++ sideLabelRotation.setY(-fractionCamX);
++ backLabelRotation.setY(-90.0f + labelAutoAngle - fractionCamX);
++ } else {
++ if (m_zFlipped)
++ sideLabelRotation.setY(180.0f - (2.0f * labelAutoAngle) - fractionCamX);
++ else
++ sideLabelRotation.setY(-fractionCamX);
++ backLabelRotation.setY(90.0f - labelAutoAngle - fractionCamX);
++ }
++ }
++ sideLabelRotation.setX(-fractionCamY);
++ backLabelRotation.setX(-fractionCamY);
++
++ QQuaternion totalSideRotation = Utils::calculateRotation(sideLabelRotation);
++ QQuaternion totalBackRotation = Utils::calculateRotation(backLabelRotation);
++
++ QVector3D backLabelTrans = QVector3D(labelXTrans, 0.0f,
++ labelZTrans + labelMarginZTrans);
++ QVector3D sideLabelTrans = QVector3D(-labelXTrans - labelMarginXTrans,
++ 0.0f, -labelZTrans);
++
++ if (m_yFlipped) {
++ startIndex = labelCount - 1;
++ endIndex = -1;
++ indexStep = -1;
++ } else {
++ startIndex = 0;
++ endIndex = labelCount;
++ indexStep = 1;
++ }
++ float offsetValue = 0.0f;
++ for (int i = startIndex; i != endIndex; i = i + indexStep) {
++ backLabelTrans.setY(m_axisCacheY.labelPosition(i));
++ sideLabelTrans.setY(backLabelTrans.y());
++
++ glPolygonOffset(offsetValue++ / -10.0f, 1.0f);
++
++ const LabelItem &axisLabelItem = *m_axisCacheY.labelItems().at(i);
++
++ if (drawSelection) {
++ QVector4D labelColor = QVector4D(0.0f, 0.0f, i / 255.0f,
++ alphaForValueSelection);
++ shader->setUniformValue(shader->color(), labelColor);
++ }
++
++ // Back wall
++ m_dummyBarRenderItem.setTranslation(backLabelTrans);
++ m_drawer->drawLabel(m_dummyBarRenderItem, axisLabelItem, viewMatrix, projectionMatrix,
++ zeroVector, totalBackRotation, 0, m_cachedSelectionMode,
++ shader, m_labelObj, activeCamera,
++ true, true, Drawer::LabelMid, backAlignment, false, drawSelection);
++
++ // Side wall
++ m_dummyBarRenderItem.setTranslation(sideLabelTrans);
++ m_drawer->drawLabel(m_dummyBarRenderItem, axisLabelItem, viewMatrix, projectionMatrix,
++ zeroVector, totalSideRotation, 0, m_cachedSelectionMode,
++ shader, m_labelObj, activeCamera,
++ true, true, Drawer::LabelMid, sideAlignment, false, drawSelection);
++
++ labelsMaxWidth = qMax(labelsMaxWidth, float(axisLabelItem.size().width()));
++ }
++
++ if (!drawSelection && m_axisCacheY.isTitleVisible()) {
++ sideLabelTrans.setY(m_backgroundAdjustment);
++ backLabelTrans.setY(m_backgroundAdjustment);
++ drawAxisTitleY(sideLabelRotation, backLabelRotation, sideLabelTrans, backLabelTrans,
++ totalSideRotation, totalBackRotation, m_dummyBarRenderItem, activeCamera,
++ labelsMaxWidth, viewMatrix, projectionMatrix, shader);
++ }
++
++ // Z labels
++ // Calculate the positions for row and column labels and store them
++ labelsMaxWidth = 0.0f;
++ labelAutoAngle = m_axisCacheZ.labelAutoRotation();
++ labelAngleFraction = labelAutoAngle / 90.0f;
++ fractionCamY = activeCamera->yRotation() * labelAngleFraction;
++ fractionCamX = activeCameraXRotation * labelAngleFraction;
++ GLfloat labelYAdjustment = 0.005f;
++ GLfloat colPosValue = m_scaleXWithBackground + labelMargin;
++ GLfloat rowPosValue = m_scaleZWithBackground + labelMargin;
++ GLfloat rowPos = 0.0f;
++ GLfloat colPos = 0.0f;
++ Qt::AlignmentFlag alignment = (m_xFlipped == m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight;
++ QVector3D labelRotation;
++
++ if (labelAutoAngle == 0.0f) {
++ if (m_zFlipped)
++ labelRotation.setY(180.0f);
++ if (m_yFlipped) {
++ if (m_zFlipped)
++ labelRotation.setY(180.0f);
++ else
++ labelRotation.setY(0.0f);
++ labelRotation.setX(90.0f);
++ } else {
++ labelRotation.setX(-90.0f);
++ }
++ } else {
++ if (m_zFlipped)
++ labelRotation.setY(180.0f);
++ if (m_yFlipped) {
++ if (m_zFlipped) {
++ if (m_xFlipped) {
++ labelRotation.setX(90.0f - (labelAutoAngle - fractionCamX)
++ * (-labelAutoAngle - fractionCamY) / labelAutoAngle);
++ labelRotation.setZ(labelAutoAngle + fractionCamY);
++ } else {
++ labelRotation.setX(90.0f + (labelAutoAngle + fractionCamX)
++ * (labelAutoAngle + fractionCamY) / labelAutoAngle);
++ labelRotation.setZ(-labelAutoAngle - fractionCamY);
++ }
++ } else {
++ if (m_xFlipped) {
++ labelRotation.setX(90.0f + (labelAutoAngle - fractionCamX)
++ * -(labelAutoAngle + fractionCamY) / labelAutoAngle);
++ labelRotation.setZ(-labelAutoAngle - fractionCamY);
++ } else {
++ labelRotation.setX(90.0f - (labelAutoAngle + fractionCamX)
++ * (labelAutoAngle + fractionCamY) / labelAutoAngle);
++ labelRotation.setZ(labelAutoAngle + fractionCamY);
++ }
++ }
++ } else {
++ if (m_zFlipped) {
++ if (m_xFlipped) {
++ labelRotation.setX(-90.0f + (labelAutoAngle - fractionCamX)
++ * (-labelAutoAngle + fractionCamY) / labelAutoAngle);
++ labelRotation.setZ(-labelAutoAngle + fractionCamY);
++ } else {
++ labelRotation.setX(-90.0f - (labelAutoAngle + fractionCamX)
++ * (labelAutoAngle - fractionCamY) / labelAutoAngle);
++ labelRotation.setZ(labelAutoAngle - fractionCamY);
++ }
++ } else {
++ if (m_xFlipped) {
++ labelRotation.setX(-90.0f - (labelAutoAngle - fractionCamX)
++ * (-labelAutoAngle + fractionCamY) / labelAutoAngle);
++ labelRotation.setZ(labelAutoAngle - fractionCamY);
++ } else {
++ labelRotation.setX(-90.0f + (labelAutoAngle + fractionCamX)
++ * (labelAutoAngle - fractionCamY) / labelAutoAngle);
++ labelRotation.setZ(-labelAutoAngle + fractionCamY);
++ }
++ }
++ }
++ }
++
++ QQuaternion totalRotation = Utils::calculateRotation(labelRotation);
++ labelCount = qMin(m_axisCacheZ.labelCount(), m_cachedRowCount);
++ if (m_zFlipped) {
++ startIndex = 0;
++ endIndex = labelCount;
++ indexStep = 1;
++ } else {
++ startIndex = labelCount - 1;
++ endIndex = -1;
++ indexStep = -1;
++ }
++ offsetValue = 0.0f;
++ for (int row = startIndex; row != endIndex; row = row + indexStep) {
++ // Go through all rows and get position of max+1 or min-1 column, depending on x flip
++ // We need only positions for them, labels have already been generated
++ rowPos = (row + 0.5f) * m_cachedBarSpacing.height();
++ if (m_xFlipped)
++ colPos = -colPosValue;
++ else
++ colPos = colPosValue;
++
++ glPolygonOffset(offsetValue++ / -10.0f, 1.0f);
++
++ QVector3D labelPos = QVector3D(colPos,
++ labelYAdjustment, // raise a bit over background to avoid depth "glimmering"
++ (m_columnDepth - rowPos) / m_scaleFactor);
++
++ m_dummyBarRenderItem.setTranslation(labelPos);
++ const LabelItem &axisLabelItem = *m_axisCacheZ.labelItems().at(row);
++
++ if (drawSelection) {
++ QVector4D labelColor = QVector4D(row / 255.0f, 0.0f, 0.0f, alphaForRowSelection);
++ shader->setUniformValue(shader->color(), labelColor);
++ }
++
++ m_drawer->drawLabel(m_dummyBarRenderItem, axisLabelItem, viewMatrix, projectionMatrix,
++ zeroVector, totalRotation, 0, m_cachedSelectionMode,
++ shader, m_labelObj, activeCamera,
++ true, true, Drawer::LabelMid, alignment,
++ false, drawSelection);
++ labelsMaxWidth = qMax(labelsMaxWidth, float(axisLabelItem.size().width()));
++ }
++
++ if (!drawSelection && m_axisCacheZ.isTitleVisible()) {
++ QVector3D titleTrans(colPos, 0.0f, 0.0f);
++ drawAxisTitleZ(labelRotation, titleTrans, totalRotation, m_dummyBarRenderItem,
++ activeCamera, labelsMaxWidth, viewMatrix, projectionMatrix, shader);
++ }
++
++ // X labels
++ labelsMaxWidth = 0.0f;
++ labelAutoAngle = m_axisCacheX.labelAutoRotation();
++ labelAngleFraction = labelAutoAngle / 90.0f;
++ fractionCamY = activeCamera->yRotation() * labelAngleFraction;
++ fractionCamX = activeCameraXRotation * labelAngleFraction;
++ alignment = (m_xFlipped != m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight;
++ if (labelAutoAngle == 0.0f) {
++ labelRotation = QVector3D(-90.0f, 90.0f, 0.0f);
++ if (m_xFlipped)
++ labelRotation.setY(-90.0f);
++ if (m_yFlipped) {
++ if (m_xFlipped)
++ labelRotation.setY(-90.0f);
++ else
++ labelRotation.setY(90.0f);
++ labelRotation.setX(90.0f);
++ }
++ } else {
++ if (m_xFlipped)
++ labelRotation.setY(-90.0f);
++ else
++ labelRotation.setY(90.0f);
++ if (m_yFlipped) {
++ if (m_zFlipped) {
++ if (m_xFlipped) {
++ labelRotation.setX(90.0f - (2.0f * labelAutoAngle - fractionCamX)
++ * (labelAutoAngle + fractionCamY) / labelAutoAngle);
++ labelRotation.setZ(-labelAutoAngle - fractionCamY);
++ } else {
++ labelRotation.setX(90.0f - (2.0f * labelAutoAngle + fractionCamX)
++ * (labelAutoAngle + fractionCamY) / labelAutoAngle);
++ labelRotation.setZ(labelAutoAngle + fractionCamY);
++ }
++ } else {
++ if (m_xFlipped) {
++ labelRotation.setX(90.0f + fractionCamX
++ * -(labelAutoAngle + fractionCamY) / labelAutoAngle);
++ labelRotation.setZ(labelAutoAngle + fractionCamY);
++ } else {
++ labelRotation.setX(90.0f - fractionCamX
++ * (-labelAutoAngle - fractionCamY) / labelAutoAngle);
++ labelRotation.setZ(-labelAutoAngle - fractionCamY);
++ }
++ }
++ } else {
++ if (m_zFlipped) {
++ if (m_xFlipped) {
++ labelRotation.setX(-90.0f + (2.0f * labelAutoAngle - fractionCamX)
++ * (labelAutoAngle - fractionCamY) / labelAutoAngle);
++ labelRotation.setZ(labelAutoAngle - fractionCamY);
++ } else {
++ labelRotation.setX(-90.0f + (2.0f * labelAutoAngle + fractionCamX)
++ * (labelAutoAngle - fractionCamY) / labelAutoAngle);
++ labelRotation.setZ(-labelAutoAngle + fractionCamY);
++ }
++ } else {
++ if (m_xFlipped) {
++ labelRotation.setX(-90.0f - fractionCamX
++ * (-labelAutoAngle + fractionCamY) / labelAutoAngle);
++ labelRotation.setZ(-labelAutoAngle + fractionCamY);
++ } else {
++ labelRotation.setX(-90.0f + fractionCamX
++ * -(labelAutoAngle - fractionCamY) / labelAutoAngle);
++ labelRotation.setZ(labelAutoAngle - fractionCamY);
++ }
++ }
++ }
++ }
++
++ totalRotation = Utils::calculateRotation(labelRotation);
++ labelCount = qMin(m_axisCacheX.labelCount(), m_cachedColumnCount);
++ if (m_xFlipped) {
++ startIndex = labelCount - 1;
++ endIndex = -1;
++ indexStep = -1;
++ } else {
++ startIndex = 0;
++ endIndex = labelCount;
++ indexStep = 1;
++ }
++ offsetValue = 0.0f;
++ for (int column = startIndex; column != endIndex; column = column + indexStep) {
++ // Go through all columns and get position of max+1 or min-1 row, depending on z flip
++ // We need only positions for them, labels have already been generated
++ colPos = (column + 0.5f) * m_cachedBarSpacing.width();
++ if (m_zFlipped)
++ rowPos = -rowPosValue;
++ else
++ rowPos = rowPosValue;
++
++ glPolygonOffset(offsetValue++ / -10.0f, 1.0f);
++
++ QVector3D labelPos = QVector3D((colPos - m_rowWidth) / m_scaleFactor,
++ labelYAdjustment, // raise a bit over background to avoid depth "glimmering"
++ rowPos);
++
++ m_dummyBarRenderItem.setTranslation(labelPos);
++ const LabelItem &axisLabelItem = *m_axisCacheX.labelItems().at(column);
++
++ if (drawSelection) {
++ QVector4D labelColor = QVector4D(0.0f, column / 255.0f, 0.0f,
++ alphaForColumnSelection);
++ shader->setUniformValue(shader->color(), labelColor);
++ }
++
++ m_drawer->drawLabel(m_dummyBarRenderItem, axisLabelItem, viewMatrix, projectionMatrix,
++ zeroVector, totalRotation, 0, m_cachedSelectionMode,
++ shader, m_labelObj, activeCamera,
++ true, true, Drawer::LabelMid, alignment, false, drawSelection);
++ labelsMaxWidth = qMax(labelsMaxWidth, float(axisLabelItem.size().width()));
++ }
++
++ if (!drawSelection && m_axisCacheX.isTitleVisible()) {
++ QVector3D titleTrans(0.0f, 0.0f, rowPos);
++ drawAxisTitleX(labelRotation, titleTrans, totalRotation, m_dummyBarRenderItem,
++ activeCamera, labelsMaxWidth, viewMatrix, projectionMatrix, shader);
++ }
++
++ #if 0 // Debug label
++ static LabelItem debugLabelItem;
++ QString debugLabelString(QStringLiteral("Flips: x:%1 y:%2 z:%3 xr:%4 yr:%5"));
++ QString finalDebugString = debugLabelString.arg(m_xFlipped).arg(m_yFlipped).arg(m_zFlipped)
++ .arg(activeCamera->xRotation()).arg(activeCamera->yRotation());
++ m_dummyBarRenderItem.setTranslation(QVector3D(m_xFlipped ? -1.5f : 1.5f,
++ m_yFlipped ? 1.5f : -1.5f,
++ m_zFlipped ? -1.5f : 1.5f));
++
++ m_drawer->generateLabelItem(debugLabelItem, finalDebugString);
++ m_drawer->drawLabel(m_dummyBarRenderItem, debugLabelItem, viewMatrix, projectionMatrix,
++ zeroVector, identityQuaternion, 0, m_cachedSelectionMode,
++ shader, m_labelObj, activeCamera,
++ true, false, Drawer::LabelMid, Qt::AlignHCenter, false, drawSelection);
++ #endif
++ glDisable(GL_POLYGON_OFFSET_FILL);
++ }
++
++ void Bars3DRenderer::updateMultiSeriesScaling(bool uniform)
++ {
++ m_keepSeriesUniform = uniform;
++
++ // Recalculate scale factors
++ m_seriesScaleX = 1.0f / float(m_visibleSeriesCount);
++ if (m_keepSeriesUniform)
++ m_seriesScaleZ = m_seriesScaleX;
++ else
++ m_seriesScaleZ = 1.0f;
++ }
++
++ void Bars3DRenderer::updateBarSpecs(GLfloat thicknessRatio, const QSizeF &spacing, bool relative)
++ {
++ // Convert ratio to QSizeF, as we need it in that format for autoscaling calculations
++ m_cachedBarThickness.setWidth(1.0f);
++ m_cachedBarThickness.setHeight(1.0f / thicknessRatio);
++
++ if (relative) {
++ m_cachedBarSpacing.setWidth((m_cachedBarThickness.width() * 2)
++ * (spacing.width() + 1.0f));
++ m_cachedBarSpacing.setHeight((m_cachedBarThickness.height() * 2)
++ * (spacing.height() + 1.0f));
++ } else {
++ m_cachedBarSpacing = m_cachedBarThickness * 2 + spacing * 2;
++ }
++
++ // Slice mode doesn't update correctly without this
++ if (m_cachedIsSlicingActivated)
++ m_selectionDirty = true;
++
++ // Calculate here and at setting sample space
++ calculateSceneScalingFactors();
++ }
++
++ void Bars3DRenderer::updateBarSeriesMargin(const QSizeF &margin)
++ {
++ m_cachedBarSeriesMargin = margin;
++ calculateSeriesStartPosition();
++ calculateSceneScalingFactors();
++ }
++
++ void Bars3DRenderer::updateAxisRange(QAbstract3DAxis::AxisOrientation orientation, float min,
++ float max)
++ {
++ Abstract3DRenderer::updateAxisRange(orientation, min, max);
++
++ if (orientation == QAbstract3DAxis::AxisOrientationY)
++ calculateHeightAdjustment();
++ }
++
++ void Bars3DRenderer::updateAxisReversed(QAbstract3DAxis::AxisOrientation orientation, bool enable)
++ {
++ Abstract3DRenderer::updateAxisReversed(orientation, enable);
++ if (orientation == QAbstract3DAxis::AxisOrientationY)
++ calculateHeightAdjustment();
++ }
++
++
++ void Bars3DRenderer::updateSelectedBar(const QPoint &position, QBar3DSeries *series)
++ {
++ m_selectedBarPos = position;
++ m_selectedSeriesCache = static_cast<BarSeriesRenderCache *>(m_renderCacheList.value(series, 0));
++ m_selectionDirty = true;
++ m_selectionLabelDirty = true;
++
++ if (!m_selectedSeriesCache
++ || !m_selectedSeriesCache->isVisible()
++ || m_selectedSeriesCache->renderArray().isEmpty()) {
++ m_visualSelectedBarPos = Bars3DController::invalidSelectionPosition();
++ return;
++ }
++
++ int adjustedZ = m_selectedBarPos.x() - int(m_axisCacheZ.min());
++ int adjustedX = m_selectedBarPos.y() - int(m_axisCacheX.min());
++ int maxZ = m_selectedSeriesCache->renderArray().size() - 1;
++ int maxX = maxZ >= 0 ? m_selectedSeriesCache->renderArray().at(0).size() - 1 : -1;
++
++ if (m_selectedBarPos == Bars3DController::invalidSelectionPosition()
++ || adjustedZ < 0 || adjustedZ > maxZ
++ || adjustedX < 0 || adjustedX > maxX) {
++ m_visualSelectedBarPos = Bars3DController::invalidSelectionPosition();
++ } else {
++ m_visualSelectedBarPos = QPoint(adjustedZ, adjustedX);
++ }
++ }
++
++ void Bars3DRenderer::resetClickedStatus()
++ {
++ m_clickedPosition = Bars3DController::invalidSelectionPosition();
++ m_clickedSeries = 0;
++ }
++
++ void Bars3DRenderer::updateShadowQuality(QAbstract3DGraph::ShadowQuality quality)
++ {
++ m_cachedShadowQuality = quality;
++ switch (quality) {
++ case QAbstract3DGraph::ShadowQualityLow:
++ m_shadowQualityToShader = 33.3f;
++ m_shadowQualityMultiplier = 1;
++ break;
++ case QAbstract3DGraph::ShadowQualityMedium:
++ m_shadowQualityToShader = 100.0f;
++ m_shadowQualityMultiplier = 3;
++ break;
++ case QAbstract3DGraph::ShadowQualityHigh:
++ m_shadowQualityToShader = 200.0f;
++ m_shadowQualityMultiplier = 5;
++ break;
++ case QAbstract3DGraph::ShadowQualitySoftLow:
++ m_shadowQualityToShader = 7.5f;
++ m_shadowQualityMultiplier = 1;
++ break;
++ case QAbstract3DGraph::ShadowQualitySoftMedium:
++ m_shadowQualityToShader = 10.0f;
++ m_shadowQualityMultiplier = 3;
++ break;
++ case QAbstract3DGraph::ShadowQualitySoftHigh:
++ m_shadowQualityToShader = 15.0f;
++ m_shadowQualityMultiplier = 4;
++ break;
++ default:
++ m_shadowQualityToShader = 0.0f;
++ m_shadowQualityMultiplier = 1;
++ break;
++ }
++
++ handleShadowQualityChange();
++
++ // Re-init depth buffer
++ updateDepthBuffer();
++
++ // Redraw to handle both reflections and shadows on background
++ if (m_reflectionEnabled)
++ needRender();
++ }
++
++ void Bars3DRenderer::loadBackgroundMesh()
++ {
++ ObjectHelper::resetObjectHelper(this, m_backgroundObj,
++ QStringLiteral(":/defaultMeshes/backgroundNoFloor"));
++ }
++
++ void Bars3DRenderer::updateTextures()
++ {
++ Abstract3DRenderer::updateTextures();
++
++ // Drawer has changed; this flag needs to be checked when checking if we need to update labels
++ m_updateLabels = true;
++ }
++
++ void Bars3DRenderer::fixMeshFileName(QString &fileName, QAbstract3DSeries::Mesh mesh)
++ {
++ if (!m_cachedTheme->isBackgroundEnabled()) {
++ // Load full version of meshes that have it available
++ // Note: Minimal, Point, and Arrow not supported in bar charts
++ if (mesh != QAbstract3DSeries::MeshSphere)
++ fileName.append(QStringLiteral("Full"));
++ }
++ }
++
++ void Bars3DRenderer::calculateSceneScalingFactors()
++ {
++ // Calculate scene scaling and translation factors
++ m_rowWidth = (m_cachedColumnCount * m_cachedBarSpacing.width()) / 2.0f;
++ m_columnDepth = (m_cachedRowCount * m_cachedBarSpacing.height()) / 2.0f;
++ m_maxDimension = qMax(m_rowWidth, m_columnDepth);
++ m_scaleFactor = qMin((m_cachedColumnCount * (m_maxDimension / m_maxSceneSize)),
++ (m_cachedRowCount * (m_maxDimension / m_maxSceneSize)));
++
++ // Single bar scaling
++ m_scaleX = m_cachedBarThickness.width() / m_scaleFactor;
++ m_scaleZ = m_cachedBarThickness.height() / m_scaleFactor;
++
++ // Adjust scaling according to margin
++ m_scaleX -= m_scaleX * m_cachedBarSeriesMargin.width();
++ m_scaleZ -= m_scaleZ * m_cachedBarSeriesMargin.height();
++
++ // Whole graph scale factors
++ m_xScaleFactor = m_rowWidth / m_scaleFactor;
++ m_zScaleFactor = m_columnDepth / m_scaleFactor;
++
++ if (m_requestedMargin < 0.0f) {
++ m_hBackgroundMargin = 0.0f;
++ m_vBackgroundMargin = 0.0f;
++ } else {
++ m_hBackgroundMargin = m_requestedMargin;
++ m_vBackgroundMargin = m_requestedMargin;
++ }
++
++ m_scaleXWithBackground = m_xScaleFactor + m_hBackgroundMargin;
++ m_scaleYWithBackground = 1.0f + m_vBackgroundMargin;
++ m_scaleZWithBackground = m_zScaleFactor + m_hBackgroundMargin;
++
++ updateCameraViewport();
++ updateCustomItemPositions();
++ }
++
++ void Bars3DRenderer::calculateHeightAdjustment()
++ {
++ float min = m_axisCacheY.min();
++ float max = m_axisCacheY.max();
++ GLfloat newAdjustment = 1.0f;
++ m_actualFloorLevel = qBound(min, m_floorLevel, max);
++ GLfloat maxAbs = qFabs(max - m_actualFloorLevel);
++
++ // Check if we have negative values
++ if (min < m_actualFloorLevel)
++ m_hasNegativeValues = true;
++ else if (min >= m_actualFloorLevel)
++ m_hasNegativeValues = false;
++
++ if (max < m_actualFloorLevel) {
++ m_heightNormalizer = GLfloat(qFabs(min) - qFabs(max));
++ maxAbs = qFabs(max) - qFabs(min);
++ } else {
++ m_heightNormalizer = GLfloat(max - min);
++ }
++
++ // Height fractions are used in gradient calculations and are therefore doubled
++ // Note that if max or min is exactly zero, we still consider it outside the range
++ if (max <= m_actualFloorLevel || min >= m_actualFloorLevel) {
++ m_noZeroInRange = true;
++ m_gradientFraction = 2.0f;
++ } else {
++ m_noZeroInRange = false;
++ GLfloat minAbs = qFabs(min - m_actualFloorLevel);
++ m_gradientFraction = qMax(minAbs, maxAbs) / m_heightNormalizer * 2.0f;
++ }
++
++ // Calculate translation adjustment for background floor
++ newAdjustment = (qBound(0.0f, (maxAbs / m_heightNormalizer), 1.0f) - 0.5f) * 2.0f;
++ if (m_axisCacheY.reversed())
++ newAdjustment = -newAdjustment;
++
++ if (newAdjustment != m_backgroundAdjustment) {
++ m_backgroundAdjustment = newAdjustment;
++ m_axisCacheY.setTranslate(m_backgroundAdjustment - 1.0f);
++ }
++ }
++
++ void Bars3DRenderer::calculateSeriesStartPosition()
++ {
++ m_seriesStart = -((float(m_visibleSeriesCount) - 1.0f) / 2.0f)
++ * (m_seriesStep - (m_seriesStep * m_cachedBarSeriesMargin.width()));
++ }
++
++ Bars3DController::SelectionType Bars3DRenderer::isSelected(int row, int bar,
++ const BarSeriesRenderCache *cache)
++ {
++ Bars3DController::SelectionType isSelectedType = Bars3DController::SelectionNone;
++
++ if ((m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionMultiSeries)
++ && m_selectedSeriesCache) || cache == m_selectedSeriesCache) {
++ if (row == m_visualSelectedBarPos.x() && bar == m_visualSelectedBarPos.y()
++ && (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionItem))) {
++ isSelectedType = Bars3DController::SelectionItem;
++ } else if (row == m_visualSelectedBarPos.x()
++ && (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionRow))) {
++ isSelectedType = Bars3DController::SelectionRow;
++ } else if (bar == m_visualSelectedBarPos.y()
++ && (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionColumn))) {
++ isSelectedType = Bars3DController::SelectionColumn;
++ }
++ }
++
++ return isSelectedType;
++ }
++
++ QPoint Bars3DRenderer::selectionColorToArrayPosition(const QVector4D &selectionColor)
++ {
++ QPoint position = Bars3DController::invalidSelectionPosition();
++ m_clickedType = QAbstract3DGraph::ElementNone;
++ m_selectedLabelIndex = -1;
++ m_selectedCustomItemIndex = -1;
++ if (selectionColor.w() == itemAlpha) {
++ // Normal selection item
++ position = QPoint(int(selectionColor.x() + int(m_axisCacheZ.min())),
++ int(selectionColor.y()) + int(m_axisCacheX.min()));
++ // Pass item clicked info to input handler
++ m_clickedType = QAbstract3DGraph::ElementSeries;
++ } else if (selectionColor.w() == labelRowAlpha) {
++ // Row selection
++ if (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionRow)) {
++ // Use column from previous selection in case we have row + column mode
++ GLint previousCol = qMax(0, m_selectedBarPos.y()); // Use 0 if previous is invalid
++ position = QPoint(int(selectionColor.x() + int(m_axisCacheZ.min())), previousCol);
++ }
++ m_selectedLabelIndex = selectionColor.x();
++ // Pass label clicked info to input handler
++ m_clickedType = QAbstract3DGraph::ElementAxisZLabel;
++ } else if (selectionColor.w() == labelColumnAlpha) {
++ // Column selection
++ if (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionColumn)) {
++ // Use row from previous selection in case we have row + column mode
++ GLint previousRow = qMax(0, m_selectedBarPos.x()); // Use 0 if previous is invalid
++ position = QPoint(previousRow, int(selectionColor.y()) + int(m_axisCacheX.min()));
++ }
++ m_selectedLabelIndex = selectionColor.y();
++ // Pass label clicked info to input handler
++ m_clickedType = QAbstract3DGraph::ElementAxisXLabel;
++ } else if (selectionColor.w() == labelValueAlpha) {
++ // Value selection
++ position = Bars3DController::invalidSelectionPosition();
++ m_selectedLabelIndex = selectionColor.z();
++ // Pass label clicked info to input handler
++ m_clickedType = QAbstract3DGraph::ElementAxisYLabel;
++ } else if (selectionColor.w() == customItemAlpha) {
++ // Custom item selection
++ position = Bars3DController::invalidSelectionPosition();
++ m_selectedCustomItemIndex = int(selectionColor.x())
++ + (int(selectionColor.y()) << 8)
++ + (int(selectionColor.z()) << 16);
++ m_clickedType = QAbstract3DGraph::ElementCustomItem;
++ }
++ return position;
++ }
++
++ QBar3DSeries *Bars3DRenderer::selectionColorToSeries(const QVector4D &selectionColor)
++ {
++ if (selectionColor == selectionSkipColor) {
++ return 0;
++ } else {
++ int seriesIndexFromColor(selectionColor.z());
++ for (auto baseCache: m_renderCacheList) {
++ BarSeriesRenderCache *cache = static_cast<BarSeriesRenderCache *>(baseCache);
++ if (cache->visualIndex() == seriesIndexFromColor)
++ return cache->series();
++ }
++ }
++ return 0;
++ }
++
++ void Bars3DRenderer::updateSlicingActive(bool isSlicing)
++ {
++ if (isSlicing == m_cachedIsSlicingActivated)
++ return;
++
++ m_cachedIsSlicingActivated = isSlicing;
++
++ if (!m_cachedIsSlicingActivated) {
++ // We need to re-init selection buffer in case there has been a resize
++ initSelectionBuffer();
++ initCursorPositionBuffer();
++ }
++
++ updateDepthBuffer(); // Re-init depth buffer as well
++ m_selectionDirty = true;
++ }
++
++ void Bars3DRenderer::initShaders(const QString &vertexShader, const QString &fragmentShader)
++ {
++ if (m_barShader)
++ delete m_barShader;
++ m_barShader = new ShaderHelper(this, vertexShader, fragmentShader);
++ m_barShader->initialize();
++ }
++
++ void Bars3DRenderer::initGradientShaders(const QString &vertexShader, const QString &fragmentShader)
++ {
++ if (m_barGradientShader)
++ delete m_barGradientShader;
++ m_barGradientShader = new ShaderHelper(this, vertexShader, fragmentShader);
++ m_barGradientShader->initialize();
++ }
++
++ void Bars3DRenderer::initSelectionShader()
++ {
++ if (m_selectionShader)
++ delete m_selectionShader;
++ m_selectionShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexPlainColor"),
++ QStringLiteral(":/shaders/fragmentPlainColor"));
++ m_selectionShader->initialize();
++ }
++
++ void Bars3DRenderer::initSelectionBuffer()
++ {
++ m_textureHelper->deleteTexture(&m_selectionTexture);
++
++ if (m_cachedIsSlicingActivated || m_primarySubViewport.size().isEmpty())
++ return;
++
++ m_selectionTexture = m_textureHelper->createSelectionTexture(m_primarySubViewport.size(),
++ m_selectionFrameBuffer,
++ m_selectionDepthBuffer);
++ }
++
++ void Bars3DRenderer::initDepthShader()
++ {
++ if (!m_isOpenGLES) {
++ if (m_depthShader)
++ delete m_depthShader;
++ m_depthShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexDepth"),
++ QStringLiteral(":/shaders/fragmentDepth"));
++ m_depthShader->initialize();
++ }
++ }
++
++ void Bars3DRenderer::updateDepthBuffer()
++ {
++ if (!m_isOpenGLES) {
++ m_textureHelper->deleteTexture(&m_depthTexture);
++
++ if (m_primarySubViewport.size().isEmpty())
++ return;
++
++ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
++ m_depthTexture =
++ m_textureHelper->createDepthTextureFrameBuffer(m_primarySubViewport.size(),
++ m_depthFrameBuffer,
++ m_shadowQualityMultiplier);
++ if (!m_depthTexture)
++ lowerShadowQuality();
++ }
++ }
++ }
++
++ void Bars3DRenderer::initBackgroundShaders(const QString &vertexShader,
++ const QString &fragmentShader)
++ {
++ if (m_backgroundShader)
++ delete m_backgroundShader;
++ m_backgroundShader = new ShaderHelper(this, vertexShader, fragmentShader);
++ m_backgroundShader->initialize();
++ }
++
++ QVector3D Bars3DRenderer::convertPositionToTranslation(const QVector3D &position, bool isAbsolute)
++ {
++ float xTrans = 0.0f;
++ float yTrans = 0.0f;
++ float zTrans = 0.0f;
++ if (!isAbsolute) {
++ // Convert row and column to translation on graph
++ xTrans = (((position.x() - m_axisCacheX.min() + 0.5f) * m_cachedBarSpacing.width())
++ - m_rowWidth) / m_scaleFactor;
++ zTrans = (m_columnDepth - ((position.z() - m_axisCacheZ.min() + 0.5f)
++ * m_cachedBarSpacing.height())) / m_scaleFactor;
++ yTrans = m_axisCacheY.positionAt(position.y());
++ } else {
++ xTrans = position.x() * m_xScaleFactor;
++ yTrans = position.y() + m_backgroundAdjustment;
++ zTrans = position.z() * -m_zScaleFactor;
++ }
++ return QVector3D(xTrans, yTrans, zTrans);
++ }
++
++ void Bars3DRenderer::updateAspectRatio(float ratio)
++ {
++ Q_UNUSED(ratio);
++ }
++
++ void Bars3DRenderer::updateFloorLevel(float level)
++ {
++ for (auto cache: m_renderCacheList)
++ cache->setDataDirty(true);
++ m_floorLevel = level;
++ calculateHeightAdjustment();
++ }
++
++ void Bars3DRenderer::updateMargin(float margin)
++ {
++ Abstract3DRenderer::updateMargin(margin);
++ calculateSceneScalingFactors();
++ }
++
++ QT_END_NAMESPACE