summaryrefslogtreecommitdiffstats
path: root/src/datavisualization/engine/scatter3drenderer.cpp
diff options
context:
space:
mode:
authorTomi Korpipää <tomi.korpipaa@digia.com>2013-09-10 11:42:13 +0300
committerTomi Korpipää <tomi.korpipaa@digia.com>2013-09-10 12:04:24 +0300
commita28cdb72a4ea768a898ca07f0df0fa3c17c073a8 (patch)
tree0bafdcfa99fc783e9f5204539a8242bf6128d795 /src/datavisualization/engine/scatter3drenderer.cpp
parentf3e38983d77c72f3121c33a149a58fdf9c64158c (diff)
Module renamed
Task-number: QTRD-2224 Change-Id: Iec18b6121809300b11d85445281d3c626c434f35 Reviewed-by: Miikka Heikkinen <miikka.heikkinen@digia.com>
Diffstat (limited to 'src/datavisualization/engine/scatter3drenderer.cpp')
-rw-r--r--src/datavisualization/engine/scatter3drenderer.cpp1608
1 files changed, 1608 insertions, 0 deletions
diff --git a/src/datavisualization/engine/scatter3drenderer.cpp b/src/datavisualization/engine/scatter3drenderer.cpp
new file mode 100644
index 00000000..a8c68fb1
--- /dev/null
+++ b/src/datavisualization/engine/scatter3drenderer.cpp
@@ -0,0 +1,1608 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#include "scatter3drenderer_p.h"
+#include "scatter3dcontroller_p.h"
+#include "q3dcamera.h"
+#include "shaderhelper_p.h"
+#include "objecthelper_p.h"
+#include "texturehelper_p.h"
+#include "utils_p.h"
+#include "q3dlight.h"
+
+#include <QMatrix4x4>
+#include <QMouseEvent>
+#include <QThread>
+#include <qmath.h>
+#include <QDebug>
+
+// Commenting this draws the shadow map with perspective projection. Otherwise it's drawn in
+// orthographic projection.
+//#define USE_WIDER_SHADOWS
+
+// 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_DATAVISUALIZATION_BEGIN_NAMESPACE
+
+//#define USE_UNIFORM_SCALING // Scale x and z uniformly, or based on autoscaled values
+
+const GLfloat aspectRatio = 2.0f; // Forced ratio of x and z to y. Dynamic will make it look odd.
+const GLfloat labelMargin = 0.05f;
+// TODO: Make margin modifiable?
+const GLfloat backgroundMargin = 1.1f; // Margin for background (1.1f = make it 10% larger to avoid items being drawn inside background)
+const GLfloat gridLineWidth = 0.005f;
+static QVector3D selectionSkipColor = QVector3D(255, 255, 255); // Selection texture's background color
+
+Scatter3DRenderer::Scatter3DRenderer(Scatter3DController *controller)
+ : Abstract3DRenderer(controller),
+ m_controller(controller),
+ m_selectedItem(0),
+ m_xFlipped(false),
+ m_zFlipped(false),
+ m_yFlipped(false),
+ m_updateLabels(false),
+ m_dotShader(0),
+ m_depthShader(0),
+ m_selectionShader(0),
+ m_backgroundShader(0),
+ m_labelShader(0),
+ m_dotObj(0),
+ m_backgroundObj(0),
+ m_gridLineObj(0),
+ m_labelObj(0),
+ m_bgrTexture(0),
+ m_depthTexture(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_scaleFactor(0),
+ m_selection(selectionSkipColor),
+ m_previousSelection(selectionSkipColor),
+ m_areaSize(QSizeF(0.0, 0.0)),
+ m_dotSizeScale(1.0f),
+ m_hasHeightAdjustmentChanged(true)
+{
+ initializeOpenGLFunctions();
+ initializeOpenGL();
+}
+
+Scatter3DRenderer::~Scatter3DRenderer()
+{
+ 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);
+ delete m_dotShader;
+ delete m_depthShader;
+ delete m_selectionShader;
+ delete m_backgroundShader;
+ delete m_labelShader;
+ delete m_dotObj;
+ delete m_backgroundObj;
+ delete m_gridLineObj;
+ delete m_labelObj;
+}
+
+void Scatter3DRenderer::initializeOpenGL()
+{
+ Abstract3DRenderer::initializeOpenGL();
+
+ // Initialize shaders
+ handleShadowQualityChange();
+
+ initLabelShaders(QStringLiteral(":/shaders/vertexLabel"),
+ QStringLiteral(":/shaders/fragmentLabel"));
+
+#if !defined(QT_OPENGL_ES_2)
+ // Init depth shader (for shadows). Init in any case, easier to handle shadow activation if done via api.
+ initDepthShader();
+#endif
+
+ // Init selection shader
+ initSelectionShader();
+
+ // Load grid line mesh
+ loadGridLineMesh();
+
+ // Load label mesh
+ loadLabelMesh();
+
+ // Set view port
+ glViewport(m_mainViewPort.x(), m_mainViewPort.y(),
+ m_mainViewPort.width(), m_mainViewPort.height());
+
+ // Load background mesh (we need to be initialized first)
+ loadBackgroundMesh();
+}
+
+void Scatter3DRenderer::updateDataModel(QScatterDataProxy *dataProxy)
+{
+ int actualDataSize = 0;
+ const QScatterDataArray &dataArray = *dataProxy->array();
+ calculateSceneScalingFactors();
+ int dataSize = dataArray.size();
+ m_renderItemArray.resize(dataSize);
+ for (int i = 0; i < dataSize ; i++) {
+ QVector3D dotPos = dataArray.at(i).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())) {
+ m_renderItemArray[actualDataSize].setPosition(dotPos);
+ calculateTranslation(m_renderItemArray[actualDataSize]);
+ actualDataSize++;
+ }
+ }
+ m_renderItemArray.resize(actualDataSize);
+ m_dotSizeScale = (GLfloat)qBound(0.01, (2.0 / qSqrt((qreal)actualDataSize)), 0.1);
+ m_selectedItem = 0;
+
+ Abstract3DRenderer::updateDataModel(dataProxy);
+}
+
+void Scatter3DRenderer::updateScene(Q3DScene *scene)
+{
+ // TODO: Move these to more suitable place e.g. controller should be controlling the viewports.
+ scene->setMainViewport(m_mainViewPort);
+ scene->setUnderSideCameraEnabled(true);
+
+ if (m_hasHeightAdjustmentChanged) {
+ // Set initial m_cachedScene->camera() position. Also update if height adjustment has changed.
+ scene->camera()->setDefaultOrientation(QVector3D(0.0f, 0.0f, 6.0f + zComp),
+ QVector3D(0.0f, 0.0f, zComp),
+ QVector3D(0.0f, 1.0f, 0.0f));
+ m_hasHeightAdjustmentChanged = false;
+ }
+
+ scene->camera()->updateViewMatrix(m_autoScaleAdjustment);
+ // Set light position (rotate light with m_cachedScene->camera(), a bit above it (as set in defaultLightPos))
+ scene->setLightPositionRelativeToCamera(defaultLightPos);
+
+ Abstract3DRenderer::updateScene(scene);
+}
+
+void Scatter3DRenderer::render(GLuint defaultFboHandle)
+{
+ // Handle GL state setup for FBO buffers and clearing of the render surface
+ Abstract3DRenderer::render(defaultFboHandle);
+
+ // Draw dots scene
+ drawScene(defaultFboHandle);
+}
+
+void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
+{
+ GLfloat backgroundRotation = 0;
+
+ // Specify viewport
+ glViewport(m_mainViewPort.x(), m_mainViewPort.y(),
+ m_mainViewPort.width(), m_mainViewPort.height());
+
+ // Set up projection matrix
+ QMatrix4x4 projectionMatrix;
+ projectionMatrix.perspective(45.0f, (GLfloat)m_mainViewPort.width()
+ / (GLfloat)m_mainViewPort.height(), 0.1f, 100.0f);
+
+ // Calculate view matrix
+ QMatrix4x4 viewMatrix = m_cachedScene->camera()->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;
+
+ // 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->light()->position();
+
+ // Map adjustment direction to model matrix scaling
+ // TODO: Let's use these for testing the autoscaling of dots based on their number
+ GLfloat heightMultiplier = m_dotSizeScale; //1.0f;
+ GLfloat widthMultiplier = m_dotSizeScale; //1.0f;
+ GLfloat depthMultiplier = m_dotSizeScale; //1.0f;
+ GLfloat heightScaler = 0.0f;
+ GLfloat widthScaler = 0.0f;
+ GLfloat depthScaler = 0.0f;
+
+ // Introduce regardless of shadow quality to simplify logic
+ QMatrix4x4 depthViewMatrix;
+ QMatrix4x4 depthProjectionMatrix;
+
+#if !defined(QT_OPENGL_ES_2)
+ if (m_cachedShadowQuality > QDataVis::ShadowNone) {
+ // 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(m_mainViewPort.x(), m_mainViewPort.y(),
+ m_mainViewPort.width() * m_shadowQualityMultiplier,
+ m_mainViewPort.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);
+
+ // Get the depth view matrix
+ // It may be possible to hack lightPos here if we want to make some tweaks to shadow
+ QVector3D depthLightPos = m_cachedScene->camera()->calculatePositionRelativeToCamera(
+ defaultLightPos, 0.0f, 1.0f / m_autoScaleAdjustment);
+ depthViewMatrix.lookAt(depthLightPos, QVector3D(0.0f, 0.0f, zComp),
+ QVector3D(0.0f, 1.0f, 0.0f));
+ // TODO: Why does depthViewMatrix.column(3).y() goes to zero when we're directly above?
+ // That causes the scene to be not drawn from above -> must be fixed
+ // qDebug() << lightPos << depthViewMatrix << depthViewMatrix.column(3);
+ // Set the depth projection matrix
+#ifndef USE_WIDER_SHADOWS
+ // Use this for perspective shadows
+ depthProjectionMatrix.perspective(15.0f, (GLfloat)m_mainViewPort.width()
+ / (GLfloat)m_mainViewPort.height(), 3.0f, 100.0f);
+#else
+ // Use these for orthographic shadows
+ //depthProjectionMatrix.ortho(-aspectRatio * 2.0f, aspectRatio * 2.0f,
+ // -m_heightNormalizer * 2.0f, m_heightNormalizer * 2.0f,
+ // 0.0f, 100.0f);
+ GLfloat testAspectRatio = (GLfloat)m_mainViewPort.width() / (GLfloat)m_mainViewPort.height();
+ depthProjectionMatrix.ortho(-testAspectRatio * 2.0f, testAspectRatio * 2.0f,
+ -m_heightNormalizer * 2.0f, m_heightNormalizer * 2.0f,
+ 0.0f, 100.0f);
+#endif
+ // Draw dots to depth buffer
+ for (int dot = 0; dot < m_renderItemArray.size(); dot++) {
+ const ScatterRenderItem &item = m_renderItemArray.at(dot);
+
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 MVPMatrix;
+
+ modelMatrix.translate(item.translation().x(),
+ item.translation().y(),
+ item.translation().z());
+ modelMatrix.scale(QVector3D(widthMultiplier + widthScaler,
+ heightMultiplier + heightScaler,
+ depthMultiplier + depthScaler));
+ //modelMatrix.scale(QVector3D(widthMultiplier * item.size() + widthScaler,
+ // heightMultiplier * item.size() + heightScaler,
+ // depthMultiplier * item.size() + depthScaler));
+
+ MVPMatrix = depthProjectionMatrix * depthViewMatrix * modelMatrix;
+
+ m_depthShader->setUniformValue(m_depthShader->MVP(), MVPMatrix);
+
+ // 1st attribute buffer : vertices
+ glEnableVertexAttribArray(m_depthShader->posAtt());
+ glBindBuffer(GL_ARRAY_BUFFER, m_dotObj->vertexBuf());
+ glVertexAttribPointer(m_depthShader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0, (void *)0);
+
+ // Index buffer
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_dotObj->elementBuf());
+
+ // Draw the triangles
+ glDrawElements(GL_TRIANGLES, m_dotObj->indexCount(), GL_UNSIGNED_SHORT, (void *)0);
+
+ // Free buffers
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+ glDisableVertexAttribArray(m_depthShader->posAtt());
+ }
+
+ // Disable drawing to framebuffer (= enable drawing to screen)
+ glBindFramebuffer(GL_FRAMEBUFFER, defaultFboHandle);
+
+ // Reset culling to normal
+ glCullFace(GL_BACK);
+
+ // Release depth shader
+ m_depthShader->release();
+
+ // Revert to original viewport
+ glViewport(m_mainViewPort.x(), m_mainViewPort.y(),
+ m_mainViewPort.width(), m_mainViewPort.height());
+
+#if 0 // Use this if you want to see what is being drawn to the framebuffer
+ // You'll also have to comment out GL_COMPARE_R_TO_TEXTURE -line in texturehelper (if using it)
+ m_labelShader->bind();
+ glEnable(GL_TEXTURE_2D);
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 viewmatrix;
+ viewmatrix.lookAt(QVector3D(0.0f, 0.0f, 2.5f + zComp),
+ QVector3D(0.0f, 0.0f, zComp),
+ QVector3D(0.0f, 1.0f, 0.0f));
+ modelMatrix.translate(0.0, 0.0, zComp);
+ QMatrix4x4 MVPMatrix = projectionMatrix * viewmatrix * modelMatrix;
+ m_labelShader->setUniformValue(m_labelShader->MVP(), MVPMatrix);
+ m_drawer->drawObject(m_labelShader, m_labelObj,
+ m_depthTexture);
+ glDisable(GL_TEXTURE_2D);
+ m_labelShader->release();
+#endif
+ }
+#endif
+
+ // Skip selection mode drawing if we have no selection mode
+ if (m_cachedSelectionMode > QDataVis::ModeNone) {
+ // Bind selection shader
+ m_selectionShader->bind();
+
+ // Draw dots to selection buffer
+ glBindFramebuffer(GL_FRAMEBUFFER, m_selectionFrameBuffer);
+ 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
+
+ int arraySize = m_renderItemArray.size();
+ if (arraySize > 0xfffffe) // Max possible different selection colors, 0xffffff being skipColor
+ qFatal("Too many objects");
+
+ for (int dot = 0; dot < arraySize; dot++) {
+ const ScatterRenderItem &item = m_renderItemArray.at(dot);
+
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 MVPMatrix;
+
+ modelMatrix.translate(item.translation().x(),
+ item.translation().y(),
+ item.translation().z());
+ modelMatrix.scale(QVector3D(widthMultiplier + widthScaler,
+ heightMultiplier + heightScaler,
+ depthMultiplier + depthScaler));
+ //modelMatrix.scale(QVector3D(widthMultiplier * item.size() + widthScaler,
+ // heightMultiplier * item.size() + heightScaler,
+ // depthMultiplier * item.size() + depthScaler));
+
+ MVPMatrix = projectionMatrix * viewMatrix * modelMatrix;
+
+ QVector3D dotColor = indexToSelectionColor(dot);
+ dotColor /= 255.0f;
+
+ m_selectionShader->setUniformValue(m_selectionShader->MVP(), MVPMatrix);
+ m_selectionShader->setUniformValue(m_selectionShader->color(), dotColor);
+
+ // 1st attribute buffer : vertices
+ glEnableVertexAttribArray(m_selectionShader->posAtt());
+ glBindBuffer(GL_ARRAY_BUFFER, m_dotObj->vertexBuf());
+ glVertexAttribPointer(m_selectionShader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0, (void *)0);
+
+ // Index buffer
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_dotObj->elementBuf());
+
+ // Draw the triangles
+ glDrawElements(GL_TRIANGLES, m_dotObj->indexCount(), GL_UNSIGNED_SHORT, (void *)0);
+
+ // Free buffers
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+ glDisableVertexAttribArray(m_selectionShader->posAtt());
+ }
+ glEnable(GL_DITHER);
+
+ // Read color under cursor
+ if (QDataVis::InputOnScene == m_controller->inputState()) {
+ m_selection = Utils::getSelection(m_controller->inputPosition(),
+ m_cachedBoundingRect.height());
+ }
+
+ glBindFramebuffer(GL_FRAMEBUFFER, defaultFboHandle);
+
+ // Release selection shader
+ m_selectionShader->release();
+
+#if 0 // Use this if you want to see what is being drawn to the framebuffer
+ m_labelShader->bind();
+ glDisable(GL_DEPTH_TEST);
+ glEnable(GL_TEXTURE_2D);
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 viewmatrix;
+ viewmatrix.lookAt(QVector3D(0.0f, 0.0f, 2.0f + zComp),
+ QVector3D(0.0f, 0.0f, zComp),
+ QVector3D(0.0f, 1.0f, 0.0f));
+ modelMatrix.translate(0.0, 0.0, zComp);
+ QMatrix4x4 MVPMatrix = projectionMatrix * viewmatrix * modelMatrix;
+ m_labelShader->setUniformValue(m_labelShader->MVP(), MVPMatrix);
+ m_drawer->drawObject(m_labelShader, m_labelObj,
+ m_selectionTexture);
+ glDisable(GL_TEXTURE_2D);
+ m_labelShader->release();
+#endif
+ }
+
+ // Bind dot shader
+ m_dotShader->bind();
+
+ // Enable texture
+ glEnable(GL_TEXTURE_2D);
+
+ // Draw dots
+ bool dotSelectionFound = false;
+ int selectedIndex;
+ if (m_selection == selectionSkipColor) {
+ selectedIndex = Scatter3DController::noSelectionIndex();
+ } else {
+ selectedIndex = int(m_selection.x())
+ + (int(m_selection.y()) << 8)
+ + (int(m_selection.z()) << 16);
+ }
+
+ if (m_selection != m_previousSelection) {
+ emit selectedItemIndexChanged(selectedIndex);
+ m_previousSelection = m_selection;
+ }
+
+ ScatterRenderItem *selectedItem(0);
+
+ for (int dot = 0; dot < m_renderItemArray.size(); dot++) {
+ ScatterRenderItem &item = m_renderItemArray[dot];
+
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 MVPMatrix;
+ QMatrix4x4 depthMVPMatrix;
+ QMatrix4x4 itModelMatrix;
+
+ modelMatrix.translate(item.translation().x(),
+ item.translation().y(),
+ item.translation().z());
+ modelMatrix.scale(QVector3D(widthMultiplier + widthScaler,
+ heightMultiplier + heightScaler,
+ depthMultiplier + depthScaler));
+ //modelMatrix.scale(QVector3D(widthMultiplier * item.size() + widthScaler,
+ // heightMultiplier * item.size() + heightScaler,
+ // depthMultiplier * item.size() + depthScaler));
+ itModelMatrix.scale(QVector3D(widthMultiplier + widthScaler,
+ heightMultiplier + heightScaler,
+ depthMultiplier + depthScaler));
+ //itModelMatrix.scale(QVector3D(widthMultiplier * item.size() + widthScaler,
+ // heightMultiplier * item.size() + heightScaler,
+ // depthMultiplier * item.size() + depthScaler));
+
+#ifdef SHOW_DEPTH_TEXTURE_SCENE
+ MVPMatrix = depthProjectionMatrix * depthViewMatrix * modelMatrix;
+#else
+ MVPMatrix = projectionMatrix * viewMatrix * modelMatrix;
+#endif
+ depthMVPMatrix = depthProjectionMatrix * depthViewMatrix * modelMatrix;
+
+#if 0
+ QVector3D baseColor = Utils::vectorFromColor(m_cachedTheme.m_baseColor);
+ QVector3D heightColor =
+ Utils::vectorFromColor(m_cachedTheme.m_heightColor) * item.translation().y();
+
+ QVector3D dotColor = baseColor + heightColor;
+#else
+ QVector3D dotColor = Utils::vectorFromColor(m_cachedTheme.m_baseColor);
+#endif
+
+ GLfloat lightStrength = m_cachedTheme.m_lightStrength;
+ if (m_cachedSelectionMode > QDataVis::ModeNone && (selectedIndex == dot)) {
+ dotColor = Utils::vectorFromColor(m_cachedTheme.m_highlightBarColor);
+ lightStrength = m_cachedTheme.m_highlightLightStrength;
+ // Insert data to ScatterRenderItem. We have no ownership, don't delete the previous one
+ selectedItem = &item;
+ dotSelectionFound = true;
+ }
+
+ // Set shader bindings
+ m_dotShader->setUniformValue(m_dotShader->lightP(), lightPos);
+ m_dotShader->setUniformValue(m_dotShader->view(), viewMatrix);
+ m_dotShader->setUniformValue(m_dotShader->model(), modelMatrix);
+ m_dotShader->setUniformValue(m_dotShader->nModel(),
+ itModelMatrix.inverted().transposed());
+ m_dotShader->setUniformValue(m_dotShader->MVP(), MVPMatrix);
+ m_dotShader->setUniformValue(m_dotShader->color(), dotColor);
+ m_dotShader->setUniformValue(m_dotShader->ambientS(), m_cachedTheme.m_ambientStrength);
+
+#if !defined(QT_OPENGL_ES_2)
+ if (m_cachedShadowQuality > QDataVis::ShadowNone) {
+ // Set shadow shader bindings
+ m_dotShader->setUniformValue(m_dotShader->shadowQ(), m_shadowQualityToShader);
+ m_dotShader->setUniformValue(m_dotShader->depth(), depthMVPMatrix);
+ m_dotShader->setUniformValue(m_dotShader->lightS(), lightStrength / 10.0f);
+
+ // Draw the object
+ m_drawer->drawObject(m_dotShader, m_dotObj, 0, m_depthTexture);
+ } else
+#endif
+ {
+ // Set shadowless shader bindings
+ m_dotShader->setUniformValue(m_dotShader->lightS(), lightStrength);
+
+ // Draw the object
+ m_drawer->drawObject(m_dotShader, m_dotObj);
+ }
+ }
+
+ // Release dot shader
+ m_dotShader->release();
+
+ // Bind background shader
+ m_backgroundShader->bind();
+
+ glCullFace(GL_BACK);
+
+ // Draw background
+ if (m_cachedIsBackgroundEnabled && m_backgroundObj) {
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 MVPMatrix;
+ QMatrix4x4 depthMVPMatrix;
+ QMatrix4x4 itModelMatrix;
+
+ modelMatrix.translate(0.0f, 0.0f, zComp);
+#ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z
+ modelMatrix.scale(
+ QVector3D(
+ (aspectRatio * backgroundMargin * m_areaSize.width()) / m_scaleFactor,
+ backgroundMargin,
+ (aspectRatio * backgroundMargin * m_areaSize.height()) / m_scaleFactor));
+#else // ..and this if we want uniform scaling based on largest dimension
+ modelMatrix.scale(QVector3D((aspectRatio * backgroundMargin),
+ backgroundMargin,
+ (aspectRatio * backgroundMargin)));
+#endif
+ // We can copy modelMatrix to itModelMatrix as it has not been translated
+ itModelMatrix = modelMatrix;
+ // If we're viewing from below, background object must be flipped
+ if (m_yFlipped) {
+ modelMatrix.rotate(180.0f, 1.0, 0.0, 0.0);
+ modelMatrix.rotate(270.0f - backgroundRotation, 0.0f, 1.0f, 0.0f);
+ } else {
+ modelMatrix.rotate(backgroundRotation, 0.0f, 1.0f, 0.0f);
+ }
+
+#ifdef SHOW_DEPTH_TEXTURE_SCENE
+ MVPMatrix = depthProjectionMatrix * depthViewMatrix * modelMatrix;
+#else
+ MVPMatrix = projectionMatrix * viewMatrix * modelMatrix;
+#endif
+ depthMVPMatrix = depthProjectionMatrix * depthViewMatrix * modelMatrix;
+
+ QVector3D backgroundColor = Utils::vectorFromColor(m_cachedTheme.m_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.m_ambientStrength * 4.0f);
+
+#if !defined(QT_OPENGL_ES_2)
+ if (m_cachedShadowQuality > QDataVis::ShadowNone) {
+ // Set shadow shader bindings
+ m_backgroundShader->setUniformValue(m_backgroundShader->shadowQ(),
+ m_shadowQualityToShader);
+ m_backgroundShader->setUniformValue(m_backgroundShader->depth(), depthMVPMatrix);
+ m_backgroundShader->setUniformValue(m_backgroundShader->lightS(),
+ m_cachedTheme.m_lightStrength / 10.0f);
+
+ // Draw the object
+ m_drawer->drawObject(m_backgroundShader, m_backgroundObj, 0, m_depthTexture);
+ } else
+#endif
+ {
+ // Set shadowless shader bindings
+ m_backgroundShader->setUniformValue(m_backgroundShader->lightS(),
+ m_cachedTheme.m_lightStrength);
+
+ // Draw the object
+ m_drawer->drawObject(m_backgroundShader, m_backgroundObj);
+ }
+ }
+
+ // Release background shader
+ m_backgroundShader->release();
+
+ // Disable textures
+ glDisable(GL_TEXTURE_2D);
+
+ // Draw grid lines
+#ifdef USE_UNIFORM_SCALING
+ AxisRenderCache *axisCacheMax;
+ if (m_axisCacheZ.max() > m_axisCacheX.max())
+ axisCacheMax = &m_axisCacheZ;
+ else
+ axisCacheMax = &m_axisCacheX;
+#endif
+
+ if (m_cachedIsGridEnabled && m_heightNormalizer) {
+ ShaderHelper *lineShader = m_backgroundShader;
+ // Bind line shader
+ lineShader->bind();
+
+ // Set unchanging shader bindings
+ QVector3D lineColor = Utils::vectorFromColor(m_cachedTheme.m_gridLine);
+ lineShader->setUniformValue(lineShader->lightP(), lightPos);
+ lineShader->setUniformValue(lineShader->view(), viewMatrix);
+ lineShader->setUniformValue(lineShader->color(), lineColor);
+ lineShader->setUniformValue(lineShader->ambientS(), m_cachedTheme.m_ambientStrength);
+
+ // Rows (= Z)
+ if (m_axisCacheZ.segmentCount() > 0) {
+ // Floor lines
+#ifndef USE_UNIFORM_SCALING
+ GLfloat lineStep = aspectRatio * m_axisCacheZ.subSegmentStep();
+ GLfloat linePos = -aspectRatio * m_axisCacheZ.min(); // Start line
+ int lastSegment = m_axisCacheZ.subSegmentCount() * m_axisCacheZ.segmentCount();
+#else
+ GLfloat lineStep = aspectRatio * axisCacheMax->subSegmentStep();
+ GLfloat linePos = -aspectRatio * m_scaleFactor; // Start line
+ int lastSegment = axisCacheMax->subSegmentCount() * axisCacheMax->segmentCount();
+#endif
+
+ for (int segment = 0; segment <= lastSegment; segment++) {
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 MVPMatrix;
+ QMatrix4x4 depthMVPMatrix;
+ QMatrix4x4 itModelMatrix;
+
+ if (m_yFlipped)
+ modelMatrix.translate(0.0f, backgroundMargin, linePos / m_scaleFactor + zComp);
+ else
+ modelMatrix.translate(0.0f, -backgroundMargin, linePos / m_scaleFactor + zComp);
+#ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z
+ modelMatrix.scale(
+ QVector3D(
+ (aspectRatio * backgroundMargin * m_areaSize.width()) / m_scaleFactor,
+ gridLineWidth, gridLineWidth));
+ itModelMatrix.scale(
+ QVector3D(
+ (aspectRatio * backgroundMargin * m_areaSize.width()) / m_scaleFactor,
+ gridLineWidth, gridLineWidth));
+#else // ..and this if we want uniform scaling based on largest dimension
+ modelMatrix.scale(QVector3D((aspectRatio * backgroundMargin),
+ gridLineWidth, gridLineWidth));
+ itModelMatrix.scale(QVector3D(aspectRatio * backgroundMargin,
+ gridLineWidth, gridLineWidth));
+#endif
+
+ // If we're viewing from below, grid line object must be flipped
+ if (m_yFlipped)
+ modelMatrix.rotate(180.0f, 1.0, 0.0, 0.0);
+
+ MVPMatrix = projectionMatrix * viewMatrix * modelMatrix;
+ depthMVPMatrix = depthProjectionMatrix * depthViewMatrix * 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 !defined(QT_OPENGL_ES_2)
+ if (m_cachedShadowQuality > QDataVis::ShadowNone) {
+ // Set shadow shader bindings
+ lineShader->setUniformValue(lineShader->shadowQ(), m_shadowQualityToShader);
+ lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
+ lineShader->setUniformValue(lineShader->lightS(),
+ m_cachedTheme.m_lightStrength / 10.0f);
+
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ } else
+#endif
+ {
+ // Set shadowless shader bindings
+ lineShader->setUniformValue(lineShader->lightS(),
+ m_cachedTheme.m_lightStrength);
+
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj);
+ }
+ linePos -= lineStep;
+ }
+
+ // Side wall lines
+#ifndef USE_UNIFORM_SCALING
+ GLfloat lineXTrans = (aspectRatio * backgroundMargin * m_areaSize.width())
+ / m_scaleFactor;
+ linePos = -aspectRatio * m_axisCacheZ.min(); // Start line
+#else
+ GLfloat lineXTrans = aspectRatio * backgroundMargin;
+ linePos = -aspectRatio * m_scaleFactor; // Start line
+#endif
+ if (!m_xFlipped)
+ lineXTrans = -lineXTrans;
+
+ for (int segment = 0; segment <= lastSegment; segment++) {
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 MVPMatrix;
+ QMatrix4x4 depthMVPMatrix;
+ QMatrix4x4 itModelMatrix;
+
+ modelMatrix.translate(lineXTrans, 0.0f, linePos / m_scaleFactor + zComp);
+ modelMatrix.scale(QVector3D(gridLineWidth, backgroundMargin, gridLineWidth));
+ itModelMatrix.scale(QVector3D(gridLineWidth, backgroundMargin, gridLineWidth));
+
+ MVPMatrix = projectionMatrix * viewMatrix * modelMatrix;
+ depthMVPMatrix = depthProjectionMatrix * depthViewMatrix * 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 !defined(QT_OPENGL_ES_2)
+ if (m_cachedShadowQuality > QDataVis::ShadowNone) {
+ // Set shadow shader bindings
+ lineShader->setUniformValue(lineShader->shadowQ(), m_shadowQualityToShader);
+ lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
+ lineShader->setUniformValue(lineShader->lightS(),
+ m_cachedTheme.m_lightStrength / 10.0f);
+
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ } else
+#endif
+ {
+ // Set shadowless shader bindings
+ lineShader->setUniformValue(lineShader->lightS(),
+ m_cachedTheme.m_lightStrength);
+
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj);
+ }
+ linePos -= lineStep;
+ }
+ }
+
+ // Columns (= X)
+ if (m_axisCacheX.segmentCount() > 0) {
+ // Floor lines
+#ifndef USE_UNIFORM_SCALING
+ GLfloat lineStep = aspectRatio * m_axisCacheX.subSegmentStep();
+ GLfloat linePos = aspectRatio * m_axisCacheX.min();
+ int lastSegment = m_axisCacheX.subSegmentCount() * m_axisCacheX.segmentCount();
+#else
+ GLfloat lineStep = aspectRatio * axisCacheMax->subSegmentStep();
+ GLfloat linePos = -aspectRatio * m_scaleFactor;
+ int lastSegment = axisCacheMax->subSegmentCount() * axisCacheMax->segmentCount();
+#endif
+
+ for (int segment = 0; segment <= lastSegment; segment++) {
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 MVPMatrix;
+ QMatrix4x4 depthMVPMatrix;
+ QMatrix4x4 itModelMatrix;
+
+ if (m_yFlipped)
+ modelMatrix.translate(linePos / m_scaleFactor, backgroundMargin, zComp);
+ else
+ modelMatrix.translate(linePos / m_scaleFactor, -backgroundMargin, zComp);
+#ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z
+ modelMatrix.scale(
+ QVector3D(
+ gridLineWidth, gridLineWidth,
+ (aspectRatio * backgroundMargin * m_areaSize.height()) / m_scaleFactor));
+ itModelMatrix.scale(
+ QVector3D(
+ gridLineWidth, gridLineWidth,
+ (aspectRatio * backgroundMargin * m_areaSize.height()) / m_scaleFactor));
+#else // ..and this if we want uniform scaling based on largest dimension
+ modelMatrix.scale(QVector3D(gridLineWidth, gridLineWidth,
+ aspectRatio * backgroundMargin));
+ itModelMatrix.scale(QVector3D(gridLineWidth, gridLineWidth,
+ aspectRatio * backgroundMargin));
+#endif
+
+ // If we're viewing from below, grid line object must be flipped
+ if (m_yFlipped)
+ modelMatrix.rotate(180.0f, 1.0, 0.0, 0.0);
+
+ MVPMatrix = projectionMatrix * viewMatrix * modelMatrix;
+ depthMVPMatrix = depthProjectionMatrix * depthViewMatrix * 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 !defined(QT_OPENGL_ES_2)
+ if (m_cachedShadowQuality > QDataVis::ShadowNone) {
+ // Set shadow shader bindings
+ lineShader->setUniformValue(lineShader->shadowQ(), m_shadowQualityToShader);
+ lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
+ lineShader->setUniformValue(lineShader->lightS(),
+ m_cachedTheme.m_lightStrength / 10.0f);
+
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ } else
+#endif
+ {
+ // Set shadowless shader bindings
+ lineShader->setUniformValue(lineShader->lightS(), m_cachedTheme.m_lightStrength);
+
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj);
+ }
+ linePos += lineStep;
+ }
+
+ // Back wall lines
+#ifndef USE_UNIFORM_SCALING
+ GLfloat lineZTrans = (aspectRatio * backgroundMargin * m_areaSize.height())
+ / m_scaleFactor;
+ linePos = aspectRatio * m_axisCacheX.min();
+#else
+ GLfloat lineZTrans = aspectRatio * backgroundMargin;
+ linePos = -aspectRatio * m_scaleFactor;
+#endif
+ if (!m_zFlipped)
+ lineZTrans = -lineZTrans;
+
+ for (int segment = 0; segment <= lastSegment; segment++) {
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 MVPMatrix;
+ QMatrix4x4 depthMVPMatrix;
+ QMatrix4x4 itModelMatrix;
+
+ modelMatrix.translate(linePos / m_scaleFactor, 0.0f, lineZTrans + zComp);
+ modelMatrix.scale(QVector3D(gridLineWidth, backgroundMargin, gridLineWidth));
+ itModelMatrix.scale(QVector3D(gridLineWidth, backgroundMargin, gridLineWidth));
+
+ MVPMatrix = projectionMatrix * viewMatrix * modelMatrix;
+ depthMVPMatrix = depthProjectionMatrix * depthViewMatrix * 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 !defined(QT_OPENGL_ES_2)
+ if (m_cachedShadowQuality > QDataVis::ShadowNone) {
+ // Set shadow shader bindings
+ lineShader->setUniformValue(lineShader->shadowQ(), m_shadowQualityToShader);
+ lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
+ lineShader->setUniformValue(lineShader->lightS(),
+ m_cachedTheme.m_lightStrength / 10.0f);
+
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ } else
+#endif
+ {
+ // Set shadowless shader bindings
+ lineShader->setUniformValue(lineShader->lightS(),
+ m_cachedTheme.m_lightStrength);
+
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj);
+ }
+ linePos += lineStep;
+ }
+ }
+
+ // Horizontal wall lines
+ if (m_axisCacheY.segmentCount() > 0) {
+ // Back wall
+ GLfloat lineStep = m_axisCacheY.subSegmentStep();
+ GLfloat linePos = m_axisCacheY.min();
+ int lastSegment = m_axisCacheY.subSegmentCount() * m_axisCacheY.segmentCount();
+
+#ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z
+ GLfloat lineZTrans = (aspectRatio * backgroundMargin * m_areaSize.height())
+ / m_scaleFactor;
+#else // ..and this if we want uniform scaling based on largest dimension
+ GLfloat lineZTrans = aspectRatio * backgroundMargin;
+#endif
+ if (!m_zFlipped)
+ lineZTrans = -lineZTrans;
+
+ for (int segment = 0; segment <= lastSegment; segment++) {
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 MVPMatrix;
+ QMatrix4x4 depthMVPMatrix;
+ QMatrix4x4 itModelMatrix;
+
+ modelMatrix.translate(0.0f, linePos / m_heightNormalizer, lineZTrans + zComp);
+#ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z
+ modelMatrix.scale(
+ QVector3D(
+ (aspectRatio * backgroundMargin * m_areaSize.width() / m_scaleFactor),
+ gridLineWidth, gridLineWidth));
+ itModelMatrix.scale(
+ QVector3D(
+ (aspectRatio * backgroundMargin * m_areaSize.width() / m_scaleFactor),
+ gridLineWidth, gridLineWidth));
+#else // ..and this if we want uniform scaling based on largest dimension
+ modelMatrix.scale(QVector3D((aspectRatio * backgroundMargin),
+ gridLineWidth, gridLineWidth));
+ itModelMatrix.scale(QVector3D(aspectRatio * backgroundMargin,
+ gridLineWidth, gridLineWidth));
+#endif
+
+ MVPMatrix = projectionMatrix * viewMatrix * modelMatrix;
+ depthMVPMatrix = depthProjectionMatrix * depthViewMatrix * 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 !defined(QT_OPENGL_ES_2)
+ if (m_cachedShadowQuality > QDataVis::ShadowNone) {
+ // Set shadow shader bindings
+ lineShader->setUniformValue(lineShader->shadowQ(), m_shadowQualityToShader);
+ lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
+ lineShader->setUniformValue(lineShader->lightS(),
+ m_cachedTheme.m_lightStrength / 10.0f);
+
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ } else
+#endif
+ {
+ // Set shadowless shader bindings
+ lineShader->setUniformValue(lineShader->lightS(), m_cachedTheme.m_lightStrength);
+
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj);
+ }
+ linePos += lineStep;
+ }
+
+ // Side wall
+ linePos = m_axisCacheY.min();
+ lastSegment = m_axisCacheY.subSegmentCount() * m_axisCacheY.segmentCount();
+#ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z
+ GLfloat lineXTrans = (aspectRatio * backgroundMargin * m_areaSize.width())
+ / m_scaleFactor;
+#else // ..and this if we want uniform scaling based on largest dimension
+ GLfloat lineXTrans = aspectRatio * backgroundMargin;
+#endif
+ if (!m_xFlipped)
+ lineXTrans = -lineXTrans;
+
+ for (int segment = 0; segment <= lastSegment; segment++) {
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 MVPMatrix;
+ QMatrix4x4 depthMVPMatrix;
+ QMatrix4x4 itModelMatrix;
+
+ modelMatrix.translate(lineXTrans, linePos / m_heightNormalizer, zComp);
+#ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z
+ modelMatrix.scale(
+ QVector3D(
+ gridLineWidth, gridLineWidth,
+ (aspectRatio * backgroundMargin * m_areaSize.height()) / m_scaleFactor));
+ itModelMatrix.scale(
+ QVector3D(
+ gridLineWidth, gridLineWidth,
+ (aspectRatio * backgroundMargin * m_areaSize.height()) / m_scaleFactor));
+
+#else // ..and this if we want uniform scaling based on largest dimension
+ modelMatrix.scale(QVector3D(gridLineWidth, gridLineWidth,
+ (aspectRatio * backgroundMargin)));
+ itModelMatrix.scale(QVector3D(gridLineWidth, gridLineWidth,
+ aspectRatio * backgroundMargin));
+
+#endif
+ MVPMatrix = projectionMatrix * viewMatrix * modelMatrix;
+ depthMVPMatrix = depthProjectionMatrix * depthViewMatrix * 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 !defined(QT_OPENGL_ES_2)
+ if (m_cachedShadowQuality > QDataVis::ShadowNone) {
+ // Set shadow shader bindings
+ lineShader->setUniformValue(lineShader->shadowQ(), m_shadowQualityToShader);
+ lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
+ lineShader->setUniformValue(lineShader->lightS(),
+ m_cachedTheme.m_lightStrength / 10.0f);
+
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ } else
+#endif
+ {
+ // Set shadowless shader bindings
+ lineShader->setUniformValue(lineShader->lightS(),
+ m_cachedTheme.m_lightStrength);
+
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj);
+ }
+ linePos += lineStep;
+ }
+ }
+
+ // Release line shader
+ lineShader->release();
+ }
+
+ // Draw axis labels
+ // TODO: Calculations done temporarily here. Should be done when calculating lines to avoid
+ // extra for -loops?
+ // Bind label shader
+ m_labelShader->bind();
+
+ glEnable(GL_TEXTURE_2D);
+ if (m_cachedLabelTransparency > QDataVis::TransparencyNone) {
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ }
+
+ // Z Labels
+ if (m_axisCacheZ.segmentCount() > 0) {
+#ifndef USE_UNIFORM_SCALING
+ GLfloat posStep = aspectRatio * m_axisCacheZ.segmentStep();
+ GLfloat labelPos = -aspectRatio * m_axisCacheZ.min();
+ int lastSegment = m_axisCacheZ.segmentCount();
+#else
+ GLfloat posStep = aspectRatio * axisCacheMax->segmentStep();
+ GLfloat labelPos = aspectRatio * m_scaleFactor;
+ int lastSegment = axisCacheMax->segmentCount();
+#endif
+ int labelNbr = 0;
+ for (int segment = 0; segment <= lastSegment; segment++) {
+#ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z
+ if (m_axisCacheZ.labelItems().size() > labelNbr) {
+ GLfloat labelXTrans = (aspectRatio * backgroundMargin * m_areaSize.width())
+ / m_scaleFactor + labelMargin;
+#else // ..and this if we want uniform scaling based on largest dimension
+ if (axisCacheMax->labelItems().size() > labelNbr) {
+ GLfloat labelXTrans = aspectRatio * backgroundMargin + labelMargin;
+#endif
+ GLfloat labelYTrans = -backgroundMargin;
+ GLfloat rotLabelX = -90.0f;
+ GLfloat rotLabelY = 0.0f;
+ GLfloat rotLabelZ = 0.0f;
+ Qt::AlignmentFlag alignment = Qt::AlignRight;
+ if (m_zFlipped)
+ rotLabelY = 180.0f;
+ if (m_xFlipped) {
+ labelXTrans = -labelXTrans;
+ alignment = Qt::AlignLeft;
+ }
+ if (m_yFlipped) {
+ rotLabelZ += 180.0f;
+ rotLabelY += 180.0f;
+ labelYTrans = -labelYTrans;
+ }
+ QVector3D labelTrans = QVector3D(labelXTrans,
+ labelYTrans,
+ labelPos / m_scaleFactor + zComp);
+
+
+ // Draw the label here
+ m_dummyRenderItem.setTranslation(labelTrans);
+#ifndef USE_UNIFORM_SCALING
+ const LabelItem &axisLabelItem = *m_axisCacheZ.labelItems().at(labelNbr);
+#else
+ const LabelItem &axisLabelItem = *axisCacheMax->labelItems().at(labelNbr);
+#endif
+
+ m_drawer->drawLabel(m_dummyRenderItem, axisLabelItem, viewMatrix, projectionMatrix,
+ QVector3D(0.0f, 0.0f, zComp),
+ QVector3D(rotLabelX, rotLabelY, rotLabelZ),
+ 0, m_cachedSelectionMode,
+ m_labelShader, m_labelObj, m_cachedScene->camera(), true, true, Drawer::LabelMid,
+ alignment);
+ }
+ labelNbr++;
+ labelPos -= posStep;
+ }
+ }
+ // X Labels
+ if (m_axisCacheX.segmentCount() > 0) {
+#ifndef USE_UNIFORM_SCALING
+ GLfloat posStep = aspectRatio * m_axisCacheX.segmentStep();
+ GLfloat labelPos = aspectRatio * m_axisCacheX.min();
+ int lastSegment = m_axisCacheX.segmentCount();
+#else
+ GLfloat posStep = aspectRatio * axisCacheMax->segmentStep();
+ GLfloat labelPos = -aspectRatio * m_scaleFactor;
+ int lastSegment = axisCacheMax->segmentCount();
+#endif
+ int labelNbr = 0;
+ for (int segment = 0; segment <= lastSegment; segment++) {
+#ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z
+ if (m_axisCacheX.labelItems().size() > labelNbr) {
+ GLfloat labelZTrans = (aspectRatio * backgroundMargin * m_areaSize.height())
+ / m_scaleFactor + labelMargin;
+#else // ..and this if we want uniform scaling based on largest dimension
+ if (axisCacheMax->labelItems().size() > labelNbr) {
+ GLfloat labelZTrans = aspectRatio * backgroundMargin + labelMargin;
+#endif
+ GLfloat labelYTrans = -backgroundMargin;
+ GLfloat rotLabelX = -90.0f;
+ GLfloat rotLabelY = 90.0f;
+ GLfloat rotLabelZ = 0.0f;
+ Qt::AlignmentFlag alignment = Qt::AlignLeft;
+ if (m_xFlipped)
+ rotLabelY = -90.0f;
+ if (m_zFlipped) {
+ labelZTrans = -labelZTrans;
+ alignment = Qt::AlignRight;
+ }
+ if (m_yFlipped) {
+ rotLabelZ += 180.0f;
+ rotLabelY += 180.0f;
+ labelYTrans = -labelYTrans;
+ }
+ QVector3D labelTrans = QVector3D(labelPos / m_scaleFactor,
+ labelYTrans,
+ labelZTrans + zComp);
+
+ // Draw the label here
+ m_dummyRenderItem.setTranslation(labelTrans);
+#ifndef USE_UNIFORM_SCALING
+ const LabelItem &axisLabelItem = *m_axisCacheX.labelItems().at(labelNbr);
+#else
+ const LabelItem &axisLabelItem = *axisCacheMax->labelItems().at(labelNbr);
+#endif
+
+ m_drawer->drawLabel(m_dummyRenderItem, axisLabelItem, viewMatrix, projectionMatrix,
+ QVector3D(0.0f, 0.0f, zComp),
+ QVector3D(rotLabelX, rotLabelY, rotLabelZ),
+ 0, m_cachedSelectionMode,
+ m_labelShader, m_labelObj, m_cachedScene->camera(), true, true, Drawer::LabelMid,
+ alignment);
+ }
+ labelNbr++;
+ labelPos += posStep;
+ }
+ }
+ // Y Labels
+ if (m_axisCacheY.segmentCount() > 0) {
+ GLfloat posStep = m_axisCacheY.segmentStep();
+ GLfloat labelPos = m_axisCacheY.min();
+ int labelNbr = 0;
+ for (int segment = 0; segment <= m_axisCacheY.segmentCount(); segment++) {
+ if (m_axisCacheY.labelItems().size() > labelNbr) {
+#ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z
+ GLfloat labelXTrans = (aspectRatio * backgroundMargin * m_areaSize.width())
+ / m_scaleFactor;
+ GLfloat labelZTrans = (aspectRatio * backgroundMargin * m_areaSize.height())
+ / m_scaleFactor;
+#else // ..and this if we want uniform scaling based on largest dimension
+ GLfloat labelXTrans = aspectRatio * backgroundMargin;
+ GLfloat labelZTrans = labelXTrans;
+#endif
+ GLfloat labelMarginXTrans = labelMargin;
+ GLfloat labelMarginZTrans = labelMargin;
+ GLfloat labelYTrans = labelPos / m_heightNormalizer;
+ GLfloat rotLabelX = 0.0f;
+ GLfloat rotLabelY = -90.0f;
+ GLfloat rotLabelZ = 0.0f;
+ Qt::AlignmentFlag alignment = Qt::AlignLeft;
+ if (!m_xFlipped) {
+ labelXTrans = -labelXTrans;
+ labelMarginXTrans = -labelMargin;
+ rotLabelY = 90.0f;
+ }
+ if (m_zFlipped) {
+ labelZTrans = -labelZTrans;
+ labelMarginZTrans = -labelMargin;
+ alignment = Qt::AlignRight;
+ }
+
+ const LabelItem &axisLabelItem = *m_axisCacheY.labelItems().at(labelNbr);
+
+ // Back wall
+ QVector3D labelTrans = QVector3D(labelXTrans, labelYTrans,
+ labelZTrans + labelMarginZTrans + zComp);
+
+ // Draw the label here
+ m_dummyRenderItem.setTranslation(labelTrans);
+ m_drawer->drawLabel(m_dummyRenderItem, axisLabelItem, viewMatrix, projectionMatrix,
+ QVector3D(0.0f, 0.0f, zComp),
+ QVector3D(rotLabelX, rotLabelY, rotLabelZ),
+ 0, m_cachedSelectionMode,
+ m_labelShader, m_labelObj, m_cachedScene->camera(), true, true, Drawer::LabelMid,
+ alignment);
+
+ // Side wall
+ if (m_xFlipped)
+ alignment = Qt::AlignLeft;
+ else
+ alignment = Qt::AlignRight;
+ if (m_zFlipped)
+ rotLabelY = 180.0f;
+ else
+ rotLabelY = 0.0f;
+
+ labelTrans = QVector3D(-labelXTrans - labelMarginXTrans, labelYTrans,
+ -labelZTrans + zComp);
+
+ // Draw the label here
+ m_dummyRenderItem.setTranslation(labelTrans);
+ m_drawer->drawLabel(m_dummyRenderItem, axisLabelItem, viewMatrix, projectionMatrix,
+ QVector3D(0.0f, 0.0f, zComp),
+ QVector3D(rotLabelX, rotLabelY, rotLabelZ),
+ 0, m_cachedSelectionMode,
+ m_labelShader, m_labelObj, m_cachedScene->camera(), true, true, Drawer::LabelMid,
+ alignment);
+ }
+ labelNbr++;
+ labelPos += posStep;
+ }
+ }
+
+ // 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 = selectedItem->selectionLabelItem();
+ if (m_selectedItem != selectedItem || m_updateLabels
+ || !labelItem.textureId()) {
+ QString labelText = selectedItem->selectionLabel();
+ if (labelText.isNull()) {
+ static const QString xTitleTag(QStringLiteral("@xTitle"));
+ static const QString yTitleTag(QStringLiteral("@yTitle"));
+ static const QString zTitleTag(QStringLiteral("@zTitle"));
+ static const QString xLabelTag(QStringLiteral("@xLabel"));
+ static const QString yLabelTag(QStringLiteral("@yLabel"));
+ static const QString zLabelTag(QStringLiteral("@zLabel"));
+
+ labelText = itemLabelFormat();
+
+ labelText.replace(xTitleTag, m_axisCacheX.title());
+ labelText.replace(yTitleTag, m_axisCacheY.title());
+ labelText.replace(zTitleTag, m_axisCacheZ.title());
+
+ if (labelText.contains(xLabelTag)) {
+ QString labelFormat = m_axisCacheX.labelFormat();
+ if (labelFormat.isEmpty())
+ labelFormat = Utils::defaultLabelFormat();
+ QString valueLabelText = generateValueLabel(labelFormat,
+ selectedItem->position().x());
+ labelText.replace(xLabelTag, valueLabelText);
+ }
+ if (labelText.contains(yLabelTag)) {
+ QString labelFormat = m_axisCacheY.labelFormat();
+ if (labelFormat.isEmpty())
+ labelFormat = Utils::defaultLabelFormat();
+ QString valueLabelText = generateValueLabel(labelFormat,
+ selectedItem->position().y());
+ labelText.replace(yLabelTag, valueLabelText);
+ }
+ if (labelText.contains(zLabelTag)) {
+ QString labelFormat = m_axisCacheZ.labelFormat();
+ if (labelFormat.isEmpty())
+ labelFormat = Utils::defaultLabelFormat();
+ QString valueLabelText = generateValueLabel(labelFormat,
+ selectedItem->position().z());
+ labelText.replace(zLabelTag, valueLabelText);
+ }
+
+ selectedItem->setSelectionLabel(labelText);
+ }
+ m_drawer->generateLabelItem(labelItem, labelText);
+ m_selectedItem = selectedItem;
+ }
+
+ m_drawer->drawLabel(*selectedItem, labelItem, viewMatrix, projectionMatrix,
+ QVector3D(0.0f, 0.0f, zComp),
+ QVector3D(0.0f, 0.0f, 0.0f), 0,
+ m_cachedSelectionMode, m_labelShader,
+ m_labelObj, m_cachedScene->camera(), true, false, Drawer::LabelMid);
+
+ // Reset label update flag; they should have been updated when we get here
+ m_updateLabels = false;
+ glEnable(GL_DEPTH_TEST);
+ }
+
+ glDisable(GL_TEXTURE_2D);
+ if (m_cachedLabelTransparency > QDataVis::TransparencyNone)
+ glDisable(GL_BLEND);
+
+ // Release label shader
+ m_labelShader->release();
+}
+
+void Scatter3DRenderer::updateSelectedItemIndex(int index)
+{
+ if (index == Scatter3DController::noSelectionIndex())
+ m_selection = selectionSkipColor;
+ else
+ m_selection = indexToSelectionColor(index);
+}
+
+void Scatter3DRenderer::handleResize()
+{
+ if (m_cachedBoundingRect.width() == 0 || m_cachedBoundingRect.height() == 0)
+ return;
+
+ // Set view port
+ m_mainViewPort = QRect(0, 0, m_cachedBoundingRect.width(), m_cachedBoundingRect.height());
+
+ Abstract3DRenderer::handleResize();
+}
+
+void Scatter3DRenderer::updateBackgroundEnabled(bool enable)
+{
+ if (enable != m_cachedIsBackgroundEnabled) {
+ Abstract3DRenderer::updateBackgroundEnabled(enable);
+ loadMeshFile(); // Load changed dot type
+ }
+}
+
+void Scatter3DRenderer::updateShadowQuality(QDataVis::ShadowQuality quality)
+{
+ m_cachedShadowQuality = quality;
+ switch (quality) {
+ case QDataVis::ShadowLow:
+ m_shadowQualityToShader = 33.3f;
+ m_shadowQualityMultiplier = 1;
+ break;
+ case QDataVis::ShadowMedium:
+ m_shadowQualityToShader = 100.0f;
+ m_shadowQualityMultiplier = 3;
+ break;
+ case QDataVis::ShadowHigh:
+ m_shadowQualityToShader = 200.0f;
+ m_shadowQualityMultiplier = 5;
+ break;
+ case QDataVis::ShadowSoftLow:
+ m_shadowQualityToShader = 5.0f;
+ m_shadowQualityMultiplier = 1;
+ break;
+ case QDataVis::ShadowSoftMedium:
+ m_shadowQualityToShader = 10.0f;
+ m_shadowQualityMultiplier = 3;
+ break;
+ case QDataVis::ShadowSoftHigh:
+ m_shadowQualityToShader = 15.0f;
+ m_shadowQualityMultiplier = 4;
+ break;
+ default:
+ m_shadowQualityToShader = 0.0f;
+ m_shadowQualityMultiplier = 1;
+ break;
+ }
+
+ handleShadowQualityChange();
+
+#if !defined(QT_OPENGL_ES_2)
+ // Re-init depth buffer
+ updateDepthBuffer();
+#endif
+}
+
+void Scatter3DRenderer::loadMeshFile()
+{
+ QString objectFileName = m_cachedObjFile;
+ if (m_dotObj)
+ delete m_dotObj;
+ m_dotObj = new ObjectHelper(objectFileName);
+ m_dotObj->load();
+}
+
+void Scatter3DRenderer::loadBackgroundMesh()
+{
+ if (m_backgroundObj)
+ delete m_backgroundObj;
+ m_backgroundObj = new ObjectHelper(QStringLiteral(":/defaultMeshes/background"));
+ m_backgroundObj->load();
+}
+
+void Scatter3DRenderer::loadGridLineMesh()
+{
+ if (m_gridLineObj)
+ delete m_gridLineObj;
+ m_gridLineObj = new ObjectHelper(QStringLiteral(":/defaultMeshes/bar"));
+ m_gridLineObj->load();
+}
+
+void Scatter3DRenderer::loadLabelMesh()
+{
+ if (m_labelObj)
+ delete m_labelObj;
+ m_labelObj = new ObjectHelper(QStringLiteral(":/defaultMeshes/label"));
+ m_labelObj->load();
+}
+
+void Scatter3DRenderer::updateTextures()
+{
+ // Drawer has changed; this flag needs to be checked when checking if we need to update labels
+ m_updateLabels = true;
+}
+
+void Scatter3DRenderer::updateAxisRange(Q3DAbstractAxis::AxisOrientation orientation,
+ qreal min, qreal max)
+{
+ Abstract3DRenderer::updateAxisRange(orientation, min, max);
+}
+
+void Scatter3DRenderer::calculateTranslation(ScatterRenderItem &item)
+{
+ // Origin should be in the center of scene, ie. both positive and negative values are drawn
+ // above background
+
+ // We need to normalize translations
+ GLfloat xTrans = (aspectRatio * item.position().x()) / m_scaleFactor;
+ GLfloat zTrans = -(aspectRatio * item.position().z()) / m_scaleFactor;
+ GLfloat yTrans = item.position().y() / m_heightNormalizer;
+ item.setTranslation(QVector3D(xTrans, yTrans, zTrans + zComp));
+ //qDebug() << item.translation();
+}
+
+void Scatter3DRenderer::calculateSceneScalingFactors()
+{
+ m_heightNormalizer = (GLfloat)qMax(qAbs(m_axisCacheY.max()), qAbs(m_axisCacheY.min()));
+ m_areaSize.setHeight(qMax(qAbs(m_axisCacheZ.max()), qAbs(m_axisCacheZ.min())));
+ m_areaSize.setWidth(qMax(qAbs(m_axisCacheX.max()), qAbs(m_axisCacheX.min())));
+ m_scaleFactor = qMax(m_areaSize.width(), m_areaSize.height());
+ //qDebug() << m_heightNormalizer << m_areaSize << m_scaleFactor << m_axisCacheY.max() << m_axisCacheX.max() << m_axisCacheZ.max();
+}
+
+QRect Scatter3DRenderer::mainViewPort()
+{
+ return m_mainViewPort;
+}
+
+void Scatter3DRenderer::initShaders(const QString &vertexShader, const QString &fragmentShader)
+{
+ if (m_dotShader)
+ delete m_dotShader;
+ m_dotShader = new ShaderHelper(this, vertexShader, fragmentShader);
+ m_dotShader->initialize();
+}
+
+void Scatter3DRenderer::initSelectionShader()
+{
+ if (m_selectionShader)
+ delete m_selectionShader;
+ m_selectionShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSelection"),
+ QStringLiteral(":/shaders/fragmentSelection"));
+ m_selectionShader->initialize();
+}
+
+void Scatter3DRenderer::initSelectionBuffer()
+{
+ if (m_selectionTexture)
+ m_textureHelper->deleteTexture(&m_selectionTexture);
+
+ m_selectionTexture = m_textureHelper->createSelectionTexture(m_mainViewPort.size(),
+ m_selectionFrameBuffer,
+ m_selectionDepthBuffer);
+}
+
+#if !defined(QT_OPENGL_ES_2)
+void Scatter3DRenderer::initDepthShader()
+{
+ 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_depthTexture) {
+ m_textureHelper->deleteTexture(&m_depthTexture);
+ m_depthTexture = 0;
+ }
+
+ if (m_cachedShadowQuality > QDataVis::ShadowNone) {
+ m_depthTexture = m_textureHelper->createDepthTexture(m_mainViewPort.size(),
+ m_depthFrameBuffer,
+ m_shadowQualityMultiplier);
+ if (!m_depthTexture) {
+ switch (m_cachedShadowQuality) {
+ case QDataVis::ShadowHigh:
+ qWarning("Creating high quality shadows failed. Changing to medium quality.");
+ (void)m_controller->setShadowQuality(QDataVis::ShadowMedium);
+ updateShadowQuality(QDataVis::ShadowMedium);
+ break;
+ case QDataVis::ShadowMedium:
+ qWarning("Creating medium quality shadows failed. Changing to low quality.");
+ (void)m_controller->setShadowQuality(QDataVis::ShadowLow);
+ updateShadowQuality(QDataVis::ShadowLow);
+ break;
+ case QDataVis::ShadowLow:
+ qWarning("Creating low quality shadows failed. Switching shadows off.");
+ (void)m_controller->setShadowQuality(QDataVis::ShadowNone);
+ updateShadowQuality(QDataVis::ShadowNone);
+ break;
+ case QDataVis::ShadowSoftHigh:
+ qWarning("Creating soft high quality shadows failed. Changing to soft medium quality.");
+ (void)m_controller->setShadowQuality(QDataVis::ShadowSoftMedium);
+ updateShadowQuality(QDataVis::ShadowSoftMedium);
+ break;
+ case QDataVis::ShadowSoftMedium:
+ qWarning("Creating soft medium quality shadows failed. Changing to soft low quality.");
+ (void)m_controller->setShadowQuality(QDataVis::ShadowSoftLow);
+ updateShadowQuality(QDataVis::ShadowSoftLow);
+ break;
+ case QDataVis::ShadowSoftLow:
+ qWarning("Creating soft low quality shadows failed. Switching shadows off.");
+ (void)m_controller->setShadowQuality(QDataVis::ShadowNone);
+ updateShadowQuality(QDataVis::ShadowNone);
+ break;
+ default:
+ // You'll never get here
+ break;
+ }
+ }
+ }
+}
+#endif
+
+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::initLabelShaders(const QString &vertexShader, const QString &fragmentShader)
+{
+ if (m_labelShader)
+ delete m_labelShader;
+ m_labelShader = new ShaderHelper(this, vertexShader, fragmentShader);
+ m_labelShader->initialize();
+}
+
+QVector3D Scatter3DRenderer::indexToSelectionColor(GLint index)
+{
+ GLubyte dotIdxRed = index & 0xff;
+ GLubyte dotIdxGreen = (index & 0xff00) >> 8;
+ GLubyte dotIdxBlue = (index & 0xff0000) >> 16;
+
+ return QVector3D(dotIdxRed, dotIdxGreen, dotIdxBlue);
+}
+
+QT_DATAVISUALIZATION_END_NAMESPACE