diff options
Diffstat (limited to 'src/datavisualization/engine/surface3drenderer.cpp')
-rw-r--r-- | src/datavisualization/engine/surface3drenderer.cpp | 2185 |
1 files changed, 2185 insertions, 0 deletions
diff --git a/src/datavisualization/engine/surface3drenderer.cpp b/src/datavisualization/engine/surface3drenderer.cpp new file mode 100644 index 00000000..a1dfc7e8 --- /dev/null +++ b/src/datavisualization/engine/surface3drenderer.cpp @@ -0,0 +1,2185 @@ +/**************************************************************************** +** +** 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 "q3dcamera_p.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 + +//#define SHOW_DEPTH_TEXTURE_SCENE + +// TODO Uniform scaling is broken on surface +//#define USE_UNIFORM_SCALING // Scale x and z uniformly, or based on autoscaled values + +const GLfloat aspectRatio = 2.0f; // Forced ratio of x and z to y. Dynamic will make it look odd. +const GLfloat 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 sliceZScale = 0.1f; +const GLfloat surfaceGridYOffsetValue = 0.001f; +const GLfloat sliceUnits = 2.5f; +const int subViewDivider = 5; +// The second offset to opposite direction is double because same matrix is translated twice +const GLfloat surfaceGridYOffset[2] = {-surfaceGridYOffsetValue, 2.0f * surfaceGridYOffsetValue}; + +Surface3DRenderer::Surface3DRenderer(Surface3DController *controller) + : Abstract3DRenderer(controller), + m_controller(controller), + m_labelStyle(QDataVis::LabelStyleFromTheme), + m_font(QFont(QStringLiteral("Arial"))), + m_isGridEnabled(true), + m_shader(0), + m_depthShader(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_scaleXWithBackground(0.0f), + m_scaleZWithBackground(0.0f), + m_surfaceScaleX(0.0f), + m_surfaceScaleZ(0.0f), + m_surfaceOffsetX(0.0f), + m_surfaceOffsetZ(0.0f), + m_minVisibleColumnValue(0.0f), + m_maxVisibleColumnValue(0.0f), + m_minVisibleRowValue(0.0f), + m_maxVisibleRowValue(0.0f), + m_visibleColumnRange(0.0f), + m_visibleRowRange(0.0f), + m_backgroundObj(0), + m_gridLineObj(0), + m_labelObj(0), + m_surfaceObj(0), + m_sliceSurfaceObj(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_flatSupported(true), + m_selectionPointer(0), + m_selectionActive(false), + m_xFlipped(false), + m_zFlipped(false), + m_yFlipped(false), + m_sampleSpace(QRect(0, 0, 0, 0)), + m_shadowQualityMultiplier(3), + m_cachedSelectionId(0), + m_selectionModeChanged(false), + m_hasHeightAdjustmentChanged(true) +{ +#if !defined(QT_OPENGL_ES_2) + // Check if flat feature is supported + ShaderHelper tester(this, QStringLiteral(":/shaders/vertexSurfaceFlat"), + QStringLiteral(":/shaders/fragmentSurfaceFlat")); + if (!tester.testCompile()) { + m_flatSupported = false; + m_controller->setSmoothSurface(true); + } +#endif + + m_cachedSmoothSurface = m_controller->smoothSurface(); + updateSurfaceGridStatus(m_controller->surfaceGrid()); + + // Shadows are disabled for Q3DSurface in Tech Preview + updateShadowQuality(QDataVis::ShadowQualityNone); + + 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_depthShader; + delete m_backgroundShader; + delete m_selectionShader; + delete m_surfaceShader; + delete m_surfaceGridShader; + delete m_labelShader; + + delete m_backgroundObj; + delete m_surfaceObj; + delete m_sliceSurfaceObj; + delete m_gridLineObj; + delete m_labelObj; + + delete m_selectionPointer; + + for (int i = 0; i < m_dataArray.size(); i++) + delete m_dataArray.at(i); + m_dataArray.clear(); + + for (int i = 0; i < m_sliceDataArray.size(); i++) + delete m_sliceDataArray.at(i); + m_sliceDataArray.clear(); +} + +void Surface3DRenderer::initializeOpenGL() +{ + Abstract3DRenderer::initializeOpenGL(); + + // Initialize shaders + handleShadowQualityChange(); + + initSurfaceShaders(); + + 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 + initSelectionShaders(); + + // Load grid line mesh + loadGridLineMesh(); + + // Load label mesh + loadLabelMesh(); + + // 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(); +} + +void Surface3DRenderer::updateDataModel(QSurfaceDataProxy *dataProxy) +{ + calculateSceneScalingFactors(); + + const QSurfaceDataArray &array = *dataProxy->array(); + + // Need minimum of 2x2 array to draw a surface + if (array.size() >= 2 && array.at(0)->size() >= 2) { + QRect sampleSpace = calculateSampleRect(array); + + bool dimensionChanged = false; + if (m_sampleSpace != sampleSpace) { + dimensionChanged = true; + m_sampleSpace = sampleSpace; + + for (int i = 0; i < m_dataArray.size(); i++) + delete m_dataArray.at(i); + m_dataArray.clear(); + } + + // TODO: Handle partial surface grids on the graph edges + if (sampleSpace.width() >= 2 && sampleSpace.height() >= 2) { + if (dimensionChanged) { + m_dataArray.reserve(sampleSpace.height()); + for (int i = 0; i < sampleSpace.height(); i++) + m_dataArray << new QSurfaceDataRow(sampleSpace.width()); + } + for (int i = 0; i < sampleSpace.height(); i++) { + for (int j = 0; j < sampleSpace.width(); j++) + (*(m_dataArray.at(i)))[j] = array.at(i + sampleSpace.y())->at(j + sampleSpace.x()); + } + + if (m_dataArray.size() > 0) { + if (!m_surfaceObj) + loadSurfaceObj(); + + // Note: Data setup can change samplespace (as min width/height is 1) + if (m_cachedSmoothSurface) { + m_surfaceObj->setUpSmoothData(m_dataArray, m_sampleSpace, m_heightNormalizer, + m_axisCacheY.min(), dimensionChanged); + } else { + m_surfaceObj->setUpData(m_dataArray, m_sampleSpace, m_heightNormalizer, + m_axisCacheY.min(), dimensionChanged); + } + + if (dimensionChanged) + updateSelectionTexture(); + } + } + } + + m_selectionActive = false; + m_cachedSelectionId = 0; + for (int i = 0; i < m_sliceDataArray.size(); i++) + delete m_sliceDataArray.at(i); + m_sliceDataArray.clear(); + + Abstract3DRenderer::updateDataModel(dataProxy); +} + +void Surface3DRenderer::updateSliceDataModel(int selectionId) +{ + int column = (selectionId - 1) % m_sampleSpace.width(); + int row = (selectionId - 1) / m_sampleSpace.width(); + + for (int i = 0; i < m_sliceDataArray.size(); i++) + delete m_sliceDataArray.at(i); + m_sliceDataArray.clear(); + + m_sliceDataArray.reserve(2); + QSurfaceDataRow *sliceRow; + + qreal adjust = (0.025 * m_heightNormalizer) / 2.0; + qreal stepDown = 2.0 * adjust; + if (m_cachedSelectionMode == QDataVis::SelectionModeSliceRow) { + QSurfaceDataRow *src = m_dataArray.at(row); + sliceRow = new QSurfaceDataRow(src->size()); + for (int i = 0; i < sliceRow->size(); i++) + (*sliceRow)[i].setPosition(QVector3D(src->at(i).x(), src->at(i).y() + adjust, -1.0)); + } else { + sliceRow = new QSurfaceDataRow(m_sampleSpace.height()); + for (int i = 0; i < m_sampleSpace.height(); i++) { + (*sliceRow)[i].setPosition(QVector3D(m_dataArray.at(i)->at(column).z(), + m_dataArray.at(i)->at(column).y() + adjust, + -1.0)); + } + } + + m_sliceDataArray << sliceRow; + + // Make a duplicate, so that we get a little bit depth + QSurfaceDataRow *duplicateRow = new QSurfaceDataRow(*sliceRow); + for (int i = 0; i < sliceRow->size(); i++) + (*sliceRow)[i].setPosition(QVector3D(sliceRow->at(i).x(), sliceRow->at(i).y() - stepDown, 1.0)); + + m_sliceDataArray << duplicateRow; + + QRect sliceRect(0, 0, sliceRow->size(), 2); + + if (sliceRow->size() > 0) { + if (!m_sliceSurfaceObj) + loadSliceSurfaceObj(); + + if (m_cachedSmoothSurface) { + m_sliceSurfaceObj->setUpSmoothData(m_sliceDataArray, sliceRect, m_heightNormalizer, + m_axisCacheY.min(), true); + } else { + m_sliceSurfaceObj->setUpData(m_sliceDataArray, sliceRect, m_heightNormalizer, + m_axisCacheY.min(), true); + } + } +} + +QRect Surface3DRenderer::calculateSampleRect(const QSurfaceDataArray &array) +{ + QRect sampleSpace; + + int rowCount = array.size(); + int columnCount = array.at(0)->size(); + + int i; + bool found; + float axisMinX = float(m_axisCacheX.min()); + float axisMaxX = float(m_axisCacheX.max()); + float axisMinZ = float(m_axisCacheZ.min()); + float axisMaxZ = float(m_axisCacheZ.max()); + + // Comparisons between float and double are not accurate, so fudge our comparison values + //a little to get all rows and columns into view that need to be visible. + const float fudgeFactor = 0.00001f; + float fudgedAxisXRange = (axisMaxX - axisMinX) * fudgeFactor; + float fudgedAxisZRange = (axisMaxZ - axisMinZ) * fudgeFactor; + axisMinX -= fudgedAxisXRange; + axisMinZ -= fudgedAxisZRange; + axisMaxX += fudgedAxisXRange; + axisMaxZ += fudgedAxisZRange; + + // m_minVisibleColumnValue + for (i = 0, found = false; i < columnCount; i++) { + if (array.at(0)->at(i).x() >= axisMinX) { + found = true; + break; + } + } + if (found) { + m_minVisibleColumnValue = array.at(0)->at(i).x(); + sampleSpace.setLeft(i); + } else { + sampleSpace.setWidth(-1); // to indicate nothing needs to be shown + return sampleSpace; + } + + // m_maxVisibleColumnValue + for (i = columnCount - 1, found = false; i >= 0; i--) { + if (array.at(0)->at(i).x() <= axisMaxX) { + found = true; + break; + } + } + if (found) { + m_maxVisibleColumnValue = array.at(0)->at(i).x(); + sampleSpace.setRight(i); + } else { + sampleSpace.setWidth(-1); // to indicate nothing needs to be shown + return sampleSpace; + } + + // m_minVisibleRowValue + for (i = 0, found = false; i < rowCount; i++) { + if (array.at(i)->at(0).z() >= axisMinZ) { + found = true; + break; + } + } + if (found) { + m_minVisibleRowValue = array.at(i)->at(0).z(); + sampleSpace.setTop(i); + } else { + sampleSpace.setWidth(-1); // to indicate nothing needs to be shown + return sampleSpace; + } + + // m_maxVisibleRowValue + for (i = rowCount - 1, found = false; i >= 0; i--) { + if (array.at(i)->at(0).z() <= axisMaxZ) { + found = true; + break; + } + } + if (found) { + m_maxVisibleRowValue = array.at(i)->at(0).z(); + sampleSpace.setBottom(i); + } else { + sampleSpace.setWidth(-1); // to indicate nothing needs to be shown + return sampleSpace; + } + + m_visibleColumnRange = m_maxVisibleColumnValue - m_minVisibleColumnValue; + m_visibleRowRange = m_maxVisibleRowValue - m_minVisibleRowValue; + m_surfaceScaleX = m_scaleX * m_visibleColumnRange / m_areaSize.width(); + m_surfaceScaleZ = m_scaleZ * m_visibleRowRange / m_areaSize.height(); + GLfloat axis2XCenterX = axisMinX + axisMaxX; + GLfloat axis2XCenterZ = axisMinZ + axisMaxZ; + GLfloat data2XCenterX = GLfloat(m_minVisibleColumnValue + m_maxVisibleColumnValue); + GLfloat data2XCenterZ = GLfloat(m_minVisibleRowValue + m_maxVisibleRowValue); + m_surfaceOffsetX = m_scaleX * (data2XCenterX - axis2XCenterX) / m_areaSize.width(); + m_surfaceOffsetZ = -m_scaleZ * (data2XCenterZ - axis2XCenterZ) / m_areaSize.height(); + + return sampleSpace; +} + +void Surface3DRenderer::updateScene(Q3DScene *scene) +{ + // TODO: Move these to more suitable place e.g. controller should be controlling the viewports. + scene->setSecondarySubViewport(m_sliceViewPort); + scene->setPrimarySubViewport(m_mainViewPort); + + // Set initial camera position + // X must be 0 for rotation to work - we can use "setCameraRotation" for setting it later + if (m_hasHeightAdjustmentChanged) { + scene->activeCamera()->setBaseOrientation(QVector3D(0.0f, 0.0f, cameraDistance + zComp), + QVector3D(0.0f, 0.0f, zComp), + QVector3D(0.0f, 1.0f, 0.0f)); + // For now this is used just to make things once. Proper use will come + m_hasHeightAdjustmentChanged = false; + } + + scene->activeCamera()->d_ptr->updateViewMatrix(m_autoScaleAdjustment); + scene->setLightPositionRelativeToCamera(defaultLightPos); + + if (m_selectionPointer) + m_selectionPointer->updateScene(scene); + + Abstract3DRenderer::updateScene(scene); +} + +void Surface3DRenderer::render(GLuint defaultFboHandle) +{ + bool slicingActivated = m_cachedScene->isSlicingActive(); + bool slicingChanged = m_cachedIsSlicingActivated != slicingActivated; + + updateSlicingActive(slicingActivated); + + // Handle GL state setup for FBO buffers and clearing of the render surface + Abstract3DRenderer::render(defaultFboHandle); + + // In slice mode; draw slice and render selection ball + if (m_cachedIsSlicingActivated && m_selectionPointer && m_selectionActive) { + drawSlicedScene(); + m_selectionPointer->render(defaultFboHandle); + } + + // Draw the surface scene + drawScene(defaultFboHandle); + + // Render selection ball if not in slice mode + if (!m_cachedIsSlicingActivated && m_selectionPointer && m_selectionActive) + m_selectionPointer->render(defaultFboHandle); + + // If slicing has been activated by this render pass, we need another render + // Also trigger another render always when slicing changes in general to ensure + // final draw is correct. + if (slicingActivated != m_cachedScene->isSlicingActive() || slicingChanged) + emit needRender(); +} + +void Surface3DRenderer::drawSlicedScene() +{ + QVector3D lightPos; + + // Specify viewport + glViewport(m_sliceViewPort.x(), m_sliceViewPort.y(), + m_sliceViewPort.width(), m_sliceViewPort.height()); + + // Set up projection matrix + QMatrix4x4 projectionMatrix; + + GLfloat aspect = (GLfloat)m_mainViewPort.width() / (GLfloat)m_mainViewPort.height(); + projectionMatrix.ortho(-sliceUnits * aspect, sliceUnits * aspect, + -sliceUnits, sliceUnits, -1.0f, 14.0f); // 14.0 because of zComp + + // Set view matrix + QMatrix4x4 viewMatrix; + viewMatrix.lookAt(QVector3D(0.0f, 0.0f, zComp + 1.0f), + QVector3D(0.0f, 0.0f, zComp), + QVector3D(0.0f, 1.0f, 0.0f)); + + // Set light position + lightPos = m_cachedScene->activeLight()->position(); + + QMatrix4x4 projectionViewMatrix = projectionMatrix * viewMatrix; + + GLfloat scaleX = 0.0f; + GLfloat scaleXBackground = 0.0f; + GLfloat offset = 0.0f; + if (m_cachedSelectionMode == QDataVis::SelectionModeSliceRow) { + scaleX = m_surfaceScaleX; + scaleXBackground = m_scaleXWithBackground; + offset = m_surfaceOffsetX; + } else if (m_cachedSelectionMode == QDataVis::SelectionModeSliceColumn) { + scaleX = m_surfaceScaleZ; + scaleXBackground = m_scaleZWithBackground; + offset = -m_surfaceOffsetZ; + } + + if (m_surfaceObj) { + ShaderHelper *surfaceShader = m_shader; + surfaceShader->bind(); + + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; + QMatrix4x4 itModelMatrix; + + modelMatrix.translate(offset, 0.0f, zComp); + QVector3D scaling(scaleX, 1.0f, sliceZScale); + modelMatrix.scale(scaling); + itModelMatrix.scale(scaling); + + MVPMatrix = projectionViewMatrix * modelMatrix; + + QVector3D color; + if (m_cachedSelectionMode == QDataVis::SelectionModeSliceRow) + color = Utils::vectorFromColor(m_cachedTheme.m_highlightRowColor); + else + color = Utils::vectorFromColor(m_cachedTheme.m_highlightColumnColor); + + // Set shader bindings + surfaceShader->setUniformValue(surfaceShader->lightP(), lightPos); + surfaceShader->setUniformValue(surfaceShader->view(), viewMatrix); + surfaceShader->setUniformValue(surfaceShader->model(), modelMatrix); + surfaceShader->setUniformValue(surfaceShader->nModel(), itModelMatrix.inverted().transposed()); + surfaceShader->setUniformValue(surfaceShader->MVP(), MVPMatrix); + surfaceShader->setUniformValue(surfaceShader->color(), color); + surfaceShader->setUniformValue(surfaceShader->lightS(), 0.25f); + surfaceShader->setUniformValue(surfaceShader->ambientS(), + m_cachedTheme.m_ambientStrength * 2.0f); + + m_drawer->drawObject(surfaceShader, m_sliceSurfaceObj); + + surfaceShader->release(); + + // Draw surface grid + if (m_cachedSurfaceGridOn) { + m_surfaceGridShader->bind(); + + m_surfaceGridShader->setUniformValue(m_surfaceGridShader->color(), + Utils::vectorFromColor(m_cachedTheme.m_gridLine)); + // Draw the grid twice, with slight offset on Y axis to each direction + for (int i = 0; i < 2; i++) { + MVPMatrix.translate(0.0f, surfaceGridYOffset[i], 0.0f); + + m_surfaceGridShader->setUniformValue(m_surfaceGridShader->MVP(), MVPMatrix); + m_drawer->drawSurfaceGrid(m_surfaceGridShader, m_sliceSurfaceObj); + } + m_surfaceGridShader->release(); + } + } + + // Disable textures + glDisable(GL_TEXTURE_2D); + + // lines to the back + if (m_cachedIsGridEnabled && m_heightNormalizer) { + ShaderHelper *lineShader = m_backgroundShader; + // Bind line shader + lineShader->bind(); + + if (m_axisCacheY.segmentCount() > 0) { + QVector3D gridLineScaleX(scaleXBackground, gridLineWidth, gridLineWidth); + + // Set unchanging shader bindings + QVector3D lineColor = Utils::vectorFromColor(m_cachedTheme.m_gridLine); + lineShader->setUniformValue(lineShader->lightP(), lightPos); + lineShader->setUniformValue(lineShader->view(), viewMatrix); + lineShader->setUniformValue(lineShader->color(), lineColor); + lineShader->setUniformValue(lineShader->ambientS(), m_cachedTheme.m_ambientStrength * 2.0f); + lineShader->setUniformValue(lineShader->lightS(), 0.25f); + + // Back wall + GLfloat lineStep = 2.0f * m_axisCacheY.subSegmentStep() / m_heightNormalizer; + GLfloat linePos = -1.0f; + int lastSegment = m_axisCacheY.subSegmentCount() * m_axisCacheY.segmentCount(); + + for (int segment = 0; segment <= lastSegment; segment++) { + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; + QMatrix4x4 itModelMatrix; + + modelMatrix.translate(0.0f, linePos, zComp - sliceZScale); + + modelMatrix.scale(gridLineScaleX); + itModelMatrix.scale(gridLineScaleX); + + MVPMatrix = projectionViewMatrix * modelMatrix; + + // Set the rest of the shader bindings + lineShader->setUniformValue(lineShader->model(), modelMatrix); + lineShader->setUniformValue(lineShader->nModel(), + itModelMatrix.inverted().transposed()); + lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); + + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj); + + linePos += lineStep; + } + } + + // Floor lines + QVector3D gridLineScaleZ(gridLineWidth, gridLineWidth, sliceZScale); + QVector3D gridLineScaleY(gridLineWidth, backgroundMargin, gridLineWidth); + + int lastSegment; + GLfloat lineStep; + GLfloat linePos; + if (m_cachedSelectionMode == QDataVis::SelectionModeSliceRow) { + lineStep = -2.0f * aspectRatio * m_axisCacheX.subSegmentStep() / m_scaleFactor; + lastSegment = m_axisCacheX.subSegmentCount() * m_axisCacheX.segmentCount(); + linePos = m_scaleX; + } else { + lineStep = -2.0f * aspectRatio * m_axisCacheZ.subSegmentStep() / m_scaleFactor; + lastSegment = m_axisCacheZ.subSegmentCount() * m_axisCacheZ.segmentCount(); + linePos = m_scaleZ; + } + + for (int segment = 0; segment <= lastSegment; segment++) { + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; + QMatrix4x4 itModelMatrix; + + modelMatrix.translate(linePos, -backgroundMargin, zComp); + + modelMatrix.scale(gridLineScaleZ); + itModelMatrix.scale(gridLineScaleZ); + + MVPMatrix = projectionViewMatrix * modelMatrix; + + // Set the rest of the shader bindings + lineShader->setUniformValue(lineShader->model(), modelMatrix); + lineShader->setUniformValue(lineShader->nModel(), + itModelMatrix.inverted().transposed()); + lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); + + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj); + + linePos += lineStep; + } + + if (m_cachedSelectionMode == QDataVis::SelectionModeSliceRow) + linePos = m_scaleX; + else + linePos = m_scaleZ; + + for (int segment = 0; segment <= lastSegment; segment++) { + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; + QMatrix4x4 itModelMatrix; + + modelMatrix.translate(linePos, 0.0f, zComp - sliceZScale); + modelMatrix.scale(gridLineScaleY); + itModelMatrix.scale(gridLineScaleY); + + MVPMatrix = projectionViewMatrix * modelMatrix; + + // Set the rest of the shader bindings + lineShader->setUniformValue(lineShader->model(), modelMatrix); + lineShader->setUniformValue(lineShader->nModel(), + itModelMatrix.inverted().transposed()); + lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); + + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj); + + linePos += lineStep; + } + + // Release line shader + lineShader->release(); + } + + // Draw axis labels + m_labelShader->bind(); + glEnable(GL_TEXTURE_2D); + glDisable(GL_DEPTH_TEST); + glCullFace(GL_BACK); + if (m_cachedLabelStyle > QDataVis::LabelStyleOpaque) { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + + // Y Labels to back wall + GLfloat posStep = 2.0f * m_axisCacheY.segmentStep() / m_heightNormalizer; + GLfloat labelPos = -1.0f; + int labelNbr = 0; + + QVector3D positionComp(0.0f, 0.0f, zComp); + QVector3D rotation(0.0f, 0.0f, 0.0f); + QVector3D labelTrans = QVector3D(scaleXBackground + labelMargin, labelPos, zComp); + for (int segment = 0; segment <= m_axisCacheY.segmentCount(); segment++) { + if (m_axisCacheY.labelItems().size() > labelNbr) { + labelTrans.setY(labelPos); + const LabelItem &axisLabelItem = *m_axisCacheY.labelItems().at(labelNbr); + + // Draw the label here + m_dummyRenderItem.setTranslation(labelTrans); + m_drawer->drawLabel(m_dummyRenderItem, axisLabelItem, viewMatrix, projectionMatrix, + positionComp, rotation, 0, m_cachedSelectionMode, m_labelShader, + m_labelObj, m_cachedScene->activeCamera(), + true, true, Drawer::LabelMid, Qt::AlignRight); + } + labelNbr++; + labelPos += posStep; + } + + // X Labels to ground + int countLabelItems; + int lastSegment; + if (m_cachedSelectionMode == QDataVis::SelectionModeSliceRow) { + posStep = 2.0f * aspectRatio * m_axisCacheX.segmentStep() / m_scaleFactor; + labelPos = -m_scaleX; + lastSegment = m_axisCacheX.segmentCount(); + countLabelItems = m_axisCacheX.labelItems().size(); + } else { + posStep = 2.0f * aspectRatio * m_axisCacheZ.segmentStep() / m_scaleFactor; + labelPos = -m_scaleZ; + lastSegment = m_axisCacheZ.segmentCount(); + countLabelItems = m_axisCacheZ.labelItems().size(); + } + + labelNbr = 0; + positionComp.setY(backgroundMargin); + rotation.setZ(-45.0f); + labelTrans.setY(-backgroundMargin); + for (int segment = 0; segment <= lastSegment; segment++) { + if (countLabelItems > labelNbr) { + // Draw the label here + labelTrans.setX(labelPos); + + m_dummyRenderItem.setTranslation(labelTrans); + + LabelItem *axisLabelItem; + if (m_cachedSelectionMode == QDataVis::SelectionModeSliceRow) + axisLabelItem = m_axisCacheX.labelItems().at(labelNbr); + else + axisLabelItem = m_axisCacheZ.labelItems().at(labelNbr); + + m_drawer->drawLabel(m_dummyRenderItem, *axisLabelItem, viewMatrix, projectionMatrix, + positionComp, rotation, 0, QDataVis::SelectionModeSliceRow, + m_labelShader, m_labelObj, m_cachedScene->activeCamera(), + false, false, Drawer::LabelBelow, Qt::AlignTop); + } + labelNbr++; + labelPos += posStep; + } + + glDisable(GL_TEXTURE_2D); + glEnable(GL_DEPTH_TEST); + if (m_cachedLabelStyle > QDataVis::LabelStyleOpaque) + glDisable(GL_BLEND); + + // Release label shader + m_labelShader->release(); +} + +void Surface3DRenderer::drawScene(GLuint defaultFboHandle) +{ + GLfloat backgroundRotation = 0; + uint selectionId = 0; + + // Specify viewport + glViewport(m_mainViewPort.x(), m_mainViewPort.y(), + m_mainViewPort.width(), m_mainViewPort.height()); + + // Set up projection matrix + QMatrix4x4 projectionMatrix; + projectionMatrix.perspective(45.0f, (GLfloat)m_mainViewPort.width() + / (GLfloat)m_mainViewPort.height(), 0.1f, 100.0f); + + // Calculate view matrix + QMatrix4x4 viewMatrix = m_cachedScene->activeCamera()->viewMatrix(); + + QMatrix4x4 projectionViewMatrix = projectionMatrix * 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; + + QVector3D lightPos = m_cachedScene->activeLight()->position(); + + QMatrix4x4 depthViewMatrix; + QMatrix4x4 depthProjectionMatrix; + QMatrix4x4 depthProjectionViewMatrix; + + GLfloat adjustedLightStrength = m_cachedTheme.m_lightStrength / 10.0f; + + QVector3D surfaceScaler(m_surfaceScaleX, 1.0f, m_surfaceScaleZ); + QVector3D surfaceOffset(m_surfaceOffsetX, 0.0f, m_surfaceOffsetZ + zComp); + + // Draw depth buffer +#if !defined(QT_OPENGL_ES_2) + if (m_cachedShadowQuality > QDataVis::ShadowQualityNone && m_surfaceObj) { + // Render scene into a depth texture for using with shadow mapping + // Enable drawing to depth framebuffer + glBindFramebuffer(GL_FRAMEBUFFER, m_depthFrameBuffer); + glClear(GL_DEPTH_BUFFER_BIT); + + // Bind depth shader + m_depthShader->bind(); + + // Set viewport for depth map rendering. Must match texture size. Larger values give smoother shadows. + glViewport(m_mainViewPort.x(), m_mainViewPort.y(), + m_mainViewPort.width() * m_shadowQualityMultiplier, + m_mainViewPort.height() * m_shadowQualityMultiplier); + + // Get the depth view matrix + // It may be possible to hack lightPos here if we want to make some tweaks to shadow + QVector3D depthLightPos = m_cachedScene->activeCamera()->calculatePositionRelativeToCamera( + QVector3D(0.0f, 0.0f, zComp), 0.0f, 1.5f / m_autoScaleAdjustment); + depthViewMatrix.lookAt(depthLightPos, QVector3D(0.0f, 0.0f, zComp), + QVector3D(0.0f, 1.0f, 0.0f)); + + // TODO: Why does depthViewMatrix.column(3).y() goes to zero when we're directly above? + // That causes the scene to be not drawn from above -> must be fixed + // qDebug() << lightPos << depthViewMatrix << depthViewMatrix.column(3); + // Set the depth projection matrix +#ifndef USE_WIDER_SHADOWS + // Use this for perspective shadows + depthProjectionMatrix.perspective(10.0f, (GLfloat)m_mainViewPort.width() + / (GLfloat)m_mainViewPort.height(), 3.0f, 100.0f); +#else + // Use these for orthographic shadows + depthProjectionMatrix.ortho(-2.0f * 2.0f, 2.0f * 2.0f, + -2.0f, 2.0f, + 0.0f, 100.0f); +#endif + depthProjectionViewMatrix = depthProjectionMatrix * depthViewMatrix; + + glCullFace(GL_FRONT); + + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; + + modelMatrix.translate(surfaceOffset); + modelMatrix.scale(surfaceScaler); + + MVPMatrix = depthProjectionViewMatrix * modelMatrix; + + m_depthShader->setUniformValue(m_depthShader->MVP(), MVPMatrix); + + // 1st attribute buffer : vertices + glEnableVertexAttribArray(m_depthShader->posAtt()); + glBindBuffer(GL_ARRAY_BUFFER, m_surfaceObj->vertexBuf()); + glVertexAttribPointer(m_depthShader->posAtt(), 3, 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(), 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 depth framebuffer (= enable drawing to screen) + glBindFramebuffer(GL_FRAMEBUFFER, defaultFboHandle); + + // Release depth shader + m_depthShader->release(); + + // Revert to original viewport + glViewport(m_mainViewPort.x(), m_mainViewPort.y(), + m_mainViewPort.width(), m_mainViewPort.height()); + + // Reset culling to normal + glCullFace(GL_BACK); + +#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 + + bool selectionDirty = false; + + // Enable texturing + glEnable(GL_TEXTURE_2D); + + // Draw selection buffer + if (!m_cachedIsSlicingActivated && m_controller->inputState() == QDataVis::InputStateOnScene + && m_surfaceObj && m_cachedSelectionMode > QDataVis::SelectionModeNone) { + 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(surfaceOffset); + modelMatrix.scale(surfaceScaler); + + MVPMatrix = projectionViewMatrix * modelMatrix; + + m_selectionShader->setUniformValue(m_selectionShader->MVP(), MVPMatrix); + + m_drawer->drawObject(m_selectionShader, m_surfaceObj, m_selectionTexture); + + glEnable(GL_DITHER); + + 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 +#if defined (Q_OS_ANDROID) + selectionId = pixel[0] + pixel[1] * 256 + pixel[2] * 65536; +#else + selectionId = pixel[0] + pixel[1] * 256 + pixel[2] * 65536 + pixel[3] * 16777216; +#endif + + selectionDirty = true; + } + + // Draw the surface + if (m_surfaceObj && m_sampleSpace.width() >= 2 && m_sampleSpace.height() >= 2) { + m_surfaceShader->bind(); + + // For surface we can see climpses from underneath + glDisable(GL_CULL_FACE); + + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; + QMatrix4x4 depthMVPMatrix; + QMatrix4x4 itModelMatrix; + + modelMatrix.translate(surfaceOffset); + modelMatrix.scale(surfaceScaler); + itModelMatrix.scale(surfaceScaler); + +#ifdef SHOW_DEPTH_TEXTURE_SCENE + MVPMatrix = depthProjectionViewMatrix * modelMatrix; +#else + MVPMatrix = projectionViewMatrix * modelMatrix; +#endif + depthMVPMatrix = depthProjectionViewMatrix * 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_surfaceShader->setUniformValue(m_surfaceShader->ambientS(), + m_cachedTheme.m_ambientStrength); + +#if !defined(QT_OPENGL_ES_2) + if (m_cachedShadowQuality > QDataVis::ShadowQualityNone) { + // Set shadow shader bindings + m_surfaceShader->setUniformValue(m_surfaceShader->shadowQ(), m_shadowQualityToShader); + m_surfaceShader->setUniformValue(m_surfaceShader->depth(), depthMVPMatrix); + m_surfaceShader->setUniformValue(m_surfaceShader->lightS(), adjustedLightStrength); + + // Draw the object + m_drawer->drawObject(m_surfaceShader, m_surfaceObj, m_gradientTexture, m_depthTexture); + } else +#endif + { + // Set shadowless shader bindings + m_surfaceShader->setUniformValue(m_surfaceShader->lightS(), + m_cachedTheme.m_lightStrength); + + // Draw the object + m_drawer->drawObject(m_surfaceShader, m_surfaceObj, m_gradientTexture); + } + + m_surfaceShader->release(); + + glEnable(GL_CULL_FACE); + + // Draw surface grid + if (m_cachedSurfaceGridOn) { + m_surfaceGridShader->bind(); + + m_surfaceGridShader->setUniformValue(m_surfaceGridShader->color(), + Utils::vectorFromColor(m_cachedTheme.m_gridLine)); + // Draw the grid twice, with slight offset on Y axis to each direction + for (int i = 0; i < 2; i++) { + MVPMatrix.translate(0.0f, surfaceGridYOffset[i], 0.0f); + + m_surfaceGridShader->setUniformValue(m_surfaceGridShader->MVP(), MVPMatrix); + m_drawer->drawSurfaceGrid(m_surfaceGridShader, m_surfaceObj); + } + m_surfaceGridShader->release(); + } + } + + // Bind background shader + m_backgroundShader->bind(); + glCullFace(GL_BACK); + + // Draw background + if (m_cachedIsBackgroundEnabled && m_backgroundObj) { + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; + QMatrix4x4 depthMVPMatrix; + QMatrix4x4 itModelMatrix; + + modelMatrix.translate(0.0f, 0.0f, zComp); + QVector3D bgScale(m_scaleXWithBackground, backgroundMargin, m_scaleZWithBackground); + modelMatrix.scale(bgScale); + itModelMatrix.scale(bgScale); + + // If we're viewing from below, background object must be flipped + if (m_yFlipped) { + modelMatrix.rotate(180.0f, 1.0, 0.0, 0.0); + modelMatrix.rotate(270.0f - backgroundRotation, 0.0f, 1.0f, 0.0f); + } else { + modelMatrix.rotate(backgroundRotation, 0.0f, 1.0f, 0.0f); + } + +#ifdef SHOW_DEPTH_TEXTURE_SCENE + MVPMatrix = depthProjectionViewMatrix * modelMatrix; +#else + MVPMatrix = projectionViewMatrix * modelMatrix; +#endif + depthMVPMatrix = depthProjectionViewMatrix * 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_cachedShadowQuality > QDataVis::ShadowQualityNone) { + // 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(), + adjustedLightStrength); + + // 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(); + + // Draw grid lines + QVector3D gridLineScaleX(m_scaleXWithBackground, gridLineWidth, gridLineWidth); + QVector3D gridLineScaleZ(gridLineWidth, gridLineWidth, m_scaleZWithBackground); + QVector3D gridLineScaleY(gridLineWidth, backgroundMargin, gridLineWidth); + + if (m_cachedIsGridEnabled && m_heightNormalizer) { + ShaderHelper *lineShader = m_backgroundShader; + // Bind line shader + lineShader->bind(); + + // Set unchanging shader bindings + QVector3D lineColor = Utils::vectorFromColor(m_cachedTheme.m_gridLine); + lineShader->setUniformValue(lineShader->lightP(), lightPos); + lineShader->setUniformValue(lineShader->view(), viewMatrix); + lineShader->setUniformValue(lineShader->color(), lineColor); + lineShader->setUniformValue(lineShader->ambientS(), m_cachedTheme.m_ambientStrength); + + // Rows (= Z) + if (m_axisCacheZ.segmentCount() > 0) { + // Floor lines + GLfloat lineStep = 2.0f * aspectRatio * m_axisCacheZ.subSegmentStep() / m_scaleFactor; + GLfloat linePos = m_scaleZ + zComp; // Start line + int lastSegment = m_axisCacheZ.subSegmentCount() * m_axisCacheZ.segmentCount(); + + for (int segment = 0; segment <= lastSegment; segment++) { + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; + QMatrix4x4 depthMVPMatrix; + QMatrix4x4 itModelMatrix; + + if (m_yFlipped) + modelMatrix.translate(0.0f, backgroundMargin, linePos); + else + modelMatrix.translate(0.0f, -backgroundMargin, linePos); + + modelMatrix.scale(gridLineScaleX); + itModelMatrix.scale(gridLineScaleX); + + // If we're viewing from below, grid line object must be flipped + if (m_yFlipped) + modelMatrix.rotate(180.0f, 1.0, 0.0, 0.0); + + MVPMatrix = projectionViewMatrix * modelMatrix; + depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; + + // Set the rest of the shader bindings + lineShader->setUniformValue(lineShader->model(), modelMatrix); + lineShader->setUniformValue(lineShader->nModel(), + itModelMatrix.inverted().transposed()); + lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); + +#if !defined(QT_OPENGL_ES_2) + if (m_cachedShadowQuality > QDataVis::ShadowQualityNone) { + // Set shadow shader bindings + lineShader->setUniformValue(lineShader->shadowQ(), m_shadowQualityToShader); + lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); + lineShader->setUniformValue(lineShader->lightS(), adjustedLightStrength); + + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); + } else +#endif + { + // Set shadowless shader bindings + lineShader->setUniformValue(lineShader->lightS(), + m_cachedTheme.m_lightStrength); + + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj); + } + linePos -= lineStep; + } + + // Side wall lines + GLfloat lineXTrans = m_scaleXWithBackground; + linePos = m_scaleZ + zComp; // Start line + + if (!m_xFlipped) + lineXTrans = -lineXTrans; + + for (int segment = 0; segment <= lastSegment; segment++) { + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; + QMatrix4x4 depthMVPMatrix; + QMatrix4x4 itModelMatrix; + + modelMatrix.translate(lineXTrans, 0.0f, linePos); + modelMatrix.scale(gridLineScaleY); + itModelMatrix.scale(gridLineScaleY); + + MVPMatrix = projectionViewMatrix * modelMatrix; + depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; + + // Set the rest of the shader bindings + lineShader->setUniformValue(lineShader->model(), modelMatrix); + lineShader->setUniformValue(lineShader->nModel(), + itModelMatrix.inverted().transposed()); + lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); + +#if !defined(QT_OPENGL_ES_2) + if (m_cachedShadowQuality > QDataVis::ShadowQualityNone) { + // Set shadow shader bindings + lineShader->setUniformValue(lineShader->shadowQ(), m_shadowQualityToShader); + lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); + lineShader->setUniformValue(lineShader->lightS(), + adjustedLightStrength); + + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); + } else +#endif + { + // Set shadowless shader bindings + lineShader->setUniformValue(lineShader->lightS(), + m_cachedTheme.m_lightStrength); + + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj); + } + linePos -= lineStep; + } + } + + // Columns (= X) + if (m_axisCacheX.segmentCount() > 0) { + // Floor lines + GLfloat lineStep = -2.0f * aspectRatio * m_axisCacheX.subSegmentStep() / m_scaleFactor; + GLfloat linePos = m_scaleX; + int lastSegment = m_axisCacheX.subSegmentCount() * m_axisCacheX.segmentCount(); + + for (int segment = 0; segment <= lastSegment; segment++) { + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; + QMatrix4x4 depthMVPMatrix; + QMatrix4x4 itModelMatrix; + + if (m_yFlipped) + modelMatrix.translate(linePos, backgroundMargin, zComp); + else + modelMatrix.translate(linePos, -backgroundMargin, zComp); + + modelMatrix.scale(gridLineScaleZ); + itModelMatrix.scale(gridLineScaleZ); + + // If we're viewing from below, grid line object must be flipped + if (m_yFlipped) + modelMatrix.rotate(180.0f, 1.0, 0.0, 0.0); + + MVPMatrix = projectionViewMatrix * modelMatrix; + depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; + + // Set the rest of the shader bindings + lineShader->setUniformValue(lineShader->model(), modelMatrix); + lineShader->setUniformValue(lineShader->nModel(), + itModelMatrix.inverted().transposed()); + lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); + +#if !defined(QT_OPENGL_ES_2) + if (m_cachedShadowQuality > QDataVis::ShadowQualityNone) { + // Set shadow shader bindings + lineShader->setUniformValue(lineShader->shadowQ(), m_shadowQualityToShader); + lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); + lineShader->setUniformValue(lineShader->lightS(), adjustedLightStrength); + + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); + } else +#endif + { + // Set shadowless shader bindings + lineShader->setUniformValue(lineShader->lightS(), + m_cachedTheme.m_lightStrength); + + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj); + } + linePos += lineStep; + } + + // Back wall lines + GLfloat lineZTrans = m_scaleZWithBackground + zComp; + linePos = m_scaleX; + + if (!m_zFlipped) + lineZTrans = -lineZTrans + zComp + zComp; + + for (int segment = 0; segment <= lastSegment; segment++) { + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; + QMatrix4x4 depthMVPMatrix; + QMatrix4x4 itModelMatrix; + + modelMatrix.translate(linePos, 0.0f, lineZTrans); + modelMatrix.scale(gridLineScaleY); + itModelMatrix.scale(gridLineScaleY); + + MVPMatrix = projectionViewMatrix * modelMatrix; + depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; + + // Set the rest of the shader bindings + lineShader->setUniformValue(lineShader->model(), modelMatrix); + lineShader->setUniformValue(lineShader->nModel(), + itModelMatrix.inverted().transposed()); + lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); + +#if !defined(QT_OPENGL_ES_2) + if (m_cachedShadowQuality > QDataVis::ShadowQualityNone) { + // Set shadow shader bindings + lineShader->setUniformValue(lineShader->shadowQ(), m_shadowQualityToShader); + lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); + lineShader->setUniformValue(lineShader->lightS(), adjustedLightStrength); + + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); + } else +#endif + { + // Set shadowless shader bindings + lineShader->setUniformValue(lineShader->lightS(), + m_cachedTheme.m_lightStrength); + + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj); + } + linePos += lineStep; + } + } + + // Horizontal wall lines + if (m_axisCacheY.segmentCount() > 0) { + // Back wall + GLfloat lineStep = 2.0f * m_axisCacheY.subSegmentStep() / m_heightNormalizer; + GLfloat linePos = -1.0f; + int lastSegment = m_axisCacheY.subSegmentCount() * m_axisCacheY.segmentCount(); + + GLfloat lineZTrans = m_scaleZWithBackground + zComp; + + if (!m_zFlipped) + lineZTrans = -lineZTrans + zComp + zComp; + + for (int segment = 0; segment <= lastSegment; segment++) { + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; + QMatrix4x4 depthMVPMatrix; + QMatrix4x4 itModelMatrix; + + modelMatrix.translate(0.0f, linePos, lineZTrans); + + modelMatrix.scale(gridLineScaleX); + itModelMatrix.scale(gridLineScaleX); + + MVPMatrix = projectionViewMatrix * modelMatrix; + depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; + + // Set the rest of the shader bindings + lineShader->setUniformValue(lineShader->model(), modelMatrix); + lineShader->setUniformValue(lineShader->nModel(), + itModelMatrix.inverted().transposed()); + lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); + +#if !defined(QT_OPENGL_ES_2) + if (m_cachedShadowQuality > QDataVis::ShadowQualityNone) { + // Set shadow shader bindings + lineShader->setUniformValue(lineShader->shadowQ(), m_shadowQualityToShader); + lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); + lineShader->setUniformValue(lineShader->lightS(), adjustedLightStrength); + + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); + } else +#endif + { + // Set shadowless shader bindings + lineShader->setUniformValue(lineShader->lightS(), + m_cachedTheme.m_lightStrength); + + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj); + } + linePos += lineStep; + } + + // Side wall + linePos = -1.0f; + lastSegment = m_axisCacheY.subSegmentCount() * m_axisCacheY.segmentCount(); + GLfloat lineXTrans = m_scaleXWithBackground; + + if (!m_xFlipped) + lineXTrans = -lineXTrans; + + for (int segment = 0; segment <= lastSegment; segment++) { + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; + QMatrix4x4 depthMVPMatrix; + QMatrix4x4 itModelMatrix; + + modelMatrix.translate(lineXTrans, linePos, zComp); + + modelMatrix.scale(gridLineScaleZ); + itModelMatrix.scale(gridLineScaleZ); + + MVPMatrix = projectionViewMatrix * modelMatrix; + depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; + + // Set the rest of the shader bindings + lineShader->setUniformValue(lineShader->model(), modelMatrix); + lineShader->setUniformValue(lineShader->nModel(), + itModelMatrix.inverted().transposed()); + lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); + +#if !defined(QT_OPENGL_ES_2) + if (m_cachedShadowQuality > QDataVis::ShadowQualityNone) { + // Set shadow shader bindings + lineShader->setUniformValue(lineShader->shadowQ(), m_shadowQualityToShader); + lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); + lineShader->setUniformValue(lineShader->lightS(), adjustedLightStrength); + + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); + } else +#endif + { + // Set shadowless shader bindings + lineShader->setUniformValue(lineShader->lightS(), + m_cachedTheme.m_lightStrength); + + // Draw the object + m_drawer->drawObject(lineShader, m_gridLineObj); + } + linePos += lineStep; + } + } + + // Release line shader + lineShader->release(); + } + + // Draw axis labels + m_labelShader->bind(); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + // Z Labels + QVector3D positionZComp(0.0f, 0.0f, zComp); + if (m_axisCacheZ.segmentCount() > 0) { + GLfloat posStep = 2.0f * aspectRatio * m_axisCacheZ.segmentStep() / m_scaleFactor; + GLfloat labelPos = m_scaleZ + zComp; + int lastSegment = m_axisCacheZ.segmentCount(); + int labelNbr = 0; + GLfloat labelXTrans = m_scaleXWithBackground + labelMargin; + GLfloat labelYTrans = -backgroundMargin; + GLfloat rotLabelX = -90.0f; + GLfloat rotLabelY = 0.0f; + GLfloat rotLabelZ = 0.0f; + Qt::AlignmentFlag alignment = Qt::AlignRight; + if (m_zFlipped) + rotLabelY = 180.0f; + if (m_xFlipped) { + labelXTrans = -labelXTrans; + alignment = Qt::AlignLeft; + } + if (m_yFlipped) { + rotLabelZ += 180.0f; + rotLabelY += 180.0f; + labelYTrans = -labelYTrans; + } + QVector3D labelTrans = QVector3D(labelXTrans, + labelYTrans, + labelPos); + QVector3D rotation(rotLabelX, rotLabelY, rotLabelZ); + + for (int segment = 0; segment <= lastSegment; segment++) { + if (m_axisCacheZ.labelItems().size() > labelNbr) { + labelTrans.setZ(labelPos); + + // Draw the label here + m_dummyRenderItem.setTranslation(labelTrans); + const LabelItem &axisLabelItem = *m_axisCacheZ.labelItems().at(labelNbr); + + m_drawer->drawLabel(m_dummyRenderItem, axisLabelItem, viewMatrix, projectionMatrix, + positionZComp, rotation, 0, m_cachedSelectionMode, + m_labelShader, m_labelObj, m_cachedScene->activeCamera(), + true, true, Drawer::LabelMid, alignment); + } + labelNbr++; + labelPos -= posStep; + } + } + // X Labels + if (m_axisCacheX.segmentCount() > 0) { + GLfloat posStep = 2.0f * aspectRatio * m_axisCacheX.segmentStep() / m_scaleFactor; + GLfloat labelPos = -m_scaleX; + int lastSegment = m_axisCacheX.segmentCount(); + + int labelNbr = 0; + GLfloat labelZTrans = m_scaleZWithBackground + labelMargin; + GLfloat labelYTrans = -backgroundMargin; + GLfloat rotLabelX = -90.0f; + GLfloat rotLabelY = 90.0f; + GLfloat rotLabelZ = 0.0f; + Qt::AlignmentFlag alignment = Qt::AlignLeft; + if (m_xFlipped) + rotLabelY = -90.0f; + if (m_zFlipped) { + labelZTrans = -labelZTrans; + alignment = Qt::AlignRight; + } + if (m_yFlipped) { + rotLabelZ += 180.0f; + rotLabelY += 180.0f; + labelYTrans = -labelYTrans; + } + QVector3D labelTrans = QVector3D(labelPos, + labelYTrans, + labelZTrans + zComp); + QVector3D rotation(rotLabelX, rotLabelY, rotLabelZ); + + for (int segment = 0; segment <= lastSegment; segment++) { + if (m_axisCacheX.labelItems().size() > labelNbr) { + // Draw the label here + labelTrans.setX(labelPos); + m_dummyRenderItem.setTranslation(labelTrans); + const LabelItem &axisLabelItem = *m_axisCacheX.labelItems().at(labelNbr); + + m_drawer->drawLabel(m_dummyRenderItem, axisLabelItem, viewMatrix, projectionMatrix, + positionZComp, rotation, 0, m_cachedSelectionMode, + m_labelShader, m_labelObj, m_cachedScene->activeCamera(), + true, true, Drawer::LabelMid, alignment); + } + labelNbr++; + labelPos += posStep; + } + } + // Y Labels + if (m_axisCacheY.segmentCount() > 0) { + GLfloat posStep = 2.0f * m_axisCacheY.segmentStep() / m_heightNormalizer; + GLfloat labelPos = -1.0f; + int labelNbr = 0; + GLfloat labelXTrans = m_scaleXWithBackground; + GLfloat labelZTrans = m_scaleZWithBackground; + + 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; + } + + // Back wall + QVector3D rotation(rotLabelX, rotLabelY, rotLabelZ); + + for (int segment = 0; segment <= m_axisCacheY.segmentCount(); segment++) { + if (m_axisCacheY.labelItems().size() > labelNbr) { + const LabelItem &axisLabelItem = *m_axisCacheY.labelItems().at(labelNbr); + + // Side wall + QVector3D labelTrans = QVector3D(labelXTrans, labelPos, + labelZTrans + labelMarginZTrans + zComp); + if (m_xFlipped) + rotation.setY(-90.0f); + else + rotation.setY(90.0f); + if (m_zFlipped) + alignment = Qt::AlignRight; + else + alignment = Qt::AlignLeft; + + // Draw the label here + m_dummyRenderItem.setTranslation(labelTrans); + m_drawer->drawLabel(m_dummyRenderItem, axisLabelItem, viewMatrix, projectionMatrix, + positionZComp, rotation, 0, m_cachedSelectionMode, + m_labelShader, m_labelObj, m_cachedScene->activeCamera(), + true, true, Drawer::LabelMid, alignment); + + // Back wall + if (m_xFlipped) + alignment = Qt::AlignLeft; + else + alignment = Qt::AlignRight; + if (m_zFlipped) + rotation.setY(180.0f); + else + rotation.setY(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, + positionZComp, rotation, 0, m_cachedSelectionMode, + m_labelShader, m_labelObj, m_cachedScene->activeCamera(), + true, true, Drawer::LabelMid, alignment); + } + labelNbr++; + labelPos += posStep; + } + } + + glDisable(GL_TEXTURE_2D); + + glDisable(GL_BLEND); + + // Release label shader + m_labelShader->release(); + + // Selection handling + if (m_selectionModeChanged || selectionDirty) { + if (selectionDirty) + m_cachedSelectionId = selectionId; + if (m_cachedSelectionMode == QDataVis::SelectionModeNone) { + m_cachedSelectionId = 0; + m_selectionActive = false; + } + if (m_cachedSelectionMode == QDataVis::SelectionModeItem) { + if (m_cachedSelectionId) + surfacePointSelected(m_cachedSelectionId); + else + m_selectionActive = false; + } + if (m_cachedSelectionMode == QDataVis::SelectionModeSliceRow + || m_cachedSelectionMode == QDataVis::SelectionModeSliceColumn) { + if (m_cachedSelectionId) { + updateSliceDataModel(m_cachedSelectionId); + m_cachedScene->setSlicingActive(true); + + surfacePointSelected(m_cachedSelectionId); + + emit needRender(); + } + } + + m_selectionModeChanged = false; + } + if (m_controller->inputState() == QDataVis::InputStateOnOverview) { + if (m_cachedIsSlicingActivated) { + m_cachedScene->setSlicingActive(false); + m_selectionActive = false; + m_cachedSelectionId = 0; + } + } +} + +void Surface3DRenderer::updateSurfaceGradient(const QLinearGradient &gradient) +{ + QImage image(QSize(1, 1000), QImage::Format_RGB32); + QPainter pmp(&image); + pmp.setBrush(QBrush(gradient)); + pmp.setPen(Qt::NoPen); + pmp.drawRect(0, 0, 1, 1000); + + if (m_gradientTexture) { + m_textureHelper->deleteTexture(&m_gradientTexture); + m_gradientTexture = 0; + } + + m_gradientTexture = m_textureHelper->create2DTexture(image, false, 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 + 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++; + } + + // 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 + QImage image = QImage(bits, idImageWidth, idImageHeight, QImage::Format_RGB32); + m_selectionTexture = m_textureHelper->create2DTexture(image, false, false, false); + + // 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() +{ + updateSurfaceGradient(m_cachedTheme.m_surfaceGradient); +} + +void Surface3DRenderer::calculateSceneScalingFactors() +{ + // Calculate scene scaling and translation factors + m_heightNormalizer = GLfloat(m_axisCacheY.max() - m_axisCacheY.min()); + m_areaSize.setHeight(m_axisCacheZ.max() - m_axisCacheZ.min()); + m_areaSize.setWidth(m_axisCacheX.max() - m_axisCacheX.min()); + m_scaleFactor = qMax(m_areaSize.width(), m_areaSize.height()); +#ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z + m_scaleX = aspectRatio * m_areaSize.width() / m_scaleFactor; + m_scaleZ = aspectRatio * m_areaSize.height() / m_scaleFactor; + m_scaleXWithBackground = m_scaleX * backgroundMargin; + m_scaleZWithBackground = m_scaleZ * backgroundMargin; +#else // ..and this if we want uniform scaling based on largest dimension + m_scaleX = aspectRatio / m_scaleFactor; + m_scaleZ = aspectRatio / m_scaleFactor; + m_scaleXWithBackground = aspectRatio * backgroundMargin; + m_scaleZWithBackground = aspectRatio * backgroundMargin; +#endif +} + +bool Surface3DRenderer::updateSmoothStatus(bool enable) +{ + if (!enable && !m_flatSupported) { + qWarning() << "Warning: Flat qualifier not supported on your platform's GLSL language." + " Requires at least GLSL version 1.5."; + enable = true; + } + + bool changed = false; + if (enable != m_cachedSmoothSurface) { + m_cachedSmoothSurface = enable; + changed = true; + initSurfaceShaders(); + } + + // If no surface object created yet, don't try to update the object + if (m_surfaceObj && changed && m_sampleSpace.width() >= 2 && m_sampleSpace.height() >= 2) { + if (m_cachedSmoothSurface) { + m_surfaceObj->setUpSmoothData(m_dataArray, m_sampleSpace, m_heightNormalizer, + m_axisCacheY.min(), true); + } else { + m_surfaceObj->setUpData(m_dataArray, m_sampleSpace, m_heightNormalizer, + m_axisCacheY.min(), true); + } + } + + return m_cachedSmoothSurface; +} + +void Surface3DRenderer::updateSelectionMode(QDataVis::SelectionMode mode) +{ + if (mode != m_cachedSelectionMode) + m_selectionModeChanged = true; + + Abstract3DRenderer::updateSelectionMode(mode); +} + +void Surface3DRenderer::updateSurfaceGridStatus(bool enable) +{ + m_cachedSurfaceGridOn = enable; +} + +void Surface3DRenderer::loadBackgroundMesh() +{ + if (m_backgroundObj) + delete m_backgroundObj; + m_backgroundObj = new ObjectHelper(QStringLiteral(":/defaultMeshes/background")); + m_backgroundObj->load(); +} + +void Surface3DRenderer::loadSurfaceObj() +{ + if (m_surfaceObj) + delete m_surfaceObj; + m_surfaceObj = new SurfaceObject(); +} + +void Surface3DRenderer::loadSliceSurfaceObj() +{ + if (m_sliceSurfaceObj) + delete m_sliceSurfaceObj; + m_sliceSurfaceObj = new SurfaceObject(); +} + +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; + + // Set view port + if (m_cachedIsSlicingActivated) { + m_mainViewPort = QRect(0, + m_cachedBoundingRect.height() - m_cachedBoundingRect.height() / subViewDivider, + m_cachedBoundingRect.width() / subViewDivider, + m_cachedBoundingRect.height() / subViewDivider); + } else { + m_mainViewPort = QRect(0, 0, m_cachedBoundingRect.width(), m_cachedBoundingRect.height()); + } + m_sliceViewPort = QRect(0, 0, m_cachedBoundingRect.width(), m_cachedBoundingRect.height()); + + if (m_selectionPointer) { + if (m_cachedIsSlicingActivated) + m_selectionPointer->updateBoundingRect(m_sliceViewPort); + else + m_selectionPointer->updateBoundingRect(m_mainViewPort); + } + + Abstract3DRenderer::handleResize(); +} + +void Surface3DRenderer::surfacePointSelected(int id) +{ + int column = (id - 1) % m_sampleSpace.width(); + int row = (id - 1) / m_sampleSpace.width(); + + if (row < 0 || column < 0 || m_dataArray.size() < row || m_dataArray.at(row)->size() < column) + return; + + qreal value = qreal(m_dataArray.at(row)->at(column).y()); + + if (!m_selectionPointer) + m_selectionPointer = new SelectionPointer(m_drawer); + + QVector3D pos; + if (m_cachedSelectionMode == QDataVis::SelectionModeSliceRow) { + pos = m_sliceSurfaceObj->vertexAt(column, 0); + pos *= QVector3D(m_surfaceScaleX, 1.0f, 0.0f); + pos += QVector3D(m_surfaceOffsetX, 0.0f, 0.0f); + m_selectionPointer->updateBoundingRect(m_sliceViewPort); + m_selectionPointer->updateSliceData(true, m_autoScaleAdjustment); + } else if (m_cachedSelectionMode == QDataVis::SelectionModeSliceColumn) { + pos = m_sliceSurfaceObj->vertexAt(row, 0); + pos *= QVector3D(m_surfaceScaleZ, 1.0f, 0.0f); + pos += QVector3D(-m_surfaceOffsetZ, 0.0f, 0.0f); + m_selectionPointer->updateBoundingRect(m_sliceViewPort); + m_selectionPointer->updateSliceData(true, m_autoScaleAdjustment); + } else { + pos = m_surfaceObj->vertexAt(column, row); + pos *= QVector3D(m_surfaceScaleX, 1.0f, m_surfaceScaleZ);; + pos += QVector3D(m_surfaceOffsetX, 0.0f, m_surfaceOffsetZ); + m_selectionPointer->updateBoundingRect(m_mainViewPort); + m_selectionPointer->updateSliceData(false, m_autoScaleAdjustment); + } + + m_selectionPointer->setPosition(pos); + m_selectionPointer->setLabel(createSelectionLabel(value, column, row)); + m_selectionPointer->updateScene(m_cachedScene); + + //Put the selection pointer flag active + m_selectionActive = true; +} + +QString Surface3DRenderer::createSelectionLabel(qreal value, int column, int row) +{ + QString labelText = itemLabelFormat(); + static const QString xTitleTag(QStringLiteral("@xTitle")); + static const QString yTitleTag(QStringLiteral("@yTitle")); + static const QString zTitleTag(QStringLiteral("@zTitle")); + static const QString xLabelTag(QStringLiteral("@xLabel")); + static const QString yLabelTag(QStringLiteral("@yLabel")); + static const QString zLabelTag(QStringLiteral("@zLabel")); + + labelText.replace(xTitleTag, m_axisCacheX.title()); + labelText.replace(yTitleTag, m_axisCacheY.title()); + labelText.replace(zTitleTag, m_axisCacheZ.title()); + + if (labelText.contains(xLabelTag)) { + QString labelFormat = m_axisCacheX.labelFormat(); + if (labelFormat.isEmpty()) + labelFormat = Utils::defaultLabelFormat(); + QString valueLabelText = generateValueLabel(labelFormat, + m_dataArray.at(row)->at(column).x()); + labelText.replace(xLabelTag, valueLabelText); + } + if (labelText.contains(yLabelTag)) { + QString labelFormat = m_axisCacheY.labelFormat(); + if (labelFormat.isEmpty()) + labelFormat = Utils::defaultLabelFormat(); + QString valueLabelText = generateValueLabel(labelFormat, value); + labelText.replace(yLabelTag, valueLabelText); + } + if (labelText.contains(zLabelTag)) { + QString labelFormat = m_axisCacheZ.labelFormat(); + if (labelFormat.isEmpty()) + labelFormat = Utils::defaultLabelFormat(); + QString valueLabelText = generateValueLabel(labelFormat, + m_dataArray.at(row)->at(column).z()); + labelText.replace(zLabelTag, valueLabelText); + } + + return labelText; +} + +void Surface3DRenderer::loadMeshFile() +{ + qDebug() << __FUNCTION__ << "should we do something"; +} + +void Surface3DRenderer::updateShadowQuality(QDataVis::ShadowQuality quality) +{ + qWarning() << "Shadows have been disabled for Q3DSurface in technology preview"; + m_cachedShadowQuality = QDataVis::ShadowQualityNone; //quality; + switch (quality) { + case QDataVis::ShadowQualityLow: + m_shadowQualityToShader = 33.3f; + m_shadowQualityMultiplier = 1; + break; + case QDataVis::ShadowQualityMedium: + m_shadowQualityToShader = 100.0f; + m_shadowQualityMultiplier = 3; + break; + case QDataVis::ShadowQualityHigh: + m_shadowQualityToShader = 200.0f; + m_shadowQualityMultiplier = 5; + break; + case QDataVis::ShadowQualitySoftLow: + m_shadowQualityToShader = 5.0f; + m_shadowQualityMultiplier = 1; + break; + case QDataVis::ShadowQualitySoftMedium: + m_shadowQualityToShader = 10.0f; + m_shadowQualityMultiplier = 3; + break; + case QDataVis::ShadowQualitySoftHigh: + m_shadowQualityToShader = 15.0f; + m_shadowQualityMultiplier = 4; + break; + default: + m_shadowQualityToShader = 0.0f; + m_shadowQualityMultiplier = 1; + break; + } + +#if !defined(QT_OPENGL_ES_2) + updateDepthBuffer(); +#endif +} + +void Surface3DRenderer::updateSlicingActive(bool isSlicing) +{ + if (isSlicing == m_cachedIsSlicingActivated) + return; + + m_cachedIsSlicingActivated = isSlicing; + if (isSlicing) { + m_mainViewPort = QRect(0, m_cachedBoundingRect.height() - m_cachedBoundingRect.height() / subViewDivider, + m_cachedBoundingRect.width() / subViewDivider, m_cachedBoundingRect.height() / subViewDivider); + if (m_depthTexture) { + m_textureHelper->deleteTexture(&m_depthTexture); + m_depthTexture = 0; + } + } else { + m_mainViewPort = QRect(0, 0, this->m_cachedBoundingRect.width(), + this->m_cachedBoundingRect.height()); + initSelectionBuffer(); // We need to re-init selection buffer in case there has been a resize +#if !defined(QT_OPENGL_ES_2) + updateDepthBuffer(); // Re-init depth buffer as well +#endif + } +} + +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 !defined(QT_OPENGL_ES_2) + 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")); + } +#else + m_surfaceShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurface"), + QStringLiteral(":/shaders/fragmentSurfaceES2")); +#endif + 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(); +} + +#if !defined(QT_OPENGL_ES_2) +void Surface3DRenderer::initDepthShader() +{ + // TODO: Implement a depth shader for surface after technology preview + if (m_depthShader) + delete m_depthShader; + m_depthShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexDepth"), + QStringLiteral(":/shaders/fragmentDepth")); + m_depthShader->initialize(); +} + +void Surface3DRenderer::updateDepthBuffer() +{ + if (m_depthTexture) { + m_textureHelper->deleteTexture(&m_depthTexture); + m_depthTexture = 0; + } + + if (m_cachedShadowQuality > QDataVis::ShadowQualityNone) { + m_depthTexture = m_textureHelper->createDepthTexture(m_mainViewPort.size(), + m_depthFrameBuffer, + m_shadowQualityMultiplier); + if (!m_depthTexture) { + switch (m_cachedShadowQuality) { + case QDataVis::ShadowQualityHigh: + qWarning("Creating high quality shadows failed. Changing to medium quality."); + (void)m_controller->setShadowQuality(QDataVis::ShadowQualityMedium); + updateShadowQuality(QDataVis::ShadowQualityMedium); + break; + case QDataVis::ShadowQualityMedium: + qWarning("Creating medium quality shadows failed. Changing to low quality."); + (void)m_controller->setShadowQuality(QDataVis::ShadowQualityLow); + updateShadowQuality(QDataVis::ShadowQualityLow); + break; + case QDataVis::ShadowQualityLow: + qWarning("Creating low quality shadows failed. Switching shadows off."); + (void)m_controller->setShadowQuality(QDataVis::ShadowQualityNone); + updateShadowQuality(QDataVis::ShadowQualityNone); + break; + case QDataVis::ShadowQualitySoftHigh: + qWarning("Creating soft high quality shadows failed. Changing to soft medium quality."); + (void)m_controller->setShadowQuality(QDataVis::ShadowQualitySoftMedium); + updateShadowQuality(QDataVis::ShadowQualitySoftMedium); + break; + case QDataVis::ShadowQualitySoftMedium: + qWarning("Creating soft medium quality shadows failed. Changing to soft low quality."); + (void)m_controller->setShadowQuality(QDataVis::ShadowQualitySoftLow); + updateShadowQuality(QDataVis::ShadowQualitySoftLow); + break; + case QDataVis::ShadowQualitySoftLow: + qWarning("Creating soft low quality shadows failed. Switching shadows off."); + (void)m_controller->setShadowQuality(QDataVis::ShadowQualityNone); + updateShadowQuality(QDataVis::ShadowQualityNone); + break; + default: + // You'll never get here + break; + } + } + } +} +#endif + +QT_DATAVISUALIZATION_END_NAMESPACE |