diff options
Diffstat (limited to 'src/datavisualization/engine/abstract3drenderer.cpp')
-rw-r--r-- | src/datavisualization/engine/abstract3drenderer.cpp | 1242 |
1 files changed, 1061 insertions, 181 deletions
diff --git a/src/datavisualization/engine/abstract3drenderer.cpp b/src/datavisualization/engine/abstract3drenderer.cpp index 04ede782..cfc691af 100644 --- a/src/datavisualization/engine/abstract3drenderer.cpp +++ b/src/datavisualization/engine/abstract3drenderer.cpp @@ -24,9 +24,24 @@ #include "shaderhelper_p.h" #include "qcustom3ditem_p.h" #include "qcustom3dlabel_p.h" +#include "qcustom3dvolume_p.h" +#include "scatter3drenderer_p.h" + +#include <QtCore/qmath.h> +#include <QtGui/QWindow> +#include <QtCore/QThread> QT_BEGIN_NAMESPACE_DATAVISUALIZATION +// Defined in shaderhelper.cpp +extern void discardDebugMsgs(QtMsgType type, const QMessageLogContext &context, const QString &msg); + +const qreal doublePi(M_PI * 2.0); +const int polarGridRoundness(64); +const qreal polarGridAngle(doublePi / qreal(polarGridRoundness)); +const float polarGridAngleDegrees(float(360.0 / qreal(polarGridRoundness))); +const qreal polarGridHalfAngle(polarGridAngle / 2.0); + Abstract3DRenderer::Abstract3DRenderer(Abstract3DController *controller) : QObject(0), m_hasNegativeValues(false), @@ -37,26 +52,83 @@ Abstract3DRenderer::Abstract3DRenderer(Abstract3DController *controller) m_cachedSelectionMode(QAbstract3DGraph::SelectionNone), m_cachedOptimizationHint(QAbstract3DGraph::OptimizationDefault), m_textureHelper(0), + m_depthTexture(0), m_cachedScene(new Q3DScene()), m_selectionDirty(true), m_selectionState(SelectNone), m_devicePixelRatio(1.0f), m_selectionLabelDirty(true), - m_clickPending(false), + m_clickResolved(false), + m_graphPositionQueryPending(false), + m_graphPositionQueryResolved(false), m_clickedSeries(0), m_clickedType(QAbstract3DGraph::ElementNone), + m_selectedLabelIndex(-1), + m_selectedCustomItemIndex(-1), m_selectionLabelItem(0), m_visibleSeriesCount(0), m_customItemShader(0), + m_volumeTextureShader(0), + m_volumeTextureLowDefShader(0), + m_volumeTextureSliceShader(0), + m_volumeSliceFrameShader(0), + m_labelShader(0), + m_cursorPositionShader(0), + m_cursorPositionFrameBuffer(0), + m_cursorPositionTexture(0), m_useOrthoProjection(false), m_xFlipped(false), m_yFlipped(false), m_zFlipped(false), + m_yFlippedForGrid(false), m_backgroundObj(0), m_gridLineObj(0), m_labelObj(0), - m_graphAspectRatio(2.0f) + m_positionMapperObj(0), + m_graphAspectRatio(2.0f), + m_graphHorizontalAspectRatio(0.0f), + m_polarGraph(false), + m_radialLabelOffset(1.0f), + m_polarRadius(2.0f), + m_xRightAngleRotation(QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, 90.0f)), + m_yRightAngleRotation(QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, 90.0f)), + m_zRightAngleRotation(QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, 90.0f)), + m_xRightAngleRotationNeg(QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, -90.0f)), + m_yRightAngleRotationNeg(QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, -90.0f)), + m_zRightAngleRotationNeg(QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, -90.0f)), + m_xFlipRotation(QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, -180.0f)), + m_zFlipRotation(QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, -180.0f)), + m_requestedMargin(-1.0f), + m_vBackgroundMargin(0.1f), + m_hBackgroundMargin(0.1f), + m_scaleXWithBackground(0.0f), + m_scaleYWithBackground(0.0f), + m_scaleZWithBackground(0.0f), + m_oldCameraTarget(QVector3D(2000.0f, 2000.0f, 2000.0f)), // Just random invalid target + m_reflectionEnabled(false), + m_reflectivity(0.5), +#if !defined(QT_OPENGL_ES_2) + m_funcs_2_1(0), +#endif + m_context(0), + m_dummySurfaceAtDelete(0), + m_isOpenGLES(true) + { + initializeOpenGLFunctions(); + m_isOpenGLES = Utils::isOpenGLES(); +#if !defined(QT_OPENGL_ES_2) + if (!m_isOpenGLES) { + // Discard warnings about deprecated functions + QtMessageHandler handler = qInstallMessageHandler(discardDebugMsgs); + + m_funcs_2_1 = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_2_1>(); + m_funcs_2_1->initializeOpenGLFunctions(); + + // Restore original message handler + qInstallMessageHandler(handler); + } +#endif QObject::connect(m_drawer, &Drawer::drawerChanged, this, &Abstract3DRenderer::updateTextures); QObject::connect(this, &Abstract3DRenderer::needRender, controller, &Abstract3DController::needRender, Qt::QueuedConnection); @@ -71,6 +143,12 @@ Abstract3DRenderer::~Abstract3DRenderer() delete m_cachedTheme; delete m_selectionLabelItem; delete m_customItemShader; + delete m_volumeTextureShader; + delete m_volumeTextureLowDefShader; + delete m_volumeSliceFrameShader; + delete m_volumeTextureSliceShader; + delete m_labelShader; + delete m_cursorPositionShader; foreach (SeriesRenderCache *cache, m_renderCacheList) { cache->cleanup(m_textureHelper); @@ -88,12 +166,29 @@ Abstract3DRenderer::~Abstract3DRenderer() ObjectHelper::releaseObjectHelper(this, m_backgroundObj); ObjectHelper::releaseObjectHelper(this, m_gridLineObj); ObjectHelper::releaseObjectHelper(this, m_labelObj); + ObjectHelper::releaseObjectHelper(this, m_positionMapperObj); + + if (m_textureHelper) { + m_textureHelper->deleteTexture(&m_depthTexture); + m_textureHelper->deleteTexture(&m_cursorPositionTexture); + + if (QOpenGLContext::currentContext()) + m_textureHelper->glDeleteFramebuffers(1, &m_cursorPositionFrameBuffer); + + delete m_textureHelper; + } + + m_axisCacheX.clearLabels(); + m_axisCacheY.clearLabels(); + m_axisCacheZ.clearLabels(); - delete m_textureHelper; + restoreContextAfterDelete(); } void Abstract3DRenderer::initializeOpenGL() { + m_context = QOpenGLContext::currentContext(); + // Set OpenGL features glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); @@ -101,9 +196,11 @@ void Abstract3DRenderer::initializeOpenGL() glCullFace(GL_BACK); #if !defined(QT_OPENGL_ES_2) - glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); - glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); - glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST); + if (!m_isOpenGLES) { + glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); + glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); + glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST); + } #endif m_textureHelper = new TextureHelper(); @@ -112,6 +209,15 @@ void Abstract3DRenderer::initializeOpenGL() axisCacheForOrientation(QAbstract3DAxis::AxisOrientationX).setDrawer(m_drawer); axisCacheForOrientation(QAbstract3DAxis::AxisOrientationY).setDrawer(m_drawer); axisCacheForOrientation(QAbstract3DAxis::AxisOrientationZ).setDrawer(m_drawer); + + initLabelShaders(QStringLiteral(":/shaders/vertexLabel"), + QStringLiteral(":/shaders/fragmentLabel")); + + initCursorPositionShaders(QStringLiteral(":/shaders/vertexPosition"), + QStringLiteral(":/shaders/fragmentPositionMap")); + + loadLabelMesh(); + loadPositionMapperMesh(); } void Abstract3DRenderer::render(const GLuint defaultFboHandle) @@ -137,7 +243,7 @@ void Abstract3DRenderer::render(const GLuint defaultFboHandle) glEnable(GL_SCISSOR_TEST); QVector4D clearColor = Utils::vectorFromColor(m_cachedTheme->windowColor()); glClearColor(clearColor.x(), clearColor.y(), clearColor.z(), 1.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); glDisable(GL_SCISSOR_TEST); } @@ -146,28 +252,87 @@ void Abstract3DRenderer::updateSelectionState(SelectionState state) m_selectionState = state; } -void Abstract3DRenderer::updateInputPosition(const QPoint &position) +void Abstract3DRenderer::initGradientShaders(const QString &vertexShader, + const QString &fragmentShader) { - m_inputPosition = position; + // Do nothing by default + Q_UNUSED(vertexShader) + Q_UNUSED(fragmentShader) } -void Abstract3DRenderer::initGradientShaders(const QString &vertexShader, - const QString &fragmentShader) +void Abstract3DRenderer::initStaticSelectedItemShaders(const QString &vertexShader, + const QString &fragmentShader, + const QString &gradientVertexShader, + const QString &gradientFragmentShader) { // Do nothing by default Q_UNUSED(vertexShader) Q_UNUSED(fragmentShader) + Q_UNUSED(gradientVertexShader) + Q_UNUSED(gradientFragmentShader) } void Abstract3DRenderer::initCustomItemShaders(const QString &vertexShader, const QString &fragmentShader) { - if (m_customItemShader) - delete m_customItemShader; + delete m_customItemShader; m_customItemShader = new ShaderHelper(this, vertexShader, fragmentShader); m_customItemShader->initialize(); } +void Abstract3DRenderer::initVolumeTextureShaders(const QString &vertexShader, + const QString &fragmentShader, + const QString &fragmentLowDefShader, + const QString &sliceShader, + const QString &sliceFrameVertexShader, + const QString &sliceFrameShader) +{ + + delete m_volumeTextureShader; + m_volumeTextureShader = new ShaderHelper(this, vertexShader, fragmentShader); + m_volumeTextureShader->initialize(); + + delete m_volumeTextureLowDefShader; + m_volumeTextureLowDefShader = new ShaderHelper(this, vertexShader, fragmentLowDefShader); + m_volumeTextureLowDefShader->initialize(); + + delete m_volumeTextureSliceShader; + m_volumeTextureSliceShader = new ShaderHelper(this, vertexShader, sliceShader); + m_volumeTextureSliceShader->initialize(); + + delete m_volumeSliceFrameShader; + m_volumeSliceFrameShader = new ShaderHelper(this, sliceFrameVertexShader, sliceFrameShader); + m_volumeSliceFrameShader->initialize(); +} + +void Abstract3DRenderer::initLabelShaders(const QString &vertexShader, const QString &fragmentShader) +{ + delete m_labelShader; + m_labelShader = new ShaderHelper(this, vertexShader, fragmentShader); + m_labelShader->initialize(); +} + +void Abstract3DRenderer::initCursorPositionShaders(const QString &vertexShader, + const QString &fragmentShader) +{ + // Init the shader + delete m_cursorPositionShader; + m_cursorPositionShader = new ShaderHelper(this, vertexShader, fragmentShader); + m_cursorPositionShader->initialize(); +} + +void Abstract3DRenderer::initCursorPositionBuffer() +{ + m_textureHelper->deleteTexture(&m_cursorPositionTexture); + + if (m_primarySubViewport.size().isEmpty()) + return; + + m_cursorPositionTexture = + m_textureHelper->createCursorPositionTexture(m_primarySubViewport.size(), + m_cursorPositionFrameBuffer); +} + void Abstract3DRenderer::updateTheme(Q3DTheme *theme) { // Synchronize the controller theme with renderer @@ -193,24 +358,22 @@ void Abstract3DRenderer::updateScene(Q3DScene *scene) handleResize(); } - scene->activeCamera()->d_ptr->updateViewMatrix(m_autoScaleAdjustment); - // Set light position (rotate light with activeCamera, a bit above it (as set in defaultLightPos)) - scene->d_ptr->setLightPositionRelativeToCamera(defaultLightPos); - QPoint logicalPixelPosition = scene->selectionQueryPosition(); - updateInputPosition(QPoint(logicalPixelPosition.x() * m_devicePixelRatio, - logicalPixelPosition.y() * m_devicePixelRatio)); + m_inputPosition = QPoint(logicalPixelPosition.x() * m_devicePixelRatio, + logicalPixelPosition.y() * m_devicePixelRatio); + + QPoint logicalGraphPosition = scene->graphPositionQuery(); + m_graphPositionQuery = QPoint(logicalGraphPosition.x() * m_devicePixelRatio, + logicalGraphPosition.y() * m_devicePixelRatio); // Synchronize the renderer scene to controller scene scene->d_ptr->sync(*m_cachedScene->d_ptr); + updateCameraViewport(); + if (Q3DScene::invalidSelectionPoint() == logicalPixelPosition) { updateSelectionState(SelectNone); } else { - // Selections are one-shot, reset selection active to false before processing - scene->setSelectionQueryPosition(Q3DScene::invalidSelectionPoint()); - m_clickPending = true; - if (scene->isSlicingActive()) { if (scene->isPointInPrimarySubView(logicalPixelPosition)) updateSelectionState(SelectOnOverview); @@ -222,53 +385,109 @@ void Abstract3DRenderer::updateScene(Q3DScene *scene) updateSelectionState(SelectOnScene); } } + + if (Q3DScene::invalidSelectionPoint() != logicalGraphPosition) + m_graphPositionQueryPending = true; + + // Queue up another render when we have a query that needs resolving. + // This is needed because QtQuick scene graph can sometimes do a sync without following it up + // with a render. + if (m_graphPositionQueryPending || m_selectionState != SelectNone) + emit needRender(); +} + +void Abstract3DRenderer::updateTextures() +{ + m_axisCacheX.updateTextures(); + m_axisCacheY.updateTextures(); + m_axisCacheZ.updateTextures(); } void Abstract3DRenderer::reInitShaders() { -#if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { - initGradientShaders(QStringLiteral(":/shaders/vertexShadow"), - QStringLiteral(":/shaders/fragmentShadowNoTexColorOnY")); - initShaders(QStringLiteral(":/shaders/vertexShadow"), - QStringLiteral(":/shaders/fragmentShadowNoTex")); - initBackgroundShaders(QStringLiteral(":/shaders/vertexShadow"), - QStringLiteral(":/shaders/fragmentShadowNoTex")); - initCustomItemShaders(QStringLiteral(":/shaders/vertexShadow"), - QStringLiteral(":/shaders/fragmentShadow")); - } else { - initGradientShaders(QStringLiteral(":/shaders/vertex"), - QStringLiteral(":/shaders/fragmentColorOnY")); - initShaders(QStringLiteral(":/shaders/vertex"), - QStringLiteral(":/shaders/fragment")); + if (!m_isOpenGLES) { + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + if (m_cachedOptimizationHint.testFlag(QAbstract3DGraph::OptimizationStatic) + && qobject_cast<Scatter3DRenderer *>(this)) { + initGradientShaders(QStringLiteral(":/shaders/vertexShadow"), + QStringLiteral(":/shaders/fragmentShadow")); + initStaticSelectedItemShaders(QStringLiteral(":/shaders/vertexShadow"), + QStringLiteral(":/shaders/fragmentShadowNoTex"), + QStringLiteral(":/shaders/vertexShadow"), + QStringLiteral(":/shaders/fragmentShadowNoTexColorOnY")); + initShaders(QStringLiteral(":/shaders/vertexShadowNoMatrices"), + QStringLiteral(":/shaders/fragmentShadowNoTex")); + } else { + initGradientShaders(QStringLiteral(":/shaders/vertexShadow"), + QStringLiteral(":/shaders/fragmentShadowNoTexColorOnY")); + initShaders(QStringLiteral(":/shaders/vertexShadow"), + QStringLiteral(":/shaders/fragmentShadowNoTex")); + } + initBackgroundShaders(QStringLiteral(":/shaders/vertexShadow"), + QStringLiteral(":/shaders/fragmentShadowNoTex")); + initCustomItemShaders(QStringLiteral(":/shaders/vertexShadow"), + QStringLiteral(":/shaders/fragmentShadow")); + } else { + if (m_cachedOptimizationHint.testFlag(QAbstract3DGraph::OptimizationStatic) + && qobject_cast<Scatter3DRenderer *>(this)) { + initGradientShaders(QStringLiteral(":/shaders/vertexTexture"), + QStringLiteral(":/shaders/fragmentTexture")); + initStaticSelectedItemShaders(QStringLiteral(":/shaders/vertex"), + QStringLiteral(":/shaders/fragment"), + QStringLiteral(":/shaders/vertex"), + QStringLiteral(":/shaders/fragmentColorOnY")); + initShaders(QStringLiteral(":/shaders/vertexNoMatrices"), + QStringLiteral(":/shaders/fragment")); + } else { + initGradientShaders(QStringLiteral(":/shaders/vertex"), + QStringLiteral(":/shaders/fragmentColorOnY")); + initShaders(QStringLiteral(":/shaders/vertex"), + QStringLiteral(":/shaders/fragment")); + } + initBackgroundShaders(QStringLiteral(":/shaders/vertex"), + QStringLiteral(":/shaders/fragment")); + initCustomItemShaders(QStringLiteral(":/shaders/vertexTexture"), + QStringLiteral(":/shaders/fragmentTexture")); + } + initVolumeTextureShaders(QStringLiteral(":/shaders/vertexTexture3D"), + QStringLiteral(":/shaders/fragmentTexture3D"), + QStringLiteral(":/shaders/fragmentTexture3DLowDef"), + QStringLiteral(":/shaders/fragmentTexture3DSlice"), + QStringLiteral(":/shaders/vertexPosition"), + QStringLiteral(":/shaders/fragment3DSliceFrames")); + } else { + if (m_cachedOptimizationHint.testFlag(QAbstract3DGraph::OptimizationStatic) + && qobject_cast<Scatter3DRenderer *>(this)) { + initGradientShaders(QStringLiteral(":/shaders/vertexTexture"), + QStringLiteral(":/shaders/fragmentTextureES2")); + initStaticSelectedItemShaders(QStringLiteral(":/shaders/vertex"), + QStringLiteral(":/shaders/fragmentES2"), + QStringLiteral(":/shaders/vertex"), + QStringLiteral(":/shaders/fragmentColorOnYES2")); + initBackgroundShaders(QStringLiteral(":/shaders/vertexNoMatrices"), + QStringLiteral(":/shaders/fragmentES2")); + } else { + initGradientShaders(QStringLiteral(":/shaders/vertex"), + QStringLiteral(":/shaders/fragmentColorOnYES2")); + initShaders(QStringLiteral(":/shaders/vertex"), + QStringLiteral(":/shaders/fragmentES2")); + } initBackgroundShaders(QStringLiteral(":/shaders/vertex"), - QStringLiteral(":/shaders/fragment")); + QStringLiteral(":/shaders/fragmentES2")); initCustomItemShaders(QStringLiteral(":/shaders/vertexTexture"), - QStringLiteral(":/shaders/fragmentTexture")); + QStringLiteral(":/shaders/fragmentTextureES2")); } -#else - initGradientShaders(QStringLiteral(":/shaders/vertex"), - QStringLiteral(":/shaders/fragmentColorOnYES2")); - initShaders(QStringLiteral(":/shaders/vertex"), - QStringLiteral(":/shaders/fragmentES2")); - initBackgroundShaders(QStringLiteral(":/shaders/vertex"), - QStringLiteral(":/shaders/fragmentES2")); - initCustomItemShaders(QStringLiteral(":/shaders/vertexTexture"), - QStringLiteral(":/shaders/fragmentTextureES2")); -#endif } void Abstract3DRenderer::handleShadowQualityChange() { reInitShaders(); -#if defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality != QAbstract3DGraph::ShadowQualityNone) { + if (m_isOpenGLES && m_cachedShadowQuality != QAbstract3DGraph::ShadowQualityNone) { emit requestShadowQuality(QAbstract3DGraph::ShadowQualityNone); qWarning("Shadows are not yet supported for OpenGL ES2"); m_cachedShadowQuality = QAbstract3DGraph::ShadowQualityNone; } -#endif } void Abstract3DRenderer::updateSelectionMode(QAbstract3DGraph::SelectionFlags mode) @@ -280,16 +499,39 @@ void Abstract3DRenderer::updateSelectionMode(QAbstract3DGraph::SelectionFlags mo void Abstract3DRenderer::updateAspectRatio(float ratio) { m_graphAspectRatio = ratio; - calculateZoomLevel(); - m_cachedScene->activeCamera()->d_ptr->updateViewMatrix(m_autoScaleAdjustment); foreach (SeriesRenderCache *cache, m_renderCacheList) cache->setDataDirty(true); - updateCustomItemPositions(); +} + +void Abstract3DRenderer::updateHorizontalAspectRatio(float ratio) +{ + m_graphHorizontalAspectRatio = ratio; + foreach (SeriesRenderCache *cache, m_renderCacheList) + cache->setDataDirty(true); +} + +void Abstract3DRenderer::updatePolar(bool enable) +{ + m_polarGraph = enable; + foreach (SeriesRenderCache *cache, m_renderCacheList) + cache->setDataDirty(true); +} + +void Abstract3DRenderer::updateRadialLabelOffset(float offset) +{ + m_radialLabelOffset = offset; +} + +void Abstract3DRenderer::updateMargin(float margin) +{ + m_requestedMargin = margin; } void Abstract3DRenderer::updateOptimizationHint(QAbstract3DGraph::OptimizationHints hint) { m_cachedOptimizationHint = hint; + foreach (SeriesRenderCache *cache, m_renderCacheList) + cache->setDataDirty(true); } void Abstract3DRenderer::handleResize() @@ -303,10 +545,10 @@ void Abstract3DRenderer::handleResize() // Re-init selection buffer initSelectionBuffer(); -#if !defined(QT_OPENGL_ES_2) // Re-init depth buffer updateDepthBuffer(); -#endif + + initCursorPositionBuffer(); } void Abstract3DRenderer::calculateZoomLevel() @@ -315,9 +557,9 @@ void Abstract3DRenderer::calculateZoomLevel() GLfloat div; GLfloat zoomAdjustment; div = qMin(m_primarySubViewport.width(), m_primarySubViewport.height()); - zoomAdjustment = 2.0f * defaultRatio + zoomAdjustment = defaultRatio * ((m_primarySubViewport.width() / div) - / (m_primarySubViewport.height() / div)) / m_graphAspectRatio; + / (m_primarySubViewport.height() / div)); m_autoScaleAdjustment = qMin(zoomAdjustment, 1.0f); // clamp to 1.0f } @@ -348,8 +590,6 @@ void Abstract3DRenderer::updateAxisRange(QAbstract3DAxis::AxisOrientation orient foreach (SeriesRenderCache *cache, m_renderCacheList) cache->setDataDirty(true); - - updateCustomItemPositions(); } void Abstract3DRenderer::updateAxisSegmentCount(QAbstract3DAxis::AxisOrientation orientation, @@ -378,8 +618,6 @@ void Abstract3DRenderer::updateAxisReversed(QAbstract3DAxis::AxisOrientation ori axisCacheForOrientation(orientation).setReversed(enable); foreach (SeriesRenderCache *cache, m_renderCacheList) cache->setDataDirty(true); - - updateCustomItemPositions(); } void Abstract3DRenderer::updateAxisFormatter(QAbstract3DAxis::AxisOrientation orientation, @@ -396,8 +634,6 @@ void Abstract3DRenderer::updateAxisFormatter(QAbstract3DAxis::AxisOrientation or foreach (SeriesRenderCache *cache, m_renderCacheList) cache->setDataDirty(true); - - updateCustomItemPositions(); } void Abstract3DRenderer::updateAxisLabelAutoRotation(QAbstract3DAxis::AxisOrientation orientation, @@ -607,10 +843,9 @@ void Abstract3DRenderer::drawAxisTitleY(const QVector3D &sideLabelRotation, QQuaternion titleRotation; if (m_axisCacheY.isTitleFixed()) { titleRotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, yRotation) - * QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, 90.0f); + * m_zRightAngleRotation; } else { - titleRotation = totalRotation - * QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, 90.0f); + titleRotation = totalRotation * m_zRightAngleRotation; } dummyItem.setTranslation(titleTrans + titleOffsetVector); @@ -628,17 +863,22 @@ void Abstract3DRenderer::drawAxisTitleX(const QVector3D &labelRotation, float labelsMaxWidth, const QMatrix4x4 &viewMatrix, const QMatrix4x4 &projectionMatrix, - ShaderHelper *shader) + ShaderHelper *shader, + bool radial) { float scaleFactor = m_drawer->scaledFontSize() / m_axisCacheX.titleItem().size().height(); - float titleOffset = 2.0f * (labelMargin + (labelsMaxWidth * scaleFactor)); + float titleOffset; + if (radial) + titleOffset = -2.0f * (labelMargin + m_drawer->scaledFontSize()); + else + titleOffset = 2.0f * (labelMargin + (labelsMaxWidth * scaleFactor)); float zRotation = 0.0f; float yRotation = 0.0f; float xRotation = -90.0f + labelRotation.z(); float offsetRotation = labelRotation.z(); float extraRotation = -90.0f; Qt::AlignmentFlag alignment = Qt::AlignTop; - if (m_yFlipped) { + if (m_yFlippedForGrid) { alignment = Qt::AlignBottom; zRotation = 180.0f; if (m_zFlipped) { @@ -678,6 +918,17 @@ void Abstract3DRenderer::drawAxisTitleX(const QVector3D &labelRotation, } } + if (radial) { + if (m_zFlipped) { + titleOffset = -titleOffset; + } else { + if (m_yFlippedForGrid) + alignment = Qt::AlignTop; + else + alignment = Qt::AlignBottom; + } + } + if (offsetRotation == 180.0f || offsetRotation == -180.0f) offsetRotation = 0.0f; QQuaternion offsetRotator = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, offsetRotation); @@ -718,7 +969,7 @@ void Abstract3DRenderer::drawAxisTitleZ(const QVector3D &labelRotation, float xRotation = -90.0f; float extraRotation = 90.0f; Qt::AlignmentFlag alignment = Qt::AlignTop; - if (m_yFlipped) { + if (m_yFlippedForGrid) { alignment = Qt::AlignBottom; xRotation = -xRotation; if (m_zFlipped) { @@ -793,6 +1044,11 @@ void Abstract3DRenderer::loadLabelMesh() QStringLiteral(":/defaultMeshes/plane")); } +void Abstract3DRenderer::loadPositionMapperMesh() +{ + ObjectHelper::resetObjectHelper(this, m_positionMapperObj, + QStringLiteral(":/defaultMeshes/barFull")); +} void Abstract3DRenderer::generateBaseColorTexture(const QColor &color, GLuint *texture) { @@ -846,11 +1102,16 @@ CustomRenderItem *Abstract3DRenderer::addCustomItem(QCustom3DItem *item) newItem->setRenderer(this); newItem->setItemPointer(item); // Store pointer for render item updates newItem->setMesh(item->meshFile()); - QVector3D scaling = item->scaling(); + newItem->setOrigPosition(item->position()); + newItem->setOrigScaling(item->scaling()); + newItem->setScalingAbsolute(item->isScalingAbsolute()); + newItem->setPositionAbsolute(item->isPositionAbsolute()); QImage textureImage = item->d_ptr->textureImage(); bool facingCamera = false; + GLuint texture = 0; if (item->d_ptr->m_isLabelItem) { QCustom3DLabel *labelItem = static_cast<QCustom3DLabel *>(item); + newItem->setLabelItem(true); float pointSize = labelItem->font().pointSizeF(); // Check do we have custom visuals or need to use theme if (!labelItem->dptr()->m_customVisuals) { @@ -864,22 +1125,52 @@ CustomRenderItem *Abstract3DRenderer::addCustomItem(QCustom3DItem *item) } // Calculate scaling based on text (texture size), font size and asked scaling float scaledFontSize = (0.05f + pointSize / 500.0f) / float(textureImage.height()); + QVector3D scaling = newItem->origScaling(); scaling.setX(scaling.x() * textureImage.width() * scaledFontSize); scaling.setY(scaling.y() * textureImage.height() * scaledFontSize); + newItem->setOrigScaling(scaling); // Check if facing camera facingCamera = labelItem->isFacingCamera(); + } else if (item->d_ptr->m_isVolumeItem && !m_isOpenGLES) { + QCustom3DVolume *volumeItem = static_cast<QCustom3DVolume *>(item); + newItem->setTextureWidth(volumeItem->textureWidth()); + newItem->setTextureHeight(volumeItem->textureHeight()); + newItem->setTextureDepth(volumeItem->textureDepth()); + if (volumeItem->textureFormat() == QImage::Format_Indexed8) + newItem->setColorTable(volumeItem->colorTable()); + newItem->setTextureFormat(volumeItem->textureFormat()); + newItem->setVolume(true); + newItem->setBlendNeeded(true); + texture = m_textureHelper->create3DTexture(volumeItem->textureData(), + volumeItem->textureWidth(), + volumeItem->textureHeight(), + volumeItem->textureDepth(), + volumeItem->textureFormat()); + newItem->setSliceIndexX(volumeItem->sliceIndexX()); + newItem->setSliceIndexY(volumeItem->sliceIndexY()); + newItem->setSliceIndexZ(volumeItem->sliceIndexZ()); + newItem->setAlphaMultiplier(volumeItem->alphaMultiplier()); + newItem->setPreserveOpacity(volumeItem->preserveOpacity()); + newItem->setUseHighDefShader(volumeItem->useHighDefShader()); + + newItem->setDrawSlices(volumeItem->drawSlices()); + newItem->setDrawSliceFrames(volumeItem->drawSliceFrames()); + newItem->setSliceFrameColor(volumeItem->sliceFrameColor()); + newItem->setSliceFrameWidths(volumeItem->sliceFrameWidths()); + newItem->setSliceFrameGaps(volumeItem->sliceFrameGaps()); + newItem->setSliceFrameThicknesses(volumeItem->sliceFrameThicknesses()); } - newItem->setScaling(scaling); + recalculateCustomItemScalingAndPos(newItem); newItem->setRotation(item->rotation()); - newItem->setPosition(item->position()); - newItem->setPositionAbsolute(item->isPositionAbsolute()); - newItem->setBlendNeeded(textureImage.hasAlphaChannel()); - GLuint texture = m_textureHelper->create2DTexture(textureImage, true, true, true); + + // In OpenGL ES we simply draw volumes as regular custom item placeholders. + if (!item->d_ptr->m_isVolumeItem || m_isOpenGLES) + { + newItem->setBlendNeeded(textureImage.hasAlphaChannel()); + texture = m_textureHelper->create2DTexture(textureImage, true, true, true); + } newItem->setTexture(texture); item->d_ptr->clearTextureImage(); - QVector3D translation = convertPositionToTranslation(item->position(), - item->isPositionAbsolute()); - newItem->setTranslation(translation); newItem->setVisible(item->isVisible()); newItem->setShadowCasting(item->isShadowCasting()); newItem->setFacingCamera(facingCamera); @@ -887,6 +1178,74 @@ CustomRenderItem *Abstract3DRenderer::addCustomItem(QCustom3DItem *item) return newItem; } +void Abstract3DRenderer::recalculateCustomItemScalingAndPos(CustomRenderItem *item) +{ + if (!m_polarGraph && !item->isLabel() && !item->isScalingAbsolute() + && !item->isPositionAbsolute()) { + QVector3D scale = item->origScaling() / 2.0f; + QVector3D pos = item->origPosition(); + QVector3D minBounds(pos.x() - scale.x(), + pos.y() - scale.y(), + pos.z() + scale.z()); + QVector3D maxBounds(pos.x() + scale.x(), + pos.y() + scale.y(), + pos.z() - scale.z()); + QVector3D minCorner = convertPositionToTranslation(minBounds, false); + QVector3D maxCorner = convertPositionToTranslation(maxBounds, false); + scale = QVector3D(qAbs(maxCorner.x() - minCorner.x()), + qAbs(maxCorner.y() - minCorner.y()), + qAbs(maxCorner.z() - minCorner.z())) / 2.0f; + if (item->isVolume()) { + // Only volume items need to scale and reposition according to bounds + QVector3D minBoundsNormal = minCorner; + QVector3D maxBoundsNormal = maxCorner; + // getVisibleItemBounds returns bounds normalized for fragment shader [-1,1] + // Y and Z are also flipped. + getVisibleItemBounds(minBoundsNormal, maxBoundsNormal); + item->setMinBounds(minBoundsNormal); + item->setMaxBounds(maxBoundsNormal); + // For scaling calculations, we want [0,1] normalized values + minBoundsNormal = item->minBoundsNormal(); + maxBoundsNormal = item->maxBoundsNormal(); + + // Rescale and reposition the item so that it doesn't go over the edges + QVector3D adjScaling = + QVector3D(scale.x() * (maxBoundsNormal.x() - minBoundsNormal.x()), + scale.y() * (maxBoundsNormal.y() - minBoundsNormal.y()), + scale.z() * (maxBoundsNormal.z() - minBoundsNormal.z())); + + item->setScaling(adjScaling); + + QVector3D adjPos = item->origPosition(); + QVector3D dataExtents = QVector3D(maxBounds.x() - minBounds.x(), + maxBounds.y() - minBounds.y(), + maxBounds.z() - minBounds.z()) / 2.0f; + adjPos.setX(adjPos.x() + (dataExtents.x() * minBoundsNormal.x()) + - (dataExtents.x() * (1.0f - maxBoundsNormal.x()))); + adjPos.setY(adjPos.y() + (dataExtents.y() * minBoundsNormal.y()) + - (dataExtents.y() * (1.0f - maxBoundsNormal.y()))); + adjPos.setZ(adjPos.z() + (dataExtents.z() * minBoundsNormal.z()) + - (dataExtents.z() * (1.0f - maxBoundsNormal.z()))); + item->setPosition(adjPos); + } else { + // Only scale for non-volume items, and do not readjust position + item->setScaling(scale); + item->setPosition(item->origPosition()); + } + } else { + item->setScaling(item->origScaling()); + item->setPosition(item->origPosition()); + if (item->isVolume()) { + // Y and Z need to be flipped as shader flips those axes + item->setMinBounds(QVector3D(-1.0f, 1.0f, 1.0f)); + item->setMaxBounds(QVector3D(1.0f, -1.0f, -1.0f)); + } + } + QVector3D translation = convertPositionToTranslation(item->position(), + item->isPositionAbsolute()); + item->setTranslation(translation); +} + void Abstract3DRenderer::updateCustomItem(CustomRenderItem *renderItem) { QCustom3DItem *item = renderItem->itemPointer(); @@ -894,8 +1253,17 @@ void Abstract3DRenderer::updateCustomItem(CustomRenderItem *renderItem) renderItem->setMesh(item->meshFile()); item->d_ptr->m_dirtyBits.meshDirty = false; } + if (item->d_ptr->m_dirtyBits.positionDirty) { + renderItem->setOrigPosition(item->position()); + renderItem->setPositionAbsolute(item->isPositionAbsolute()); + if (!item->d_ptr->m_dirtyBits.scalingDirty) + recalculateCustomItemScalingAndPos(renderItem); + item->d_ptr->m_dirtyBits.positionDirty = false; + } if (item->d_ptr->m_dirtyBits.scalingDirty) { QVector3D scaling = item->scaling(); + renderItem->setOrigScaling(scaling); + renderItem->setScalingAbsolute(item->isScalingAbsolute()); // In case we have label item, we need to recreate texture for scaling adjustment if (item->d_ptr->m_isLabelItem) { QCustom3DLabel *labelItem = static_cast<QCustom3DLabel *>(item); @@ -918,8 +1286,9 @@ void Abstract3DRenderer::updateCustomItem(CustomRenderItem *renderItem) scaling.setX(scaling.x() * textureImage.width() * scaledFontSize); scaling.setY(scaling.y() * textureImage.height() * scaledFontSize); item->d_ptr->clearTextureImage(); + renderItem->setOrigScaling(scaling); } - renderItem->setScaling(scaling); + recalculateCustomItemScalingAndPos(renderItem); item->d_ptr->m_dirtyBits.scalingDirty = false; } if (item->d_ptr->m_dirtyBits.rotationDirty) { @@ -939,24 +1308,16 @@ void Abstract3DRenderer::updateCustomItem(CustomRenderItem *renderItem) m_cachedTheme->isLabelBorderEnabled()); textureImage = item->d_ptr->textureImage(); } + } else if (!item->d_ptr->m_isVolumeItem || m_isOpenGLES) { + renderItem->setBlendNeeded(textureImage.hasAlphaChannel()); + GLuint oldTexture = renderItem->texture(); + m_textureHelper->deleteTexture(&oldTexture); + GLuint texture = m_textureHelper->create2DTexture(textureImage, true, true, true); + renderItem->setTexture(texture); } - renderItem->setBlendNeeded(textureImage.hasAlphaChannel()); - GLuint oldTexture = renderItem->texture(); - m_textureHelper->deleteTexture(&oldTexture); - GLuint texture = m_textureHelper->create2DTexture(textureImage, true, true, true); - renderItem->setTexture(texture); item->d_ptr->clearTextureImage(); item->d_ptr->m_dirtyBits.textureDirty = false; } - if (item->d_ptr->m_dirtyBits.positionDirty || item->d_ptr->m_dirtyBits.positionAbsoluteDirty) { - renderItem->setPosition(item->position()); - renderItem->setPositionAbsolute(item->isPositionAbsolute()); - QVector3D translation = convertPositionToTranslation(item->position(), - item->isPositionAbsolute()); - renderItem->setTranslation(translation); - item->d_ptr->m_dirtyBits.positionDirty = false; - item->d_ptr->m_dirtyBits.positionAbsoluteDirty = false; - } if (item->d_ptr->m_dirtyBits.visibleDirty) { renderItem->setVisible(item->isVisible()); item->d_ptr->m_dirtyBits.visibleDirty = false; @@ -971,130 +1332,649 @@ void Abstract3DRenderer::updateCustomItem(CustomRenderItem *renderItem) renderItem->setFacingCamera(labelItem->isFacingCamera()); labelItem->dptr()->m_facingCameraDirty = false; } + } else if (item->d_ptr->m_isVolumeItem && !m_isOpenGLES) { + QCustom3DVolume *volumeItem = static_cast<QCustom3DVolume *>(item); + if (volumeItem->dptr()->m_dirtyBitsVolume.colorTableDirty) { + renderItem->setColorTable(volumeItem->colorTable()); + volumeItem->dptr()->m_dirtyBitsVolume.colorTableDirty = false; + } + if (volumeItem->dptr()->m_dirtyBitsVolume.textureDimensionsDirty + || volumeItem->dptr()->m_dirtyBitsVolume.textureDataDirty + || volumeItem->dptr()->m_dirtyBitsVolume.textureFormatDirty) { + GLuint oldTexture = renderItem->texture(); + m_textureHelper->deleteTexture(&oldTexture); + GLuint texture = m_textureHelper->create3DTexture(volumeItem->textureData(), + volumeItem->textureWidth(), + volumeItem->textureHeight(), + volumeItem->textureDepth(), + volumeItem->textureFormat()); + renderItem->setTexture(texture); + renderItem->setTextureWidth(volumeItem->textureWidth()); + renderItem->setTextureHeight(volumeItem->textureHeight()); + renderItem->setTextureDepth(volumeItem->textureDepth()); + renderItem->setTextureFormat(volumeItem->textureFormat()); + volumeItem->dptr()->m_dirtyBitsVolume.textureDimensionsDirty = false; + volumeItem->dptr()->m_dirtyBitsVolume.textureDataDirty = false; + volumeItem->dptr()->m_dirtyBitsVolume.textureFormatDirty = false; + } + if (volumeItem->dptr()->m_dirtyBitsVolume.slicesDirty) { + renderItem->setDrawSlices(volumeItem->drawSlices()); + renderItem->setDrawSliceFrames(volumeItem->drawSliceFrames()); + renderItem->setSliceFrameColor(volumeItem->sliceFrameColor()); + renderItem->setSliceFrameWidths(volumeItem->sliceFrameWidths()); + renderItem->setSliceFrameGaps(volumeItem->sliceFrameGaps()); + renderItem->setSliceFrameThicknesses(volumeItem->sliceFrameThicknesses()); + renderItem->setSliceIndexX(volumeItem->sliceIndexX()); + renderItem->setSliceIndexY(volumeItem->sliceIndexY()); + renderItem->setSliceIndexZ(volumeItem->sliceIndexZ()); + volumeItem->dptr()->m_dirtyBitsVolume.slicesDirty = false; + } + if (volumeItem->dptr()->m_dirtyBitsVolume.alphaDirty) { + renderItem->setAlphaMultiplier(volumeItem->alphaMultiplier()); + renderItem->setPreserveOpacity(volumeItem->preserveOpacity()); + volumeItem->dptr()->m_dirtyBitsVolume.alphaDirty = false; + } + if (volumeItem->dptr()->m_dirtyBitsVolume.shaderDirty) { + renderItem->setUseHighDefShader(volumeItem->useHighDefShader()); + volumeItem->dptr()->m_dirtyBitsVolume.shaderDirty = false; + } } } void Abstract3DRenderer::updateCustomItemPositions() { - foreach (CustomRenderItem *renderItem, m_customRenderCache) { - QVector3D translation = convertPositionToTranslation(renderItem->position(), - renderItem->isPositionAbsolute()); - renderItem->setTranslation(translation); - } + foreach (CustomRenderItem *renderItem, m_customRenderCache) + recalculateCustomItemScalingAndPos(renderItem); } void Abstract3DRenderer::drawCustomItems(RenderingState state, - ShaderHelper *shader, + ShaderHelper *regularShader, const QMatrix4x4 &viewMatrix, const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &depthProjectionViewMatrix, GLuint depthTexture, - GLfloat shadowQuality) + GLfloat shadowQuality, + GLfloat reflection) { if (m_customRenderCache.isEmpty()) return; + ShaderHelper *shader = regularShader; + shader->bind(); + if (RenderingNormal == state) { - shader->bind(); shader->setUniformValue(shader->lightP(), m_cachedScene->activeLight()->position()); shader->setUniformValue(shader->ambientS(), m_cachedTheme->ambientLightStrength()); shader->setUniformValue(shader->lightColor(), Utils::vectorFromColor(m_cachedTheme->lightColor())); shader->setUniformValue(shader->view(), viewMatrix); - - glEnable(GL_TEXTURE_2D); } - // Draw custom items - foreach (CustomRenderItem *item, m_customRenderCache) { - // Check that the render item is visible, and skip drawing if not - if (!item->isVisible()) - continue; - - // Check if the render item is in data coordinates and not within axis ranges, and skip drawing if it is - if (!item->isPositionAbsolute() - && (item->position().x() < m_axisCacheX.min() - || item->position().x() > m_axisCacheX.max() - || item->position().z() < m_axisCacheZ.min() - || item->position().z() > m_axisCacheZ.max() - || item->position().y() < m_axisCacheY.min() - || item->position().y() > m_axisCacheY.max())) { - continue; - } + // Draw custom items - first regular and then volumes + bool volumeDetected = false; + int loopCount = 0; + while (loopCount < 2) { + foreach (CustomRenderItem *item, m_customRenderCache) { + // Check that the render item is visible, and skip drawing if not + // Also check if reflected item is on the "wrong" side, and skip drawing if it is + if (!item->isVisible() || ((m_reflectionEnabled && reflection < 0.0f) + && (m_yFlipped == (item->translation().y() >= 0.0)))) { + continue; + } + if (loopCount == 0) { + if (item->isVolume()) { + volumeDetected = true; + continue; + } + } else { + if (!item->isVolume()) + continue; + } - QMatrix4x4 modelMatrix; - QMatrix4x4 itModelMatrix; - QMatrix4x4 MVPMatrix; - - QQuaternion rotation = item->rotation(); - // Check if the (label) item should be facing camera, and adjust rotation accordingly - if (item->isFacingCamera()) { - float camRotationX = m_cachedScene->activeCamera()->xRotation(); - float camRotationY = m_cachedScene->activeCamera()->yRotation(); - rotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, -camRotationX) - * QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, -camRotationY); + // If the render item is in data coordinates and not within axis ranges, skip it + if (!item->isPositionAbsolute() + && (item->position().x() < m_axisCacheX.min() + || item->position().x() > m_axisCacheX.max() + || item->position().z() < m_axisCacheZ.min() + || item->position().z() > m_axisCacheZ.max() + || item->position().y() < m_axisCacheY.min() + || item->position().y() > m_axisCacheY.max())) { + continue; + } + + QMatrix4x4 modelMatrix; + QMatrix4x4 itModelMatrix; + QMatrix4x4 MVPMatrix; + + QQuaternion rotation = item->rotation(); + // Check if the (label) item should be facing camera, and adjust rotation accordingly + if (item->isFacingCamera()) { + float camRotationX = m_cachedScene->activeCamera()->xRotation(); + float camRotationY = m_cachedScene->activeCamera()->yRotation(); + rotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, -camRotationX) + * QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, -camRotationY); + } + + if (m_reflectionEnabled) { + if (reflection < 0.0f) { + if (item->itemPointer()->d_ptr->m_isLabelItem) + continue; + else + glCullFace(GL_FRONT); + } else { + glCullFace(GL_BACK); + } + QVector3D trans = item->translation(); + trans.setY(reflection * trans.y()); + modelMatrix.translate(trans); + if (reflection < 0.0f) { + QQuaternion mirror = QQuaternion(rotation.scalar(), + -rotation.x(), rotation.y(), -rotation.z()); + modelMatrix.rotate(mirror); + itModelMatrix.rotate(mirror); + } else { + modelMatrix.rotate(rotation); + itModelMatrix.rotate(rotation); + } + QVector3D scale = item->scaling(); + scale.setY(reflection * scale.y()); + modelMatrix.scale(scale); + } else { + modelMatrix.translate(item->translation()); + modelMatrix.rotate(rotation); + modelMatrix.scale(item->scaling()); + itModelMatrix.rotate(rotation); + } + if (!item->isFacingCamera()) + itModelMatrix.scale(item->scaling()); + MVPMatrix = projectionViewMatrix * modelMatrix; + + if (RenderingNormal == state) { + // Normal render + ShaderHelper *prevShader = shader; + if (item->isVolume() && !m_isOpenGLES) { + if (item->drawSlices() && + (item->sliceIndexX() >= 0 + || item->sliceIndexY() >= 0 + || item->sliceIndexZ() >= 0)) { + shader = m_volumeTextureSliceShader; + } else if (item->useHighDefShader()) { + shader = m_volumeTextureShader; + } else { + shader = m_volumeTextureLowDefShader; + } + } else if (item->isLabel()) { + shader = m_labelShader; + } else { + shader = regularShader; + } + if (shader != prevShader) + shader->bind(); + shader->setUniformValue(shader->model(), modelMatrix); + shader->setUniformValue(shader->MVP(), MVPMatrix); + shader->setUniformValue(shader->nModel(), itModelMatrix.inverted().transposed()); + + if (item->isBlendNeeded()) { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + if (!item->isVolume() && !m_isOpenGLES) + glDisable(GL_CULL_FACE); + } else { + glDisable(GL_BLEND); + glEnable(GL_CULL_FACE); + } + + if (!m_isOpenGLES && m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone + && !item->isVolume()) { + // Set shadow shader bindings + shader->setUniformValue(shader->shadowQ(), shadowQuality); + shader->setUniformValue(shader->depth(), depthProjectionViewMatrix * modelMatrix); + shader->setUniformValue(shader->lightS(), m_cachedTheme->lightStrength() / 10.0f); + m_drawer->drawObject(shader, item->mesh(), item->texture(), depthTexture); + } else { + // Set shadowless shader bindings + if (item->isVolume() && !m_isOpenGLES) { + QVector3D cameraPos = m_cachedScene->activeCamera()->position(); + cameraPos = MVPMatrix.inverted().map(cameraPos); + // Adjust camera position according to min/max bounds + cameraPos = -(cameraPos + + ((oneVector - cameraPos) * item->minBoundsNormal()) + - ((oneVector + cameraPos) * (oneVector - item->maxBoundsNormal()))); + shader->setUniformValue(shader->cameraPositionRelativeToModel(), cameraPos); + GLint color8Bit = (item->textureFormat() == QImage::Format_Indexed8) ? 1 : 0; + if (color8Bit) { + shader->setUniformValueArray(shader->colorIndex(), + item->colorTable().constData(), 256); + } + shader->setUniformValue(shader->color8Bit(), color8Bit); + shader->setUniformValue(shader->alphaMultiplier(), item->alphaMultiplier()); + shader->setUniformValue(shader->preserveOpacity(), + item->preserveOpacity() ? 1 : 0); + + shader->setUniformValue(shader->minBounds(), item->minBounds()); + shader->setUniformValue(shader->maxBounds(), item->maxBounds()); + + if (shader == m_volumeTextureSliceShader) { + shader->setUniformValue(shader->volumeSliceIndices(), + item->sliceFractions()); + } else { + // Precalculate texture dimensions so we can optimize + // ray stepping to hit every texture layer. + QVector3D textureDimensions(1.0f / float(item->textureWidth()), + 1.0f / float(item->textureHeight()), + 1.0f / float(item->textureDepth())); + + // Worst case scenario sample count + int sampleCount; + if (shader == m_volumeTextureLowDefShader) { + sampleCount = qMax(item->textureWidth(), + qMax(item->textureDepth(), item->textureHeight())); + // Further improve speed with big textures by simply dropping every + // other sample: + if (sampleCount > 256) + sampleCount /= 2; + } else { + sampleCount = item->textureWidth() + item->textureHeight() + + item->textureDepth(); + } + shader->setUniformValue(shader->textureDimensions(), textureDimensions); + shader->setUniformValue(shader->sampleCount(), sampleCount); + } + if (item->drawSliceFrames()) { + // Set up the slice frame shader + glDisable(GL_CULL_FACE); + m_volumeSliceFrameShader->bind(); + m_volumeSliceFrameShader->setUniformValue( + m_volumeSliceFrameShader->color(), item->sliceFrameColor()); + + // Draw individual slice frames. + if (item->sliceIndexX() >= 0) + drawVolumeSliceFrame(item, Qt::XAxis, projectionViewMatrix); + if (item->sliceIndexY() >= 0) + drawVolumeSliceFrame(item, Qt::YAxis, projectionViewMatrix); + if (item->sliceIndexZ() >= 0) + drawVolumeSliceFrame(item, Qt::ZAxis, projectionViewMatrix); + + glEnable(GL_CULL_FACE); + shader->bind(); + } + m_drawer->drawObject(shader, item->mesh(), 0, 0, item->texture()); + } else { + shader->setUniformValue(shader->lightS(), m_cachedTheme->lightStrength()); + m_drawer->drawObject(shader, item->mesh(), item->texture()); + } + } + } else if (RenderingSelection == state) { + // Selection render + shader->setUniformValue(shader->MVP(), MVPMatrix); + QVector4D itemColor = indexToSelectionColor(item->index()); + itemColor.setW(customItemAlpha); + itemColor /= 255.0f; + shader->setUniformValue(shader->color(), itemColor); + m_drawer->drawObject(shader, item->mesh()); + } else if (item->isShadowCasting()) { + // Depth render + shader->setUniformValue(shader->MVP(), depthProjectionViewMatrix * modelMatrix); + m_drawer->drawObject(shader, item->mesh()); + } } + loopCount++; + if (!volumeDetected) + loopCount++; // Skip second run if no volumes detected + } + + if (RenderingNormal == state) { + glDisable(GL_BLEND); + glEnable(GL_CULL_FACE); + } +} + +void Abstract3DRenderer::drawVolumeSliceFrame(const CustomRenderItem *item, Qt::Axis axis, + const QMatrix4x4 &projectionViewMatrix) +{ + QVector2D frameWidth; + QVector3D frameScaling; + QVector3D translation = item->translation(); + QQuaternion rotation = item->rotation(); + float fracTrans; + bool needRotate = !rotation.isIdentity(); + QMatrix4x4 rotationMatrix; + if (needRotate) + rotationMatrix.rotate(rotation); + + if (axis == Qt::XAxis) { + fracTrans = item->sliceFractions().x(); + float range = item->maxBoundsNormal().x() - item->minBoundsNormal().x(); + float minMult = item->minBoundsNormal().x() / range; + float maxMult = (1.0f - item->maxBoundsNormal().x()) / range; + fracTrans = fracTrans - ((1.0f - fracTrans) * minMult) + ((1.0f + fracTrans) * maxMult); + if (needRotate) + translation += rotationMatrix.map(QVector3D(fracTrans * item->scaling().x(), 0.0f, 0.0f)); + else + translation.setX(translation.x() + fracTrans * item->scaling().x()); + frameScaling = QVector3D(item->scaling().z() + + (item->scaling().z() * item->sliceFrameGaps().z()) + + (item->scaling().z() * item->sliceFrameWidths().z()), + item->scaling().y() + + (item->scaling().y() * item->sliceFrameGaps().y()) + + (item->scaling().y() * item->sliceFrameWidths().y()), + item->scaling().x() * item->sliceFrameThicknesses().x()); + frameWidth = QVector2D(item->scaling().z() * item->sliceFrameWidths().z(), + item->scaling().y() * item->sliceFrameWidths().y()); + rotation *= m_yRightAngleRotation; + } else if (axis == Qt::YAxis) { + fracTrans = item->sliceFractions().y(); + float range = item->maxBoundsNormal().y() - item->minBoundsNormal().y(); + // Y axis is logically flipped, so we need to swam min and max bounds + float minMult = (1.0f - item->maxBoundsNormal().y()) / range; + float maxMult = item->minBoundsNormal().y() / range; + fracTrans = fracTrans - ((1.0f - fracTrans) * minMult) + ((1.0f + fracTrans) * maxMult); + if (needRotate) + translation -= rotationMatrix.map(QVector3D(0.0f, fracTrans * item->scaling().y(), 0.0f)); + else + translation.setY(translation.y() - fracTrans * item->scaling().y()); + frameScaling = QVector3D(item->scaling().x() + + (item->scaling().x() * item->sliceFrameGaps().x()) + + (item->scaling().x() * item->sliceFrameWidths().x()), + item->scaling().z() + + (item->scaling().z() * item->sliceFrameGaps().z()) + + (item->scaling().z() * item->sliceFrameWidths().z()), + item->scaling().y() * item->sliceFrameThicknesses().y()); + frameWidth = QVector2D(item->scaling().x() * item->sliceFrameWidths().x(), + item->scaling().z() * item->sliceFrameWidths().z()); + rotation *= m_xRightAngleRotation; + } else { // Z axis + fracTrans = item->sliceFractions().z(); + float range = item->maxBoundsNormal().z() - item->minBoundsNormal().z(); + // Z axis is logically flipped, so we need to swam min and max bounds + float minMult = (1.0f - item->maxBoundsNormal().z()) / range; + float maxMult = item->minBoundsNormal().z() / range; + fracTrans = fracTrans - ((1.0f - fracTrans) * minMult) + ((1.0f + fracTrans) * maxMult); + if (needRotate) + translation -= rotationMatrix.map(QVector3D(0.0f, 0.0f, fracTrans * item->scaling().z())); + else + translation.setZ(translation.z() - fracTrans * item->scaling().z()); + frameScaling = QVector3D(item->scaling().x() + + (item->scaling().x() * item->sliceFrameGaps().x()) + + (item->scaling().x() * item->sliceFrameWidths().x()), + item->scaling().y() + + (item->scaling().y() * item->sliceFrameGaps().y()) + + (item->scaling().y() * item->sliceFrameWidths().y()), + item->scaling().z() * item->sliceFrameThicknesses().z()); + frameWidth = QVector2D(item->scaling().x() * item->sliceFrameWidths().x(), + item->scaling().y() * item->sliceFrameWidths().y()); + } + + // If the slice is outside the shown area, don't show the frame + if (fracTrans < -1.0 || fracTrans > 1.0) + return; + + // Shader needs the width of clear space in the middle. + frameWidth.setX(1.0f - (frameWidth.x() / frameScaling.x())); + frameWidth.setY(1.0f - (frameWidth.y() / frameScaling.y())); - modelMatrix.translate(item->translation()); - modelMatrix.rotate(rotation); - modelMatrix.scale(item->scaling()); - itModelMatrix.rotate(rotation); - if (!item->isFacingCamera()) - itModelMatrix.scale(item->scaling()); - MVPMatrix = projectionViewMatrix * modelMatrix; + QMatrix4x4 modelMatrix; + QMatrix4x4 mvpMatrix; + + modelMatrix.translate(translation); + modelMatrix.rotate(rotation); + modelMatrix.scale(frameScaling); + mvpMatrix = projectionViewMatrix * modelMatrix; + m_volumeSliceFrameShader->setUniformValue(m_volumeSliceFrameShader->MVP(), mvpMatrix); + m_volumeSliceFrameShader->setUniformValue(m_volumeSliceFrameShader->sliceFrameWidth(), + frameWidth); + + m_drawer->drawObject(m_volumeSliceFrameShader, item->mesh()); + +} + +void Abstract3DRenderer::queriedGraphPosition(const QMatrix4x4 &projectionViewMatrix, + const QVector3D &scaling, + GLuint defaultFboHandle) +{ + m_cursorPositionShader->bind(); + + // Set up mapper framebuffer + glBindFramebuffer(GL_FRAMEBUFFER, m_cursorPositionFrameBuffer); + glViewport(0, 0, + m_primarySubViewport.width(), + m_primarySubViewport.height()); + glClearColor(1.0f, 1.0f, 1.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + glDisable(GL_DITHER); // Dither may affect colors if enabled + glEnable(GL_CULL_FACE); + glCullFace(GL_FRONT); + + // Draw a cube scaled to the graph dimensions + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; + + modelMatrix.scale(scaling); + + MVPMatrix = projectionViewMatrix * modelMatrix; + m_cursorPositionShader->setUniformValue(m_cursorPositionShader->MVP(), MVPMatrix); + m_drawer->drawObject(m_cursorPositionShader, m_positionMapperObj); + + QVector4D dataColor = Utils::getSelection(m_graphPositionQuery, + m_primarySubViewport.height()); + if (dataColor.w() > 0.0f) { + // If position is outside the graph, set the position well outside the graph boundaries + dataColor = QVector4D(-10000.0f, -10000.0f, -10000.0f, 0.0f); + } else { + // Normalize to range [0.0, 1.0] + dataColor /= 255.0f; + } + + // Restore state + glEnable(GL_DITHER); + glCullFace(GL_BACK); + glBindFramebuffer(GL_FRAMEBUFFER, defaultFboHandle); + glViewport(m_primarySubViewport.x(), + m_primarySubViewport.y(), + m_primarySubViewport.width(), + m_primarySubViewport.height()); + + QVector3D normalizedValues = dataColor.toVector3D() * 2.0f; + normalizedValues -= oneVector; + m_queriedGraphPosition = QVector3D(normalizedValues.x(), + normalizedValues.y(), + normalizedValues.z()); + m_graphPositionQueryResolved = true; + m_graphPositionQueryPending = false; +} + +void Abstract3DRenderer::fixContextBeforeDelete() +{ + // Only need to fix context if the current context is null. + // Otherwise we expect it to be our shared context, so we can use it for cleanup. + if (!QOpenGLContext::currentContext() && !m_context.isNull() + && QThread::currentThread() == this->thread()) { + m_dummySurfaceAtDelete = new QWindow(); + m_dummySurfaceAtDelete->setSurfaceType(QWindow::OpenGLSurface); + m_dummySurfaceAtDelete->setFormat(m_context->format()); + m_dummySurfaceAtDelete->create(); + + m_context->makeCurrent(m_dummySurfaceAtDelete); + } +} + +void Abstract3DRenderer::restoreContextAfterDelete() +{ + if (m_dummySurfaceAtDelete) + m_context->doneCurrent(); + + delete m_dummySurfaceAtDelete; + m_dummySurfaceAtDelete = 0; +} + +void Abstract3DRenderer::calculatePolarXZ(const QVector3D &dataPos, float &x, float &z) const +{ + // x is angular, z is radial + qreal angle = m_axisCacheX.formatter()->positionAt(dataPos.x()) * doublePi; + qreal radius = m_axisCacheZ.formatter()->positionAt(dataPos.z()); + + // Convert angle & radius to X and Z coords + x = float(radius * qSin(angle)) * m_polarRadius; + z = -float(radius * qCos(angle)) * m_polarRadius; +} + +void Abstract3DRenderer::drawRadialGrid(ShaderHelper *shader, float yFloorLinePos, + const QMatrix4x4 &projectionViewMatrix, + const QMatrix4x4 &depthMatrix) +{ + static QVector<QQuaternion> lineRotations; + if (!lineRotations.size()) { + lineRotations.resize(polarGridRoundness); + for (int j = 0; j < polarGridRoundness; j++) { + lineRotations[j] = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, + polarGridAngleDegrees * float(j)); + } + } + int gridLineCount = m_axisCacheZ.gridLineCount(); + const QVector<float> &gridPositions = m_axisCacheZ.formatter()->gridPositions(); + const QVector<float> &subGridPositions = m_axisCacheZ.formatter()->subGridPositions(); + int mainSize = gridPositions.size(); + QVector3D translateVector(0.0f, yFloorLinePos, 0.0f); + QQuaternion finalRotation = m_xRightAngleRotationNeg; + if (m_yFlippedForGrid) + finalRotation *= m_xFlipRotation; + + for (int i = 0; i < gridLineCount; i++) { + float gridPosition = (i >= mainSize) + ? subGridPositions.at(i - mainSize) : gridPositions.at(i); + float radiusFraction = m_polarRadius * gridPosition; + QVector3D gridLineScaler(radiusFraction * float(qSin(polarGridHalfAngle)), + gridLineWidth, gridLineWidth); + translateVector.setZ(gridPosition * m_polarRadius); + for (int j = 0; j < polarGridRoundness; j++) { + QMatrix4x4 modelMatrix; + QMatrix4x4 itModelMatrix; + modelMatrix.rotate(lineRotations.at(j)); + itModelMatrix.rotate(lineRotations.at(j)); + modelMatrix.translate(translateVector); + modelMatrix.scale(gridLineScaler); + itModelMatrix.scale(gridLineScaler); + modelMatrix.rotate(finalRotation); + itModelMatrix.rotate(finalRotation); + QMatrix4x4 MVPMatrix = projectionViewMatrix * modelMatrix; - if (RenderingNormal == state) { - // Normal render shader->setUniformValue(shader->model(), modelMatrix); - shader->setUniformValue(shader->MVP(), MVPMatrix); shader->setUniformValue(shader->nModel(), itModelMatrix.inverted().transposed()); - - if (item->isBlendNeeded()) { - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glDisable(GL_CULL_FACE); + shader->setUniformValue(shader->MVP(), MVPMatrix); + if (!m_isOpenGLES) { + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + // Set shadow shader bindings + QMatrix4x4 depthMVPMatrix = depthMatrix * modelMatrix; + shader->setUniformValue(shader->depth(), depthMVPMatrix); + // Draw the object + m_drawer->drawObject(shader, m_gridLineObj, 0, m_depthTexture); + } else { + // Draw the object + m_drawer->drawObject(shader, m_gridLineObj); + } } else { - glDisable(GL_BLEND); - glEnable(GL_CULL_FACE); + m_drawer->drawLine(shader); } + } + } +} -#if !defined(QT_OPENGL_ES_2) +void Abstract3DRenderer::drawAngularGrid(ShaderHelper *shader, float yFloorLinePos, + const QMatrix4x4 &projectionViewMatrix, + const QMatrix4x4 &depthMatrix) +{ + float halfRatio((m_polarRadius + (labelMargin / 2.0f)) / 2.0f); + QVector3D gridLineScaler(gridLineWidth, gridLineWidth, halfRatio); + int gridLineCount = m_axisCacheX.gridLineCount(); + const QVector<float> &gridPositions = m_axisCacheX.formatter()->gridPositions(); + const QVector<float> &subGridPositions = m_axisCacheX.formatter()->subGridPositions(); + int mainSize = gridPositions.size(); + QVector3D translateVector(0.0f, yFloorLinePos, -halfRatio); + QQuaternion finalRotation; + if (m_isOpenGLES) + finalRotation = m_yRightAngleRotationNeg; + else + finalRotation = m_xRightAngleRotationNeg; + if (m_yFlippedForGrid) + finalRotation *= m_xFlipRotation; + for (int i = 0; i < gridLineCount; i++) { + QMatrix4x4 modelMatrix; + QMatrix4x4 itModelMatrix; + float gridPosition = (i >= mainSize) + ? subGridPositions.at(i - mainSize) : gridPositions.at(i); + QQuaternion lineRotation = QQuaternion::fromAxisAndAngle(upVector, gridPosition * 360.0f); + modelMatrix.rotate(lineRotation); + itModelMatrix.rotate(lineRotation); + modelMatrix.translate(translateVector); + modelMatrix.scale(gridLineScaler); + itModelMatrix.scale(gridLineScaler); + modelMatrix.rotate(finalRotation); + itModelMatrix.rotate(finalRotation); + QMatrix4x4 MVPMatrix = projectionViewMatrix * modelMatrix; + + shader->setUniformValue(shader->model(), modelMatrix); + shader->setUniformValue(shader->nModel(), itModelMatrix.inverted().transposed()); + shader->setUniformValue(shader->MVP(), MVPMatrix); + if (m_isOpenGLES) { + m_drawer->drawLine(shader); + } else { if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { // Set shadow shader bindings - shader->setUniformValue(shader->shadowQ(), shadowQuality); - shader->setUniformValue(shader->depth(), depthProjectionViewMatrix * modelMatrix); - shader->setUniformValue(shader->lightS(), m_cachedTheme->lightStrength() / 10.0f); - m_drawer->drawObject(shader, item->mesh(), item->texture(), depthTexture); - } else -#else - Q_UNUSED(depthTexture) - Q_UNUSED(shadowQuality) -#endif - { - // Set shadowless shader bindings - shader->setUniformValue(shader->lightS(), m_cachedTheme->lightStrength()); - m_drawer->drawObject(shader, item->mesh(), item->texture()); + QMatrix4x4 depthMVPMatrix = depthMatrix * modelMatrix; + shader->setUniformValue(shader->depth(), depthMVPMatrix); + // Draw the object + m_drawer->drawObject(shader, m_gridLineObj, 0, m_depthTexture); + } else { + // Draw the object + m_drawer->drawObject(shader, m_gridLineObj); } - } else if (RenderingSelection == state) { - // Selection render - shader->setUniformValue(shader->MVP(), MVPMatrix); - QVector4D itemColor = indexToSelectionColor(item->index()); - itemColor.setW(customItemAlpha); - itemColor /= 255.0f; - shader->setUniformValue(shader->color(), itemColor); - m_drawer->drawObject(shader, item->mesh()); - } else if (item->isShadowCasting()) { - // Depth render - shader->setUniformValue(shader->MVP(), depthProjectionViewMatrix * modelMatrix); - m_drawer->drawObject(shader, item->mesh()); } } +} - if (RenderingNormal == state) { - glDisable(GL_TEXTURE_2D); - glDisable(GL_BLEND); - glEnable(GL_CULL_FACE); +float Abstract3DRenderer::calculatePolarBackgroundMargin() +{ + // Check each extents of each angular label + // Calculate angular position + QVector<float> &labelPositions = m_axisCacheX.formatter()->labelPositions(); + float actualLabelHeight = m_drawer->scaledFontSize() * 2.0f; // All labels are same height + float maxNeededMargin = 0.0f; + + // Axis title needs to be accounted for + if (m_axisCacheX.isTitleVisible()) + maxNeededMargin = 2.0f * actualLabelHeight + 3.0f * labelMargin; + + for (int label = 0; label < labelPositions.size(); label++) { + QSize labelSize = m_axisCacheX.labelItems().at(label)->size(); + float actualLabelWidth = actualLabelHeight / labelSize.height() * labelSize.width(); + float labelPosition = labelPositions.at(label); + qreal angle = labelPosition * M_PI * 2.0; + float x = qAbs((m_polarRadius + labelMargin) * float(qSin(angle))) + + actualLabelWidth - m_polarRadius + labelMargin; + float z = qAbs(-(m_polarRadius + labelMargin) * float(qCos(angle))) + + actualLabelHeight - m_polarRadius + labelMargin; + float neededMargin = qMax(x, z); + maxNeededMargin = qMax(maxNeededMargin, neededMargin); } + + return maxNeededMargin; +} + +void Abstract3DRenderer::updateCameraViewport() +{ + QVector3D adjustedTarget = m_cachedScene->activeCamera()->target(); + fixCameraTarget(adjustedTarget); + if (m_oldCameraTarget != adjustedTarget) { + QVector3D cameraBase = cameraDistanceVector + adjustedTarget; + + m_cachedScene->activeCamera()->d_ptr->setBaseOrientation(cameraBase, + adjustedTarget, + upVector); + m_oldCameraTarget = adjustedTarget; + } + m_cachedScene->activeCamera()->d_ptr->updateViewMatrix(m_autoScaleAdjustment); + // Set light position (i.e rotate light with activeCamera, a bit above it) + m_cachedScene->d_ptr->setLightPositionRelativeToCamera(defaultLightPos); } QT_END_NAMESPACE_DATAVISUALIZATION |