blob: fbf2b2cf46e80e8b7d60a213fb21fd58587f5ee9 [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 "scatter3drenderer_p.h"
#include "q3dcamera_p.h"
#include "shaderhelper_p.h"
#include "texturehelper_p.h"
#include "utils_p.h"
#include "scatterseriesrendercache_p.h"
#include "scatterobjectbufferhelper_p.h"
#include "scatterpointbufferhelper_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 GLfloat defaultMinSize = 0.01f;
const GLfloat defaultMaxSize = 0.1f;
const GLfloat itemScaler = 3.0f;
Scatter3DRenderer::Scatter3DRenderer(Scatter3DController *controller)
: Abstract3DRenderer(controller),
m_selectedItem(0),
m_updateLabels(false),
m_dotShader(0),
m_dotGradientShader(0),
m_staticSelectedItemGradientShader(0),
m_staticSelectedItemShader(0),
m_pointShader(0),
m_depthShader(0),
m_selectionShader(0),
m_backgroundShader(0),
m_staticGradientPointShader(0),
m_bgrTexture(0),
m_selectionTexture(0),
m_depthFrameBuffer(0),
m_selectionFrameBuffer(0),
m_selectionDepthBuffer(0),
m_shadowQualityToShader(100.0f),
m_shadowQualityMultiplier(3),
m_scaleX(0.0f),
m_scaleY(0.0f),
m_scaleZ(0.0f),
m_selectedItemIndex(Scatter3DController::invalidSelectionIndex()),
m_selectedSeriesCache(0),
m_oldSelectedSeriesCache(0),
m_dotSizeScale(1.0f),
m_maxItemSize(0.0f),
m_clickedIndex(Scatter3DController::invalidSelectionIndex()),
m_havePointSeries(false),
m_haveMeshSeries(false),
m_haveUniformColorMeshSeries(false),
m_haveGradientMeshSeries(false)
{
initializeOpenGL();
}
Scatter3DRenderer::~Scatter3DRenderer()
{
contextCleanup();
delete m_dotShader;
delete m_staticSelectedItemGradientShader;
delete m_staticSelectedItemShader;
delete m_dotGradientShader;
delete m_depthShader;
delete m_selectionShader;
delete m_backgroundShader;
delete m_staticGradientPointShader;
}
void Scatter3DRenderer::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 Scatter3DRenderer::initializeOpenGL()
{
Abstract3DRenderer::initializeOpenGL();
// Initialize shaders
if (!m_isOpenGLES) {
initDepthShader(); // For shadows
loadGridLineMesh();
} else {
initPointShader();
}
// Init selection shader
initSelectionShader();
// Set view port
glViewport(m_primarySubViewport.x(),
m_primarySubViewport.y(),
m_primarySubViewport.width(),
m_primarySubViewport.height());
// Load background mesh (we need to be initialized first)
loadBackgroundMesh();
}
void Scatter3DRenderer::fixCameraTarget(QVector3D &target)
{
target.setX(target.x() * m_scaleX);
target.setY(target.y() * m_scaleY);
target.setZ(target.z() * -m_scaleZ);
}
void Scatter3DRenderer::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_scaleX)
minBounds.setX(-1.0f + (2.0f * qAbs(minBounds.x() + m_scaleX) / itemRangeX));
else
minBounds.setX(-1.0f);
if (minBounds.y() < -m_scaleY)
minBounds.setY(-(-1.0f + (2.0f * qAbs(minBounds.y() + m_scaleY) / itemRangeY)));
else
minBounds.setY(1.0f);
if (minBounds.z() < -m_scaleZ)
minBounds.setZ(-(-1.0f + (2.0f * qAbs(minBounds.z() + m_scaleZ) / itemRangeZ)));
else
minBounds.setZ(1.0f);
if (maxBounds.x() > m_scaleX)
maxBounds.setX(1.0f - (2.0f * qAbs(maxBounds.x() - m_scaleX) / itemRangeX));
else
maxBounds.setX(1.0f);
if (maxBounds.y() > m_scaleY)
maxBounds.setY(-(1.0f - (2.0f * qAbs(maxBounds.y() - m_scaleY) / itemRangeY)));
else
maxBounds.setY(-1.0f);
if (maxBounds.z() > m_scaleZ)
maxBounds.setZ(-(1.0f - (2.0f * qAbs(maxBounds.z() - m_scaleZ) / itemRangeZ)));
else
maxBounds.setZ(-1.0f);
}
void Scatter3DRenderer::updateData()
{
calculateSceneScalingFactors();
int totalDataSize = 0;
foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
ScatterSeriesRenderCache *cache = static_cast<ScatterSeriesRenderCache *>(baseCache);
if (cache->isVisible()) {
const QScatter3DSeries *currentSeries = cache->series();
ScatterRenderItemArray &renderArray = cache->renderArray();
QScatterDataProxy *dataProxy = currentSeries->dataProxy();
const QScatterDataArray &dataArray = *dataProxy->array();
int dataSize = dataArray.size();
totalDataSize += dataSize;
if (cache->dataDirty()) {
if (dataSize != renderArray.size())
renderArray.resize(dataSize);
for (int i = 0; i < dataSize; i++)
updateRenderItem(dataArray.at(i), renderArray[i]);
if (m_cachedOptimizationHint.testFlag(QAbstract3DGraph::OptimizationStatic))
cache->setStaticBufferDirty(true);
cache->setDataDirty(false);
}
}
}
if (totalDataSize) {
m_dotSizeScale = GLfloat(qBound(defaultMinSize,
2.0f / float(qSqrt(qreal(totalDataSize))),
defaultMaxSize));
}
if (m_cachedOptimizationHint.testFlag(QAbstract3DGraph::OptimizationStatic)) {
foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
ScatterSeriesRenderCache *cache = static_cast<ScatterSeriesRenderCache *>(baseCache);
if (cache->isVisible()) {
ScatterRenderItemArray &renderArray = cache->renderArray();
const int renderArraySize = renderArray.size();
if (cache->mesh() == QAbstract3DSeries::MeshPoint) {
ScatterPointBufferHelper *points = cache->bufferPoints();
if (!points) {
points = new ScatterPointBufferHelper();
cache->setBufferPoints(points);
}
points->setScaleY(m_scaleY);
points->load(cache);
} else {
ScatterObjectBufferHelper *object = cache->bufferObject();
if (!object) {
object = new ScatterObjectBufferHelper();
cache->setBufferObject(object);
}
if (renderArraySize != cache->oldArraySize()
|| cache->object()->objectFile() != cache->oldMeshFileName()
|| cache->staticBufferDirty()) {
object->setScaleY(m_scaleY);
object->fullLoad(cache, m_dotSizeScale);
cache->setOldArraySize(renderArraySize);
cache->setOldMeshFileName(cache->object()->objectFile());
} else {
object->update(cache, m_dotSizeScale);
}
}
cache->setStaticBufferDirty(false);
}
}
}
updateSelectedItem(m_selectedItemIndex,
m_selectedSeriesCache ? m_selectedSeriesCache->series() : 0);
}
void Scatter3DRenderer::updateSeries(const QList<QAbstract3DSeries *> &seriesList)
{
int seriesCount = seriesList.size();
// Check OptimizationStatic specific issues before populate marks changeTracker done
if (m_cachedOptimizationHint.testFlag(QAbstract3DGraph::OptimizationStatic)) {
for (int i = 0; i < seriesCount; i++) {
QScatter3DSeries *scatterSeries = static_cast<QScatter3DSeries *>(seriesList[i]);
if (scatterSeries->isVisible()) {
QAbstract3DSeriesChangeBitField &changeTracker = scatterSeries->d_ptr->m_changeTracker;
ScatterSeriesRenderCache *cache =
static_cast<ScatterSeriesRenderCache *>(m_renderCacheList.value(scatterSeries));
if (cache) {
if (changeTracker.baseGradientChanged || changeTracker.colorStyleChanged)
cache->setStaticObjectUVDirty(true);
if (cache->itemSize() != scatterSeries->itemSize())
cache->setStaticBufferDirty(true);
}
}
}
}
Abstract3DRenderer::updateSeries(seriesList);
float maxItemSize = 0.0f;
float itemSize = 0.0f;
bool noSelection = true;
m_havePointSeries = false;
m_haveMeshSeries = false;
m_haveUniformColorMeshSeries = false;
m_haveGradientMeshSeries = false;
for (int i = 0; i < seriesCount; i++) {
QScatter3DSeries *scatterSeries = static_cast<QScatter3DSeries *>(seriesList[i]);
if (scatterSeries->isVisible()) {
ScatterSeriesRenderCache *cache =
static_cast<ScatterSeriesRenderCache *>(m_renderCacheList.value(scatterSeries));
itemSize = scatterSeries->itemSize();
if (maxItemSize < itemSize)
maxItemSize = itemSize;
if (cache->itemSize() != itemSize)
cache->setItemSize(itemSize);
if (noSelection
&& scatterSeries->selectedItem() != QScatter3DSeries::invalidSelectionIndex()) {
if (m_selectionLabel != cache->itemLabel())
m_selectionLabelDirty = true;
noSelection = false;
}
if (cache->mesh() == QAbstract3DSeries::MeshPoint) {
m_havePointSeries = true;
} else {
m_haveMeshSeries = true;
if (cache->colorStyle() == Q3DTheme::ColorStyleUniform)
m_haveUniformColorMeshSeries = true;
else
m_haveGradientMeshSeries = true;
}
if (cache->staticBufferDirty()) {
if (cache->mesh() != QAbstract3DSeries::MeshPoint) {
ScatterObjectBufferHelper *object = cache->bufferObject();
object->update(cache, m_dotSizeScale);
}
cache->setStaticBufferDirty(false);
}
if (cache->staticObjectUVDirty()) {
if (cache->mesh() == QAbstract3DSeries::MeshPoint) {
ScatterPointBufferHelper *object = cache->bufferPoints();
object->updateUVs(cache);
} else {
ScatterObjectBufferHelper *object = cache->bufferObject();
object->updateUVs(cache);
}
cache->setStaticObjectUVDirty(false);
}
}
}
m_maxItemSize = maxItemSize;
calculateSceneScalingFactors();
if (noSelection) {
if (!selectionLabel().isEmpty())
m_selectionLabelDirty = true;
m_selectedSeriesCache = 0;
}
}
SeriesRenderCache *Scatter3DRenderer::createNewCache(QAbstract3DSeries *series)
{
return new ScatterSeriesRenderCache(series, this);
}
void Scatter3DRenderer::updateItems(const QVector<Scatter3DController::ChangeItem> &items)
{
ScatterSeriesRenderCache *cache = 0;
const QScatter3DSeries *prevSeries = 0;
const QScatterDataArray *dataArray = 0;
const bool optimizationStatic = m_cachedOptimizationHint.testFlag(
QAbstract3DGraph::OptimizationStatic);
foreach (Scatter3DController::ChangeItem item, items) {
QScatter3DSeries *currentSeries = item.series;
if (currentSeries != prevSeries) {
cache = static_cast<ScatterSeriesRenderCache *>(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()) {
const int index = item.index;
if (index >= cache->renderArray().size())
continue; // Items removed from array for same render
bool oldVisibility;
ScatterRenderItem &item = cache->renderArray()[index];
if (optimizationStatic)
oldVisibility = item.isVisible();
updateRenderItem(dataArray->at(index), item);
if (optimizationStatic) {
if (!cache->visibilityChanged() && oldVisibility != item.isVisible())
cache->setVisibilityChanged(true);
cache->updateIndices().append(index);
}
}
}
if (optimizationStatic) {
foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
ScatterSeriesRenderCache *cache = static_cast<ScatterSeriesRenderCache *>(baseCache);
if (cache->isVisible() && cache->updateIndices().size()) {
if (cache->mesh() == QAbstract3DSeries::MeshPoint) {
cache->bufferPoints()->update(cache);
if (cache->colorStyle() == Q3DTheme::ColorStyleRangeGradient)
cache->bufferPoints()->updateUVs(cache);
} else {
if (cache->visibilityChanged()) {
// If any change changes item visibility, full load is needed to
// resize the buffers.
cache->updateIndices().clear();
cache->bufferObject()->fullLoad(cache, m_dotSizeScale);
} else {
cache->bufferObject()->update(cache, m_dotSizeScale);
if (cache->colorStyle() == Q3DTheme::ColorStyleRangeGradient)
cache->bufferObject()->updateUVs(cache);
}
}
cache->updateIndices().clear();
}
cache->setVisibilityChanged(false);
}
}
}
void Scatter3DRenderer::updateScene(Q3DScene *scene)
{
scene->activeCamera()->d_ptr->setMinYRotation(-90.0f);
Abstract3DRenderer::updateScene(scene);
}
void Scatter3DRenderer::updateAxisLabels(QAbstract3DAxis::AxisOrientation orientation,
const QStringList &labels)
{
Abstract3DRenderer::updateAxisLabels(orientation, labels);
// Angular axis label dimensions affect the chart dimensions
if (m_polarGraph && orientation == QAbstract3DAxis::AxisOrientationX)
calculateSceneScalingFactors();
}
void Scatter3DRenderer::updateAxisTitleVisibility(QAbstract3DAxis::AxisOrientation orientation,
bool visible)
{
Abstract3DRenderer::updateAxisTitleVisibility(orientation, visible);
// Angular axis title existence affects the chart dimensions
if (m_polarGraph && orientation == QAbstract3DAxis::AxisOrientationX)
calculateSceneScalingFactors();
}
void Scatter3DRenderer::updateOptimizationHint(QAbstract3DGraph::OptimizationHints hint)
{
Abstract3DRenderer::updateOptimizationHint(hint);
Abstract3DRenderer::reInitShaders();
if (m_isOpenGLES && hint.testFlag(QAbstract3DGraph::OptimizationStatic)
&& !m_staticGradientPointShader) {
initStaticPointShaders(QStringLiteral(":/shaders/vertexPointES2_UV"),
QStringLiteral(":/shaders/fragmentLabel"));
}
}
void Scatter3DRenderer::updateMargin(float margin)
{
Abstract3DRenderer::updateMargin(margin);
calculateSceneScalingFactors();
}
void Scatter3DRenderer::resetClickedStatus()
{
m_clickedIndex = Scatter3DController::invalidSelectionIndex();
m_clickedSeries = 0;
}
void Scatter3DRenderer::render(GLuint defaultFboHandle)
{
// Handle GL state setup for FBO buffers and clearing of the render surface
Abstract3DRenderer::render(defaultFboHandle);
if (m_axisCacheX.positionsDirty())
m_axisCacheX.updateAllPositions();
if (m_axisCacheY.positionsDirty())
m_axisCacheY.updateAllPositions();
if (m_axisCacheZ.positionsDirty())
m_axisCacheZ.updateAllPositions();
// Draw dots scene
drawScene(defaultFboHandle);
}
void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
{
GLfloat backgroundRotation = 0;
GLfloat selectedItemSize = 0.0f;
// Get the optimization flag
const bool optimizationDefault =
!m_cachedOptimizationHint.testFlag(QAbstract3DGraph::OptimizationStatic);
const Q3DCamera *activeCamera = m_cachedScene->activeCamera();
QVector4D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor());
// Specify viewport
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);
}
// Calculate view matrix
QMatrix4x4 viewMatrix = activeCamera->d_ptr->viewMatrix();
QMatrix4x4 projectionViewMatrix = projectionMatrix * viewMatrix;
// Calculate label flipping
if (viewMatrix.row(0).x() > 0)
m_zFlipped = false;
else
m_zFlipped = true;
if (viewMatrix.row(0).z() <= 0)
m_xFlipped = false;
else
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;
m_yFlippedForGrid = m_yFlipped; // Polar axis grid drawing in abstract needs this
// Calculate background rotation
if (!m_zFlipped && !m_xFlipped)
backgroundRotation = 270.0f;
else if (!m_zFlipped && m_xFlipped)
backgroundRotation = 180.0f;
else if (m_zFlipped && m_xFlipped)
backgroundRotation = 90.0f;
else if (m_zFlipped && !m_xFlipped)
backgroundRotation = 0.0f;
// Get light position from the scene
QVector3D lightPos = m_cachedScene->activeLight()->position();
// Introduce regardless of shadow quality to simplify logic
QMatrix4x4 depthProjectionViewMatrix;
ShaderHelper *pointSelectionShader;
if (!m_isOpenGLES) {
#if !defined(QT_OPENGL_ES_2)
if (m_havePointSeries) {
glEnable(GL_POINT_SMOOTH);
glEnable(GL_PROGRAM_POINT_SIZE);
}
if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
// Render scene into a depth texture for using with shadow mapping
// Bind depth shader
m_depthShader->bind();
// Set viewport for depth map rendering. Must match texture size. Larger values give smoother shadows.
glViewport(0, 0,
m_primarySubViewport.width() * m_shadowQualityMultiplier,
m_primarySubViewport.height() * m_shadowQualityMultiplier);
// Enable drawing to framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, m_depthFrameBuffer);
glClear(GL_DEPTH_BUFFER_BIT);
// Set front face culling to reduce self-shadowing issues
glCullFace(GL_FRONT);
QMatrix4x4 depthViewMatrix;
QMatrix4x4 depthProjectionMatrix;
// 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, 2.5f / m_autoScaleAdjustment);
depthViewMatrix.lookAt(depthLightPos, zeroVector, upVector);
// Set the depth projection matrix
depthProjectionMatrix.perspective(15.0f, viewPortRatio, 3.0f, 100.0f);
depthProjectionViewMatrix = depthProjectionMatrix * depthViewMatrix;
// Draw dots to depth buffer
foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
if (baseCache->isVisible()) {
ScatterSeriesRenderCache *cache =
static_cast<ScatterSeriesRenderCache *>(baseCache);
ObjectHelper *dotObj = cache->object();
QQuaternion seriesRotation(cache->meshRotation());
const ScatterRenderItemArray &renderArray = cache->renderArray();
const int renderArraySize = renderArray.size();
bool drawingPoints = (cache->mesh() == QAbstract3DSeries::MeshPoint);
float itemSize = cache->itemSize() / itemScaler;
if (itemSize == 0.0f)
itemSize = m_dotSizeScale;
if (drawingPoints) {
// Scale points based on shadow quality for shadows, not by zoom level
m_funcs_2_1->glPointSize(itemSize * 100.0f * m_shadowQualityMultiplier);
}
QVector3D modelScaler(itemSize, itemSize, itemSize);
if (!optimizationDefault
&& ((drawingPoints && cache->bufferPoints()->indexCount() == 0)
|| (!drawingPoints && cache->bufferObject()->indexCount() == 0))) {
continue;
}
int loopCount = 1;
if (optimizationDefault)
loopCount = renderArraySize;
for (int dot = 0; dot < loopCount; dot++) {
const ScatterRenderItem &item = renderArray.at(dot);
if (!item.isVisible() && optimizationDefault)
continue;
QMatrix4x4 modelMatrix;
QMatrix4x4 MVPMatrix;
if (optimizationDefault) {
modelMatrix.translate(item.translation());
if (!drawingPoints) {
if (!seriesRotation.isIdentity() || !item.rotation().isIdentity())
modelMatrix.rotate(seriesRotation * item.rotation());
modelMatrix.scale(modelScaler);
}
}
MVPMatrix = depthProjectionViewMatrix * modelMatrix;
m_depthShader->setUniformValue(m_depthShader->MVP(), MVPMatrix);
if (drawingPoints) {
if (optimizationDefault)
m_drawer->drawPoint(m_depthShader);
else
m_drawer->drawPoints(m_depthShader, cache->bufferPoints(), 0);
} else {
if (optimizationDefault) {
// 1st attribute buffer : vertices
glEnableVertexAttribArray(m_depthShader->posAtt());
glBindBuffer(GL_ARRAY_BUFFER, dotObj->vertexBuf());
glVertexAttribPointer(m_depthShader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0,
(void *)0);
// Index buffer
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dotObj->elementBuf());
// Draw the triangles
glDrawElements(GL_TRIANGLES, dotObj->indexCount(),
GL_UNSIGNED_INT, (void *)0);
// Free buffers
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDisableVertexAttribArray(m_depthShader->posAtt());
} else {
ScatterObjectBufferHelper *object = cache->bufferObject();
// 1st attribute buffer : vertices
glEnableVertexAttribArray(m_depthShader->posAtt());
glBindBuffer(GL_ARRAY_BUFFER, object->vertexBuf());
glVertexAttribPointer(m_depthShader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0,
(void *)0);
// Index buffer
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, object->elementBuf());
// Draw the triangles
glDrawElements(GL_TRIANGLES, object->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 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());
}
#endif
pointSelectionShader = m_selectionShader;
} else {
pointSelectionShader = m_pointShader;
}
ShaderHelper *selectionShader = m_selectionShader;
// Do position mapping when necessary
if (m_graphPositionQueryPending) {
QVector3D graphDimensions(m_scaleX, m_scaleY, m_scaleZ);
queriedGraphPosition(projectionViewMatrix, graphDimensions, defaultFboHandle);
emit needRender();
}
// Skip selection mode drawing if we have no selection mode
if (m_cachedSelectionMode > QAbstract3DGraph::SelectionNone
&& SelectOnScene == m_selectionState
&& (m_visibleSeriesCount > 0 || !m_customRenderCache.isEmpty())
&& m_selectionTexture) {
// Draw dots 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 (= skipColor)
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
bool previousDrawingPoints = false;
int totalIndex = 0;
foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
if (baseCache->isVisible()) {
ScatterSeriesRenderCache *cache =
static_cast<ScatterSeriesRenderCache *>(baseCache);
ObjectHelper *dotObj = cache->object();
QQuaternion seriesRotation(cache->meshRotation());
const ScatterRenderItemArray &renderArray = cache->renderArray();
const int renderArraySize = renderArray.size();
bool drawingPoints = (cache->mesh() == QAbstract3DSeries::MeshPoint);
float itemSize = cache->itemSize() / itemScaler;
if (itemSize == 0.0f)
itemSize = m_dotSizeScale;
#if !defined(QT_OPENGL_ES_2)
if (drawingPoints && !m_isOpenGLES)
m_funcs_2_1->glPointSize(itemSize * activeCamera->zoomLevel());
#endif
QVector3D modelScaler(itemSize, itemSize, itemSize);
// Rebind selection shader if it has changed
if (!totalIndex || drawingPoints != previousDrawingPoints) {
previousDrawingPoints = drawingPoints;
if (drawingPoints)
selectionShader = pointSelectionShader;
else
selectionShader = m_selectionShader;
selectionShader->bind();
}
cache->setSelectionIndexOffset(totalIndex);
for (int dot = 0; dot < renderArraySize; dot++) {
const ScatterRenderItem &item = renderArray.at(dot);
if (!item.isVisible()) {
totalIndex++;
continue;
}
QMatrix4x4 modelMatrix;
QMatrix4x4 MVPMatrix;
modelMatrix.translate(item.translation());
if (!drawingPoints) {
if (!seriesRotation.isIdentity() || !item.rotation().isIdentity())
modelMatrix.rotate(seriesRotation * item.rotation());
modelMatrix.scale(modelScaler);
}
MVPMatrix = projectionViewMatrix * modelMatrix;
QVector4D dotColor = indexToSelectionColor(totalIndex++);
dotColor /= 255.0f;
selectionShader->setUniformValue(selectionShader->MVP(), MVPMatrix);
selectionShader->setUniformValue(selectionShader->color(), dotColor);
if (drawingPoints)
m_drawer->drawPoint(selectionShader);
else
m_drawer->drawSelectionObject(selectionShader, dotObj);
}
}
}
Abstract3DRenderer::drawCustomItems(RenderingSelection, m_selectionShader,
viewMatrix, projectionViewMatrix,
depthProjectionViewMatrix, m_depthTexture,
m_shadowQualityToShader);
drawLabels(true, activeCamera, viewMatrix, projectionMatrix);
glEnable(GL_DITHER);
// Read color under cursor
QVector4D clickedColor = Utils::getSelection(m_inputPosition,
m_viewport.height());
selectionColorToSeriesAndIndex(clickedColor, m_clickedIndex, m_clickedSeries);
m_clickResolved = true;
emit needRender();
// Revert to original fbo and viewport
glBindFramebuffer(GL_FRAMEBUFFER, defaultFboHandle);
glViewport(m_primarySubViewport.x(),
m_primarySubViewport.y(),
m_primarySubViewport.width(),
m_primarySubViewport.height());
}
// Draw dots
ShaderHelper *dotShader = 0;
GLuint gradientTexture = 0;
bool dotSelectionFound = false;
ScatterRenderItem *selectedItem(0);
QVector4D baseColor;
QVector4D dotColor;
bool previousDrawingPoints = false;
Q3DTheme::ColorStyle previousMeshColorStyle = Q3DTheme::ColorStyleUniform;
if (m_haveMeshSeries) {
// Set unchanging shader bindings
if (m_haveGradientMeshSeries) {
m_dotGradientShader->bind();
m_dotGradientShader->setUniformValue(m_dotGradientShader->lightP(), lightPos);
m_dotGradientShader->setUniformValue(m_dotGradientShader->view(), viewMatrix);
m_dotGradientShader->setUniformValue(m_dotGradientShader->ambientS(),
m_cachedTheme->ambientLightStrength());
m_dotGradientShader->setUniformValue(m_dotGradientShader->lightColor(), lightColor);
}
if (m_haveUniformColorMeshSeries) {
m_dotShader->bind();
m_dotShader->setUniformValue(m_dotShader->lightP(), lightPos);
m_dotShader->setUniformValue(m_dotShader->view(), viewMatrix);
m_dotShader->setUniformValue(m_dotShader->ambientS(),
m_cachedTheme->ambientLightStrength());
m_dotShader->setUniformValue(m_dotShader->lightColor(), lightColor);
dotShader = m_dotShader;
} else {
dotShader = m_dotGradientShader;
previousMeshColorStyle = Q3DTheme::ColorStyleRangeGradient;
m_dotGradientShader->setUniformValue(m_dotGradientShader->gradientHeight(), 0.0f);
}
} else {
dotShader = pointSelectionShader;
}
float rangeGradientYScaler = 0.5f / m_scaleY;
foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
if (baseCache->isVisible()) {
ScatterSeriesRenderCache *cache =
static_cast<ScatterSeriesRenderCache *>(baseCache);
ObjectHelper *dotObj = cache->object();
QQuaternion seriesRotation(cache->meshRotation());
ScatterRenderItemArray &renderArray = cache->renderArray();
const int renderArraySize = renderArray.size();
bool selectedSeries = m_cachedSelectionMode > QAbstract3DGraph::SelectionNone
&& (m_selectedSeriesCache == cache);
bool drawingPoints = (cache->mesh() == QAbstract3DSeries::MeshPoint);
Q3DTheme::ColorStyle colorStyle = cache->colorStyle();
bool colorStyleIsUniform = (colorStyle == Q3DTheme::ColorStyleUniform);
bool useColor = colorStyleIsUniform || drawingPoints;
bool rangeGradientPoints = drawingPoints
&& (colorStyle == Q3DTheme::ColorStyleRangeGradient);
float itemSize = cache->itemSize() / itemScaler;
if (itemSize == 0.0f)
itemSize = m_dotSizeScale;
#if !defined(QT_OPENGL_ES_2)
if (drawingPoints && !m_isOpenGLES)
m_funcs_2_1->glPointSize(itemSize * activeCamera->zoomLevel());
#endif
QVector3D modelScaler(itemSize, itemSize, itemSize);
int gradientImageHeight = cache->gradientImage().height();
int maxGradientPositition = gradientImageHeight - 1;
if (!optimizationDefault
&& ((drawingPoints && cache->bufferPoints()->indexCount() == 0)
|| (!drawingPoints && cache->bufferObject()->indexCount() == 0))) {
continue;
}
// Rebind shader if it has changed
if (drawingPoints != previousDrawingPoints
|| (!drawingPoints &&
(colorStyleIsUniform != (previousMeshColorStyle
== Q3DTheme::ColorStyleUniform)))
|| (!optimizationDefault && drawingPoints)) {
previousDrawingPoints = drawingPoints;
if (drawingPoints) {
if (!optimizationDefault && rangeGradientPoints) {
if (m_isOpenGLES)
dotShader = m_staticGradientPointShader;
else
dotShader = m_labelShader;
} else {
dotShader = pointSelectionShader;
}
} else {
if (colorStyleIsUniform)
dotShader = m_dotShader;
else
dotShader = m_dotGradientShader;
}
dotShader->bind();
}
if (!drawingPoints && !colorStyleIsUniform && previousMeshColorStyle != colorStyle) {
if (colorStyle == Q3DTheme::ColorStyleObjectGradient) {
dotShader->setUniformValue(dotShader->gradientMin(), 0.0f);
dotShader->setUniformValue(dotShader->gradientHeight(),
0.5f);
} else {
// Each dot is of uniform color according to its Y-coordinate
dotShader->setUniformValue(dotShader->gradientHeight(),
0.0f);
}
}
if (!drawingPoints)
previousMeshColorStyle = colorStyle;
if (useColor) {
baseColor = cache->baseColor();
dotColor = baseColor;
}
int loopCount = 1;
if (optimizationDefault)
loopCount = renderArraySize;
for (int i = 0; i < loopCount; i++) {
ScatterRenderItem &item = renderArray[i];
if (!item.isVisible() && optimizationDefault)
continue;
QMatrix4x4 modelMatrix;
QMatrix4x4 MVPMatrix;
QMatrix4x4 itModelMatrix;
if (optimizationDefault) {
modelMatrix.translate(item.translation());
if (!drawingPoints) {
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
if (useColor) {
if (rangeGradientPoints) {
// Drawing points with range gradient
// Get color from gradient based on items y position converted to percent
int position = ((item.translation().y() + m_scaleY) * rangeGradientYScaler) * gradientImageHeight;
position = qMin(maxGradientPositition, position); // clamp to edge
dotColor = Utils::vectorFromColor(
cache->gradientImage().pixel(0, position));
} else {
dotColor = baseColor;
}
} else {
gradientTexture = cache->baseGradientTexture();
}
if (!optimizationDefault && rangeGradientPoints)
gradientTexture = cache->baseGradientTexture();
GLfloat lightStrength = m_cachedTheme->lightStrength();
if (optimizationDefault && selectedSeries && (m_selectedItemIndex == i)) {
if (useColor)
dotColor = cache->singleHighlightColor();
else
gradientTexture = cache->singleHighlightGradientTexture();
lightStrength = m_cachedTheme->highlightLightStrength();
// Save the reference to the item to be used in label drawing
selectedItem = &item;
dotSelectionFound = true;
// Save selected item size (adjusted with font size) for selection label
// positioning
selectedItemSize = itemSize + m_drawer->scaledFontSize() - 0.05f;
}
if (!drawingPoints) {
// Set shader bindings
dotShader->setUniformValue(dotShader->model(), modelMatrix);
dotShader->setUniformValue(dotShader->nModel(),
itModelMatrix.inverted().transposed());
}
dotShader->setUniformValue(dotShader->MVP(), MVPMatrix);
if (useColor) {
dotShader->setUniformValue(dotShader->color(), dotColor);
} else if (colorStyle == Q3DTheme::ColorStyleRangeGradient) {
dotShader->setUniformValue(dotShader->gradientMin(),
(item.translation().y() + m_scaleY)
* rangeGradientYScaler);
}
if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) {
if (!drawingPoints) {
// Set shadow shader bindings
QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
dotShader->setUniformValue(dotShader->shadowQ(), m_shadowQualityToShader);
dotShader->setUniformValue(dotShader->depth(), depthMVPMatrix);
dotShader->setUniformValue(dotShader->lightS(), lightStrength / 10.0f);
// Draw the object
if (optimizationDefault) {
m_drawer->drawObject(dotShader, dotObj, gradientTexture,
m_depthTexture);
} else {
m_drawer->drawObject(dotShader, cache->bufferObject(), gradientTexture,
m_depthTexture);
}
} else {
// Draw the object
if (optimizationDefault)
m_drawer->drawPoint(dotShader);
else
m_drawer->drawPoints(dotShader, cache->bufferPoints(), gradientTexture);
}
} else {
if (!drawingPoints) {
// Set shadowless shader bindings
dotShader->setUniformValue(dotShader->lightS(), lightStrength);
// Draw the object
if (optimizationDefault)
m_drawer->drawObject(dotShader, dotObj, gradientTexture);
else
m_drawer->drawObject(dotShader, cache->bufferObject(), gradientTexture);
} else {
// Draw the object
if (optimizationDefault)
m_drawer->drawPoint(dotShader);
else
m_drawer->drawPoints(dotShader, cache->bufferPoints(), gradientTexture);
}
}
}
// Draw the selected item on static optimization
if (!optimizationDefault && selectedSeries
&& m_selectedItemIndex != Scatter3DController::invalidSelectionIndex()) {
ScatterRenderItem &item = renderArray[m_selectedItemIndex];
if (item.isVisible()) {
ShaderHelper *selectionShader;
if (drawingPoints) {
selectionShader = pointSelectionShader;
} else {
if (colorStyleIsUniform)
selectionShader = m_staticSelectedItemShader;
else
selectionShader = m_staticSelectedItemGradientShader;
}
selectionShader->bind();
ObjectHelper *dotObj = cache->object();
QMatrix4x4 modelMatrix;
QMatrix4x4 itModelMatrix;
modelMatrix.translate(item.translation());
if (!drawingPoints) {
if (!seriesRotation.isIdentity() || !item.rotation().isIdentity()) {
QQuaternion totalRotation = seriesRotation * item.rotation();
modelMatrix.rotate(totalRotation);
itModelMatrix.rotate(totalRotation);
}
modelMatrix.scale(modelScaler);
itModelMatrix.scale(modelScaler);
selectionShader->setUniformValue(selectionShader->lightP(),
lightPos);
selectionShader->setUniformValue(selectionShader->view(),
viewMatrix);
selectionShader->setUniformValue(selectionShader->ambientS(),
m_cachedTheme->ambientLightStrength());
selectionShader->setUniformValue(selectionShader->lightColor(),
lightColor);
}
QMatrix4x4 MVPMatrix;
#ifdef SHOW_DEPTH_TEXTURE_SCENE
MVPMatrix = depthProjectionViewMatrix * modelMatrix;
#else
MVPMatrix = projectionViewMatrix * modelMatrix;
#endif
if (useColor)
dotColor = cache->singleHighlightColor();
else
gradientTexture = cache->singleHighlightGradientTexture();
GLfloat lightStrength = m_cachedTheme->highlightLightStrength();
// Save the reference to the item to be used in label drawing
selectedItem = &item;
dotSelectionFound = true;
// Save selected item size (adjusted with font size) for selection label
// positioning
selectedItemSize = itemSize + m_drawer->scaledFontSize() - 0.05f;
if (!drawingPoints) {
// Set shader bindings
selectionShader->setUniformValue(selectionShader->model(), modelMatrix);
selectionShader->setUniformValue(selectionShader->nModel(),
itModelMatrix.inverted().transposed());
if (!colorStyleIsUniform) {
if (colorStyle == Q3DTheme::ColorStyleObjectGradient) {
selectionShader->setUniformValue(selectionShader->gradientMin(),
0.0f);
selectionShader->setUniformValue(selectionShader->gradientHeight(),
0.5f);
} else {
// Each dot is of uniform color according to its Y-coordinate
selectionShader->setUniformValue(selectionShader->gradientHeight(),
0.0f);
selectionShader->setUniformValue(selectionShader->gradientMin(),
(item.translation().y() + m_scaleY)
* rangeGradientYScaler);
}
}
}
selectionShader->setUniformValue(selectionShader->MVP(), MVPMatrix);
if (useColor)
selectionShader->setUniformValue(selectionShader->color(), dotColor);
if (!drawingPoints) {
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(-1.0f, 1.0f);
}
if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone
&& !m_isOpenGLES) {
if (!drawingPoints) {
// Set shadow shader bindings
QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
selectionShader->setUniformValue(selectionShader->shadowQ(),
m_shadowQualityToShader);
selectionShader->setUniformValue(selectionShader->depth(),
depthMVPMatrix);
selectionShader->setUniformValue(selectionShader->lightS(),
lightStrength / 10.0f);
// Draw the object
m_drawer->drawObject(selectionShader, dotObj, gradientTexture,
m_depthTexture);
} else {
// Draw the object
m_drawer->drawPoint(selectionShader);
}
} else {
if (!drawingPoints) {
// Set shadowless shader bindings
selectionShader->setUniformValue(selectionShader->lightS(),
lightStrength);
// Draw the object
m_drawer->drawObject(selectionShader, dotObj, gradientTexture);
} else {
// Draw the object
m_drawer->drawPoint(selectionShader);
}
}
if (!drawingPoints)
glDisable(GL_POLYGON_OFFSET_FILL);
}
dotShader->bind();
}
}
}
#if !defined(QT_OPENGL_ES_2)
if (m_havePointSeries) {
glDisable(GL_POINT_SMOOTH);
glDisable(GL_PROGRAM_POINT_SIZE);
}
#endif
// Bind background shader
m_backgroundShader->bind();
glCullFace(GL_BACK);
// Draw background
if (m_cachedTheme->isBackgroundEnabled() && m_backgroundObj) {
QMatrix4x4 modelMatrix;
QMatrix4x4 MVPMatrix;
QMatrix4x4 itModelMatrix;
QVector3D bgScale(m_scaleXWithBackground, m_scaleYWithBackground,
m_scaleZWithBackground);
modelMatrix.scale(bgScale);
// If we're viewing from below, background object must be flipped
if (m_yFlipped) {
modelMatrix.rotate(m_xFlipRotation);
modelMatrix.rotate(270.0f - backgroundRotation, 0.0f, 1.0f, 0.0f);
} else {
modelMatrix.rotate(backgroundRotation, 0.0f, 1.0f, 0.0f);
}
itModelMatrix = modelMatrix; // Only scaling and rotations, can be used directly
#ifdef SHOW_DEPTH_TEXTURE_SCENE
MVPMatrix = depthProjectionViewMatrix * modelMatrix;
#else
MVPMatrix = projectionViewMatrix * modelMatrix;
#endif
QVector4D backgroundColor = Utils::vectorFromColor(m_cachedTheme->backgroundColor());
// Set shader bindings
m_backgroundShader->setUniformValue(m_backgroundShader->lightP(), lightPos);
m_backgroundShader->setUniformValue(m_backgroundShader->view(), viewMatrix);
m_backgroundShader->setUniformValue(m_backgroundShader->model(), modelMatrix);
m_backgroundShader->setUniformValue(m_backgroundShader->nModel(),
itModelMatrix.inverted().transposed());
m_backgroundShader->setUniformValue(m_backgroundShader->MVP(), MVPMatrix);
m_backgroundShader->setUniformValue(m_backgroundShader->color(), backgroundColor);
m_backgroundShader->setUniformValue(m_backgroundShader->ambientS(),
m_cachedTheme->ambientLightStrength() * 2.0f);
m_backgroundShader->setUniformValue(m_backgroundShader->lightColor(), lightColor);
if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) {
// Set shadow shader bindings
QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
m_backgroundShader->setUniformValue(m_backgroundShader->shadowQ(),
m_shadowQualityToShader);
m_backgroundShader->setUniformValue(m_backgroundShader->depth(), depthMVPMatrix);
m_backgroundShader->setUniformValue(m_backgroundShader->lightS(),
m_cachedTheme->lightStrength() / 10.0f);
// Draw the object
m_drawer->drawObject(m_backgroundShader, m_backgroundObj, 0, m_depthTexture);
} else {
// Set shadowless shader bindings
m_backgroundShader->setUniformValue(m_backgroundShader->lightS(),
m_cachedTheme->lightStrength());
// Draw the object
m_drawer->drawObject(m_backgroundShader, m_backgroundObj);
}
}
// Draw grid lines
QVector3D gridLineScaleX(m_scaleXWithBackground, gridLineWidth, gridLineWidth);
QVector3D gridLineScaleZ(gridLineWidth, gridLineWidth, m_scaleZWithBackground);
QVector3D gridLineScaleY(gridLineWidth, m_scaleYWithBackground, gridLineWidth);
if (m_cachedTheme->isGridEnabled()) {
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());
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);
}
QQuaternion lineYRotation;
QQuaternion lineXRotation;
if (m_xFlipped)
lineYRotation = m_yRightAngleRotationNeg;
else
lineYRotation = m_yRightAngleRotation;
if (m_yFlippedForGrid)
lineXRotation = m_xRightAngleRotation;
else
lineXRotation = m_xRightAngleRotationNeg;
GLfloat yFloorLinePosition = -m_scaleYWithBackground + gridLineOffset;
if (m_yFlippedForGrid)
yFloorLinePosition = -yFloorLinePosition;
// Rows (= Z)
if (m_axisCacheZ.segmentCount() > 0) {
// Floor lines
int gridLineCount = m_axisCacheZ.gridLineCount();
if (m_polarGraph) {
drawRadialGrid(lineShader, yFloorLinePosition, projectionViewMatrix,
depthProjectionViewMatrix);
} else {
for (int line = 0; line < gridLineCount; line++) {
QMatrix4x4 modelMatrix;
QMatrix4x4 MVPMatrix;
QMatrix4x4 itModelMatrix;
modelMatrix.translate(0.0f, yFloorLinePosition,
m_axisCacheZ.gridLinePosition(line));
modelMatrix.scale(gridLineScaleX);
itModelMatrix.scale(gridLineScaleX);
modelMatrix.rotate(lineXRotation);
itModelMatrix.rotate(lineXRotation);
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) {
QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
// Set shadow shader bindings
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);
}
}
}
// Side wall lines
GLfloat lineXTrans = m_scaleXWithBackground - gridLineOffset;
if (!m_xFlipped)
lineXTrans = -lineXTrans;
for (int line = 0; line < gridLineCount; line++) {
QMatrix4x4 modelMatrix;
QMatrix4x4 MVPMatrix;
QMatrix4x4 itModelMatrix;
modelMatrix.translate(lineXTrans, 0.0f, m_axisCacheZ.gridLinePosition(line));
modelMatrix.scale(gridLineScaleY);
itModelMatrix.scale(gridLineScaleY);
if (m_isOpenGLES) {
modelMatrix.rotate(m_zRightAngleRotation);
itModelMatrix.rotate(m_zRightAngleRotation);
} else {
modelMatrix.rotate(lineYRotation);
itModelMatrix.rotate(lineYRotation);
}
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) {
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);
}
} else {
m_drawer->drawLine(lineShader);
}
}
}
}
// Columns (= X)
if (m_axisCacheX.segmentCount() > 0) {
if (m_isOpenGLES)
lineXRotation = m_yRightAngleRotation;
// Floor lines
int gridLineCount = m_axisCacheX.gridLineCount();
if (m_polarGraph) {
drawAngularGrid(lineShader, yFloorLinePosition, projectionViewMatrix,
depthProjectionViewMatrix);
} else {
for (int line = 0; line < gridLineCount; line++) {
QMatrix4x4 modelMatrix;
QMatrix4x4 MVPMatrix;
QMatrix4x4 itModelMatrix;
modelMatrix.translate(m_axisCacheX.gridLinePosition(line), yFloorLinePosition,
0.0f);
modelMatrix.scale(gridLineScaleZ);
itModelMatrix.scale(gridLineScaleZ);
modelMatrix.rotate(lineXRotation);
itModelMatrix.rotate(lineXRotation);
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) {
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);
}
} else {
m_drawer->drawLine(lineShader);
}
}
// Back wall lines
GLfloat lineZTrans = m_scaleZWithBackground - gridLineOffset;
if (!m_zFlipped)
lineZTrans = -lineZTrans;
for (int line = 0; line < gridLineCount; line++) {
QMatrix4x4 modelMatrix;
QMatrix4x4 MVPMatrix;
QMatrix4x4 itModelMatrix;
modelMatrix.translate(m_axisCacheX.gridLinePosition(line), 0.0f, lineZTrans);
modelMatrix.scale(gridLineScaleY);
itModelMatrix.scale(gridLineScaleY);
if (m_isOpenGLES) {
modelMatrix.rotate(m_zRightAngleRotation);
itModelMatrix.rotate(m_zRightAngleRotation);
} else {
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) {
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);
}
} else {
m_drawer->drawLine(lineShader);
}
}
}
}
// Horizontal wall lines
if (m_axisCacheY.segmentCount() > 0) {
// Back wall
int gridLineCount = m_axisCacheY.gridLineCount();
GLfloat lineZTrans = m_scaleZWithBackground - gridLineOffset;
if (!m_zFlipped)
lineZTrans = -lineZTrans;
for (int line = 0; line < gridLineCount; line++) {
QMatrix4x4 modelMatrix;
QMatrix4x4 MVPMatrix;
QMatrix4x4 itModelMatrix;
modelMatrix.translate(0.0f, m_axisCacheY.gridLinePosition(line), lineZTrans);
modelMatrix.scale(gridLineScaleX);
itModelMatrix.scale(gridLineScaleX);
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) {
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);
}
} else {
m_drawer->drawLine(lineShader);
}
}
// Side wall
GLfloat lineXTrans = m_scaleXWithBackground - gridLineOffset;
if (!m_xFlipped)
lineXTrans = -lineXTrans;
for (int line = 0; line < gridLineCount; line++) {
QMatrix4x4 modelMatrix;
QMatrix4x4 MVPMatrix;
QMatrix4x4 itModelMatrix;
modelMatrix.translate(lineXTrans, m_axisCacheY.gridLinePosition(line), 0.0f);
modelMatrix.scale(gridLineScaleZ);
itModelMatrix.scale(gridLineScaleZ);
modelMatrix.rotate(lineYRotation);
itModelMatrix.rotate(lineYRotation);
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) {
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);
}
} else {
m_drawer->drawLine(lineShader);
}
}
}
}
Abstract3DRenderer::drawCustomItems(RenderingNormal, m_customItemShader, viewMatrix,
projectionViewMatrix, depthProjectionViewMatrix,
m_depthTexture, m_shadowQualityToShader);
drawLabels(false, activeCamera, viewMatrix, projectionMatrix);
// Handle selection clearing and selection label drawing
if (!dotSelectionFound) {
// We have no ownership, don't delete. Just NULL the pointer.
m_selectedItem = NULL;
} else {
glDisable(GL_DEPTH_TEST);
// Draw the selection label
LabelItem &labelItem = selectionLabelItem();
if (m_selectedItem != selectedItem || 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_selectedItem = selectedItem;
}
m_drawer->drawLabel(*selectedItem, labelItem, viewMatrix, projectionMatrix,
zeroVector, identityQuaternion, selectedItemSize, m_cachedSelectionMode,
m_labelShader, m_labelObj, activeCamera, true, false,
Drawer::LabelOver);
// Reset label update flag; they should have been updated when we get here
m_updateLabels = false;
glEnable(GL_DEPTH_TEST);
}
glDisable(GL_BLEND);
// Release shader
glUseProgram(0);
m_selectionDirty = false;
}
void Scatter3DRenderer::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_axisCacheZ.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;
// Z Labels
if (m_axisCacheZ.segmentCount() > 0) {
int labelCount = m_axisCacheZ.labelCount();
float labelXTrans = m_scaleXWithBackground + labelMargin;
float labelYTrans = -m_scaleYWithBackground;
if (m_polarGraph) {
labelXTrans *= m_radialLabelOffset;
// YTrans up only if over background
if (m_radialLabelOffset < 1.0f)
labelYTrans += gridLineOffset + gridLineWidth;
}
Qt::AlignmentFlag alignment = (m_xFlipped == m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight;
QVector3D labelRotation;
if (m_xFlipped)
labelXTrans = -labelXTrans;
if (m_yFlipped)
labelYTrans = -labelYTrans;
if (labelAutoAngle == 0.0f) {
if (m_zFlipped)
labelRotation.setY(180.0f);
if (m_yFlippedForGrid) {
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_yFlippedForGrid) {
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);
QVector3D labelTrans = QVector3D(labelXTrans, labelYTrans, 0.0f);
if (m_zFlipped) {
startIndex = 0;
endIndex = labelCount;
indexStep = 1;
} else {
startIndex = labelCount - 1;
endIndex = -1;
indexStep = -1;
}
float offsetValue = 0.0f;
for (int label = startIndex; label != endIndex; label = label + indexStep) {
glPolygonOffset(offsetValue++ / -10.0f, 1.0f);
const LabelItem &axisLabelItem = *m_axisCacheZ.labelItems().at(label);
// Draw the label here
if (m_polarGraph) {
float direction = m_zFlipped ? -1.0f : 1.0f;
labelTrans.setZ((m_axisCacheZ.formatter()->labelPositions().at(label)
* -m_polarRadius
+ m_drawer->scaledFontSize() + gridLineWidth) * direction);
} else {
labelTrans.setZ(m_axisCacheZ.labelPosition(label));
}
if (label == 0 || label == (labelCount - 1)) {
// If the margin is small, adjust the position of the edge labels to avoid overlapping
// with labels of the other axes.
float scaleFactor = m_drawer->scaledFontSize() / axisLabelItem.size().height();
float labelOverlap = qAbs(labelTrans.z())
+ (scaleFactor * axisLabelItem.size().height() / 2.0f)
- m_scaleZWithBackground + labelMargin;
// No need to adjust quite as much on the front edges
if (label != startIndex)
labelOverlap /= 2.0f;
if (labelOverlap > 0.0f) {
if (label == 0)
labelTrans.setZ(labelTrans.z() - labelOverlap);
else
labelTrans.setZ(labelTrans.z() + labelOverlap);
}
}
m_dummyRenderItem.setTranslation(labelTrans);
if (drawSelection) {
QVector4D labelColor = QVector4D(label / 255.0f, 0.0f, 0.0f,
alphaForRowSelection);
shader->setUniformValue(shader->color(), labelColor);
}
m_drawer->drawLabel(m_dummyRenderItem, 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()) {
if (m_polarGraph) {
float titleZ = -m_polarRadius / 2.0f;
if (m_zFlipped)
titleZ = -titleZ;
labelTrans.setZ(titleZ);
} else {
labelTrans.setZ(0.0f);
}
drawAxisTitleZ(labelRotation, labelTrans, totalRotation, m_dummyRenderItem,
activeCamera, labelsMaxWidth, viewMatrix, projectionMatrix, shader);
}
}
// X Labels
if (m_axisCacheX.segmentCount() > 0) {
labelsMaxWidth = 0.0f;
labelAutoAngle = m_axisCacheX.labelAutoRotation();
labelAngleFraction = labelAutoAngle / 90.0f;
fractionCamY = activeCamera->yRotation() * labelAngleFraction;
fractionCamX = activeCamera->xRotation() * labelAngleFraction;
int labelCount = m_axisCacheX.labelCount();
float labelZTrans = 0.0f;
float labelYTrans = -m_scaleYWithBackground;
if (m_polarGraph)
labelYTrans += gridLineOffset + gridLineWidth;
else
labelZTrans = m_scaleZWithBackground + labelMargin;
Qt::Alignment alignment = (m_xFlipped != m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight;
QVector3D labelRotation;
if (m_zFlipped)
labelZTrans = -labelZTrans;
if (m_yFlipped)
labelYTrans = -labelYTrans;
if (labelAutoAngle == 0.0f) {
labelRotation = QVector3D(-90.0f, 90.0f, 0.0f);
if (m_xFlipped)
labelRotation.setY(-90.0f);
if (m_yFlippedForGrid) {
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_yFlippedForGrid) {
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);
}
}
}
}
QQuaternion totalRotation = Utils::calculateRotation(labelRotation);
if (m_polarGraph) {
if ((!m_yFlippedForGrid && (m_zFlipped != m_xFlipped))
|| (m_yFlippedForGrid && (m_zFlipped == m_xFlipped))) {
totalRotation *= m_zRightAngleRotation;
} else {
totalRotation *= m_zRightAngleRotationNeg;
}
}
QVector3D labelTrans = QVector3D(0.0f, labelYTrans, labelZTrans);
if (m_xFlipped) {
startIndex = labelCount - 1;
endIndex = -1;
indexStep = -1;
} else {
startIndex = 0;
endIndex = labelCount;
indexStep = 1;
}
float offsetValue = 0.0f;
bool showLastLabel = false;
QVector<float> &labelPositions = m_axisCacheX.formatter()->labelPositions();
int lastLabelPosIndex = labelPositions.size() - 1;
if (labelPositions.size()
&& (labelPositions.at(lastLabelPosIndex) != 1.0f || labelPositions.at(0) != 0.0f)) {
// Avoid overlapping first and last label if they would get on same position
showLastLabel = true;
}
for (int label = startIndex; label != endIndex; label = label + indexStep) {
glPolygonOffset(offsetValue++ / -10.0f, 1.0f);
// Draw the label here
if (m_polarGraph) {
// Calculate angular position
if (label == lastLabelPosIndex && !showLastLabel)
continue;
float labelPosition = labelPositions.at(label);
qreal angle = labelPosition * M_PI * 2.0;
labelTrans.setX((m_polarRadius + labelMargin) * float(qSin(angle)));
labelTrans.setZ(-(m_polarRadius + labelMargin) * float(qCos(angle)));
// Alignment depends on label angular position, as well as flips
Qt::AlignmentFlag vAlignment = Qt::AlignCenter;
Qt::AlignmentFlag hAlignment = Qt::AlignCenter;
const float centerMargin = 0.005f;
if (labelPosition < 0.25f - centerMargin || labelPosition > 0.75f + centerMargin)
vAlignment = m_zFlipped ? Qt::AlignTop : Qt::AlignBottom;
else if (labelPosition > 0.25f + centerMargin && labelPosition < 0.75f - centerMargin)
vAlignment = m_zFlipped ? Qt::AlignBottom : Qt::AlignTop;
if (labelPosition < 0.50f - centerMargin && labelPosition > centerMargin)
hAlignment = m_zFlipped ? Qt::AlignRight : Qt::AlignLeft;
else if (labelPosition < 1.0f - centerMargin && labelPosition > 0.5f + centerMargin)
hAlignment = m_zFlipped ? Qt::AlignLeft : Qt::AlignRight;
if (m_yFlippedForGrid && vAlignment != Qt::AlignCenter)
vAlignment = (vAlignment == Qt::AlignTop) ? Qt::AlignBottom : Qt::AlignTop;
alignment = vAlignment | hAlignment;
} else {
labelTrans.setX(m_axisCacheX.labelPosition(label));
}
const LabelItem &axisLabelItem = *m_axisCacheX.labelItems().at(label);
if (label == 0 || label == (labelCount - 1)) {
// If the margin is small, adjust the position of the edge labels to avoid overlapping
// with labels of the other axes.
float scaleFactor = m_drawer->scaledFontSize() / axisLabelItem.size().height();
float labelOverlap = qAbs(labelTrans.x())
+ (scaleFactor * axisLabelItem.size().height() / 2.0f)
- m_scaleXWithBackground + labelMargin;
// No need to adjust quite as much on the front edges
if (label != startIndex)
labelOverlap /= 2.0f;
if (labelOverlap > 0.0f) {
if (label == 0)
labelTrans.setX(labelTrans.x() + labelOverlap);
else
labelTrans.setX(labelTrans.x() - labelOverlap);
}
}
m_dummyRenderItem.setTranslation(labelTrans);
if (drawSelection) {
QVector4D labelColor = QVector4D(0.0f, label / 255.0f, 0.0f,
alphaForColumnSelection);
shader->setUniformValue(shader->color(), labelColor);
}
m_drawer->drawLabel(m_dummyRenderItem, 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()) {
labelTrans.setX(0.0f);
bool radial = false;
if (m_polarGraph) {
if (m_xFlipped == m_zFlipped)
totalRotation *= m_zRightAngleRotation;
else
totalRotation *= m_zRightAngleRotationNeg;
if (m_yFlippedForGrid)
totalRotation *= QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, -180.0f);
labelTrans.setZ(-m_polarRadius);
radial = true;
}
drawAxisTitleX(labelRotation, labelTrans, totalRotation, m_dummyRenderItem,
activeCamera, labelsMaxWidth, viewMatrix, projectionMatrix, shader,
radial);
}
}
// Y Labels
if (m_axisCacheY.segmentCount() > 0) {
labelsMaxWidth = 0.0f;
labelAutoAngle = m_axisCacheY.labelAutoRotation();
labelAngleFraction = labelAutoAngle / 90.0f;
fractionCamY = activeCamera->yRotation() * labelAngleFraction;
fractionCamX = activeCamera->xRotation() * labelAngleFraction;
int labelCount = m_axisCacheY.labelCount();
float labelXTrans = m_scaleXWithBackground;
float labelZTrans = m_scaleZWithBackground;
// Back & side wall
float labelMarginXTrans = labelMargin;
float labelMarginZTrans = labelMargin;
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 labelTransBack = QVector3D(labelXTrans, 0.0f, labelZTrans + labelMarginZTrans);
QVector3D labelTransSide(-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 label = startIndex; label != endIndex; label = label + indexStep) {
const LabelItem &axisLabelItem = *m_axisCacheY.labelItems().at(label);
float labelYTrans = m_axisCacheY.labelPosition(label);
glPolygonOffset(offsetValue++ / -10.0f, 1.0f);
if (drawSelection) {
QVector4D labelColor = QVector4D(0.0f, 0.0f, label / 255.0f,
alphaForValueSelection);
shader->setUniformValue(shader->color(), labelColor);
}
if (label == startIndex) {
// If the margin is small, adjust the position of the edge label to avoid
// overlapping with labels of the other axes.
float scaleFactor = m_drawer->scaledFontSize() / axisLabelItem.size().height();
float labelOverlap = qAbs(labelYTrans)
+ (scaleFactor * axisLabelItem.size().height() / 2.0f)
- m_scaleYWithBackground + labelMargin;
if (labelOverlap > 0.0f) {
if (label == 0)
labelYTrans += labelOverlap;
else
labelYTrans -= labelOverlap;
}
}
// Back wall
labelTransBack.setY(labelYTrans);
m_dummyRenderItem.setTranslation(labelTransBack);
m_drawer->drawLabel(m_dummyRenderItem, axisLabelItem, viewMatrix, projectionMatrix,
zeroVector, totalBackRotation, 0, m_cachedSelectionMode,
shader, m_labelObj, activeCamera, true, true,
Drawer::LabelMid, backAlignment, false, drawSelection);
// Side wall
labelTransSide.setY(labelYTrans);
m_dummyRenderItem.setTranslation(labelTransSide);
m_drawer->drawLabel(m_dummyRenderItem, 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()) {
labelTransSide.setY(0.0f);
labelTransBack.setY(0.0f);
drawAxisTitleY(sideLabelRotation, backLabelRotation, labelTransSide, labelTransBack,
totalSideRotation, totalBackRotation, m_dummyRenderItem, activeCamera,
labelsMaxWidth, viewMatrix, projectionMatrix,
shader);
}
}
glDisable(GL_POLYGON_OFFSET_FILL);
}
void Scatter3DRenderer::updateSelectedItem(int index, QScatter3DSeries *series)
{
m_selectionDirty = true;
m_selectionLabelDirty = true;
m_selectedSeriesCache =
static_cast<ScatterSeriesRenderCache *>(m_renderCacheList.value(series, 0));
m_selectedItemIndex = Scatter3DController::invalidSelectionIndex();
if (m_cachedOptimizationHint.testFlag(QAbstract3DGraph::OptimizationStatic)
&& m_oldSelectedSeriesCache
&& m_oldSelectedSeriesCache->mesh() == QAbstract3DSeries::MeshPoint) {
m_oldSelectedSeriesCache->bufferPoints()->popPoint();
m_oldSelectedSeriesCache = 0;
}
if (m_selectedSeriesCache) {
const ScatterRenderItemArray &renderArray = m_selectedSeriesCache->renderArray();
if (index < renderArray.size() && index >= 0) {
m_selectedItemIndex = index;
if (m_cachedOptimizationHint.testFlag(QAbstract3DGraph::OptimizationStatic)
&& m_selectedSeriesCache->mesh() == QAbstract3DSeries::MeshPoint) {
m_selectedSeriesCache->bufferPoints()->pushPoint(m_selectedItemIndex);
m_oldSelectedSeriesCache = m_selectedSeriesCache;
}
}
}
}
void Scatter3DRenderer::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 = 5.0f;
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();
}
void Scatter3DRenderer::loadBackgroundMesh()
{
ObjectHelper::resetObjectHelper(this, m_backgroundObj,
QStringLiteral(":/defaultMeshes/background"));
}
void Scatter3DRenderer::updateTextures()
{
Abstract3DRenderer::updateTextures();
// Drawer has changed; this flag needs to be checked when checking if we need to update labels
m_updateLabels = true;
if (m_polarGraph)
calculateSceneScalingFactors();
}
void Scatter3DRenderer::fixMeshFileName(QString &fileName, QAbstract3DSeries::Mesh mesh)
{
// Load full version of meshes that have it available
if (mesh != QAbstract3DSeries::MeshSphere
&& mesh != QAbstract3DSeries::MeshMinimal
&& mesh != QAbstract3DSeries::MeshPoint
&& mesh != QAbstract3DSeries::MeshArrow) {
fileName.append(QStringLiteral("Full"));
}
}
void Scatter3DRenderer::calculateTranslation(ScatterRenderItem &item)
{
// We need to normalize translations
const QVector3D &pos = item.position();
float xTrans;
float yTrans = m_axisCacheY.positionAt(pos.y());
float zTrans;
if (m_polarGraph) {
calculatePolarXZ(pos, xTrans, zTrans);
} else {
xTrans = m_axisCacheX.positionAt(pos.x());
zTrans = m_axisCacheZ.positionAt(pos.z());
}
item.setTranslation(QVector3D(xTrans, yTrans, zTrans));
}
void Scatter3DRenderer::calculateSceneScalingFactors()
{
if (m_requestedMargin < 0.0f) {
if (m_maxItemSize > defaultMaxSize)
m_hBackgroundMargin = m_maxItemSize / itemScaler;
else
m_hBackgroundMargin = defaultMaxSize;
m_vBackgroundMargin = m_hBackgroundMargin;
} else {
m_hBackgroundMargin = m_requestedMargin;
m_vBackgroundMargin = m_requestedMargin;
}
if (m_polarGraph) {
float polarMargin = calculatePolarBackgroundMargin();
m_hBackgroundMargin = qMax(m_hBackgroundMargin, polarMargin);
}
float horizontalAspectRatio;
if (m_polarGraph)
horizontalAspectRatio = 1.0f;
else
horizontalAspectRatio = m_graphHorizontalAspectRatio;
QSizeF areaSize;
if (horizontalAspectRatio == 0.0f) {
areaSize.setHeight(m_axisCacheZ.max() - m_axisCacheZ.min());
areaSize.setWidth(m_axisCacheX.max() - m_axisCacheX.min());
} else {
areaSize.setHeight(1.0f);
areaSize.setWidth(horizontalAspectRatio);
}
float horizontalMaxDimension;
if (m_graphAspectRatio > 2.0f) {
horizontalMaxDimension = 2.0f;
m_scaleY = 2.0f / m_graphAspectRatio;
} else {
horizontalMaxDimension = m_graphAspectRatio;
m_scaleY = 1.0f;
}
if (m_polarGraph)
m_polarRadius = horizontalMaxDimension;
float scaleFactor = qMax(areaSize.width(), areaSize.height());
m_scaleX = horizontalMaxDimension * areaSize.width() / scaleFactor;
m_scaleZ = horizontalMaxDimension * areaSize.height() / scaleFactor;
m_scaleXWithBackground = m_scaleX + m_hBackgroundMargin;
m_scaleYWithBackground = m_scaleY + m_vBackgroundMargin;
m_scaleZWithBackground = m_scaleZ + m_hBackgroundMargin;
m_axisCacheX.setScale(m_scaleX * 2.0f);
m_axisCacheY.setScale(m_scaleY * 2.0f);
m_axisCacheZ.setScale(-m_scaleZ * 2.0f);
m_axisCacheX.setTranslate(-m_scaleX);
m_axisCacheY.setTranslate(-m_scaleY);
m_axisCacheZ.setTranslate(m_scaleZ);
updateCameraViewport();
updateCustomItemPositions();
}
void Scatter3DRenderer::initShaders(const QString &vertexShader, const QString &fragmentShader)
{
delete m_dotShader;
m_dotShader = new ShaderHelper(this, vertexShader, fragmentShader);
m_dotShader->initialize();
}
void Scatter3DRenderer::initGradientShaders(const QString &vertexShader,
const QString &fragmentShader)
{
delete m_dotGradientShader;
m_dotGradientShader = new ShaderHelper(this, vertexShader, fragmentShader);
m_dotGradientShader->initialize();
}
void Scatter3DRenderer::initStaticSelectedItemShaders(const QString &vertexShader,
const QString &fragmentShader,
const QString &gradientVertexShader,
const QString &gradientFragmentShader)
{
delete m_staticSelectedItemShader;
m_staticSelectedItemShader = new ShaderHelper(this, vertexShader, fragmentShader);
m_staticSelectedItemShader->initialize();
delete m_staticSelectedItemGradientShader;
m_staticSelectedItemGradientShader = new ShaderHelper(this, gradientVertexShader,
gradientFragmentShader);
m_staticSelectedItemGradientShader->initialize();
}
void Scatter3DRenderer::initSelectionShader()
{
delete m_selectionShader;
m_selectionShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexPlainColor"),
QStringLiteral(":/shaders/fragmentPlainColor"));
m_selectionShader->initialize();
}
void Scatter3DRenderer::initSelectionBuffer()
{
m_textureHelper->deleteTexture(&m_selectionTexture);
if (m_primarySubViewport.size().isEmpty())
return;
m_selectionTexture = m_textureHelper->createSelectionTexture(m_primarySubViewport.size(),
m_selectionFrameBuffer,
m_selectionDepthBuffer);
}
void Scatter3DRenderer::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 Scatter3DRenderer::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 Scatter3DRenderer::initPointShader()
{
if (m_isOpenGLES) {
if (m_pointShader)
delete m_pointShader;
m_pointShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexPointES2"),
QStringLiteral(":/shaders/fragmentPlainColor"));
m_pointShader->initialize();
}
}
void Scatter3DRenderer::initBackgroundShaders(const QString &vertexShader,
const QString &fragmentShader)
{
if (m_backgroundShader)
delete m_backgroundShader;
m_backgroundShader = new ShaderHelper(this, vertexShader, fragmentShader);
m_backgroundShader->initialize();
}
void Scatter3DRenderer::initStaticPointShaders(const QString &vertexShader,
const QString &fragmentShader)
{
if (m_staticGradientPointShader)
delete m_staticGradientPointShader;
m_staticGradientPointShader = new ShaderHelper(this, vertexShader, fragmentShader);
m_staticGradientPointShader->initialize();
}
void Scatter3DRenderer::selectionColorToSeriesAndIndex(const QVector4D &color,
int &index,
QAbstract3DSeries *&series)
{
m_clickedType = QAbstract3DGraph::ElementNone;
m_selectedLabelIndex = -1;
m_selectedCustomItemIndex = -1;
if (color != selectionSkipColor) {
if (color.w() == labelRowAlpha) {
// Row selection
index = Scatter3DController::invalidSelectionIndex();
m_selectedLabelIndex = color.x();
m_clickedType = QAbstract3DGraph::ElementAxisZLabel;
} else if (color.w() == labelColumnAlpha) {
// Column selection
index = Scatter3DController::invalidSelectionIndex();
m_selectedLabelIndex = color.y();
m_clickedType = QAbstract3DGraph::ElementAxisXLabel;
} else if (color.w() == labelValueAlpha) {
// Value selection
index = Scatter3DController::invalidSelectionIndex();
m_selectedLabelIndex = color.z();
m_clickedType = QAbstract3DGraph::ElementAxisYLabel;
} else if (color.w() == customItemAlpha) {
// Custom item selection
index = Scatter3DController::invalidSelectionIndex();
m_selectedCustomItemIndex = int(color.x())
+ (int(color.y()) << 8)
+ (int(color.z()) << 16);
m_clickedType = QAbstract3DGraph::ElementCustomItem;
} else {
int totalIndex = int(color.x())
+ (int(color.y()) << 8)
+ (int(color.z()) << 16);
// Find the series and adjust the index accordingly
foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
if (baseCache->isVisible()) {
ScatterSeriesRenderCache *cache =
static_cast<ScatterSeriesRenderCache *>(baseCache);
int offset = cache->selectionIndexOffset();
if (totalIndex >= offset
&& totalIndex < (offset + cache->renderArray().size())) {
index = totalIndex - offset;
series = cache->series();
m_clickedType = QAbstract3DGraph::ElementSeries;
return;
}
}
}
}
}
// No valid match found
index = Scatter3DController::invalidSelectionIndex();
series = 0;
}
void Scatter3DRenderer::updateRenderItem(const QScatterDataItem &dataItem,
ScatterRenderItem &renderItem)
{
QVector3D dotPos = dataItem.position();
if ((dotPos.x() >= m_axisCacheX.min() && dotPos.x() <= m_axisCacheX.max() )
&& (dotPos.y() >= m_axisCacheY.min() && dotPos.y() <= m_axisCacheY.max())
&& (dotPos.z() >= m_axisCacheZ.min() && dotPos.z() <= m_axisCacheZ.max())) {
renderItem.setPosition(dotPos);
renderItem.setVisible(true);
if (!dataItem.rotation().isIdentity())
renderItem.setRotation(dataItem.rotation().normalized());
else
renderItem.setRotation(identityQuaternion);
calculateTranslation(renderItem);
} else {
renderItem.setVisible(false);
}
}
QVector3D Scatter3DRenderer::convertPositionToTranslation(const QVector3D &position,
bool isAbsolute)
{
float xTrans = 0.0f;
float yTrans = 0.0f;
float zTrans = 0.0f;
if (!isAbsolute) {
if (m_polarGraph) {
calculatePolarXZ(position, xTrans, zTrans);
} else {
xTrans = m_axisCacheX.positionAt(position.x());
zTrans = m_axisCacheZ.positionAt(position.z());
}
yTrans = m_axisCacheY.positionAt(position.y());
} else {
xTrans = position.x() * m_scaleX;
yTrans = position.y() * m_scaleY;
zTrans = position.z() * -m_scaleZ;
}
return QVector3D(xTrans, yTrans, zTrans);
}
QT_END_NAMESPACE_DATAVISUALIZATION