diff options
Diffstat (limited to 'src/datavisualization/engine/surface3drenderer.cpp')
-rw-r--r-- | src/datavisualization/engine/surface3drenderer.cpp | 1256 |
1 files changed, 1256 insertions, 0 deletions
diff --git a/src/datavisualization/engine/surface3drenderer.cpp b/src/datavisualization/engine/surface3drenderer.cpp new file mode 100644 index 00000000..f39a0228 --- /dev/null +++ b/src/datavisualization/engine/surface3drenderer.cpp @@ -0,0 +1,1256 @@ +/**************************************************************************** +** +** 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 "surface3dcontroller_p.h" +#include "surface3drenderer_p.h" +#include "q3dcamera.h" +#include "shaderhelper_p.h" +#include "objecthelper_p.h" +#include "surfaceobject_p.h" +#include "texturehelper_p.h" +#include "selectionpointer_p.h" +#include "theme_p.h" +#include "utils_p.h" +#include "drawer_p.h" +#include "q3dlight.h" + +#include <QMatrix4x4> +#include <QMouseEvent> +#include <qmath.h> + +#include <QLinearGradient> +#include <QPainter> + +#include <QDebug> + +static const int ID_TO_RGBA_MASK = 0xff; + +QT_DATAVISUALIZATION_BEGIN_NAMESPACE + +const GLfloat backgroundMargin = 1.1f; // Margin for background (1.1f = make it 10% larger to avoid items being drawn inside background) +const GLfloat labelMargin = 0.05f; +const GLfloat backgroundBottom = 1.0f; +const GLfloat gridLineWidth = 0.005f; +const GLfloat coordSpace = 2.0f; // From -1.0 to 1.0 + +Surface3DRenderer::Surface3DRenderer(Surface3DController *controller) + : Abstract3DRenderer(controller), + m_controller(controller), + m_labelTransparency(QDataVis::TransparencyFromTheme), + m_font(QFont(QStringLiteral("Arial"))), + m_isGridEnabled(true), + m_shadowQuality(QDataVis::ShadowLow), + m_segmentYCount(0), + m_segmentYStep(0.0f), + m_shader(0), + m_backgroundShader(0), + m_surfaceShader(0), + m_surfaceGridShader(0), + m_selectionShader(0), + m_labelShader(0), + m_heightNormalizer(0.0f), + m_scaleFactor(0.0f), + m_scaleX(0.0f), + m_scaleZ(0.0f), + m_backgroundObj(0), + m_gridLineObj(0), + m_labelObj(0), + m_surfaceObj(0), + m_depthTexture(0), + m_depthFrameBuffer(0), + m_selectionFrameBuffer(0), + m_selectionDepthBuffer(0), + m_gradientTexture(0), + m_selectionTexture(0), + m_selectionResultTexture(0), + m_shadowQualityToShader(33.3f), + m_querySelection(false), + m_selectionPointer(0), + m_selectionActive(false), + m_xFlipped(false), + m_zFlipped(false), + m_yFlipped(false), + m_sampleSpace(QRect(0, 0, 0, 0)) +{ + // Listen to changes in the controller + QObject::connect(m_controller, &Surface3DController::smoothStatusChanged, this, + &Surface3DRenderer::updateSmoothStatus); + QObject::connect(m_controller, &Surface3DController::surfaceGridChanged, this, + &Surface3DRenderer::updateSurfaceGridStatus); + QObject::connect(m_controller, &Surface3DController::leftMousePressed, this, + &Surface3DRenderer::requestSelectionAtPoint); // TODO: Possible temp + + m_cachedSmoothSurface = m_controller->smoothSurface(); + updateSurfaceGridStatus(m_controller->surfaceGrid()); + + initializeOpenGLFunctions(); + initializeOpenGL(); +} + +Surface3DRenderer::~Surface3DRenderer() +{ + m_textureHelper->glDeleteFramebuffers(1, &m_depthFrameBuffer); + m_textureHelper->glDeleteRenderbuffers(1, &m_selectionDepthBuffer); + m_textureHelper->glDeleteFramebuffers(1, &m_selectionFrameBuffer); + + m_textureHelper->deleteTexture(&m_depthTexture); + m_textureHelper->deleteTexture(&m_gradientTexture); + m_textureHelper->deleteTexture(&m_selectionTexture); + m_textureHelper->deleteTexture(&m_selectionResultTexture); + + delete m_shader; + delete m_backgroundShader; + delete m_selectionShader; + delete m_surfaceShader; + delete m_surfaceGridShader; + delete m_labelShader; + + delete m_backgroundObj; + delete m_surfaceObj; + delete m_gridLineObj; + delete m_labelObj; + + if (m_selectionPointer) + delete m_selectionPointer; +} + +void Surface3DRenderer::initializeOpenGL() +{ + Abstract3DRenderer::initializeOpenGL(); + + // Initialize shaders + handleShadowQualityChange(); + + initSurfaceShaders(); + + initLabelShaders(QStringLiteral(":/shaders/vertexLabel"), + QStringLiteral(":/shaders/fragmentLabel")); + + // Init selection shader + initSelectionShaders(); + + // 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()); + + // Resize in case we've missed resize events + // Resize calls initSelectionBuffer and initDepthBuffer, so they don't need to be called here + handleResize(); + + // Load background mesh (we need to be initialized first) + loadBackgroundMesh(); + + updateSurfaceGradient(); +} + +void Surface3DRenderer::updateDataModel(QSurfaceDataProxy *dataProxy) +{ + // Make a copy of data array. + for (int i = 0; i < m_dataArray.size(); i++) + delete m_dataArray.at(i); + m_dataArray.clear(); + + const QSurfaceDataArray *array = dataProxy->array(); + + m_dataArray.reserve(array->size()); + for (int i = 0; i < array->size(); i++) { + QSurfaceDataRow *newRow = new QSurfaceDataRow(*array->at(i)); + m_dataArray << newRow; + } + + if (m_dataArray.size() > 0) { + if (!m_surfaceObj) + loadSurfaceObj(); + + QRect sampleSpace(0, 0, m_dataArray.at(0)->size(), m_dataArray.size()); + + bool dimensionChanged = false; + if (m_sampleSpace != sampleSpace) { + dimensionChanged = true; + m_sampleSpace = sampleSpace; + } + + // TODO: Setting height bigger than biggest value on data works nicely, but smaller + // creates odd scaling. That's not going to be trivial to clip 'peaks' off + m_heightNormalizer = (GLfloat)qMax(qAbs(m_axisCacheY.max()), qAbs(m_axisCacheY.min())); + + if (m_cachedSmoothSurface) + m_surfaceObj->setUpSmoothData(m_dataArray, m_sampleSpace, m_heightNormalizer, dimensionChanged); + else + m_surfaceObj->setUpData(m_dataArray, m_sampleSpace, m_heightNormalizer, dimensionChanged); + + if (dimensionChanged) + updateSelectionTexture(); + } + + calculateSceneScalingFactors(); + + Abstract3DRenderer::updateDataModel(dataProxy); +} + +void Surface3DRenderer::updateScene(Q3DScene *scene) +{ + // TODO: Move these to more suitable place e.g. controller should be controlling the viewports. + scene->setMainViewport(m_mainViewPort); + scene->setUnderSideCameraEnabled(m_hasNegativeValues); + + // TODO: bars have m_hasHeightAdjustmentChanged, which is always true! + // Set initial camera position + // X must be 0 for rotation to work - we can use "setCameraRotation" for setting it later + scene->camera()->setDefaultOrientation(QVector3D(0.0f, 0.0f, 6.0f + zComp), + QVector3D(0.0f, 0.0f, zComp), + QVector3D(0.0f, 1.0f, 0.0f)); + + // TODO: m_autoScaleAdjustment + scene->camera()->updateViewMatrix(1.0f); + scene->setLightPositionRelativeToCamera(defaultLightPos); + + Abstract3DRenderer::updateScene(scene); +} + +void Surface3DRenderer::render(GLuint defaultFboHandle) +{ + m_cachedScene->setUnderSideCameraEnabled(m_hasNegativeValues); + Q3DCamera *camera = m_cachedScene->camera(); + if (defaultFboHandle) { + glDepthMask(true); + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LESS); + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + } + + QVector3D clearColor = Utils::vectorFromColor(m_cachedTheme.m_windowColor); + glClearColor(clearColor.x(), clearColor.y(), clearColor.z(), 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + drawScene(defaultFboHandle); + + // If selection pointer is active, pass the render request for it also + if (m_selectionPointer && m_selectionActive) + m_selectionPointer->render(defaultFboHandle); +} + +void Surface3DRenderer::drawScene(GLuint defaultFboHandle) +{ + Q3DCamera *camera = m_cachedScene->camera(); + 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 TODO: m_autoScaleAdjustment + camera->updateViewMatrix(1.0f); + QMatrix4x4 viewMatrix = camera->viewMatrix(); + + // Calculate flipping indicators + 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; + + // calculate background rotation based on view matrix rotation + if (viewMatrix.row(0).x() > 0 && viewMatrix.row(0).z() <= 0) + backgroundRotation = 270.0f; + else if (viewMatrix.row(0).x() > 0 && viewMatrix.row(0).z() > 0) + backgroundRotation = 180.0f; + else if (viewMatrix.row(0).x() <= 0 && viewMatrix.row(0).z() > 0) + backgroundRotation = 90.0f; + else if (viewMatrix.row(0).x() <= 0 && viewMatrix.row(0).z() <= 0) + backgroundRotation = 0.0f; + + // TODO: add 0.0f, 1.0f / m_autoScaleAdjustment + QVector3D lightPos = m_cachedScene->light()->position(); + + QMatrix4x4 depthViewMatrix; + QMatrix4x4 depthProjectionMatrix; + depthProjectionMatrix = projectionMatrix; // TODO + depthViewMatrix.lookAt(lightPos, QVector3D(0.0f, 0.0f, zComp), + QVector3D(0.0f, 1.0f, 0.0f)); // TODO: Move + + // Enable texturing + glEnable(GL_TEXTURE_2D); + + // + // Do the surface drawing + // + + if (m_querySelection && m_surfaceObj) { + m_selectionShader->bind(); + glBindFramebuffer(GL_FRAMEBUFFER, m_selectionFrameBuffer); + glEnable(GL_DEPTH_TEST); // Needed, otherwise the depth render buffer is not used + glClearColor(0, 0, 0, 0); + 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 + + glDisable(GL_CULL_FACE); + + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; + + modelMatrix.translate(0.0f, 0.0f, zComp); + modelMatrix.scale(QVector3D(m_scaleX, 1.0f, m_scaleZ)); + + MVPMatrix = projectionMatrix * viewMatrix * modelMatrix; + + m_selectionShader->setUniformValue(m_selectionShader->MVP(), MVPMatrix); + + // Activate texture + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, m_selectionTexture); + m_selectionShader->setUniformValue(m_selectionShader->texture(), 0); + + // 1st attribute buffer : vertices + glEnableVertexAttribArray(m_selectionShader->posAtt()); + glBindBuffer(GL_ARRAY_BUFFER, m_surfaceObj->vertexBuf()); + glVertexAttribPointer(m_selectionShader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0, (void *)0); + + // 3rd attribute buffer : UVs + glEnableVertexAttribArray(m_selectionShader->uvAtt()); + glBindBuffer(GL_ARRAY_BUFFER, m_surfaceObj->uvBuf()); + glVertexAttribPointer(m_selectionShader->uvAtt(), 2, GL_FLOAT, GL_FALSE, 0, (void *)0); + + // Index buffer + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_surfaceObj->elementBuf()); + + // Draw the triangles + glDrawElements(GL_TRIANGLES, m_surfaceObj->indexCount(), m_surfaceObj->indicesType(), (void *)0); + //m_drawer->drawObject(m_selectionShader, m_surfaceObj, m_selectionTexture, 0); + + // Free buffers + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + glDisableVertexAttribArray(m_selectionShader->uvAtt()); + glDisableVertexAttribArray(m_selectionShader->posAtt()); + + glEnable(GL_DITHER); + + m_querySelection = false; + + QPoint point = m_controller->inputPosition(); + GLubyte pixel[4] = {0}; + glReadPixels(point.x(), m_cachedBoundingRect.height() - point.y(), 1, 1, + GL_RGBA, GL_UNSIGNED_BYTE, (void *)pixel); + + glBindFramebuffer(GL_FRAMEBUFFER, defaultFboHandle); + + // Release selection shader + m_selectionShader->release(); + + // Put the RGBA value back to uint + uint id = pixel[0] + pixel[1] * 256 + pixel[2] * 65536 + pixel[3] * 16777216; + if (id) { + surfacePointSelected(id); + } else { + //surfacePointCleared(); + m_selectionActive = false; + } + } + + if (m_surfaceObj) { + m_surfaceShader->bind(); + // m_selectionShader->bind(); // IFDEF print selection + + // For surface we can see climpses from underneath + glDisable(GL_CULL_FACE); + + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; + QMatrix4x4 depthMVPMatrix; + QMatrix4x4 itModelMatrix; + + modelMatrix.translate(0.0f, 0.0f, zComp); + modelMatrix.scale(QVector3D(m_scaleX, 1.0f, m_scaleZ)); + itModelMatrix.scale(QVector3D(m_scaleX, 1.0f, m_scaleZ)); + +#ifdef SHOW_DEPTH_TEXTURE_SCENE + MVPMatrix = depthProjectionMatrix * depthViewMatrix * modelMatrix; +#else + MVPMatrix = projectionMatrix * viewMatrix * modelMatrix; +#endif + // TODO Check the usage? + depthMVPMatrix = depthProjectionMatrix * depthViewMatrix * modelMatrix; + + // Set shader bindings + m_surfaceShader->setUniformValue(m_surfaceShader->lightP(), lightPos); + m_surfaceShader->setUniformValue(m_surfaceShader->view(), viewMatrix); + m_surfaceShader->setUniformValue(m_surfaceShader->model(), modelMatrix); + m_surfaceShader->setUniformValue(m_surfaceShader->nModel(), itModelMatrix.inverted().transposed()); + m_surfaceShader->setUniformValue(m_surfaceShader->MVP(), MVPMatrix); + //m_selectionShader->setUniformValue(m_selectionShader->MVP(), MVPMatrix); // IFDEF print selection + m_surfaceShader->setUniformValue(m_surfaceShader->ambientS(), m_cachedTheme.m_ambientStrength); + + //IF QT_OPENGL_ES_2 TODO + // Shadow quality etc. + //m_backgroundShader->setUniformValue(m_backgroundShader->shadowQ(), m_shadowQualityToShader); + //m_backgroundShader->setUniformValue(m_backgroundShader->depth(), depthMVPMatrix); + m_surfaceShader->setUniformValue(m_surfaceShader->lightS(), + m_cachedTheme.m_lightStrength * 2.0f); + + m_drawer->drawObject(m_surfaceShader, m_surfaceObj, m_gradientTexture, m_depthTexture); + //m_drawer->drawObject(m_selectionShader, m_surfaceObj, m_selectionTexture, 0); // IFDEF print selection + + m_surfaceShader->release(); + //m_selectionShader->release(); // IFDEF print selection + + if (m_cachedSurfaceGridOn) { + // Draw the grid over the surface + glPolygonOffset(1.0f, 1.0f); + glEnable(GL_POLYGON_OFFSET_FILL); + + m_surfaceGridShader->bind(); + + QVector3D gridColor = Utils::vectorFromColor(QColor(Qt::white)); + // Set shader bindings + m_surfaceGridShader->setUniformValue(m_surfaceGridShader->view(), viewMatrix); + m_surfaceGridShader->setUniformValue(m_surfaceGridShader->model(), modelMatrix); + m_surfaceGridShader->setUniformValue(m_surfaceGridShader->nModel(), itModelMatrix.inverted().transposed()); + m_surfaceGridShader->setUniformValue(m_surfaceGridShader->MVP(), MVPMatrix); + m_surfaceGridShader->setUniformValue(m_surfaceGridShader->color(), gridColor); + //m_surfaceGridShader->setUniformValue(m_surfaceGridShader->ambientS(), m_theme->m_ambientStrength); + m_drawer->drawSurfaceGrid(m_surfaceGridShader, m_surfaceObj); + + m_surfaceGridShader->release(); + + glPolygonOffset(0.0f, 0.0f); + glDisable(GL_POLYGON_OFFSET_FILL); + } + } + + // Bind background shader + m_backgroundShader->bind(); + + if (m_hasNegativeValues) + glDisable(GL_CULL_FACE); + else + 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); + modelMatrix.scale(QVector3D(m_scaleX * backgroundMargin, 1.0f, m_scaleZ * backgroundMargin)); + modelMatrix.rotate(backgroundRotation, 0.0f, 1.0f, 0.0f); + itModelMatrix.scale(QVector3D(m_scaleX * backgroundMargin, 1.0f, m_scaleZ * backgroundMargin)); + +#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 * 2.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_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); + + // Reset culling + if (m_hasNegativeValues) { + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + } + + // Draw grid lines + if (m_cachedIsGridEnabled && m_surfaceObj /*&& m_heightNormalizer*/) { + // Bind shader + m_shader->bind(); + + // Set unchanging shader bindings + QVector3D color = Utils::vectorFromColor(m_cachedTheme.m_gridLine); + m_shader->setUniformValue(m_shader->lightP(), lightPos); + m_shader->setUniformValue(m_shader->view(), viewMatrix); + m_shader->setUniformValue(m_shader->color(), color); + m_shader->setUniformValue(m_shader->ambientS(), m_cachedTheme.m_ambientStrength); + + GLfloat yPos = -backgroundBottom; + if (m_yFlipped) + yPos = backgroundBottom; + + if (m_axisCacheZ.segmentCount() > 0) { + GLfloat lineStep = 2.0f / (m_axisCacheZ.segmentCount()); + GLfloat linePos = -1.0; + + for (int segment = 0; segment <= m_axisCacheZ.segmentCount(); segment++) { + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; + QMatrix4x4 depthMVPMatrix; + QMatrix4x4 itModelMatrix; + + modelMatrix.translate(0.0f, yPos, linePos * m_scaleZ + zComp); + + modelMatrix.scale(QVector3D(m_scaleX * backgroundMargin, gridLineWidth, gridLineWidth)); + itModelMatrix.scale(QVector3D(m_scaleX * backgroundMargin, gridLineWidth, gridLineWidth)); + + MVPMatrix = projectionMatrix * viewMatrix * modelMatrix; + depthMVPMatrix = depthProjectionMatrix * depthViewMatrix * modelMatrix; + + // Set the rest of the shader bindings + m_shader->setUniformValue(m_shader->model(), modelMatrix); + m_shader->setUniformValue(m_shader->nModel(), + itModelMatrix.inverted().transposed()); + m_shader->setUniformValue(m_shader->MVP(), MVPMatrix); + + #if !defined(QT_OPENGL_ES_2) + if (m_cachedShadowQuality > QDataVis::ShadowNone) { + // Set shadow shader bindings + m_shader->setUniformValue(m_shader->shadowQ(), m_shadowQualityToShader); + m_shader->setUniformValue(m_shader->depth(), depthMVPMatrix); + m_shader->setUniformValue(m_shader->lightS(), + m_cachedTheme.m_lightStrength / 10.0f); + + // Draw the object + m_drawer->drawObject(m_shader, m_gridLineObj, 0, m_depthTexture); + } else + #endif + { + // Set shadowless shader bindings + m_shader->setUniformValue(m_shader->lightS(), + m_cachedTheme.m_lightStrength); + + // Draw the object + m_drawer->drawObject(m_shader, m_gridLineObj); + } + + linePos += lineStep; + } + } + + // Floor lines: columns (= X) + if (m_axisCacheX.segmentCount() > 0) { + GLfloat lineStep = 2.0f / (m_axisCacheX.segmentCount()); + GLfloat linePos = -1.0; + + for (int segment = 0; segment <= m_axisCacheX.segmentCount(); segment++) { + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; + QMatrix4x4 depthMVPMatrix; + QMatrix4x4 itModelMatrix; + + modelMatrix.translate(linePos * m_scaleX, yPos, zComp); + + modelMatrix.scale(QVector3D(gridLineWidth, gridLineWidth, m_scaleZ * backgroundMargin)); + itModelMatrix.scale(QVector3D(gridLineWidth, gridLineWidth, m_scaleZ * backgroundMargin)); + + MVPMatrix = projectionMatrix * viewMatrix * modelMatrix; + depthMVPMatrix = depthProjectionMatrix * depthViewMatrix * modelMatrix; + + // Set the rest of the shader bindings + m_shader->setUniformValue(m_shader->model(), modelMatrix); + m_shader->setUniformValue(m_shader->nModel(), + itModelMatrix.inverted().transposed()); + m_shader->setUniformValue(m_shader->MVP(), MVPMatrix); + + #if !defined(QT_OPENGL_ES_2) + if (m_cachedShadowQuality > QDataVis::ShadowNone) { + // Set shadow shader bindings + m_shader->setUniformValue(m_shader->shadowQ(), m_shadowQualityToShader); + m_shader->setUniformValue(m_shader->depth(), depthMVPMatrix); + m_shader->setUniformValue(m_shader->lightS(), + m_cachedTheme.m_lightStrength / 10.0f); + + // Draw the object + m_drawer->drawObject(m_shader, m_gridLineObj, 0, m_depthTexture); + } else + #endif + { + // Set shadowless shader bindings + m_shader->setUniformValue(m_shader->lightS(), + m_cachedTheme.m_lightStrength); + + // Draw the object + m_drawer->drawObject(m_shader, m_gridLineObj); + } + + linePos += lineStep; + } + } + + // Wall lines: back wall + if (m_axisCacheY.segmentCount() > 0) { + GLfloat lineStep = 2.0f / (m_axisCacheY.segmentCount()); + GLfloat linePos = -1.0; + GLfloat zPos = -backgroundMargin; + + if (m_zFlipped) + zPos = backgroundMargin; + + for (int segment = 0; segment <= m_axisCacheY.segmentCount(); segment++) { + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; + QMatrix4x4 depthMVPMatrix; + QMatrix4x4 itModelMatrix; + + modelMatrix.translate(0.0f, linePos, zPos * m_scaleZ + zComp); + + modelMatrix.scale(QVector3D(m_scaleX * backgroundMargin, gridLineWidth, gridLineWidth)); + itModelMatrix.scale(QVector3D(m_scaleX * backgroundMargin, gridLineWidth, gridLineWidth)); + + MVPMatrix = projectionMatrix * viewMatrix * modelMatrix; + depthMVPMatrix = depthProjectionMatrix * depthViewMatrix * modelMatrix; + + // Set the rest of the shader bindings + m_shader->setUniformValue(m_shader->model(), modelMatrix); + m_shader->setUniformValue(m_shader->nModel(), + itModelMatrix.inverted().transposed()); + m_shader->setUniformValue(m_shader->MVP(), MVPMatrix); + + #if !defined(QT_OPENGL_ES_2) + if (m_cachedShadowQuality > QDataVis::ShadowNone) { + // Set shadow shader bindings + m_shader->setUniformValue(m_shader->shadowQ(), m_shadowQualityToShader); + m_shader->setUniformValue(m_shader->depth(), depthMVPMatrix); + m_shader->setUniformValue(m_shader->lightS(), + m_cachedTheme.m_lightStrength / 10.0f); + + // Draw the object + m_drawer->drawObject(m_shader, m_gridLineObj, 0, m_depthTexture); + } else + #endif + { + // Set shadowless shader bindings + m_shader->setUniformValue(m_shader->lightS(), + m_cachedTheme.m_lightStrength); + + // Draw the object + m_drawer->drawObject(m_shader, m_gridLineObj); + } + + linePos += lineStep; + } + + // Wall lines: side wall + linePos = -1.0; + GLfloat xPos = -backgroundMargin; + + if (m_xFlipped) + xPos = backgroundMargin; + + for (int segment = 0; segment <= m_axisCacheY.segmentCount(); segment++) { + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; + QMatrix4x4 depthMVPMatrix; + QMatrix4x4 itModelMatrix; + + modelMatrix.translate(xPos * m_scaleX, linePos, 0.0f + zComp); + + modelMatrix.scale(QVector3D(gridLineWidth, gridLineWidth, m_scaleZ * backgroundMargin)); + itModelMatrix.scale(QVector3D(gridLineWidth, gridLineWidth, m_scaleZ * backgroundMargin)); + + MVPMatrix = projectionMatrix * viewMatrix * modelMatrix; + depthMVPMatrix = depthProjectionMatrix * depthViewMatrix * modelMatrix; + + // Set the rest of the shader bindings + m_shader->setUniformValue(m_shader->model(), modelMatrix); + m_shader->setUniformValue(m_shader->nModel(), + itModelMatrix.inverted().transposed()); + m_shader->setUniformValue(m_shader->MVP(), MVPMatrix); + + #if !defined(QT_OPENGL_ES_2) + if (m_cachedShadowQuality > QDataVis::ShadowNone) { + // Set shadow shader bindings + m_shader->setUniformValue(m_shader->shadowQ(), m_shadowQualityToShader); + m_shader->setUniformValue(m_shader->depth(), depthMVPMatrix); + m_shader->setUniformValue(m_shader->lightS(), + m_cachedTheme.m_lightStrength / 10.0f); + + // Draw the object + m_drawer->drawObject(m_shader, m_gridLineObj, 0, m_depthTexture); + } else + #endif + { + // Set shadowless shader bindings + m_shader->setUniformValue(m_shader->lightS(), + m_cachedTheme.m_lightStrength); + + // Draw the object + m_drawer->drawObject(m_shader, m_gridLineObj); + } + + linePos += lineStep; + } + } + } + + // Draw axis labels + 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 && m_surfaceObj) { + GLfloat posStep = coordSpace / (m_axisCacheZ.segmentCount()); + GLfloat labelPos = 1.0; + + for (int segment = 0; segment <= m_axisCacheZ.segmentCount(); segment++) { + GLfloat labelXTrans = backgroundMargin * m_scaleX + labelMargin; + GLfloat labelYTrans = -backgroundBottom; + GLfloat rotLabelX = -90.0f; + GLfloat rotLabelY = 0.0f; + GLfloat rotLabelZ = 0.0f; + Qt::AlignmentFlag alignment = Qt::AlignRight; + + 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_scaleZ + zComp); + + // Draw the label here + m_dummyRenderItem.setTranslation(labelTrans); + const LabelItem &axisLabelItem = *m_axisCacheZ.labelItems().at(segment); + + 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, camera, true, true, Drawer::LabelMid, + alignment); + + labelPos -= posStep; + } + } + + // X Labels + if (m_axisCacheX.segmentCount() > 0 && m_surfaceObj) { + GLfloat posStep = coordSpace / (m_axisCacheX.segmentCount()); + GLfloat labelPos = -1.0; + + for (int segment = 0; segment <= m_axisCacheX.segmentCount(); segment++) { + GLfloat labelYTrans = -backgroundBottom; + GLfloat labelZTrans = backgroundMargin * m_scaleZ + labelMargin; + 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_scaleX, + labelYTrans, + labelZTrans + zComp); + + // Draw the label here + m_dummyRenderItem.setTranslation(labelTrans); + const LabelItem &axisLabelItem = *m_axisCacheX.labelItems().at(segment); + + 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, camera, true, true, Drawer::LabelMid, + alignment); + + labelPos += posStep; + } + } + + // Y Labels + if (m_axisCacheY.segmentCount() > 0 && m_surfaceObj) { + GLfloat posStep = coordSpace / (m_axisCacheY.segmentCount()); + GLfloat labelPos = -1.0; + + for (int segment = 0; segment <= m_axisCacheY.segmentCount(); segment++) { + GLfloat labelXTrans = backgroundMargin * m_scaleX; + GLfloat labelZTrans = backgroundMargin * m_scaleZ; + GLfloat labelMarginXTrans = labelMargin; + GLfloat labelMarginZTrans = labelMargin; + 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; + } + + QVector3D labelTrans = QVector3D(labelXTrans, + labelPos, + labelZTrans + labelMarginZTrans + zComp); + + // Draw the label here + m_dummyRenderItem.setTranslation(labelTrans); + const LabelItem &axisLabelItem = *m_axisCacheY.labelItems().at(segment); + + 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, 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, + labelPos, + -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, camera, true, true, Drawer::LabelMid, + alignment); + + labelPos += posStep; + } + } + + glDisable(GL_TEXTURE_2D); + if (m_cachedLabelTransparency > QDataVis::TransparencyNone) + glDisable(GL_BLEND); + + // Release label shader + m_labelShader->release(); + +} + +void Surface3DRenderer::updateSurfaceGradient() +{ + QImage image(QSize(4, 100), QImage::Format_RGB32); + QPainter pmp(&image); + pmp.setBrush(QBrush(m_cachedTheme.m_surfaceGradient)); + pmp.setPen(Qt::NoPen); + pmp.drawRect(0, 0, 4, 100); + + if (m_gradientTexture) { + m_textureHelper->deleteTexture(&m_gradientTexture); + m_gradientTexture = 0; + } + + m_gradientTexture = m_textureHelper->create2DTexture(image, false, true); +} + +void Surface3DRenderer::requestSelectionAtPoint(const QPoint &point) +{ + Q_UNUSED(point) + +// QMutexLocker locker(&m_mutex); +// m_selectionPointRequest.setX(point.x()); +// m_selectionPointRequest.setY(point.y()); +// m_isSelectionPointRequestActive = true; + + m_querySelection = true; +} + +// This one needs to be called when the data size changes +void Surface3DRenderer::updateSelectionTexture() +{ + // Create the selection ID image. Each grid corner gets 2x2 pixel area of + // ID color so that each vertex (data point) has 4x4 pixel area of ID color + // TODO: power of two thing for ES + int idImageWidth = (m_sampleSpace.width() - 1) * 4; + int idImageHeight = (m_sampleSpace.height() - 1) * 4; + int stride = idImageWidth * 4 * sizeof(uchar); // 4 = number of color components (rgba) + + uchar *bits = new uchar[idImageWidth * idImageHeight * 4 * sizeof(uchar)]; + uint id = 1; + for (int i = 0; i < idImageHeight; i += 4) { + for (int j = 0; j < idImageWidth; j += 4) { + int p = (i * idImageWidth + j) * 4; + uchar r, g, b, a; + idToRGBA(id, &r, &g, &b, &a); + fillIdCorner(&bits[p], r, g, b, a, stride); + + idToRGBA(id + 1, &r, &g, &b, &a); + fillIdCorner(&bits[p + 8], r, g, b, a, stride); + + idToRGBA(id + m_sampleSpace.width(), &r, &g, &b, &a); + fillIdCorner(&bits[p + 2 * stride], r, g, b, a, stride); + + idToRGBA(id + m_sampleSpace.width() + 1, &r, &g, &b, &a); + fillIdCorner(&bits[p + 2 * stride + 8], r, g, b, a, stride); + + id++; + } + id++; + } + + // Use this to save the ID image to file + //QImage image(bits, idImageWidth, idImageHeight, QImage::Format_ARGB32); + //image.save("C:\\Users\\misalmel\\Work\\gerrit\\qtdatavisualization_2\\selection.png"); + + // If old texture exists, delete it + if (m_selectionTexture) { + m_textureHelper->deleteTexture(&m_selectionTexture); + m_selectionTexture = 0; + } + + // Move the ID image (bits) to the texture + m_selectionTexture = m_textureHelper->create2DTexture(bits, idImageWidth, idImageHeight); + + // Release the temp bits allocation + delete[] bits; +} + +void Surface3DRenderer::initSelectionBuffer() +{ + // Create the result selection texture and buffers + if (m_selectionResultTexture) { + m_textureHelper->deleteTexture(&m_selectionResultTexture); + m_selectionResultTexture = 0; + } + + m_selectionResultTexture = m_textureHelper->createSelectionTexture(m_mainViewPort.size(), + m_selectionFrameBuffer, + m_selectionDepthBuffer); +} + +void Surface3DRenderer::fillIdCorner(uchar *p, uchar r, uchar g, uchar b, uchar a, int stride) +{ + p[0] = r; + p[1] = g; + p[2] = b; + p[3] = a; + p[4] = r; + p[5] = g; + p[6] = b; + p[7] = a; + p[stride + 0] = r; + p[stride + 1] = g; + p[stride + 2] = b; + p[stride + 3] = a; + p[stride + 4] = r; + p[stride + 5] = g; + p[stride + 6] = b; + p[stride + 7] = a; +} + +void Surface3DRenderer::idToRGBA(uint id, uchar *r, uchar *g, uchar *b, uchar *a) +{ + *r = id & ID_TO_RGBA_MASK; + *g = (id >> 8) & ID_TO_RGBA_MASK; + *b = (id >> 16) & ID_TO_RGBA_MASK; + *a = (id >> 24) & ID_TO_RGBA_MASK; +} + +void Surface3DRenderer::updateTextures() +{ + qDebug() << __FUNCTION__ << "NEED TO DO SOMETHING"; + // Drawer has changed; this flag needs to be checked when checking if we need to update labels + //m_updateLabels = true; +} + +void Surface3DRenderer::calculateSceneScalingFactors() +{ + // Calculate scene scaling and translation factors + m_scaleFactor = qMax(m_sampleSpace.width(), m_sampleSpace.height()); + m_scaleX = (coordSpace * m_sampleSpace.width()) / m_scaleFactor; + m_scaleZ = (coordSpace * m_sampleSpace.height()) / m_scaleFactor; +} + +void Surface3DRenderer::updateSmoothStatus(bool enable) +{ + m_cachedSmoothSurface = enable; + + if (!m_surfaceObj) + return; + + if (m_cachedSmoothSurface) + m_surfaceObj->setUpSmoothData(m_dataArray, m_sampleSpace, m_heightNormalizer, true); + else + m_surfaceObj->setUpData(m_dataArray, m_sampleSpace, m_heightNormalizer, true); + + initSurfaceShaders(); +} + +void Surface3DRenderer::updateSurfaceGridStatus(bool enable) +{ + m_cachedSurfaceGridOn = enable; +} + +void Surface3DRenderer::loadBackgroundMesh() +{ + if (m_backgroundObj) + delete m_backgroundObj; + if (m_hasNegativeValues) + m_backgroundObj = new ObjectHelper(QStringLiteral(":/defaultMeshes/negativeBackground")); + else + m_backgroundObj = new ObjectHelper(QStringLiteral(":/defaultMeshes/background")); + m_backgroundObj->load(); +} + +void Surface3DRenderer::loadSurfaceObj() +{ + if (m_surfaceObj) + delete m_surfaceObj; + m_surfaceObj = new SurfaceObject(); + //m_surfaceObj->setUpData(); +} + +void Surface3DRenderer::loadGridLineMesh() +{ + if (m_gridLineObj) + delete m_gridLineObj; + m_gridLineObj = new ObjectHelper(QStringLiteral(":/defaultMeshes/bar")); + m_gridLineObj->load(); +} + +void Surface3DRenderer::handleResize() +{ + if (m_cachedBoundingRect.width() == 0 || m_cachedBoundingRect.height() == 0) + return; + + m_mainViewPort = QRect(0, 0, m_cachedBoundingRect.width(), m_cachedBoundingRect.height()); + + if (m_selectionPointer) + m_selectionPointer->updateBoundingRect(m_mainViewPort); + + Abstract3DRenderer::handleResize(); +} + +#if !defined(QT_OPENGL_ES_2) +void Surface3DRenderer::updateDepthBuffer() +{ + if (m_depthTexture) { + m_textureHelper->deleteTexture(&m_depthTexture); + m_depthTexture = 0; + } + + // TODO: bars uses some m_cachedShadowQuality + if (m_shadowQuality > QDataVis::ShadowNone && !m_mainViewPort.size().isEmpty()) { + m_depthTexture = m_textureHelper->createDepthTexture(m_mainViewPort.size(), + m_depthFrameBuffer, + m_shadowQuality); + if (!m_depthTexture) { + qDebug() << "Failed to create m_depthTexture"; + // switch (m_shadowQuality) { + // case ShadowHigh: + // qWarning("Creating high quality shadows failed. Changing to medium quality."); + // (void)setShadowQuality(ShadowMedium); + // break; + // case ShadowMedium: + // qWarning("Creating medium quality shadows failed. Changing to low quality."); + // (void)setShadowQuality(ShadowLow); + // break; + // case ShadowLow: + // qWarning("Creating low quality shadows failed. Switching shadows off."); + // (void)setShadowQuality(ShadowNone); + // break; + // default: + // // You'll never get here + // break; + // } + } + } +} +#endif + +void Surface3DRenderer::surfacePointSelected(int id) +{ + int column = (id - 1) % m_sampleSpace.width(); + int row = (id - 1) / m_sampleSpace.width(); + qreal value = m_dataArray.at(row)->at(column); + + if (!m_selectionPointer) + m_selectionPointer = new SelectionPointer(m_controller); + + m_selectionPointer->setPosition(normalize(float(column), value, float(row))); + m_selectionPointer->setScaling(QVector3D(m_scaleX, 1.0f, m_scaleZ)); + m_selectionPointer->setLabel(QString::number(value)); + m_selectionPointer->updateBoundingRect(m_mainViewPort); + + //Put the selection pointer flag active + m_selectionActive = true; +} + +QVector3D Surface3DRenderer::normalize(float x, float y, float z) +{ + float resX = x / ((float(m_sampleSpace.width()) - 1.0f) / 2.0f) - 1.0f; + float resY = y / (m_heightNormalizer / 2.0f) - 1.0f; + float resZ = z / ((float(m_sampleSpace.height()) - 1.0f) / -2.0f) + 1.0f; + + return QVector3D(resX, resY, resZ); +} + +void Surface3DRenderer::surfacePointCleared() +{ + if (m_selectionPointer) { + delete m_selectionPointer; + m_selectionPointer = 0; + m_selectionActive = false; + } +} + +void Surface3DRenderer::loadMeshFile() +{ + qDebug() << __FUNCTION__ << "should we do something"; +} + +void Surface3DRenderer::updateShadowQuality(QDataVis::ShadowQuality quality) +{ + Q_UNUSED(quality) + qDebug() << __FUNCTION__ << "NEED TO DO SOMETHING"; +} + +void Surface3DRenderer::loadLabelMesh() +{ + if (m_labelObj) + delete m_labelObj; + m_labelObj = new ObjectHelper(QStringLiteral(":/defaultMeshes/label")); + m_labelObj->load(); +} + +void Surface3DRenderer::initShaders(const QString &vertexShader, const QString &fragmentShader) +{ + if (m_shader) + delete m_shader; + m_shader = new ShaderHelper(this, vertexShader, fragmentShader); + m_shader->initialize(); +} + +void Surface3DRenderer::initBackgroundShaders(const QString &vertexShader, + const QString &fragmentShader) +{ + if (m_backgroundShader) + delete m_backgroundShader; + m_backgroundShader = new ShaderHelper(this, vertexShader, fragmentShader); + m_backgroundShader->initialize(); +} + +void Surface3DRenderer::initSelectionShaders() +{ + if (m_selectionShader) + delete m_selectionShader; + m_selectionShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexLabel"), + QStringLiteral(":/shaders/fragmentLabel")); + m_selectionShader->initialize(); +} + +void Surface3DRenderer::initSurfaceShaders() +{ + if (m_surfaceShader) + delete m_surfaceShader; + if (m_cachedSmoothSurface) { + m_surfaceShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurface"), + QStringLiteral(":/shaders/fragmentSurface")); + } else { + m_surfaceShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceFlat"), + QStringLiteral(":/shaders/fragmentSurfaceFlat")); + } + m_surfaceShader->initialize(); + + if (m_surfaceGridShader) + delete m_surfaceGridShader; + m_surfaceGridShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceGrid"), + QStringLiteral(":/shaders/fragmentSurfaceGrid")); + m_surfaceGridShader->initialize(); +} + +void Surface3DRenderer::initLabelShaders(const QString &vertexShader, const QString &fragmentShader) +{ + if (m_labelShader) + delete m_labelShader; + m_labelShader = new ShaderHelper(this, vertexShader, fragmentShader); + m_labelShader->initialize(); +} + +QT_DATAVISUALIZATION_END_NAMESPACE |