| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the Qt Data Visualization module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:GPL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 3 or (at your option) any later version |
| ** approved by the KDE Free Qt Foundation. The licenses are as published by |
| ** the Free Software Foundation and appearing in the file LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #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 <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_DATAVISUALIZATION |
| |
| const bool sliceGridLabels = true; |
| |
| Bars3DRenderer::Bars3DRenderer(Bars3DController *controller) |
| : Abstract3DRenderer(controller), |
| m_cachedIsSlicingActivated(false), |
| m_cachedRowCount(0), |
| m_cachedColumnCount(0), |
| 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; |
| |
| 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); |
| |
| foreach (SeriesRenderCache *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 QVector<Bars3DController::ChangeRow> &rows) |
| { |
| int minRow = m_axisCacheZ.min(); |
| int maxRow = m_axisCacheZ.max(); |
| BarSeriesRenderCache *cache = 0; |
| const QBar3DSeries *prevSeries = 0; |
| const QBarDataArray *dataArray = 0; |
| |
| foreach (Bars3DController::ChangeRow 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 QVector<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; |
| |
| foreach (Bars3DController::ChangeItem 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(); |
| QVector<BarRenderSliceItem> *firstVisualSliceArray = 0; |
| BarRenderSliceItem *selectedItem = 0; |
| |
| QQuaternion seriesRotation; |
| foreach (SeriesRenderCache *baseCache, m_renderCacheList) { |
| if (baseCache->isVisible() |
| && (baseCache == m_selectedSeriesCache |
| || m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionMultiSeries))) { |
| BarSeriesRenderCache *cache = static_cast<BarSeriesRenderCache *>(baseCache); |
| QVector<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); |
| |
| BarRenderItem *dummyItem(0); |
| 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) { |
| foreach (SeriesRenderCache *baseCache, m_renderCacheList) { |
| if (baseCache->isVisible()) { |
| BarSeriesRenderCache *cache = static_cast<BarSeriesRenderCache *>(baseCache); |
| QVector<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(*dummyItem, sliceSelectionLabel, viewMatrix, projectionMatrix, |
| positionComp, identityQuaternion, 0, m_cachedSelectionMode, |
| m_labelShader, m_labelObj, activeCamera, false, false, |
| Drawer::LabelTop, Qt::AlignCenter, true); |
| } |
| m_drawer->drawLabel(*dummyItem, 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(*dummyItem, 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(*dummyItem, 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); |
| foreach (SeriesRenderCache *baseCache, m_renderCacheList) { |
| if (baseCache->isVisible()) { |
| BarSeriesRenderCache *cache = static_cast<BarSeriesRenderCache *>(baseCache); |
| float seriesPos = m_seriesStart + m_seriesStep * cache->visualIndex() + 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 |
| foreach (SeriesRenderCache *baseCache, m_renderCacheList) { |
| if (baseCache->isVisible()) { |
| BarSeriesRenderCache *cache = static_cast<BarSeriesRenderCache *>(baseCache); |
| float seriesPos = m_seriesStart + m_seriesStep * cache->visualIndex() + 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()); |
| foreach (SeriesRenderCache *baseCache, m_renderCacheList) { |
| if (baseCache->isVisible()) { |
| BarSeriesRenderCache *cache = static_cast<BarSeriesRenderCache *>(baseCache); |
| float seriesPos = m_seriesStart + m_seriesStep * cache->visualIndex() + 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) |
| barColor = baseColor; |
| 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); |
| |
| float labelAutoAngle = m_axisCacheY.labelAutoRotation(); |
| float labelAngleFraction = labelAutoAngle / 90.0f; |
| float fractionCamY = activeCamera->yRotation() * labelAngleFraction; |
| float fractionCamX = activeCamera->xRotation() * 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 = activeCamera->xRotation() * 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 = activeCamera->xRotation() * 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::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; |
| |
| // 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); |
| } |
| } |
| |
| 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()); |
| foreach (SeriesRenderCache *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) |
| { |
| foreach (SeriesRenderCache *cache, m_renderCacheList) |
| cache->setDataDirty(true); |
| m_floorLevel = level; |
| calculateHeightAdjustment(); |
| } |
| |
| void Bars3DRenderer::updateMargin(float margin) |
| { |
| Abstract3DRenderer::updateMargin(margin); |
| calculateSceneScalingFactors(); |
| } |
| |
| QT_END_NAMESPACE_DATAVISUALIZATION |