/****************************************************************************** ** ** Copyright (C) 2015 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Data Visualization module. ** ** $QT_BEGIN_LICENSE:COMM$ ** ** 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 http://www.qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** $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 // 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() { fixContextBeforeDelete(); 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); } 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::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(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(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 &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(seriesList[i]); if (scatterSeries->isVisible()) { QAbstract3DSeriesChangeBitField &changeTracker = scatterSeries->d_ptr->m_changeTracker; ScatterSeriesRenderCache *cache = static_cast(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(seriesList[i]); if (scatterSeries->isVisible()) { ScatterSeriesRenderCache *cache = static_cast(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 &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(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(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(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(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(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::AlignmentFlag 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 &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 = Qt::AlignmentFlag(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(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(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