summaryrefslogtreecommitdiffstats
path: root/src/datavis3d/engine/maps3dcontroller.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/datavis3d/engine/maps3dcontroller.cpp')
-rw-r--r--src/datavis3d/engine/maps3dcontroller.cpp1763
1 files changed, 1763 insertions, 0 deletions
diff --git a/src/datavis3d/engine/maps3dcontroller.cpp b/src/datavis3d/engine/maps3dcontroller.cpp
new file mode 100644
index 00000000..e86bdfa2
--- /dev/null
+++ b/src/datavis3d/engine/maps3dcontroller.cpp
@@ -0,0 +1,1763 @@
+/****************************************************************************
+**
+** 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 QtDataVis3D 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 "maps3dcontroller_p.h"
+#include "maps3drenderer_p.h"
+#include "camerahelper_p.h"
+#include "shaderhelper_p.h"
+#include "objecthelper_p.h"
+#include "texturehelper_p.h"
+#include "theme_p.h"
+#include "utils_p.h"
+#include "drawer_p.h"
+#include "maprenderitem_p.h"
+#include "qmapdataproxy_p.h"
+
+#include <QOpenGLFunctions>
+#include <QMatrix4x4>
+#include <QMouseEvent>
+#include <qmath.h>
+
+#include <QDebug>
+
+//#define DISPLAY_RENDER_SPEED
+
+// 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
+
+#ifdef DISPLAY_RENDER_SPEED
+#include <QTime>
+#endif
+
+QT_DATAVIS3D_BEGIN_NAMESPACE
+
+//#define DISPLAY_FULL_DATA_ON_SELECTION // Append selection value text with row and column labels
+
+const GLfloat gridLineWidth = 0.005f;
+GLfloat distanceMod = 0.0f;
+static QVector3D skipColor = QVector3D(255, 255, 255); // Selection texture's background color
+
+Maps3DController::Maps3DController(const QRect &rect)
+ : m_camera(new CameraHelper()),
+ m_barShader(0),
+ m_depthShader(0),
+ m_selectionShader(0),
+ m_backgroundShader(0),
+ m_labelShader(0),
+ m_barObj(0),
+ m_backgroundObj(0),
+ m_gridLineObj(0),
+ m_labelObj(0),
+ m_objFile(QStringLiteral(":/defaultMeshes/bar")),
+ m_mousePressed(MouseNone),
+ m_mousePos(QPoint(0, 0)),
+ m_zoomLevel(100),
+ m_autoScaleAdjustment(1.0f),
+ m_horizontalRotation(0.0f),
+ m_verticalRotation(45.0f),
+ m_barThickness(QVector3D(1.0f, 1.0f, 1.0f)),
+ m_heightNormalizer(0.0f),
+ m_yAdjustment(0.0f),
+ m_scaleFactor(1.0f),
+ m_theme(new Theme()),
+ m_isInitialized(false),
+ m_selectionMode(QDataVis::ModeItem),
+ m_selectedBar(0),
+ m_previouslySelectedBar(0),
+ m_axisLabelX(QStringLiteral("X")),
+ m_axisLabelZ(QStringLiteral("Z")),
+ m_axisLabelY(QStringLiteral("Y")),
+ m_sceneViewPort(rect.x(), rect.y(), rect.width(), rect.height()),
+ m_zoomViewPort(rect.x(), rect.y(), rect.width(), rect.height()),
+ m_zoomActivated(false),
+ m_textureHelper(new TextureHelper()),
+ m_labelTransparency(QDataVis::TransparencyFromTheme),
+ m_font(QFont(QStringLiteral("Arial"))),
+ m_drawer(new Drawer(*m_theme, m_font, m_labelTransparency)),
+ m_areaSize(QSizeF(1.0f, 1.0f)),
+ m_bgrTexture(0),
+ m_depthTexture(0),
+ m_selectionTexture(0),
+ m_depthFrameBuffer(0),
+ m_selectionFrameBuffer(0),
+ m_selectionDepthBuffer(0),
+ m_updateLabels(true),
+ m_adjustDirection(Q3DMaps::AdjustHeight),
+ m_shadowQuality(QDataVis::ShadowLow),
+ m_shadowQualityToShader(33.3f),
+ m_bgrHasAlpha(false),
+ m_boundingRect(rect.x(), rect.y(), rect.width(), rect.height()),
+ m_data(0),
+ m_valuesDirty(false)
+{
+ //m_data->setDrawer(m_drawer);
+ //QObject::connect(m_drawer, &Drawer::drawerChanged, this, &Maps3DController::updateTextures);
+}
+
+Maps3DController::~Maps3DController()
+{
+ m_textureHelper->glDeleteFramebuffers(1, &m_depthFrameBuffer);
+ m_textureHelper->glDeleteFramebuffers(1, &m_selectionFrameBuffer);
+ m_textureHelper->glDeleteRenderbuffers(1, &m_selectionDepthBuffer);
+ m_textureHelper->deleteTexture(&m_selectionTexture);
+ m_textureHelper->deleteTexture(&m_bgrTexture);
+ delete m_barShader;
+ delete m_selectionShader;
+ delete m_backgroundShader;
+ delete m_barObj;
+ delete m_backgroundObj;
+ delete m_gridLineObj;
+ delete m_textureHelper;
+ delete m_drawer;
+ delete m_camera;
+}
+
+void Maps3DController::initializeOpenGL()
+{
+ // Initialization is called multiple times when Qt Quick components are used
+ if (m_isInitialized)
+ return;
+
+ m_renderer = new Maps3DRenderer(this);
+ initializeOpenGLFunctions();
+
+ m_textureHelper = new TextureHelper();
+ m_drawer->initializeOpenGL();
+
+ // Initialize shaders
+#if !defined(QT_OPENGL_ES_2)
+ if (m_shadowQuality > QDataVis::ShadowNone) {
+ if (!m_theme->m_uniformColor) {
+ initShaders(QStringLiteral(":/shaders/vertexShadow"),
+ QStringLiteral(":/shaders/fragmentShadowNoTexColorOnY"));
+ } else {
+ initShaders(QStringLiteral(":/shaders/vertexShadow"),
+ QStringLiteral(":/shaders/fragmentShadowNoTex"));
+ }
+ initBackgroundShaders(QStringLiteral(":/shaders/vertexShadow"),
+ QStringLiteral(":/shaders/fragmentShadow"));
+ } else {
+ if (!m_theme->m_uniformColor) {
+ initShaders(QStringLiteral(":/shaders/vertex"),
+ QStringLiteral(":/shaders/fragmentColorOnY"));
+ } else {
+ initShaders(QStringLiteral(":/shaders/vertex"),
+ QStringLiteral(":/shaders/fragment"));
+ }
+ initBackgroundShaders(QStringLiteral(":/shaders/vertexTexture"),
+ QStringLiteral(":/shaders/fragmentTexture"));
+ }
+#else
+ if (!m_theme->m_uniformColor) {
+ initShaders(QStringLiteral(":/shaders/vertexES2"),
+ QStringLiteral(":/shaders/fragmentColorOnYES2"));
+ } else {
+ initShaders(QStringLiteral(":/shaders/vertexES2"),
+ QStringLiteral(":/shaders/fragmentES2"));
+ }
+ initBackgroundShaders(QStringLiteral(":/shaders/vertexTexture"), // Same vertex shader ok for ES2
+ QStringLiteral(":/shaders/fragmentTextureES2"));
+#endif
+ 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 default mesh
+ loadBarMesh();
+
+ // Load grid line mesh
+ loadGridLineMesh();
+
+ // Load label mesh
+ loadLabelMesh();
+
+ // Set OpenGL features
+ glEnable(GL_DEPTH_TEST);
+ glDepthFunc(GL_LESS);
+ glEnable(GL_CULL_FACE);
+ glCullFace(GL_BACK);
+
+#if !defined(QT_OPENGL_ES_2)
+ glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
+ glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
+ glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
+#endif
+
+ // Set initial camera position
+ // X must be 0 for rotation to work - we can use "setCameraRotation" for setting it later
+ m_camera->setDefaultCameraOrientation(QVector3D(0.0f, 0.0f, 1.0f + 2.9f * zComp),
+ QVector3D(0.0f, 0.0f, zComp),
+ QVector3D(0.0f, 1.0f, 0.0f));
+
+ // Adjust to default rotation
+ setCameraPosition(m_horizontalRotation, m_verticalRotation, m_zoomLevel);
+
+ // Set view port
+ glViewport(0, 0, width(), height());
+
+ // Set initialized -flag
+ m_isInitialized = true;
+
+ // Resize in case we've missed resize events
+ // Resize calls initSelectionBuffer and initDepthBuffer, so they don't need to be called here
+ resizeNotify();
+
+ // Load background mesh (we need to be initialized first)
+ loadBackgroundMesh();
+
+ // Update default light position
+#ifndef USE_WIDER_SHADOWS
+ distanceMod = 5.0f;
+#endif
+}
+
+void Maps3DController::synchDataToRenderer()
+{
+ // TODO: Implement!
+}
+
+
+/*!
+ * \internal
+ */
+void Maps3DController::render(const GLuint defaultFboHandle)
+{
+ if (!m_isInitialized)
+ return;
+
+#ifdef DISPLAY_RENDER_SPEED
+ // For speed computation
+ static bool firstRender = true;
+ static QTime lastTime;
+ static GLint nbFrames = 0;
+ if (firstRender) {
+ lastTime.start();
+ firstRender = false;
+ }
+
+ // Measure speed (as milliseconds per frame)
+ nbFrames++;
+ if (lastTime.elapsed() >= 1000) { // print only if last measurement was more than 1s ago
+ qDebug() << qreal(lastTime.elapsed()) / qreal(nbFrames) << "ms/frame (=" << qreal(nbFrames) << "fps)";
+ nbFrames = 0;
+ lastTime.restart();
+ }
+#endif
+
+ // Update cached values
+ if (m_valuesDirty) {
+ const QMapDataArray &dataArray = *m_data->array();
+ int dataSize = dataArray.size();
+ m_renderItemArray.resize(dataSize);
+ for (int i = 0; i < dataSize ; i++) {
+ qreal value = dataArray.at(i).value();
+ m_renderItemArray[i].setValue(value);
+ m_renderItemArray[i].setMapPosition(dataArray.at(i).mapPosition());
+ m_renderItemArray[i].setHeight(value / m_heightNormalizer);
+ m_renderItemArray[i].setItemLabel(dataArray.at(i).label());
+ calculateTranslation(m_renderItemArray[i]);
+ m_renderItemArray[i].setRenderer(this);
+ }
+ m_valuesDirty = false;
+ }
+
+ if (defaultFboHandle) {
+ glDepthMask(true);
+ glEnable(GL_DEPTH_TEST);
+ glDepthFunc(GL_LESS);
+ glEnable(GL_CULL_FACE);
+ glCullFace(GL_BACK);
+ }
+
+ // Draw scene
+ drawScene(defaultFboHandle);
+}
+
+/*!
+ * \internal
+ */
+void Maps3DController::drawScene(const GLuint defaultFboHandle)
+{
+ // Set clear color
+ QVector3D clearColor = Utils::vectorFromColor(m_theme->m_windowColor);
+ glClearColor(clearColor.x(), clearColor.y(), clearColor.z(), 1.0f);
+
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ static QVector3D selection = skipColor;
+
+ // Specify viewport
+ glViewport(m_sceneViewPort.x(), m_sceneViewPort.y(),
+ m_sceneViewPort.width(), m_sceneViewPort.height());
+
+ // Set up projection matrix
+ QMatrix4x4 projectionMatrix;
+ projectionMatrix.perspective(45.0f, (GLfloat)m_sceneViewPort.width()
+ / (GLfloat)m_sceneViewPort.height(), 0.1f, 100.0f);
+
+ // Calculate view matrix
+ QMatrix4x4 viewMatrix = m_camera->calculateViewMatrix(m_mousePos,
+ m_zoomLevel * m_autoScaleAdjustment,
+ m_sceneViewPort.width(),
+ m_sceneViewPort.height());
+
+ // Get light position (rotate light with camera, a bit above it (as set in defaultLightPos))
+ QVector3D lightPos = m_camera->calculateLightPosition(defaultLightPos, 0.0f, distanceMod);
+
+ // Map adjustment direction to model matrix scaling
+ GLfloat heightMultiplier = 0.0f;
+ GLfloat widthMultiplier = 0.0f;
+ GLfloat depthMultiplier = 0.0f;
+ GLfloat heightScaler = 0.0f;
+ GLfloat widthScaler = 0.0f;
+ GLfloat depthScaler = 0.0f;
+ switch (m_adjustDirection) {
+ case Q3DMaps::AdjustHeight:
+ widthMultiplier = 0.0f;
+ heightMultiplier = 1.0f;
+ depthMultiplier = 0.0f;
+ widthScaler = m_barThickness.x() / m_scaleFactor;
+ heightScaler = 0.0f;
+ depthScaler = m_barThickness.z() / m_scaleFactor;
+ break;
+ case Q3DMaps::AdjustWidth:
+ widthMultiplier = 1.0f;
+ heightMultiplier = 0.0f;
+ depthMultiplier = 0.0f;
+ widthScaler = 0.0f;
+ heightScaler = m_barThickness.y() / m_scaleFactor;
+ depthScaler = m_barThickness.z() / m_scaleFactor;
+ break;
+ case Q3DMaps::AdjustDepth:
+ widthMultiplier = 0.0f;
+ heightMultiplier = 0.0f;
+ depthMultiplier = 1.0f;
+ widthScaler = m_barThickness.x() / m_scaleFactor;
+ heightScaler = m_barThickness.y() / m_scaleFactor;
+ depthScaler = 0.0f;
+ break;
+ case Q3DMaps::AdjustRadius:
+ widthMultiplier = 1.0f;
+ heightMultiplier = 0.0f;
+ depthMultiplier = 1.0f;
+ widthScaler = 0.0f;
+ heightScaler = m_barThickness.y() / m_scaleFactor;
+ depthScaler = 0.0f;
+ break;
+ case Q3DMaps::AdjustAll:
+ widthMultiplier = 1.0f;
+ heightMultiplier = 1.0f;
+ depthMultiplier = 1.0f;
+ widthScaler = 0.0f;
+ heightScaler = 0.0f;
+ depthScaler = 0.0f;
+ break;
+ }
+
+ // Introduce regardless of shadow quality to simplify logic
+ QMatrix4x4 depthViewMatrix;
+ QMatrix4x4 depthProjectionMatrix;
+
+#if !defined(QT_OPENGL_ES_2)
+ if (m_shadowQuality > 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_sceneViewPort.x(), m_sceneViewPort.y(),
+ m_sceneViewPort.width() * m_shadowQuality,
+ m_sceneViewPort.height() * m_shadowQuality);
+
+ // 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_camera->calculateLightPosition(
+ defaultLightPos, 0.0f, (distanceMod + 1.5f) / m_autoScaleAdjustment);
+ depthViewMatrix.lookAt(depthLightPos, QVector3D(0.0f, -m_yAdjustment, 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_sceneViewPort.width()
+ / (GLfloat)m_sceneViewPort.height(), 3.0f, 200.0f);
+#else
+ // Use these for orthographic shadows
+ //qDebug() << m_areaSize.width() / m_scaleFactor << m_yAdjustment;
+ GLfloat testAspectRatio = (GLfloat)m_sceneViewPort.width() / (GLfloat)m_sceneViewPort.height();
+ depthProjectionMatrix.ortho(-(2.0f * m_areaSize.width()) / m_scaleFactor,
+ (2.0f * m_areaSize.width()) / m_scaleFactor,
+ -m_yAdjustment * 4.0f * testAspectRatio,
+ m_yAdjustment * 4.0f * testAspectRatio,
+ 0.0f, 100.0f);
+#endif
+#if 0
+ // Draw background to depth buffer (You don't want to do this, except maybe for debugging purposes)
+ if (m_backgroundObj) {
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 MVPMatrix;
+
+ modelMatrix.translate(0.0f, -m_yAdjustment, zComp);
+ modelMatrix.scale(QVector3D(m_areaSize.width() / m_scaleFactor,
+ 1.0f,
+ m_areaSize.height() / m_scaleFactor));
+ modelMatrix.rotate(-90.0f, 1.0f, 0.0f, 0.0f);
+
+ MVPMatrix = depthProjectionMatrix * depthViewMatrix * modelMatrix;
+
+ m_depthShader->setUniformValue(m_depthShader->MVP(), MVPMatrix);
+
+ // 1st attribute buffer : vertices
+ glEnableVertexAttribArray(m_depthShader->posAtt());
+ glBindBuffer(GL_ARRAY_BUFFER, m_backgroundObj->vertexBuf());
+ glVertexAttribPointer(m_depthShader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0, (void *)0);
+
+ // Index buffer
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_backgroundObj->elementBuf());
+
+ // Draw the triangles
+ glDrawElements(GL_TRIANGLES, m_backgroundObj->indexCount(), GL_UNSIGNED_SHORT,
+ (void *)0);
+
+ // Free buffers
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+ glDisableVertexAttribArray(m_depthShader->posAtt());
+ }
+#endif
+ // Draw bars to depth buffer
+ for (int bar = 0; bar < m_renderItemArray.size(); bar++) {
+ const MapRenderItem &item = m_renderItemArray.at(bar);
+ if (!item.value())
+ continue;
+
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 MVPMatrix;
+
+ modelMatrix.translate(item.translation().x(),
+ heightMultiplier * item.height() + heightScaler - m_yAdjustment,
+ item.translation().z());
+ modelMatrix.scale(QVector3D(widthMultiplier * item.height() + widthScaler,
+ heightMultiplier * item.height() + heightScaler,
+ depthMultiplier * item.height() + 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_barObj->vertexBuf());
+ glVertexAttribPointer(m_depthShader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0, (void *)0);
+
+ // Index buffer
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_barObj->elementBuf());
+
+ // Draw the triangles
+ glDrawElements(GL_TRIANGLES, m_barObj->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_sceneViewPort.x(), m_sceneViewPort.y(),
+ m_sceneViewPort.width(), m_sceneViewPort.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
+
+#if 1
+ // Skip selection mode drawing if we're zoomed or have no selection mode
+ if (!m_zoomActivated && m_selectionMode > QDataVis::ModeNone) {
+ // Bind selection shader
+ m_selectionShader->bind();
+
+ // Draw bars to selection buffer
+ glBindFramebuffer(GL_FRAMEBUFFER, m_selectionFrameBuffer);
+ glEnable(GL_DEPTH_TEST); // Needed, otherwise the depth render buffer is not used
+ glClearColor(skipColor.x() / 255, skipColor.y() / 255, skipColor.z() / 255, 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
+ GLint barIdxRed = 0;
+ GLint barIdxGreen = 0;
+ GLint barIdxBlue = 0;
+ for (int bar = 0; bar < m_renderItemArray.size(); bar++, barIdxRed++) {
+ const MapRenderItem &item = m_renderItemArray.at(bar);
+ if (!item.value())
+ continue;
+
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 MVPMatrix;
+
+ modelMatrix.translate(item.translation().x(),
+ heightMultiplier * item.height() + heightScaler - m_yAdjustment,
+ item.translation().z());
+ modelMatrix.scale(QVector3D(widthMultiplier * item.height() + widthScaler,
+ heightMultiplier * item.height() + heightScaler,
+ depthMultiplier * item.height() + depthScaler));
+
+ MVPMatrix = projectionMatrix * viewMatrix * modelMatrix;
+
+ if (barIdxRed > 0 && barIdxRed % 256 == 0) {
+ barIdxRed = 0;
+ barIdxGreen++;
+ }
+ if (barIdxGreen > 0 && barIdxGreen % 256 == 0) {
+ barIdxGreen = 0;
+ barIdxBlue++;
+ }
+ if (barIdxBlue > 255)
+ qFatal("Too many objects");
+
+ QVector3D barColor = QVector3D((GLfloat)barIdxRed / 255.0f,
+ (GLfloat)barIdxGreen / 255.0f,
+ (GLfloat)barIdxBlue / 255.0f);
+
+ m_selectionShader->setUniformValue(m_selectionShader->MVP(), MVPMatrix);
+ m_selectionShader->setUniformValue(m_selectionShader->color(), barColor);
+
+ // 1st attribute buffer : vertices
+ glEnableVertexAttribArray(m_selectionShader->posAtt());
+ glBindBuffer(GL_ARRAY_BUFFER, m_barObj->vertexBuf());
+ glVertexAttribPointer(m_selectionShader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0, (void *)0);
+
+ // Index buffer
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_barObj->elementBuf());
+
+ // Draw the triangles
+ glDrawElements(GL_TRIANGLES, m_barObj->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 (Maps3DController::MouseOnScene == m_mousePressed)
+ selection = Utils::getSelection(m_mousePos, 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
+ }
+#if 1
+ // Bind bar shader
+ m_barShader->bind();
+
+ // Enable texture
+ glEnable(GL_TEXTURE_2D);
+
+ // Draw bars
+ // TODO: Handle zoom by camera transformations
+ //if (!m_zoomActivated)
+
+ bool barSelectionFound = false;
+ for (int bar = 0; bar < m_renderItemArray.size(); bar++) {
+ MapRenderItem &item = m_renderItemArray[bar];
+ if (!item.value())
+ continue;
+
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 MVPMatrix;
+ QMatrix4x4 depthMVPMatrix;
+ QMatrix4x4 itModelMatrix;
+
+ modelMatrix.translate(item.translation().x(),
+ heightMultiplier * item.height() + heightScaler - m_yAdjustment,
+ item.translation().z());
+ modelMatrix.scale(QVector3D(widthMultiplier * item.height() + widthScaler,
+ heightMultiplier * item.height() + heightScaler,
+ depthMultiplier * item.height() + depthScaler));
+ itModelMatrix.scale(QVector3D(widthMultiplier * item.height() + widthScaler,
+ heightMultiplier * item.height() + heightScaler,
+ depthMultiplier * item.height() + depthScaler));
+
+#ifdef SHOW_DEPTH_TEXTURE_SCENE
+ MVPMatrix = depthProjectionMatrix * depthViewMatrix * modelMatrix;
+#else
+ MVPMatrix = projectionMatrix * viewMatrix * modelMatrix;
+#endif
+ depthMVPMatrix = depthProjectionMatrix * depthViewMatrix * modelMatrix;
+
+ QVector3D baseColor = Utils::vectorFromColor(m_theme->m_baseColor);
+ QVector3D heightColor = Utils::vectorFromColor(m_theme->m_heightColor) * item.height();
+
+ QVector3D barColor = baseColor + heightColor;
+
+ GLfloat lightStrength = m_theme->m_lightStrength;
+ if (m_selectionMode > QDataVis::ModeNone) {
+ Maps3DController::SelectionType selectionType = isSelected(bar, selection);
+ switch (selectionType) {
+ case Maps3DController::SelectionBar: {
+ barColor = Utils::vectorFromColor(m_theme->m_highlightBarColor);
+ lightStrength = m_theme->m_highlightLightStrength;
+ // Insert data to QDataItem. We have no ownership, don't delete the previous one
+ if (!m_zoomActivated) {
+ m_selectedBar = &item;
+ barSelectionFound = true;
+ }
+ break;
+ }
+ case Maps3DController::SelectionNone: {
+ // Current bar is not selected, nor on a row or column
+ // do nothing
+ break;
+ }
+ default: {
+ // Unsupported selection mode
+ // do nothing
+ break;
+ }
+ }
+ }
+
+ if (item.height() != 0) {
+ // Set shader bindings
+ m_barShader->setUniformValue(m_barShader->lightP(), lightPos);
+ m_barShader->setUniformValue(m_barShader->view(), viewMatrix);
+ m_barShader->setUniformValue(m_barShader->model(), modelMatrix);
+ m_barShader->setUniformValue(m_barShader->nModel(),
+ itModelMatrix.inverted().transposed());
+ m_barShader->setUniformValue(m_barShader->MVP(), MVPMatrix);
+ m_barShader->setUniformValue(m_barShader->color(), barColor);
+ m_barShader->setUniformValue(m_barShader->ambientS(), m_theme->m_ambientStrength);
+
+#if !defined(QT_OPENGL_ES_2)
+ if (m_shadowQuality > QDataVis::ShadowNone) {
+ // Set shadow shader bindings
+ m_barShader->setUniformValue(m_barShader->shadowQ(), m_shadowQualityToShader);
+ m_barShader->setUniformValue(m_barShader->depth(), depthMVPMatrix);
+ m_barShader->setUniformValue(m_barShader->lightS(), lightStrength / 10.0f);
+
+ // Draw the object
+ m_drawer->drawObject(m_barShader, m_barObj, 0, m_depthTexture);
+ } else
+#endif
+ {
+ // Set shadowless shader bindings
+ m_barShader->setUniformValue(m_barShader->lightS(), lightStrength);
+
+ // Draw the object
+ m_drawer->drawObject(m_barShader, m_barObj);
+ }
+ }
+ }
+
+ // Release bar shader
+ m_barShader->release();
+#if 1
+ // Bind background shader
+ m_backgroundShader->bind();
+ if (m_bgrHasAlpha) {
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ }
+
+ // Draw background
+ if (m_backgroundObj) {
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 MVPMatrix;
+ QMatrix4x4 depthMVPMatrix;
+ QMatrix4x4 itModelMatrix;
+
+ modelMatrix.translate(0.0f, -m_yAdjustment, zComp);
+ modelMatrix.scale(QVector3D(m_areaSize.width() / m_scaleFactor,
+ 1.0f,
+ m_areaSize.height() / m_scaleFactor));
+ modelMatrix.rotate(-90.0f, 1.0f, 0.0f, 0.0f);
+ itModelMatrix.scale(QVector3D(m_areaSize.width() / m_scaleFactor,
+ 1.0f,
+ m_areaSize.height() / m_scaleFactor));
+
+#ifdef SHOW_DEPTH_TEXTURE_SCENE
+ MVPMatrix = depthProjectionMatrix * depthViewMatrix * modelMatrix;
+#else
+ MVPMatrix = projectionMatrix * viewMatrix * modelMatrix;
+#endif
+ depthMVPMatrix = depthProjectionMatrix * depthViewMatrix * modelMatrix;
+
+ // 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->ambientS(),
+ m_theme->m_ambientStrength * 3.0f);
+
+#if !defined(QT_OPENGL_ES_2)
+ if (m_shadowQuality > 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_theme->m_lightStrength / 25.0f);
+
+ // Draw the object
+ m_drawer->drawObject(m_backgroundShader, m_backgroundObj, m_bgrTexture, m_depthTexture);
+ } else
+#endif
+ {
+ // Set shadowless shader bindings
+ m_backgroundShader->setUniformValue(m_backgroundShader->lightS(),
+ m_theme->m_lightStrength);
+
+ // Draw the object
+ m_drawer->drawObject(m_backgroundShader, m_backgroundObj, m_bgrTexture);
+ }
+ }
+
+ // Disable textures
+ glDisable(GL_TEXTURE_2D);
+ if (m_bgrHasAlpha)
+ glDisable(GL_BLEND);
+
+ // Release background shader
+ m_backgroundShader->release();
+#endif
+
+ // Handle zoom activation and label drawing
+ if (!barSelectionFound) {
+ // We have no ownership, don't delete. Just NULL the pointer.
+ m_selectedBar = NULL;
+ //if (m_zoomActivated && Maps3DController::MouseOnOverview == m_mousePressed) {
+ //m_sceneViewPort = QRect(0, 0, width(), height());
+ //m_zoomActivated = false;
+ //}
+ } /*else if (m_selectionMode >= ModeZoomRow
+ && Maps3DController::MouseOnScene == m_mousePressed) {
+ // Activate zoom mode
+ m_zoomActivated = true;
+ m_sceneViewPort = QRect(0, height() - height() / 5, width() / 5, height() / 5);
+
+ // Create label textures
+ for (int col = 0; col < m_zoomSelection->row().size(); col++) {
+ QDataItem *item = m_zoomSelection->getItem(col);
+ m_drawer->generateLabelTexture(item);
+ }
+ }*/ else {
+ // Print value of selected bar
+ m_labelShader->bind();
+ glDisable(GL_DEPTH_TEST);
+ glEnable(GL_TEXTURE_2D);
+ if (m_labelTransparency > QDataVis::TransparencyNone) {
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ }
+#ifndef DISPLAY_FULL_DATA_ON_SELECTION
+ // Draw just the value string of the selected bar
+ if (m_previouslySelectedBar != m_selectedBar || m_updateLabels) {
+ m_drawer->generateLabelTexture(m_selectedBar);
+ m_previouslySelectedBar = m_selectedBar;
+ }
+
+ m_drawer->drawLabel(*m_selectedBar, m_selectedBar->labelItem(),
+ viewMatrix, projectionMatrix,
+ QVector3D(0.0f, m_yAdjustment, zComp),
+ QVector3D(0.0f, 0.0f, 0.0f), m_selectedBar->height(),
+ m_selectionMode, m_labelShader,
+ m_labelObj, m_camera, true);
+#else
+ // Draw the value string followed by row label and column label
+ LabelItem &labelItem = m_selectedBar->selectionLabel();
+ if (m_previouslySelectedBar != m_selectedBar || m_updateLabels || !labelItem.textureId()) {
+ QString labelText = m_selectedBar->label();
+ // TODO More elaborate label?
+ m_drawer->generateLabelItem(&labelItem, labelText);
+ m_previouslySelectedBar = m_selectedBar;
+ }
+
+ m_drawer->drawLabel(*m_selectedBar, labelItem, viewMatrix, projectionMatrix,
+ QVector3D(0.0f, m_yAdjustment, zComp),
+ QVector3D(0.0f, 0.0f, 0.0f), m_selectedBar->height(),
+ m_selectionMode, m_labelShader,
+ m_labelObj, true, false);
+#endif
+ glDisable(GL_TEXTURE_2D);
+ if (m_labelTransparency > QDataVis::TransparencyNone)
+ glDisable(GL_BLEND);
+ glEnable(GL_DEPTH_TEST);
+
+ // Release label shader
+ m_labelShader->release();
+
+ // Reset label update flag; they should have been updated when we get here
+ m_updateLabels = false;
+ }
+#if 0
+ // TODO: Calculations done temporarily here. When optimizing, move to after data set addition? Keep drawing of the labels here.
+ // Bind label shader
+ m_labelShader->bind();
+
+ glEnable(GL_TEXTURE_2D);
+ if (m_labelTransparency > TransparencyNone) {
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ }
+
+ // Calculate the positions for row and column labels and store them into QDataItems (and QDataRows?)
+ for (int row = 0; row != m_sampleCount.second; row += 1) {
+ // Go through all rows and get position of max+1 or min-1 column, depending on x flip
+ // We need only positions for them, labels have already been generated at QDataSet. Just add LabelItems
+ rowPos = (row + 1) * (m_barSpacing.height());
+ barPos = 0;
+ GLfloat rotLabelX = -90.0f;
+ GLfloat rotLabelY = 0.0f;
+ Qt::AlignmentFlag alignment = Qt::AlignRight;
+ if (m_zFlipped)
+ rotLabelY = 180.0f;
+ if (m_xFlipped) {
+ barPos = (m_sampleCount.first + 1) * (m_barSpacing.width());
+ alignment = Qt::AlignLeft;
+ }
+ QVector3D labelPos = QVector3D((m_rowWidth - barPos) / m_scaleFactor,
+ -m_yAdjustment + 0.005f, // raise a bit over background to avoid depth "glimmering"
+ (m_columnDepth - rowPos) / m_scaleFactor + zComp);
+
+ // TODO: Try it; draw the label here
+
+ // Create a data item
+ QDataItem *label = new QDataItem();
+ label->setTranslation(labelPos);
+ if (m_data->d_ptr->rowLabelItems().size() > row) {
+ label->setLabel(m_data->d_ptr->rowLabelItems().at(m_data->d_ptr->rowLabelItems().size()
+ - row - 1));
+ }
+
+ //qDebug() << "labelPos, row" << row + 1 << ":" << labelPos << m_dataSet->rowLabels().at(row);
+
+ m_drawer->drawLabel(*label, label->label(), viewMatrix, projectionMatrix,
+ QVector3D(0.0f, m_yAdjustment, zComp),
+ QVector3D(rotLabelX, rotLabelY, 0.0f), m_heightNormalizer,
+ m_selectionMode, m_labelShader,
+ m_labelObj, true, true, Drawer::LabelMid, alignment);
+
+ delete label;
+ }
+ for (int bar = 0; bar != m_sampleCount.first; bar += 1) {
+ // Go through all columns and get position of max+1 or min-1 row, depending on z flip
+ // We need only positions for them, labels have already been generated at QDataSet. Just add LabelItems
+ barPos = (bar + 1) * (m_barSpacing.width());
+ rowPos = 0;
+ GLfloat rotLabelX = -90.0f;
+ GLfloat rotLabelY = 90.0f;
+ Qt::AlignmentFlag alignment = Qt::AlignLeft;
+ if (m_xFlipped)
+ rotLabelY = -90.0f;
+ if (m_zFlipped) {
+ rowPos = (m_sampleCount.second + 1) * (m_barSpacing.height());
+ alignment = Qt::AlignRight;
+ }
+ QVector3D labelPos = QVector3D((m_rowWidth - barPos) / m_scaleFactor,
+ -m_yAdjustment + 0.005f, // raise a bit over background to avoid depth "glimmering"
+ (m_columnDepth - rowPos) / m_scaleFactor + zComp);
+
+ // TODO: Try it; draw the label here
+
+ // Create a data item
+ QDataItem *label = new QDataItem();
+ label->setTranslation(labelPos);
+ if (m_data->d_ptr->columnLabelItems().size() > bar) {
+ label->setLabel(m_data->d_ptr->columnLabelItems().at(
+ m_data->d_ptr->columnLabelItems().size() - bar - 1));
+ }
+
+ //qDebug() << "labelPos, col" << bar + 1 << ":" << labelPos << m_dataSet->columnLabels().at(bar);
+
+ m_drawer->drawLabel(*label, label->label(), viewMatrix, projectionMatrix,
+ QVector3D(0.0f, m_yAdjustment, zComp),
+ QVector3D(rotLabelX, rotLabelY, 0.0f), m_heightNormalizer,
+ m_selectionMode, m_labelShader,
+ m_labelObj, true, true, Drawer::LabelMid, alignment);
+
+ delete label;
+ }
+ glDisable(GL_TEXTURE_2D);
+ if (m_labelTransparency > TransparencyNone)
+ glDisable(GL_BLEND);
+
+ // Release label shader
+ m_labelShader->release();
+#endif
+#endif
+#endif
+}
+
+#if defined(Q_OS_ANDROID)
+/*!
+ * \internal
+ */
+void Maps3DController::mouseDoubleClickEvent(QMouseEvent *event)
+{
+ if (!m_zoomActivated) {
+ m_mousePressed = Maps3DController::MouseOnScene;
+ // update mouse positions to prevent jumping when releasing or repressing a button
+ m_mousePos = event->pos();
+ }
+}
+
+/*!
+ * \internal
+ */
+void Maps3DController::touchEvent(QTouchEvent *event)
+{
+ static int prevDistance = 0;
+
+ QList<QTouchEvent::TouchPoint> points;
+ points = event->touchPoints();
+
+ if (points.count() == 2) {
+ m_mousePressed = Maps3DController::MouseOnPinch;
+
+ QPointF distance = points.at(0).pos() - points.at(1).pos();
+ int newDistance = distance.manhattanLength();
+ int zoomRate = 1;
+ if (m_zoomLevel > 100)
+ zoomRate = 5;
+ if (newDistance > prevDistance)
+ m_zoomLevel += zoomRate;
+ else
+ m_zoomLevel -= zoomRate;
+ if (m_zoomLevel > 500)
+ m_zoomLevel = 500;
+ else if (m_zoomLevel < 10)
+ m_zoomLevel = 10;
+ prevDistance = newDistance;
+ //qDebug() << "distance" << distance.manhattanLength();
+ }
+}
+#endif
+
+/*!
+ * \internal
+ */
+void Maps3DController::mousePressEvent(QMouseEvent *event, const QPoint &mousePos)
+{
+ if (Qt::LeftButton == event->button()) {
+ if (m_zoomActivated) {
+ //qDebug() << event->pos().x() << event->pos().y() << m_sceneViewPort << m_zoomViewPort;
+ if (mousePos.x() <= m_sceneViewPort.width()
+ && mousePos.y() <= m_sceneViewPort.height()) {
+ m_mousePressed = Maps3DController::MouseOnOverview;
+ //qDebug() << "Mouse pressed on overview";
+ } else {
+ m_mousePressed = Maps3DController::MouseOnZoom;
+ //qDebug() << "Mouse pressed on zoom";
+ }
+ } else {
+#if !defined(Q_OS_ANDROID)
+ m_mousePressed = Maps3DController::MouseOnScene;
+#else
+ m_mousePressed = Maps3DController::MouseRotating;
+#endif
+ // update mouse positions to prevent jumping when releasing or repressing a button
+ m_mousePos = mousePos;
+ //qDebug() << "Mouse pressed on scene";
+ }
+ } else if (Qt::MiddleButton == event->button()) {
+ // reset rotations
+ m_mousePos = QPoint(0, 0);
+ } else if (Qt::RightButton == event->button()) {
+#if !defined(Q_OS_ANDROID)
+ m_mousePressed = Maps3DController::MouseRotating;
+#else
+ m_mousePressed = Maps3DController::MouseOnScene;
+#endif
+ // update mouse positions to prevent jumping when releasing or repressing a button
+ m_mousePos = mousePos;
+ }
+ m_camera->updateMousePos(m_mousePos);
+}
+
+/*!
+ * \internal
+ */
+void Maps3DController::mouseReleaseEvent(QMouseEvent *event, const QPoint &mousePos)
+{
+ Q_UNUSED(event);
+ //qDebug() << "mouse button released" << event->button();
+ if (Maps3DController::MouseRotating == m_mousePressed) {
+ // update mouse positions to prevent jumping when releasing or repressing a button
+ m_mousePos = mousePos;
+ m_camera->updateMousePos(mousePos);
+ }
+ m_mousePressed = Maps3DController::MouseNone;
+}
+
+/*!
+ * \internal
+ */
+void Maps3DController::mouseMoveEvent(QMouseEvent *event, const QPoint &mousePos)
+{
+ Q_UNUSED(event);
+ if (Maps3DController::MouseRotating == m_mousePressed) {
+ //qDebug() << "mouse moved while pressed" << event->pos();
+ m_mousePos = mousePos;
+ }
+#if 0
+ // TODO: Testi - laske kursorin sijainti scenessä
+ QPointF mouse3D((2.0f * event->pos().x() - width()) / height(),
+ 1.0f - (2.0f * event->pos().y()) / height());
+ //qDebug() << "mouse position in scene" << mouse3D;
+
+ // TODO: Testi laske focal point
+ GLfloat focalPoint = tan(45.0f / 2.0f);
+
+ // TODO: Testi - laske viewmatriisin kerroin
+ QVector3D worldRay = QVector3D(0.0f, 0.0f, 0.0f) - QVector3D(mouse3D.x(), mouse3D.y(),
+ -focalPoint);
+ //qDebug() << "worldRay" << worldRay;
+ // multiply viewmatrix with this to get something?
+#endif
+}
+
+/*!
+ * \internal
+ */
+void Maps3DController::wheelEvent(QWheelEvent *event)
+{
+ if (m_zoomLevel > 100)
+ m_zoomLevel += event->angleDelta().y() / 12;
+ else if (m_zoomLevel > 50)
+ m_zoomLevel += event->angleDelta().y() / 60;
+ else
+ m_zoomLevel += event->angleDelta().y() / 120;
+ if (m_zoomLevel > 500)
+ m_zoomLevel = 500;
+ else if (m_zoomLevel < 10)
+ m_zoomLevel = 10;
+}
+
+/*!
+ * \internal
+ */
+void Maps3DController::resizeNotify()
+{
+ if (!m_isInitialized)
+ return;
+
+ // Set view port
+ if (m_zoomActivated)
+ m_sceneViewPort = QRect(0, height() - height() / 5, width() / 5, height() / 5);
+ else
+ m_sceneViewPort = QRect(0, 0, width(), height());
+ m_zoomViewPort = QRect(0, 0, width(), height());
+
+ // Calculate zoom level based on aspect ratio
+ GLfloat div;
+ GLfloat zoomAdjustment;
+ div = qMin(width(), height());
+ zoomAdjustment = defaultRatio * ((width() / div) / (height() / div));
+ //qDebug() << "zoom adjustment" << zoomAdjustment;
+ m_autoScaleAdjustment = qMin(zoomAdjustment, 1.0f); // clamp to 1.0f
+
+ // Re-init selection buffer
+ initSelectionBuffer();
+
+#if !defined(QT_OPENGL_ES_2)
+ // Re-init depth buffer
+ initDepthBuffer();
+#endif
+}
+
+void Maps3DController::setBarSpecs(const QVector3D &thickness,
+ Q3DMaps::AdjustmentDirection direction)
+{
+ m_barThickness = thickness;
+ m_adjustDirection = direction;
+}
+
+void Maps3DController::setBarType(QDataVis::MeshStyle style, bool smooth)
+{
+ if (style == QDataVis::Bars) {
+ if (smooth)
+ m_objFile = QStringLiteral(":/defaultMeshes/barSmooth");
+ else
+ m_objFile = QStringLiteral(":/defaultMeshes/bar");
+ } else if (style == QDataVis::Pyramids) {
+ if (smooth)
+ m_objFile = QStringLiteral(":/defaultMeshes/pyramidSmooth");
+ else
+ m_objFile = QStringLiteral(":/defaultMeshes/pyramid");
+ } else if (style == QDataVis::Cones) {
+ if (smooth)
+ m_objFile = QStringLiteral(":/defaultMeshes/coneSmooth");
+ else
+ m_objFile = QStringLiteral(":/defaultMeshes/cone");
+ } else if (style == QDataVis::Cylinders) {
+ if (smooth)
+ m_objFile = QStringLiteral(":/defaultMeshes/cylinderSmooth");
+ else
+ m_objFile = QStringLiteral(":/defaultMeshes/cylinder");
+ } else if (style == QDataVis::BevelBars) {
+ if (smooth)
+ m_objFile = QStringLiteral(":/defaultMeshes/bevelbarSmooth");
+ else
+ m_objFile = QStringLiteral(":/defaultMeshes/bevelbar");
+ } else if (style == QDataVis::Spheres) {
+ if (smooth)
+ m_objFile = QStringLiteral(":/defaultMeshes/sphereSmooth");
+ else
+ m_objFile = QStringLiteral(":/defaultMeshes/sphere");
+ }
+ // Reload mesh data
+ if (m_isInitialized)
+ loadBarMesh();
+}
+
+void Maps3DController::setMeshFileName(const QString &objFileName)
+{
+ m_objFile = objFileName;
+}
+
+void Maps3DController::setCameraPreset(QDataVis::CameraPreset preset)
+{
+ m_camera->setCameraPreset(preset);
+}
+
+void Maps3DController::setCameraPosition(GLfloat horizontal, GLfloat vertical, GLint distance)
+{
+ m_horizontalRotation = qBound(-180.0f, horizontal, 180.0f);
+ m_verticalRotation = qBound(0.0f, vertical, 90.0f);
+ m_zoomLevel = qBound(10, distance, 500);
+ m_camera->setCameraRotation(QPointF(m_horizontalRotation, m_verticalRotation));
+ //qDebug() << "camera rotation set to" << m_horizontalRotation << m_verticalRotation;
+}
+
+void Maps3DController::setTheme(QDataVis::ColorTheme colorTheme)
+{
+ m_theme->useColorTheme(colorTheme);
+ m_drawer->setTheme(*m_theme);
+#if !defined(QT_OPENGL_ES_2)
+ if (m_shadowQuality > QDataVis::ShadowNone) {
+ // Re-init shaders
+ if (!m_theme->m_uniformColor) {
+ initShaders(QStringLiteral(":/shaders/vertexShadow"),
+ QStringLiteral(":/shaders/fragmentShadowNoTexColorOnY"));
+ } else {
+ initShaders(QStringLiteral(":/shaders/vertexShadow"),
+ QStringLiteral(":/shaders/fragmentShadowNoTex"));
+ }
+ } else {
+ // Re-init shaders
+ if (!m_theme->m_uniformColor) {
+ initShaders(QStringLiteral(":/shaders/vertex"),
+ QStringLiteral(":/shaders/fragmentColorOnY"));
+ } else {
+ initShaders(QStringLiteral(":/shaders/vertex"),
+ QStringLiteral(":/shaders/fragment"));
+ }
+ }
+#else
+ if (!m_theme->m_uniformColor) {
+ initShaders(QStringLiteral(":/shaders/vertexES2"),
+ QStringLiteral(":/shaders/fragmentColorOnYES2"));
+ } else {
+ initShaders(QStringLiteral(":/shaders/vertexES2"),
+ QStringLiteral(":/shaders/fragmentES2"));
+ }
+#endif
+ m_updateLabels = true;
+}
+
+void Maps3DController::setBarColor(QColor baseColor, QColor heightColor, bool uniform)
+{
+ m_theme->m_baseColor = baseColor;
+ m_theme->m_heightColor = heightColor;
+ if (m_theme->m_uniformColor != uniform) {
+#if !defined(QT_OPENGL_ES_2)
+ if (m_shadowQuality > QDataVis::ShadowNone) {
+ // Re-init shaders
+ if (!m_theme->m_uniformColor) {
+ initShaders(QStringLiteral(":/shaders/vertexShadow"),
+ QStringLiteral(":/shaders/fragmentShadowNoTexColorOnY"));
+ } else {
+ initShaders(QStringLiteral(":/shaders/vertexShadow"),
+ QStringLiteral(":/shaders/fragmentShadowNoTex"));
+ }
+ } else {
+ // Re-init shaders
+ if (!m_theme->m_uniformColor) {
+ initShaders(QStringLiteral(":/shaders/vertex"),
+ QStringLiteral(":/shaders/fragmentColorOnY"));
+ } else {
+ initShaders(QStringLiteral(":/shaders/vertex"),
+ QStringLiteral(":/shaders/fragment"));
+ }
+ }
+#else
+ if (!m_theme->m_uniformColor) {
+ initShaders(QStringLiteral(":/shaders/vertexES2"),
+ QStringLiteral(":/shaders/fragmentColorOnYES2"));
+ } else {
+ initShaders(QStringLiteral(":/shaders/vertexES2"),
+ QStringLiteral(":/shaders/fragmentES2"));
+ }
+#endif
+ }
+ m_theme->m_uniformColor = uniform;
+}
+
+void Maps3DController::setAreaSpecs(const QRect &areaRect, const QImage &image)
+{
+ calculateSceneScalingFactors(areaRect);
+ setImage(image);
+}
+
+void Maps3DController::setImage(const QImage &image)
+{
+ m_bgrHasAlpha = image.hasAlphaChannel();
+ if (m_bgrTexture)
+ glDeleteTextures(1, &m_bgrTexture);
+ m_bgrTexture = m_textureHelper->create2DTexture(image, true, true);
+}
+
+void Maps3DController::setSelectionMode(QDataVis::SelectionMode mode)
+{
+ m_selectionMode = mode;
+ // Disable zoom if mode changes
+ //m_zoomActivated = false;
+ //m_sceneViewPort = QRect(0, 0, width(), height());
+}
+
+QDataVis::SelectionMode Maps3DController::selectionMode()
+{
+ return m_selectionMode;
+}
+
+void Maps3DController::setFontSize(float fontsize)
+{
+ m_font.setPointSizeF(fontsize);
+ m_drawer->setFont(m_font);
+ m_updateLabels = true;
+}
+
+float Maps3DController::fontSize()
+{
+ return m_font.pointSizeF();
+}
+
+void Maps3DController::setFont(const QFont &font)
+{
+ m_font = font;
+ m_drawer->setFont(font);
+ m_updateLabels = true;
+}
+
+QFont Maps3DController::font()
+{
+ return m_font;
+}
+
+void Maps3DController::setLabelTransparency(QDataVis::LabelTransparency transparency)
+{
+ m_labelTransparency = transparency;
+ m_drawer->setTransparency(transparency);
+ m_updateLabels = true;
+}
+
+QDataVis::LabelTransparency Maps3DController::labelTransparency()
+{
+ return m_labelTransparency;
+}
+
+QDataVis::ShadowQuality Maps3DController::setShadowQuality(QDataVis::ShadowQuality quality)
+{
+ m_shadowQuality = quality;
+ switch (quality) {
+ case QDataVis::ShadowLow:
+ //qDebug() << "ShadowLow";
+ m_shadowQualityToShader = 33.3f;
+ break;
+ case QDataVis::ShadowMedium:
+ //qDebug() << "ShadowMedium";
+ m_shadowQualityToShader = 100.0f;
+ break;
+ case QDataVis::ShadowHigh:
+ //qDebug() << "ShadowHigh";
+ m_shadowQualityToShader = 200.0f;
+ break;
+ default:
+ m_shadowQualityToShader = 0.0f;
+ break;
+ }
+ if (m_isInitialized) {
+#if !defined(QT_OPENGL_ES_2)
+ if (m_shadowQuality > QDataVis::ShadowNone) {
+ // Re-init shaders
+ if (!m_theme->m_uniformColor) {
+ initShaders(QStringLiteral(":/shaders/vertexShadow"),
+ QStringLiteral(":/shaders/fragmentShadowNoTexColorOnY"));
+ } else {
+ initShaders(QStringLiteral(":/shaders/vertexShadow"),
+ QStringLiteral(":/shaders/fragmentShadowNoTex"));
+ }
+ initBackgroundShaders(QStringLiteral(":/shaders/vertexShadow"),
+ QStringLiteral(":/shaders/fragmentShadow"));
+ } else {
+ // Re-init shaders
+ if (!m_theme->m_uniformColor) {
+ initShaders(QStringLiteral(":/shaders/vertex"),
+ QStringLiteral(":/shaders/fragmentColorOnY"));
+ } else {
+ initShaders(QStringLiteral(":/shaders/vertex"),
+ QStringLiteral(":/shaders/fragment"));
+ }
+ initBackgroundShaders(QStringLiteral(":/shaders/vertexTexture"),
+ QStringLiteral(":/shaders/fragmentTexture"));
+ }
+ // Re-init depth buffer
+ initDepthBuffer();
+#else
+ if (!m_theme->m_uniformColor) {
+ initShaders(QStringLiteral(":/shaders/vertexES2"),
+ QStringLiteral(":/shaders/fragmentColorOnYES2"));
+ } else {
+ initShaders(QStringLiteral(":/shaders/vertexES2"),
+ QStringLiteral(":/shaders/fragmentES2"));
+ }
+ initBackgroundShaders(QStringLiteral(":/shaders/vertexTexture"), // Same vertex shader ok for ES2
+ QStringLiteral(":/shaders/fragmentTextureES2"));
+#endif
+ }
+ return m_shadowQuality;
+}
+
+QDataVis::ShadowQuality Maps3DController::shadowQuality()
+{
+ return m_shadowQuality;
+}
+
+
+
+const QSize Maps3DController::size()
+{
+ return m_boundingRect.size();
+}
+
+const QRect Maps3DController::boundingRect()
+{
+ return m_boundingRect;
+}
+
+void Maps3DController::setBoundingRect(const QRect boundingRect)
+{
+ m_boundingRect = boundingRect;
+ resizeNotify();
+}
+
+void Maps3DController::setWidth(const int width)
+{
+ m_boundingRect.setWidth(width);
+ resizeNotify();
+}
+
+int Maps3DController::width()
+{
+ return m_boundingRect.width();
+}
+
+void Maps3DController::setHeight(const int height)
+{
+ m_boundingRect.setHeight(height);
+ resizeNotify();
+}
+
+int Maps3DController::height()
+{
+ return m_boundingRect.height();
+}
+
+void Maps3DController::setX(const int x)
+{
+ m_boundingRect.setX(x);
+}
+
+int Maps3DController::x()
+{
+ return m_boundingRect.x();
+}
+
+void Maps3DController::setY(const int y)
+{
+ m_boundingRect.setY(y);
+}
+
+int Maps3DController::y()
+{
+ return m_boundingRect.y();
+}
+
+void Maps3DController::loadBarMesh()
+{
+ if (m_barObj)
+ delete m_barObj;
+ m_barObj = new ObjectHelper(m_objFile);
+ m_barObj->load();
+}
+
+void Maps3DController::loadBackgroundMesh()
+{
+ if (m_backgroundObj)
+ delete m_backgroundObj;
+ m_backgroundObj = new ObjectHelper(QStringLiteral(":/defaultMeshes/label"));
+ m_backgroundObj->load();
+}
+
+void Maps3DController::loadGridLineMesh()
+{
+ if (m_gridLineObj)
+ delete m_gridLineObj;
+ m_gridLineObj = new ObjectHelper(QStringLiteral(":/defaultMeshes/bar"));
+ m_gridLineObj->load();
+}
+
+void Maps3DController::loadLabelMesh()
+{
+ if (m_labelObj)
+ delete m_labelObj;
+ m_labelObj = new ObjectHelper(QStringLiteral(":/defaultMeshes/label"));
+ m_labelObj->load();
+}
+
+void Maps3DController::initShaders(const QString &vertexShader, const QString &fragmentShader)
+{
+ if (m_barShader)
+ delete m_barShader;
+ m_barShader = new ShaderHelper(this, vertexShader, fragmentShader);
+ m_barShader->initialize();
+}
+
+void Maps3DController::initSelectionShader()
+{
+ if (m_selectionShader)
+ delete m_selectionShader;
+ m_selectionShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSelection"),
+ QStringLiteral(":/shaders/fragmentSelection"));
+ m_selectionShader->initialize();
+}
+
+void Maps3DController::initSelectionBuffer()
+{
+ if (m_selectionTexture)
+ m_textureHelper->deleteTexture(&m_selectionTexture);
+
+ m_selectionTexture = m_textureHelper->createSelectionTexture(this->size(),
+ m_selectionFrameBuffer,
+ m_selectionDepthBuffer);
+}
+
+#if !defined(QT_OPENGL_ES_2)
+void Maps3DController::initDepthShader()
+{
+ if (m_depthShader)
+ delete m_depthShader;
+ m_depthShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexDepth"),
+ QStringLiteral(":/shaders/fragmentDepth"));
+ m_depthShader->initialize();
+}
+
+void Maps3DController::initDepthBuffer()
+{
+ if (!m_isInitialized)
+ return;
+
+ if (m_depthTexture) {
+ m_textureHelper->deleteTexture(&m_depthTexture);
+ m_depthTexture = 0;
+ }
+
+ if (m_shadowQuality > QDataVis::ShadowNone) {
+ m_depthTexture = m_textureHelper->createDepthTexture(this->size(), m_depthFrameBuffer,
+ m_shadowQuality);
+ if (!m_depthTexture) {
+ switch (m_shadowQuality) {
+ case QDataVis::ShadowHigh:
+ qWarning("Creating high quality shadows failed. Changing to medium quality.");
+ (void)setShadowQuality(QDataVis::ShadowMedium);
+ break;
+ case QDataVis::ShadowMedium:
+ qWarning("Creating medium quality shadows failed. Changing to low quality.");
+ (void)setShadowQuality(QDataVis::ShadowLow);
+ break;
+ case QDataVis::ShadowLow:
+ qWarning("Creating low quality shadows failed. Switching shadows off.");
+ (void)setShadowQuality(QDataVis::ShadowNone);
+ break;
+ default:
+ // Cannot get here
+ break;
+ }
+ }
+ }
+}
+#endif
+
+void Maps3DController::initBackgroundShaders(const QString &vertexShader,
+ const QString &fragmentShader)
+{
+ if (m_backgroundShader)
+ delete m_backgroundShader;
+ m_backgroundShader = new ShaderHelper(this, vertexShader, fragmentShader);
+ m_backgroundShader->initialize();
+}
+
+void Maps3DController::initLabelShaders(const QString &vertexShader, const QString &fragmentShader)
+{
+ if (m_labelShader)
+ delete m_labelShader;
+ m_labelShader = new ShaderHelper(this, vertexShader, fragmentShader);
+ m_labelShader->initialize();
+}
+
+void Maps3DController::updateTextures()
+{
+ // Drawer has changed; this flag needs to be checked when checking if we need to update labels
+ m_updateLabels = true;
+}
+
+void Maps3DController::calculateSceneScalingFactors(const QRect &areaRect)
+{
+ m_areaSize = areaRect.size();
+ // Calculate scaling factor so that we can be sure the whole area fits to positive z space
+ if (zComp > 1.0f)
+ m_scaleFactor = qMax(m_areaSize.width(), m_areaSize.height()) / zComp;
+ else
+ m_scaleFactor = qMax(m_areaSize.width(), m_areaSize.height());
+ //qDebug() << "scaleFactor" << m_scaleFactor;
+}
+
+void Maps3DController::calculateHeightAdjustment(const QPair<GLfloat, GLfloat> &limits)
+{
+ // 2.0f = max difference between minimum and maximum value after scaling with m_heightNormalizer
+ m_yAdjustment = 2.0f - ((limits.second - limits.first) / m_heightNormalizer);
+ //qDebug() << m_yAdjustment;
+}
+
+void Maps3DController::calculateTranslation(MapRenderItem &item)
+{
+ // We need to convert position (which is in coordinates), to translation (which has origin in the center and is scaled)
+ // -> move pos(center, center) to trans(0, 0) and pos(0, 0) to trans(left, top)
+ GLfloat xTrans = 2.0f * (item.mapPosition().x() - (m_areaSize.width() / 2.0f))
+ / m_scaleFactor;
+ GLfloat zTrans = 2.0f * (item.mapPosition().y() - (m_areaSize.height() / 2.0f))
+ / m_scaleFactor;
+ //qDebug() << "x, y" << item.mapPosition().x() << item.mapPosition().y();
+ item.setTranslation(QVector3D(xTrans, 0.0f, zTrans + zComp));
+ //qDebug() << item.translation();
+}
+
+Maps3DController::SelectionType Maps3DController::isSelected(GLint bar, const QVector3D &selection)
+{
+ GLubyte barIdxRed = 0;
+ GLubyte barIdxGreen = 0;
+ GLubyte barIdxBlue = 0;
+ //static QVector3D prevSel = selection; // TODO: For debugging
+ SelectionType isSelectedType = SelectionNone;
+
+ if (selection == skipColor)
+ return isSelectedType; // skip window
+
+ if (bar <= 255) {
+ barIdxRed = bar;
+ } else if (bar <= 65535) {
+ barIdxGreen = bar / 256;
+ barIdxRed = bar % 256;
+ } else {
+ barIdxBlue = bar / 65535;
+ barIdxGreen = bar % 65535;
+ barIdxRed = bar % 256;
+ }
+
+ QVector3D current = QVector3D(barIdxRed, barIdxGreen, barIdxBlue);
+
+ // TODO: For debugging
+ //if (selection != prevSel) {
+ // qDebug() << selection.x() << selection .y() << selection.z();
+ // prevSel = selection;
+ //}
+
+ if (current == selection)
+ isSelectedType = SelectionBar;
+
+ return isSelectedType;
+}
+
+bool Maps3DController::isValid(const MapRenderItem &item)
+{
+ bool retval = true;
+ if (item.value() < 0) {
+ qCritical("Data item value out of range");
+ retval = false;
+ } else if (item.mapPosition().x() < 0 || item.mapPosition().x() > m_areaSize.width()) {
+ qCritical("Data item x position out of range");
+ retval = false;
+ } else if (item.mapPosition().y() < 0 || item.mapPosition().y() > m_areaSize.height()) {
+ qCritical("Data item y position out of range");
+ retval = false;
+ }
+ return retval;
+}
+
+void Maps3DController::setDataProxy(QMapDataProxy *proxy)
+{
+ delete m_data;
+ m_data = proxy;
+
+ QObject::connect(m_data, &QMapDataProxy::arrayReset, this, &Maps3DController::handleArrayReset);
+ QObject::connect(m_data, &QMapDataProxy::itemsAdded, this, &Maps3DController::handleItemsAdded);
+ QObject::connect(m_data, &QMapDataProxy::itemsChanged, this, &Maps3DController::handleItemsChanged);
+ QObject::connect(m_data, &QMapDataProxy::itemsRemoved, this, &Maps3DController::handleItemsRemoved);
+ QObject::connect(m_data, &QMapDataProxy::itemsInserted, this, &Maps3DController::handleItemsInserted);
+
+ // emit something? Renderer might be interested?
+}
+
+QMapDataProxy *Maps3DController::dataProxy()
+{
+ return m_data;
+}
+
+void Maps3DController::handleLimitChange()
+{
+ QPair<GLfloat, GLfloat> limits = m_data->dptr()->limitValues();
+ m_heightNormalizer = qMax(qAbs(limits.second), qAbs(limits.first));
+ calculateHeightAdjustment(limits);
+
+ //emit limitsChanged(limits);
+}
+
+void Maps3DController::handleArrayReset()
+{
+ handleLimitChange();
+ m_valuesDirty = true;
+}
+
+void Maps3DController::handleItemsAdded(int startIndex, int count)
+{
+ Q_UNUSED(startIndex)
+ Q_UNUSED(count)
+ // TODO should dirty only affected values?
+ handleLimitChange();
+ m_valuesDirty = true;
+}
+
+void Maps3DController::handleItemsChanged(int startIndex, int count)
+{
+ Q_UNUSED(startIndex)
+ Q_UNUSED(count)
+ // TODO should dirty only affected values?
+ handleLimitChange();
+ m_valuesDirty = true;
+}
+
+void Maps3DController::handleItemsRemoved(int startIndex, int count)
+{
+ Q_UNUSED(startIndex)
+ Q_UNUSED(count)
+ // TODO should dirty only affected values?
+ handleLimitChange();
+ m_valuesDirty = true;
+}
+
+void Maps3DController::handleItemsInserted(int startIndex, int count)
+{
+ Q_UNUSED(startIndex)
+ Q_UNUSED(count)
+ // TODO should dirty only affected values?
+ handleLimitChange();
+ m_valuesDirty = true;
+}
+
+QT_DATAVIS3D_END_NAMESPACE