Revision control
++ // Copyright (C) 2016 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
++
++ #undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
++
++ #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 <QtGui/qquaternion.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
++
++ 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;
++
++ for (auto 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)) {
++ for (auto 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 QList<Scatter3DController::ChangeItem> &items)
++ {
++ ScatterSeriesRenderCache *cache = 0;
++ const QScatter3DSeries *prevSeries = 0;
++ const QScatterDataArray *dataArray = 0;
++ const bool optimizationStatic = m_cachedOptimizationHint.testFlag(
++ QAbstract3DGraph::OptimizationStatic);
++
++ for (auto 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 = false;
++ 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) {
++ for (auto 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 !QT_CONFIG(opengles2)
++ 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
++ for (auto 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;
++ for (auto 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 !QT_CONFIG(opengles2)
++ 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;
++
++ for (auto 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 !QT_CONFIG(opengles2)
++ 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 !QT_CONFIG(opengles2)
++ 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;
++ QList<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
++ for (auto 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