blob: c57339db3f7ac3f1fab3582e214b067b86f1368b [file] [log] [blame]
/****************************************************************************
**
** 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