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