diff options
author | Miikka Heikkinen <miikka.heikkinen@theqtcompany.com> | 2014-10-29 09:45:41 +0200 |
---|---|---|
committer | Miikka Heikkinen <miikka.heikkinen@theqtcompany.com> | 2014-10-29 09:45:41 +0200 |
commit | e3a4f132ca2a42af3d4bb889d6a17948b88d26a2 (patch) | |
tree | 554d2b8b3020dc71438301aa696dea1e9a36943a /src/datavisualization/engine | |
parent | cc50608385cf77a0803431ece1385f341a400b75 (diff) | |
parent | bf716cfdf0afecccdb1f2eabb2e6a172c620fbff (diff) |
Merge branch 'develop'
Conflicts:
.qmake.conf
README
src/datavisualization/global/qdatavisualizationglobal.h
Change-Id: Ia6941dcf3e6aa17e2e9ebc6f60fac16ef5049f11
Diffstat (limited to 'src/datavisualization/engine')
65 files changed, 5573 insertions, 1980 deletions
diff --git a/src/datavisualization/engine/abstract3dcontroller.cpp b/src/datavisualization/engine/abstract3dcontroller.cpp index 30434dca..275d0fe2 100644 --- a/src/datavisualization/engine/abstract3dcontroller.cpp +++ b/src/datavisualization/engine/abstract3dcontroller.cpp @@ -25,6 +25,7 @@ #include "thememanager_p.h" #include "q3dtheme_p.h" #include "qcustom3ditem_p.h" +#include "utils_p.h" #include <QtCore/QThread> #include <QtGui/QOpenGLFramebufferObject> @@ -37,8 +38,12 @@ Abstract3DController::Abstract3DController(QRect initialViewport, Q3DScene *scen m_selectionMode(QAbstract3DGraph::SelectionItem), m_shadowQuality(QAbstract3DGraph::ShadowQualityMedium), m_useOrthoProjection(false), - m_aspectRatio(2.0f), + m_aspectRatio(2.0), + m_horizontalAspectRatio(0.0), m_optimizationHints(QAbstract3DGraph::OptimizationDefault), + m_reflectionEnabled(false), + m_reflectivity(0.5), + m_locale(QLocale::c()), m_scene(scene), m_activeInputHandler(0), m_axisX(0), @@ -50,12 +55,19 @@ Abstract3DController::Abstract3DController(QRect initialViewport, Q3DScene *scen m_isCustomItemDirty(true), m_isSeriesVisualsDirty(true), m_renderPending(false), + m_isPolar(false), + m_radialLabelOffset(1.0f), m_measureFps(false), m_numFrames(0), - m_currentFps(0.0) + m_currentFps(0.0), + m_clickedType(QAbstract3DGraph::ElementNone), + m_selectedLabelIndex(-1), + m_selectedCustomItemIndex(-1), + m_margin(-1.0) { if (!m_scene) m_scene = new Q3DScene; + m_scene->setParent(this); // Set initial theme Q3DTheme *defaultTheme = new Q3DTheme(Q3DTheme::ThemeQt); @@ -72,10 +84,6 @@ Abstract3DController::Abstract3DController(QRect initialViewport, Q3DScene *scen inputHandler = new QTouch3DInputHandler(); inputHandler->d_ptr->m_isDefaultHandler = true; setActiveInputHandler(inputHandler); - connect(inputHandler, &QAbstract3DInputHandler::inputViewChanged, this, - &Abstract3DController::handleInputViewChanged); - connect(inputHandler, &QAbstract3DInputHandler::positionChanged, this, - &Abstract3DController::handleInputPositionChanged); connect(m_scene->d_ptr.data(), &Q3DScenePrivate::needRender, this, &Abstract3DController::emitNeedRender); } @@ -93,7 +101,7 @@ Abstract3DController::~Abstract3DController() void Abstract3DController::destroyRenderer() { // Renderer can be in another thread, don't delete it directly in that case - if (m_renderer && m_renderer->thread() != QThread::currentThread()) + if (m_renderer && m_renderer->thread() && m_renderer->thread() != this->thread()) m_renderer->deleteLater(); else delete m_renderer; @@ -107,6 +115,13 @@ void Abstract3DController::destroyRenderer() void Abstract3DController::setRenderer(Abstract3DRenderer *renderer) { m_renderer = renderer; + + // If renderer is created in different thread than controller, make sure renderer gets + // destroyed before the render thread finishes. + if (renderer->thread() != this->thread()) { + QObject::connect(renderer->thread(), &QThread::finished, this, + &Abstract3DController::destroyRenderer, Qt::DirectConnection); + } } void Abstract3DController::addSeries(QAbstract3DSeries *series) @@ -163,11 +178,14 @@ void Abstract3DController::synchDataToRenderer() { // Subclass implementations check for renderer validity already, so no need to check here. - // If there is a pending click from renderer, handle that first. - if (m_renderer->isClickPending()) { + m_renderPending = false; + + // If there are pending queries, handle those first + if (m_renderer->isGraphPositionQueryResolved()) + handlePendingGraphPositionQuery(); + + if (m_renderer->isClickQueryResolved()) handlePendingClick(); - m_renderer->clearClickPending(); - } startRecordingRemovesAndInserts(); @@ -176,6 +194,16 @@ void Abstract3DController::synchDataToRenderer() m_renderer->updateTheme(m_themeManager->activeTheme()); + if (m_changeTracker.polarChanged) { + m_renderer->updatePolar(m_isPolar); + m_changeTracker.polarChanged = false; + } + + if (m_changeTracker.radialLabelOffsetChanged) { + m_renderer->updateRadialLabelOffset(m_radialLabelOffset); + m_changeTracker.radialLabelOffsetChanged = false; + } + if (m_changeTracker.shadowQualityChanged) { m_renderer->updateShadowQuality(m_shadowQuality); m_changeTracker.shadowQualityChanged = false; @@ -192,15 +220,31 @@ void Abstract3DController::synchDataToRenderer() } if (m_changeTracker.aspectRatioChanged) { - m_renderer->updateAspectRatio(m_aspectRatio); + m_renderer->updateAspectRatio(float(m_aspectRatio)); m_changeTracker.aspectRatioChanged = false; } + if (m_changeTracker.horizontalAspectRatioChanged) { + m_renderer->updateHorizontalAspectRatio(float(m_horizontalAspectRatio)); + m_changeTracker.horizontalAspectRatioChanged = false; + } + if (m_changeTracker.optimizationHintChanged) { m_renderer->updateOptimizationHint(m_optimizationHints); m_changeTracker.optimizationHintChanged = false; } + if (m_changeTracker.reflectionChanged) { + m_renderer->m_reflectionEnabled = m_reflectionEnabled; + m_changeTracker.reflectionChanged = false; + } + + if (m_changeTracker.reflectivityChanged) { + // Invert value to match functionality to the property description + m_renderer->m_reflectivity = -(m_reflectivity - 1.0); + m_changeTracker.reflectivityChanged = false; + } + if (m_changeTracker.axisXFormatterChanged) { m_changeTracker.axisXFormatterChanged = false; if (m_axisX->type() & QAbstract3DAxis::AxisTypeValue) { @@ -445,6 +489,11 @@ void Abstract3DController::synchDataToRenderer() m_changeTracker.axisZTitleFixedChanged = false; } + if (m_changeTracker.marginChanged) { + m_renderer->updateMargin(float(m_margin)); + m_changeTracker.marginChanged = false; + } + if (m_changedSeriesList.size()) { m_renderer->modifiedSeriesList(m_changedSeriesList); m_changedSeriesList.clear(); @@ -475,8 +524,6 @@ void Abstract3DController::synchDataToRenderer() void Abstract3DController::render(const GLuint defaultFboHandle) { - m_renderPending = false; - // If not initialized, do nothing. if (!m_renderer) return; @@ -765,8 +812,9 @@ void Abstract3DController::setActiveInputHandler(QAbstract3DInputHandler *inputH m_inputHandlers.removeAll(m_activeInputHandler); delete m_activeInputHandler; } else { - // Disconnect the old input handler from the scene + // Disconnect the old input handler m_activeInputHandler->setScene(0); + QObject::disconnect(m_activeInputHandler, 0, this, 0); } } @@ -775,9 +823,16 @@ void Abstract3DController::setActiveInputHandler(QAbstract3DInputHandler *inputH addInputHandler(inputHandler); m_activeInputHandler = inputHandler; - if (m_activeInputHandler) + if (m_activeInputHandler) { m_activeInputHandler->setScene(m_scene); + // Connect the input handler + QObject::connect(m_activeInputHandler, &QAbstract3DInputHandler::inputViewChanged, this, + &Abstract3DController::handleInputViewChanged); + QObject::connect(m_activeInputHandler, &QAbstract3DInputHandler::positionChanged, this, + &Abstract3DController::handleInputPositionChanged); + } + // Notify change of input handler emit activeInputHandlerChanged(m_activeInputHandler); } @@ -886,11 +941,7 @@ QAbstract3DGraph::OptimizationHints Abstract3DController::optimizationHints() co bool Abstract3DController::shadowsSupported() const { -#if defined(QT_OPENGL_ES_2) - return false; -#else - return true; -#endif + return !isOpenGLES(); } bool Abstract3DController::isSlicingActive() const @@ -989,6 +1040,11 @@ void Abstract3DController::releaseCustomItem(QCustom3DItem *item) } } +QList<QCustom3DItem *> Abstract3DController::customItems() const +{ + return m_customItems; +} + void Abstract3DController::updateCustomItem() { m_isCustomItemDirty = true; @@ -1303,6 +1359,11 @@ void Abstract3DController::markSeriesItemLabelsDirty() m_seriesList.at(i)->d_ptr->markItemLabelDirty(); } +bool Abstract3DController::isOpenGLES() const +{ + return Utils::isOpenGLES(); +} + void Abstract3DController::setAxisHelper(QAbstract3DAxis::AxisOrientation orientation, QAbstract3DAxis *axis, QAbstract3DAxis **axisPtr) { @@ -1381,6 +1442,8 @@ void Abstract3DController::setAxisHelper(QAbstract3DAxis::AxisOrientation orient handleAxisLabelFormatChangedBySender(valueAxis); handleAxisReversedChangedBySender(valueAxis); handleAxisFormatterDirtyBySender(valueAxis->dptr()); + + valueAxis->formatter()->setLocale(m_locale); } } @@ -1427,13 +1490,37 @@ void Abstract3DController::emitNeedRender() void Abstract3DController::handlePendingClick() { - QAbstract3DGraph::ElementType type = m_renderer->clickedType(); - emit elementSelected(type); + m_clickedType = m_renderer->clickedType(); + m_selectedLabelIndex = m_renderer->m_selectedLabelIndex; + m_selectedCustomItemIndex = m_renderer->m_selectedCustomItemIndex; + + // Invalidate query position to indicate the query has been handled, unless another + // point has been queried. + if (m_renderer->cachedClickQuery() == m_scene->selectionQueryPosition()) + m_scene->setSelectionQueryPosition(Q3DScene::invalidSelectionPoint()); + + m_renderer->clearClickQueryResolved(); + + emit elementSelected(m_clickedType); +} + +void Abstract3DController::handlePendingGraphPositionQuery() +{ + m_queriedGraphPosition = m_renderer->queriedGraphPosition(); + + // Invalidate query position to indicate the query has been handled, unless another + // point has been queried. + if (m_renderer->cachedGraphPositionQuery() == m_scene->graphPositionQuery()) + m_scene->setGraphPositionQuery(Q3DScene::invalidSelectionPoint()); + + m_renderer->clearGraphPositionQueryResolved(); + + emit queriedGraphPositionChanged(m_queriedGraphPosition); } int Abstract3DController::selectedLabelIndex() const { - int index = m_renderer->m_selectedLabelIndex; + int index = m_selectedLabelIndex; QAbstract3DAxis *axis = selectedAxis(); if (axis && axis->labels().count() <= index) index = -1; @@ -1443,7 +1530,7 @@ int Abstract3DController::selectedLabelIndex() const QAbstract3DAxis *Abstract3DController::selectedAxis() const { QAbstract3DAxis *axis = 0; - QAbstract3DGraph::ElementType type = m_renderer->clickedType(); + QAbstract3DGraph::ElementType type = m_clickedType; switch (type) { case QAbstract3DGraph::ElementAxisXLabel: axis = axisX(); @@ -1464,7 +1551,7 @@ QAbstract3DAxis *Abstract3DController::selectedAxis() const int Abstract3DController::selectedCustomItemIndex() const { - int index = m_renderer->m_selectedCustomItemIndex; + int index = m_selectedCustomItemIndex; if (m_customItems.count() <= index) index = -1; return index; @@ -1481,10 +1568,7 @@ QCustom3DItem *Abstract3DController::selectedCustomItem() const QAbstract3DGraph::ElementType Abstract3DController::selectedElement() const { - if (m_renderer) - return m_renderer->clickedType(); - else - return QAbstract3DGraph::ElementNone; + return m_clickedType; } void Abstract3DController::setOrthoProjection(bool enable) @@ -1505,7 +1589,7 @@ bool Abstract3DController::isOrthoProjection() const return m_useOrthoProjection; } -void Abstract3DController::setAspectRatio(float ratio) +void Abstract3DController::setAspectRatio(qreal ratio) { if (m_aspectRatio != ratio) { m_aspectRatio = ratio; @@ -1516,9 +1600,131 @@ void Abstract3DController::setAspectRatio(float ratio) } } -float Abstract3DController::aspectRatio() +qreal Abstract3DController::aspectRatio() { return m_aspectRatio; } +void Abstract3DController::setHorizontalAspectRatio(qreal ratio) +{ + if (m_horizontalAspectRatio != ratio) { + m_horizontalAspectRatio = ratio; + m_changeTracker.horizontalAspectRatioChanged = true; + emit horizontalAspectRatioChanged(m_horizontalAspectRatio); + m_isDataDirty = true; + emitNeedRender(); + } +} + +qreal Abstract3DController::horizontalAspectRatio() const +{ + return m_horizontalAspectRatio; +} + +void Abstract3DController::setReflection(bool enable) +{ + if (m_reflectionEnabled != enable) { + m_reflectionEnabled = enable; + m_changeTracker.reflectionChanged = true; + emit reflectionChanged(m_reflectionEnabled); + emitNeedRender(); + } +} + +bool Abstract3DController::reflection() const +{ + return m_reflectionEnabled; +} + +void Abstract3DController::setReflectivity(qreal reflectivity) +{ + if (m_reflectivity != reflectivity) { + m_reflectivity = reflectivity; + m_changeTracker.reflectivityChanged = true; + emit reflectivityChanged(m_reflectivity); + emitNeedRender(); + } +} + +qreal Abstract3DController::reflectivity() const +{ + return m_reflectivity; +} + +void Abstract3DController::setPolar(bool enable) +{ + if (enable != m_isPolar) { + m_isPolar = enable; + m_changeTracker.polarChanged = true; + m_isDataDirty = true; + emit polarChanged(m_isPolar); + emitNeedRender(); + } +} + +bool Abstract3DController::isPolar() const +{ + return m_isPolar; +} + +void Abstract3DController::setRadialLabelOffset(float offset) +{ + if (m_radialLabelOffset != offset) { + m_radialLabelOffset = offset; + m_changeTracker.radialLabelOffsetChanged = true; + emit radialLabelOffsetChanged(m_radialLabelOffset); + emitNeedRender(); + } +} + +float Abstract3DController::radialLabelOffset() const +{ + return m_radialLabelOffset; +} + +void Abstract3DController::setLocale(const QLocale &locale) +{ + if (m_locale != locale) { + m_locale = locale; + + // Value axis formatters need to be updated + QValue3DAxis *axis = qobject_cast<QValue3DAxis *>(m_axisX); + if (axis) + axis->formatter()->setLocale(m_locale); + axis = qobject_cast<QValue3DAxis *>(m_axisY); + if (axis) + axis->formatter()->setLocale(m_locale); + axis = qobject_cast<QValue3DAxis *>(m_axisZ); + if (axis) + axis->formatter()->setLocale(m_locale); + emit localeChanged(m_locale); + } +} + +QLocale Abstract3DController::locale() const +{ + return m_locale; +} + +QVector3D Abstract3DController::queriedGraphPosition() const +{ + return m_queriedGraphPosition; +} + +void Abstract3DController::setMargin(qreal margin) +{ + if (m_margin != margin) { + m_margin = margin; + m_changeTracker.marginChanged = true; + emit marginChanged(margin); + emitNeedRender(); + } +} + +qreal Abstract3DController::margin() const +{ + return m_margin; +} + + QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/engine/abstract3dcontroller_p.h b/src/datavisualization/engine/abstract3dcontroller_p.h index 0e4d1add..d5a1ac8f 100644 --- a/src/datavisualization/engine/abstract3dcontroller_p.h +++ b/src/datavisualization/engine/abstract3dcontroller_p.h @@ -38,6 +38,7 @@ #include "qcustom3ditem.h" #include <QtGui/QLinearGradient> #include <QtCore/QTime> +#include <QtCore/QLocale> class QOpenGLFramebufferObject; @@ -84,12 +85,18 @@ struct Abstract3DChangeBitField { bool axisYLabelAutoRotationChanged : 1; bool axisZLabelAutoRotationChanged : 1; bool aspectRatioChanged : 1; + bool horizontalAspectRatioChanged : 1; bool axisXTitleVisibilityChanged : 1; bool axisYTitleVisibilityChanged : 1; bool axisZTitleVisibilityChanged : 1; bool axisXTitleFixedChanged : 1; bool axisYTitleFixedChanged : 1; bool axisZTitleFixedChanged : 1; + bool polarChanged : 1; + bool radialLabelOffsetChanged : 1; + bool reflectionChanged : 1; + bool reflectivityChanged : 1; + bool marginChanged : 1; Abstract3DChangeBitField() : themeChanged(true), @@ -128,12 +135,18 @@ struct Abstract3DChangeBitField { axisYLabelAutoRotationChanged(true), axisZLabelAutoRotationChanged(true), aspectRatioChanged(true), + horizontalAspectRatioChanged(true), axisXTitleVisibilityChanged(true), axisYTitleVisibilityChanged(true), axisZTitleVisibilityChanged(true), axisXTitleFixedChanged(true), axisYTitleFixedChanged(true), - axisZTitleFixedChanged(true) + axisZTitleFixedChanged(true), + polarChanged(true), + radialLabelOffsetChanged(true), + reflectionChanged(true), + reflectivityChanged(true), + marginChanged(true) { } }; @@ -156,8 +169,13 @@ private: QAbstract3DGraph::SelectionFlags m_selectionMode; QAbstract3DGraph::ShadowQuality m_shadowQuality; bool m_useOrthoProjection; - float m_aspectRatio; + qreal m_aspectRatio; + qreal m_horizontalAspectRatio; QAbstract3DGraph::OptimizationHints m_optimizationHints; + bool m_reflectionEnabled; + qreal m_reflectivity; + QLocale m_locale; + QVector3D m_queriedGraphPosition; protected: Q3DScene *m_scene; @@ -175,6 +193,8 @@ protected: bool m_isCustomItemDirty; bool m_isSeriesVisualsDirty; bool m_renderPending; + bool m_isPolar; + float m_radialLabelOffset; QList<QAbstract3DSeries *> m_seriesList; @@ -187,13 +207,17 @@ protected: QList<QCustom3DItem *> m_customItems; + QAbstract3DGraph::ElementType m_clickedType; + int m_selectedLabelIndex; + int m_selectedCustomItemIndex; + qreal m_margin; + explicit Abstract3DController(QRect initialViewport, Q3DScene *scene, QObject *parent = 0); public: virtual ~Abstract3DController(); inline bool isInitialized() { return (m_renderer != 0); } - virtual void destroyRenderer(); virtual void synchDataToRenderer(); virtual void render(const GLuint defaultFboHandle = 0); virtual void initializeOpenGL() = 0; @@ -252,6 +276,7 @@ public: void deleteCustomItem(QCustom3DItem *item); void deleteCustomItem(const QVector3D &position); void releaseCustomItem(QCustom3DItem *item); + QList<QCustom3DItem *> customItems() const; int selectedLabelIndex() const; QAbstract3DAxis *selectedAxis() const; @@ -261,6 +286,35 @@ public: void setOrthoProjection(bool enable); bool isOrthoProjection() const; + void setMeasureFps(bool enable); + inline bool measureFps() const { return m_measureFps; } + inline qreal currentFps() const { return m_currentFps; } + + QAbstract3DGraph::ElementType selectedElement() const; + + void setAspectRatio(qreal ratio); + qreal aspectRatio(); + void setHorizontalAspectRatio(qreal ratio); + qreal horizontalAspectRatio() const; + + void setReflection(bool enable); + bool reflection() const; + void setReflectivity(qreal reflectivity); + qreal reflectivity() const; + + void setPolar(bool enable); + bool isPolar() const; + void setRadialLabelOffset(float offset); + float radialLabelOffset() const; + + void setLocale(const QLocale &locale); + QLocale locale() const; + + QVector3D queriedGraphPosition() const; + + void setMargin(qreal margin); + qreal margin() const; + void emitNeedRender(); virtual void clearSelection() = 0; @@ -286,12 +340,16 @@ public: virtual void handleAxisTitleVisibilityChangedBySender(QObject *sender); virtual void handleAxisTitleFixedChangedBySender(QObject *sender); virtual void handleSeriesVisibilityChangedBySender(QObject *sender); - virtual void handlePendingClick() = 0; + virtual void handlePendingClick(); + virtual void handlePendingGraphPositionQuery(); virtual void adjustAxisRanges() = 0; void markSeriesItemLabelsDirty(); + bool isOpenGLES() const; public slots: + void destroyRenderer(); + void handleAxisTitleChanged(const QString &title); void handleAxisLabelsChanged(); void handleAxisRangeChanged(float min, float max); @@ -320,17 +378,8 @@ public slots: // Renderer callback handlers void handleRequestShadowQuality(QAbstract3DGraph::ShadowQuality quality); - void setMeasureFps(bool enable); - inline bool measureFps() const { return m_measureFps; } - inline qreal currentFps() const { return m_currentFps; } - - QAbstract3DGraph::ElementType selectedElement() const; - void updateCustomItem(); - void setAspectRatio(float ratio); - float aspectRatio(); - signals: void shadowQualityChanged(QAbstract3DGraph::ShadowQuality quality); void activeInputHandlerChanged(QAbstract3DInputHandler *inputHandler); @@ -344,8 +393,16 @@ signals: void measureFpsChanged(bool enabled); void currentFpsChanged(qreal fps); void orthoProjectionChanged(bool enabled); - void aspectRatioChanged(float ratio); + void aspectRatioChanged(qreal ratio); + void horizontalAspectRatioChanged(qreal ratio); void optimizationHintsChanged(QAbstract3DGraph::OptimizationHints hints); + void polarChanged(bool enabled); + void radialLabelOffsetChanged(float offset); + void reflectionChanged(bool enabled); + void reflectivityChanged(qreal reflectivity); + void localeChanged(const QLocale &locale); + void queriedGraphPositionChanged(const QVector3D &data); + void marginChanged(qreal margin); protected: virtual QAbstract3DAxis *createDefaultAxis(QAbstract3DAxis::AxisOrientation orientation); 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 diff --git a/src/datavisualization/engine/abstract3drenderer_p.h b/src/datavisualization/engine/abstract3drenderer_p.h index 0dfc7367..1e38023d 100644 --- a/src/datavisualization/engine/abstract3drenderer_p.h +++ b/src/datavisualization/engine/abstract3drenderer_p.h @@ -30,13 +30,17 @@ #define ABSTRACT3DRENDERER_P_H #include <QtGui/QOpenGLFunctions> - +#if !defined(QT_OPENGL_ES_2) +# include <QtGui/QOpenGLFunctions_2_1> +#endif #include "datavisualizationglobal_p.h" #include "abstract3dcontroller_p.h" #include "axisrendercache_p.h" #include "seriesrendercache_p.h" #include "customrenderitem_p.h" +class QSurface; + QT_BEGIN_NAMESPACE_DATAVISUALIZATION class TextureHelper; @@ -77,21 +81,33 @@ public: virtual void updateSelectionMode(QAbstract3DGraph::SelectionFlags newMode); virtual void updateOptimizationHint(QAbstract3DGraph::OptimizationHints hint); virtual void updateScene(Q3DScene *scene); - virtual void updateTextures() = 0; + virtual void updateTextures(); virtual void initSelectionBuffer() = 0; virtual void updateSelectionState(SelectionState state); - virtual void updateInputPosition(const QPoint &position); -#if !defined(QT_OPENGL_ES_2) virtual void updateDepthBuffer() = 0; -#endif virtual void updateShadowQuality(QAbstract3DGraph::ShadowQuality quality) = 0; virtual void initShaders(const QString &vertexShader, const QString &fragmentShader) = 0; virtual void initGradientShaders(const QString &vertexShader, const QString &fragmentShader); + virtual void initStaticSelectedItemShaders(const QString &vertexShader, + const QString &fragmentShader, + const QString &gradientVertexShader, + const QString &gradientFragmentShader); virtual void initBackgroundShaders(const QString &vertexShader, const QString &fragmentShader) = 0; virtual void initCustomItemShaders(const QString &vertexShader, const QString &fragmentShader); + virtual void initVolumeTextureShaders(const QString &vertexShader, + const QString &fragmentShader, + const QString &fragmentLowDefShader, + const QString &sliceShader, + const QString &sliceFrameVertexShader, + const QString &sliceFrameShader); + virtual void initLabelShaders(const QString &vertexShader, const QString &fragmentShader); + virtual void initCursorPositionShaders(const QString &vertexShader, + const QString &fragmentShader); + virtual void initCursorPositionBuffer(); + virtual void updateAxisType(QAbstract3DAxis::AxisOrientation orientation, QAbstract3DAxis::AxisType type); virtual void updateAxisTitle(QAbstract3DAxis::AxisOrientation orientation, @@ -112,7 +128,7 @@ public: virtual void updateAxisLabelAutoRotation(QAbstract3DAxis::AxisOrientation orientation, float angle); virtual void updateAxisTitleVisibility(QAbstract3DAxis::AxisOrientation orientation, - bool visible); + bool visible); virtual void updateAxisTitleFixed(QAbstract3DAxis::AxisOrientation orientation, bool fixed); virtual void modifiedSeriesList(const QVector<QAbstract3DSeries *> &seriesList); @@ -123,6 +139,10 @@ public: virtual void updateCustomItem(CustomRenderItem *renderItem); virtual void updateAspectRatio(float ratio); + virtual void updateHorizontalAspectRatio(float ratio); + virtual void updatePolar(bool enable); + virtual void updateRadialLabelOffset(float offset); + virtual void updateMargin(float margin); virtual QVector3D convertPositionToTranslation(const QVector3D &position, bool isAbsolute) = 0; @@ -130,21 +150,28 @@ public: void generateBaseColorTexture(const QColor &color, GLuint *texture); void fixGradientAndGenerateTexture(QLinearGradient *gradient, GLuint *gradientTexture); - inline bool isClickPending() { return m_clickPending; } - inline void clearClickPending() { m_clickPending = false; } + inline bool isClickQueryResolved() const { return m_clickResolved; } + inline void clearClickQueryResolved() { m_clickResolved = false; } + inline QPoint cachedClickQuery() const { return m_cachedScene->selectionQueryPosition(); } inline QAbstract3DSeries *clickedSeries() const { return m_clickedSeries; } inline QAbstract3DGraph::ElementType clickedType() { return m_clickedType; } + inline bool isGraphPositionQueryResolved() const { return m_graphPositionQueryResolved; } + inline void clearGraphPositionQueryResolved() { m_graphPositionQueryResolved = false; } + inline QVector3D queriedGraphPosition() const { return m_queriedGraphPosition; } + inline QPoint cachedGraphPositionQuery() const { return m_cachedScene->graphPositionQuery(); } LabelItem &selectionLabelItem(); void setSelectionLabel(const QString &label); QString &selectionLabel(); - void drawCustomItems(RenderingState state, ShaderHelper *shader, + void drawCustomItems(RenderingState state, ShaderHelper *regularShader, const QMatrix4x4 &viewMatrix, const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &depthProjectionViewMatrix, - GLuint depthTexture, GLfloat shadowQuality); + GLuint depthTexture, GLfloat shadowQuality, GLfloat reflection = 1.0f); + QVector4D indexToSelectionColor(GLint index); + void calculatePolarXZ(const QVector3D &dataPos, float &x, float &z) const; signals: void needRender(); // Emit this if something in renderer causes need for another render pass. @@ -177,7 +204,7 @@ protected: const QQuaternion &totalRotation, AbstractRenderItem &dummyItem, const Q3DCamera *activeCamera, float labelsMaxWidth, const QMatrix4x4 &viewMatrix, const QMatrix4x4 &projectionMatrix, - ShaderHelper *shader); + ShaderHelper *shader, bool radial = false); void drawAxisTitleZ(const QVector3D &labelRotation, const QVector3D &labelTrans, const QQuaternion &totalRotation, AbstractRenderItem &dummyItem, const Q3DCamera *activeCamera, float labelsMaxWidth, @@ -186,6 +213,26 @@ protected: void loadGridLineMesh(); void loadLabelMesh(); + void loadPositionMapperMesh(); + + void drawRadialGrid(ShaderHelper *shader, float yFloorLinePos, + const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &depthMatrix); + void drawAngularGrid(ShaderHelper *shader, float yFloorLinePos, + const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &depthMatrix); + + float calculatePolarBackgroundMargin(); + virtual void fixCameraTarget(QVector3D &target) = 0; + void updateCameraViewport(); + + void recalculateCustomItemScalingAndPos(CustomRenderItem *item); + virtual void getVisibleItemBounds(QVector3D &minBounds, QVector3D &maxBounds) = 0; + void drawVolumeSliceFrame(const CustomRenderItem *item, Qt::Axis axis, + const QMatrix4x4 &projectionViewMatrix); + void queriedGraphPosition(const QMatrix4x4 &projectionViewMatrix, const QVector3D &scaling, + GLuint defaultFboHandle); + + void fixContextBeforeDelete(); + void restoreContextAfterDelete(); bool m_hasNegativeValues; Q3DTheme *m_cachedTheme; @@ -201,6 +248,7 @@ protected: AxisRenderCache m_axisCacheY; AxisRenderCache m_axisCacheZ; TextureHelper *m_textureHelper; + GLuint m_depthTexture; Q3DScene *m_cachedScene; bool m_selectionDirty; @@ -212,28 +260,75 @@ protected: QRect m_secondarySubViewport; float m_devicePixelRatio; bool m_selectionLabelDirty; - bool m_clickPending; + bool m_clickResolved; + bool m_graphPositionQueryPending; + bool m_graphPositionQueryResolved; QAbstract3DSeries *m_clickedSeries; QAbstract3DGraph::ElementType m_clickedType; int m_selectedLabelIndex; int m_selectedCustomItemIndex; + QVector3D m_queriedGraphPosition; + QPoint m_graphPositionQuery; QString m_selectionLabel; LabelItem *m_selectionLabelItem; int m_visibleSeriesCount; ShaderHelper *m_customItemShader; + ShaderHelper *m_volumeTextureShader; + ShaderHelper *m_volumeTextureLowDefShader; + ShaderHelper *m_volumeTextureSliceShader; + ShaderHelper *m_volumeSliceFrameShader; + ShaderHelper *m_labelShader; + ShaderHelper *m_cursorPositionShader; + GLuint m_cursorPositionFrameBuffer; + GLuint m_cursorPositionTexture; bool m_useOrthoProjection; bool m_xFlipped; bool m_yFlipped; bool m_zFlipped; + bool m_yFlippedForGrid; ObjectHelper *m_backgroundObj; // Shared reference ObjectHelper *m_gridLineObj; // Shared reference ObjectHelper *m_labelObj; // Shared reference + ObjectHelper *m_positionMapperObj; // Shared reference float m_graphAspectRatio; + float m_graphHorizontalAspectRatio; + bool m_polarGraph; + float m_radialLabelOffset; + float m_polarRadius; + + QQuaternion m_xRightAngleRotation; + QQuaternion m_yRightAngleRotation; + QQuaternion m_zRightAngleRotation; + QQuaternion m_xRightAngleRotationNeg; + QQuaternion m_yRightAngleRotationNeg; + QQuaternion m_zRightAngleRotationNeg; + QQuaternion m_xFlipRotation; + QQuaternion m_zFlipRotation; + + float m_requestedMargin; + float m_vBackgroundMargin; + float m_hBackgroundMargin; + float m_scaleXWithBackground; + float m_scaleYWithBackground; + float m_scaleZWithBackground; + + QVector3D m_oldCameraTarget; + + bool m_reflectionEnabled; + qreal m_reflectivity; + + QLocale m_locale; +#if !defined(QT_OPENGL_ES_2) + QOpenGLFunctions_2_1 *m_funcs_2_1; // Not owned +#endif + QPointer<QOpenGLContext> m_context; // Not owned + QWindow *m_dummySurfaceAtDelete; + bool m_isOpenGLES; private: friend class Abstract3DController; diff --git a/src/datavisualization/engine/axisrendercache.cpp b/src/datavisualization/engine/axisrendercache.cpp index 0b7a5c7a..02e3b7f6 100644 --- a/src/datavisualization/engine/axisrendercache.cpp +++ b/src/datavisualization/engine/axisrendercache.cpp @@ -46,6 +46,7 @@ AxisRenderCache::~AxisRenderCache() { foreach (LabelItem *label, m_labelItems) delete label; + m_titleItem.clear(); delete m_formatter; } @@ -54,10 +55,8 @@ void AxisRenderCache::setDrawer(Drawer *drawer) { m_drawer = drawer; m_font = m_drawer->font(); - if (m_drawer) { - QObject::connect(m_drawer, &Drawer::drawerChanged, this, &AxisRenderCache::updateTextures); + if (m_drawer) updateTextures(); - } } void AxisRenderCache::setType(QAbstract3DAxis::AxisType type) @@ -106,10 +105,12 @@ void AxisRenderCache::setLabels(const QStringList &labels) if (i >= oldSize) m_labelItems.append(new LabelItem); if (m_drawer) { - if (labels.at(i).isEmpty()) + if (labels.at(i).isEmpty()) { m_labelItems[i]->clear(); - else if (i >= oldSize || labels.at(i) != m_labels.at(i)) + } else if (i >= oldSize || labels.at(i) != m_labels.at(i) + || m_labelItems[i]->size().width() != widest) { m_drawer->generateLabelItem(*m_labelItems[i], labels.at(i), widest); + } } } m_labels = labels; @@ -175,6 +176,13 @@ void AxisRenderCache::updateTextures() } } +void AxisRenderCache::clearLabels() +{ + m_titleItem.clear(); + for (int i = 0; i < m_labels.size(); i++) + m_labelItems[i]->clear(); +} + int AxisRenderCache::maxLabelWidth(const QStringList &labels) const { int labelWidth = 0; diff --git a/src/datavisualization/engine/axisrendercache_p.h b/src/datavisualization/engine/axisrendercache_p.h index 90321740..e32c590e 100644 --- a/src/datavisualization/engine/axisrendercache_p.h +++ b/src/datavisualization/engine/axisrendercache_p.h @@ -107,8 +107,8 @@ public: inline bool isTitleFixed() const { return m_titleFixed; } inline void setTitleFixed(bool fixed) { m_titleFixed = fixed; } -public slots: void updateTextures(); + void clearLabels(); private: int maxLabelWidth(const QStringList &labels) const; diff --git a/src/datavisualization/engine/bars3dcontroller.cpp b/src/datavisualization/engine/bars3dcontroller.cpp index 3a240c24..2092dd60 100644 --- a/src/datavisualization/engine/bars3dcontroller.cpp +++ b/src/datavisualization/engine/bars3dcontroller.cpp @@ -36,6 +36,7 @@ Bars3DController::Bars3DController(QRect boundRect, Q3DScene *scene) m_isBarSpecRelative(true), m_barThicknessRatio(1.0f), m_barSpacing(QSizeF(1.0, 1.0)), + m_floorLevel(0.0f), m_renderer(0) { // Setting a null axis creates a new default axis according to orientation and graph type. @@ -83,6 +84,12 @@ void Bars3DController::synchDataToRenderer() needSceneUpdate = true; } + // Floor level update requires data update, so do before abstract sync + if (m_changeTracker.floorLevelChanged) { + m_renderer->updateFloorLevel(m_floorLevel); + m_changeTracker.floorLevelChanged = false; + } + Abstract3DController::synchDataToRenderer(); // Notify changes to renderer @@ -114,12 +121,10 @@ void Bars3DController::synchDataToRenderer() m_changeTracker.selectedBarChanged = false; } - if (needSceneUpdate) { - // Since scene is updated before axis updates are handled, - // do another render pass for scene update - m_scene->d_ptr->m_sceneDirty = true; - emitNeedRender(); - } + // Since scene is updated before axis updates are handled, do another render pass to + // properly update controller side camera limits. + if (needSceneUpdate) + m_scene->d_ptr->markDirty(); } void Bars3DController::handleArrayReset() @@ -487,6 +492,19 @@ bool Bars3DController::isBarSpecRelative() return m_isBarSpecRelative; } +void Bars3DController::setFloorLevel(float level) +{ + m_floorLevel = level; + m_isDataDirty = true; + m_changeTracker.floorLevelChanged = true; + emitNeedRender(); +} + +float Bars3DController::floorLevel() const +{ + return m_floorLevel; +} + void Bars3DController::setSelectionMode(QAbstract3DGraph::SelectionFlags mode) { if (mode.testFlag(QAbstract3DGraph::SelectionSlice) diff --git a/src/datavisualization/engine/bars3dcontroller_p.h b/src/datavisualization/engine/bars3dcontroller_p.h index 4f0e1f20..ac62f37d 100644 --- a/src/datavisualization/engine/bars3dcontroller_p.h +++ b/src/datavisualization/engine/bars3dcontroller_p.h @@ -43,13 +43,15 @@ struct Bars3DChangeBitField { bool selectedBarChanged : 1; bool rowsChanged : 1; bool itemChanged : 1; + bool floorLevelChanged : 1; Bars3DChangeBitField() : multiSeriesScalingChanged(true), barSpecsChanged(true), selectedBarChanged(true), rowsChanged(false), - itemChanged(false) + itemChanged(false), + floorLevelChanged(false) { } }; @@ -84,6 +86,7 @@ private: bool m_isBarSpecRelative; GLfloat m_barThicknessRatio; QSizeF m_barSpacing; + float m_floorLevel; // Rendering Bars3DRenderer *m_renderer; @@ -107,6 +110,8 @@ public: GLfloat barThickness(); QSizeF barSpacing(); bool isBarSpecRelative(); + void setFloorLevel(float level); + float floorLevel() const; inline QBar3DSeries *selectedSeries() const { return m_selectedBarSeries; } diff --git a/src/datavisualization/engine/bars3drenderer.cpp b/src/datavisualization/engine/bars3drenderer.cpp index 689f3f5d..b6a191a9 100644 --- a/src/datavisualization/engine/bars3drenderer.cpp +++ b/src/datavisualization/engine/bars3drenderer.cpp @@ -31,7 +31,6 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION -const GLfloat gridLineWidth = 0.005f; const bool sliceGridLabels = true; Bars3DRenderer::Bars3DRenderer(Bars3DController *controller) @@ -48,9 +47,7 @@ Bars3DRenderer::Bars3DRenderer(Bars3DController *controller) m_depthShader(0), m_selectionShader(0), m_backgroundShader(0), - m_labelShader(0), m_bgrTexture(0), - m_depthTexture(0), m_selectionTexture(0), m_depthFrameBuffer(0), m_selectionFrameBuffer(0), @@ -67,7 +64,6 @@ Bars3DRenderer::Bars3DRenderer(Bars3DController *controller) m_scaleFactor(0), m_maxSceneSize(40.0f), m_visualSelectedBarPos(Bars3DController::invalidSelectionPosition()), - m_resetCameraBaseOrientation(true), m_selectedBarPos(Bars3DController::invalidSelectionPosition()), m_selectedSeriesCache(0), m_noZeroInRange(false), @@ -79,17 +75,22 @@ Bars3DRenderer::Bars3DRenderer(Bars3DController *controller) m_keepSeriesUniform(false), m_haveUniformColorSeries(false), m_haveGradientSeries(false), - m_zeroPosition(0.0f) + m_zeroPosition(0.0f), + m_xScaleFactor(1.0f), + m_zScaleFactor(1.0f), + m_floorLevel(0.0f), + m_actualFloorLevel(0.0f) { m_axisCacheY.setScale(2.0f); m_axisCacheY.setTranslate(-1.0f); - initializeOpenGLFunctions(); initializeOpenGL(); } Bars3DRenderer::~Bars3DRenderer() { + fixContextBeforeDelete(); + if (QOpenGLContext::currentContext()) { m_textureHelper->glDeleteFramebuffers(1, &m_selectionFrameBuffer); m_textureHelper->glDeleteRenderbuffers(1, &m_selectionDepthBuffer); @@ -103,7 +104,6 @@ Bars3DRenderer::~Bars3DRenderer() delete m_depthShader; delete m_selectionShader; delete m_backgroundShader; - delete m_labelShader; } void Bars3DRenderer::initializeOpenGL() @@ -111,13 +111,9 @@ void Bars3DRenderer::initializeOpenGL() Abstract3DRenderer::initializeOpenGL(); // Initialize shaders - initLabelShaders(QStringLiteral(":/shaders/vertexLabel"), - QStringLiteral(":/shaders/fragmentLabel")); -#if !defined(QT_OPENGL_ES_2) // Init depth shader (for shadows). Init in any case, easier to handle shadow activation if done via api. initDepthShader(); -#endif // Init selection shader initSelectionShader(); @@ -125,13 +121,57 @@ void Bars3DRenderer::initializeOpenGL() // Load grid line mesh loadGridLineMesh(); - // Load label mesh - loadLabelMesh(); - // Load background mesh (we need to be initialized first) loadBackgroundMesh(); } +void Bars3DRenderer::fixCameraTarget(QVector3D &target) +{ + target.setX(target.x() * m_xScaleFactor); + target.setY(0.0f); + target.setZ(target.z() * -m_zScaleFactor); +} + +void Bars3DRenderer::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_xScaleFactor) + minBounds.setX(-1.0f + (2.0f * qAbs(minBounds.x() + m_xScaleFactor) / itemRangeX)); + else + minBounds.setX(-1.0f); + + if (minBounds.y() < -1.0f + m_backgroundAdjustment) + minBounds.setY(-(-1.0f + (2.0f * qAbs(minBounds.y() + 1.0f - m_backgroundAdjustment) / itemRangeY))); + else + minBounds.setY(1.0f); + + if (minBounds.z() < -m_zScaleFactor) + minBounds.setZ(-(-1.0f + (2.0f * qAbs(minBounds.z() + m_zScaleFactor) / itemRangeZ))); + else + minBounds.setZ(1.0f); + + if (maxBounds.x() > m_xScaleFactor) + maxBounds.setX(1.0f - (2.0f * qAbs(maxBounds.x() - m_xScaleFactor) / itemRangeX)); + else + maxBounds.setX(1.0f); + + if (maxBounds.y() > 1.0f + m_backgroundAdjustment) + maxBounds.setY(-(1.0f - (2.0f * qAbs(maxBounds.y() - 1.0f - m_backgroundAdjustment) / itemRangeY))); + else + maxBounds.setY(-1.0f); + + if (maxBounds.z() > m_zScaleFactor) + maxBounds.setZ(-(1.0f - (2.0f * qAbs(maxBounds.z() - m_zScaleFactor) / itemRangeZ))); + else + maxBounds.setZ(-1.0f); +} + void Bars3DRenderer::updateData() { int minRow = m_axisCacheZ.min(); @@ -163,11 +203,11 @@ void Bars3DRenderer::updateData() GLfloat sceneRatio = qMin(GLfloat(newColumns) / GLfloat(newRows), GLfloat(newRows) / GLfloat(newColumns)); m_maxSceneSize = 2.0f * qSqrt(sceneRatio * newColumns * newRows); - // Calculate here and at setting bar specs - calculateSceneScalingFactors(); } - m_zeroPosition = m_axisCacheY.formatter()->positionAt(0.0f); + calculateSceneScalingFactors(); + + m_zeroPosition = m_axisCacheY.formatter()->positionAt(m_actualFloorLevel); foreach (SeriesRenderCache *baseCache, m_renderCacheList) { BarSeriesRenderCache *cache = static_cast<BarSeriesRenderCache *>(baseCache); @@ -390,14 +430,6 @@ void Bars3DRenderer::updateScene(Q3DScene *scene) } } - if (m_resetCameraBaseOrientation) { - // Set initial camera position. Also update if height adjustment has changed. - scene->activeCamera()->d_ptr->setBaseOrientation(cameraDistanceVector, - zeroVector, - upVector); - m_resetCameraBaseOrientation = false; - } - Abstract3DRenderer::updateScene(scene); updateSlicingActive(scene->isSlicingActive()); @@ -418,10 +450,17 @@ void Bars3DRenderer::render(GLuint defaultFboHandle) void Bars3DRenderer::drawSlicedScene() { + if (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionRow) + == m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionColumn)) { + qWarning("Invalid selection mode. Either QAbstract3DGraph::SelectionRow or" + " QAbstract3DGraph::SelectionColumn must be set before calling" + " setSlicingActive(true)."); + return; + } + GLfloat barPosX = 0; QVector3D lightPos; QVector4D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor()); - static QQuaternion ninetyDegreeRotation = QQuaternion::fromAxisAndAngle(upVector, 90.0f); // Specify viewport glViewport(m_secondarySubViewport.x(), @@ -482,11 +521,12 @@ void Bars3DRenderer::drawSlicedScene() // Draw grid lines if (m_cachedTheme->isGridEnabled()) { glDisable(GL_DEPTH_TEST); -#if !(defined QT_OPENGL_ES_2) - ShaderHelper *lineShader = m_backgroundShader; -#else - ShaderHelper *lineShader = m_selectionShader; // Plain color shader for GL_LINES -#endif + ShaderHelper *lineShader; + if (m_isOpenGLES) + lineShader = m_selectionShader; // Plain color shader for GL_LINES + else + lineShader = m_backgroundShader; + // Bind line shader lineShader->bind(); @@ -496,7 +536,8 @@ void Bars3DRenderer::drawSlicedScene() lineShader->setUniformValue(lineShader->view(), viewMatrix); lineShader->setUniformValue(lineShader->color(), lineColor); lineShader->setUniformValue(lineShader->ambientS(), - m_cachedTheme->ambientLightStrength() * 2.3f); + m_cachedTheme->ambientLightStrength() + + m_cachedTheme->lightStrength() / 7.0f); lineShader->setUniformValue(lineShader->lightS(), 0.0f); lineShader->setUniformValue(lineShader->lightColor(), lightColor); @@ -524,11 +565,10 @@ void Bars3DRenderer::drawSlicedScene() lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); // Draw the object -#if !(defined QT_OPENGL_ES_2) - m_drawer->drawObject(lineShader, m_gridLineObj); -#else - m_drawer->drawLine(lineShader); -#endif + if (m_isOpenGLES) + m_drawer->drawLine(lineShader); + else + m_drawer->drawObject(lineShader, m_gridLineObj); // Check if we have a line at zero position already if (gridPos == (barPosYAdjustment + zeroPosAdjustment)) @@ -553,18 +593,16 @@ void Bars3DRenderer::drawSlicedScene() m_cachedTheme->labelTextColor())); // Draw the object -#if !(defined QT_OPENGL_ES_2) - m_drawer->drawObject(lineShader, m_gridLineObj); -#else - m_drawer->drawLine(lineShader); -#endif + if (m_isOpenGLES) + m_drawer->drawLine(lineShader); + else + m_drawer->drawObject(lineShader, m_gridLineObj); } } if (sliceGridLabels) { // Bind label shader m_labelShader->bind(); - glEnable(GL_TEXTURE_2D); glCullFace(GL_BACK); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); @@ -587,7 +625,6 @@ void Bars3DRenderer::drawSlicedScene() } labelNbr++; } - glDisable(GL_TEXTURE_2D); glDisable(GL_BLEND); glEnable(GL_DEPTH_TEST); } @@ -606,14 +643,16 @@ void Bars3DRenderer::drawSlicedScene() m_barShader->setUniformValue(m_barShader->view(), viewMatrix); m_barShader->setUniformValue(m_barShader->lightS(), 0.15f); m_barShader->setUniformValue(m_barShader->ambientS(), - m_cachedTheme->ambientLightStrength() * 2.3f); + m_cachedTheme->ambientLightStrength() + + m_cachedTheme->lightStrength() / 7.0f); m_barShader->setUniformValue(m_barShader->lightColor(), lightColor); m_barGradientShader->bind(); m_barGradientShader->setUniformValue(m_barGradientShader->lightP(), lightPos); m_barGradientShader->setUniformValue(m_barGradientShader->view(), viewMatrix); m_barGradientShader->setUniformValue(m_barGradientShader->lightS(), 0.15f); m_barGradientShader->setUniformValue(m_barGradientShader->ambientS(), - m_cachedTheme->ambientLightStrength() * 2.3f); + m_cachedTheme->ambientLightStrength() + + m_cachedTheme->lightStrength() / 7.0f); m_barGradientShader->setUniformValue(m_barGradientShader->gradientMin(), 0.0f); m_barGradientShader->setUniformValue(m_barGradientShader->lightColor(), lightColor); @@ -700,7 +739,7 @@ void Bars3DRenderer::drawSlicedScene() barPosX = item.translation().x(); } else { barPosX = -(item.translation().z()); // flip z; frontmost bar to the left - barRotation *= ninetyDegreeRotation; + barRotation *= m_yRightAngleRotation; } modelMatrix.translate(barPosX, barPosY, 0.0f); @@ -760,7 +799,6 @@ void Bars3DRenderer::drawSlicedScene() // Draw labels m_labelShader->bind(); glDisable(GL_DEPTH_TEST); - glEnable(GL_TEXTURE_2D); glCullFace(GL_BACK); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); @@ -778,6 +816,12 @@ void Bars3DRenderer::drawSlicedScene() int labelCount = m_sliceCache->labelItems().size(); for (int labelNo = 0; labelNo < labelCount; labelNo++) { + // Check for invalid usage (no selection when setting slicing active) + if (!firstVisualSliceArray) { + qWarning("No slice data found. Make sure there is a valid selection."); + continue; + } + // Get labels from first series only const BarRenderSliceItem &item = firstVisualSliceArray->at(labelNo); m_dummyBarRenderItem.setTranslation(QVector3D(item.translation().x(), @@ -889,7 +933,6 @@ void Bars3DRenderer::drawSlicedScene() m_cachedSelectionMode, m_labelShader, m_labelObj, activeCamera, false, false, Drawer::LabelMid, Qt::AlignBottom); - glDisable(GL_TEXTURE_2D); glDisable(GL_BLEND); glEnable(GL_DEPTH_TEST); @@ -912,8 +955,6 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) GLfloat colPos = 0; GLfloat rowPos = 0; - QVector4D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor()); - const Q3DCamera *activeCamera = m_cachedScene->activeCamera(); glViewport(m_primarySubViewport.x(), @@ -990,15 +1031,9 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) QMatrix4x4 projectionViewMatrix = projectionMatrix * viewMatrix; - bool rowMode = m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionRow); - - GLfloat rowScaleFactor = m_rowWidth / m_scaleFactor; - GLfloat columnScaleFactor = m_columnDepth / m_scaleFactor; - BarRenderItem *selectedBar(0); -#if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) { // Render scene into a depth texture for using with shadow mapping // Enable drawing to depth framebuffer glBindFramebuffer(GL_FRAMEBUFFER, m_depthFrameBuffer); @@ -1052,6 +1087,12 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) shadowOffset = -0.015f; } + if (m_cachedTheme->isBackgroundEnabled() && m_reflectionEnabled + && ((m_yFlipped && item.height() > 0.0) + || (!m_yFlipped && item.height() < 0.0))) { + continue; + } + QMatrix4x4 modelMatrix; QMatrix4x4 MVPMatrix; @@ -1097,8 +1138,9 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) } Abstract3DRenderer::drawCustomItems(RenderingDepth, m_depthShader, viewMatrix, - projectionViewMatrix, depthProjectionViewMatrix, - m_depthTexture, m_shadowQualityToShader); + projectionViewMatrix, + depthProjectionViewMatrix, m_depthTexture, + m_shadowQualityToShader); // Disable drawing to depth framebuffer (= enable drawing to screen) glBindFramebuffer(GL_FRAMEBUFFER, defaultFboHandle); @@ -1112,12 +1154,22 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) m_primarySubViewport.width(), m_primarySubViewport.height()); } -#endif + + // Do position mapping when necessary + if (m_graphPositionQueryPending) { + QVector3D graphDimensions(m_xScaleFactor, 0.0f, m_zScaleFactor); + queriedGraphPosition(projectionViewMatrix, graphDimensions, defaultFboHandle); + + // Y is always at floor level + m_queriedGraphPosition.setY(0.0f); + emit needRender(); + } // Skip selection mode drawing if we're slicing or have no selection mode if (!m_cachedIsSlicingActivated && m_cachedSelectionMode > QAbstract3DGraph::SelectionNone && m_selectionState == SelectOnScene - && (m_visibleSeriesCount > 0 || !m_customRenderCache.isEmpty())) { + && (m_visibleSeriesCount > 0 || !m_customRenderCache.isEmpty()) + && m_selectionTexture) { // Bind selection shader m_selectionShader->bind(); @@ -1181,18 +1233,20 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) } } glCullFace(GL_BACK); - Abstract3DRenderer::drawCustomItems(RenderingSelection, m_selectionShader, viewMatrix, + Abstract3DRenderer::drawCustomItems(RenderingSelection, m_selectionShader, + viewMatrix, projectionViewMatrix, depthProjectionViewMatrix, m_depthTexture, m_shadowQualityToShader); - drawLabels(true, activeCamera, viewMatrix, projectionMatrix, rowScaleFactor, - columnScaleFactor); + drawLabels(true, activeCamera, viewMatrix, projectionMatrix); + drawBackground(backgroundRotation, depthProjectionViewMatrix, projectionViewMatrix, + viewMatrix, false, true); glEnable(GL_DITHER); // Read color under cursor - QVector4D clickedColor = Utils::getSelection(m_inputPosition, - m_viewport.height()); + QVector4D clickedColor = Utils::getSelection(m_inputPosition, m_viewport.height()); m_clickedPosition = selectionColorToArrayPosition(clickedColor); m_clickedSeries = selectionColorToSeries(clickedColor); + m_clickResolved = true; emit needRender(); @@ -1204,8 +1258,133 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) m_primarySubViewport.height()); } - // Enable texturing - glEnable(GL_TEXTURE_2D); + if (m_reflectionEnabled) { + // + // Draw reflections + // + glDisable(GL_DEPTH_TEST); + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + glEnable(GL_STENCIL_TEST); + glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE); + glStencilFunc(GL_ALWAYS, 1, 0xffffffff); + + // Draw background stencil + drawBackground(backgroundRotation, depthProjectionViewMatrix, projectionViewMatrix, + viewMatrix); + + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glEnable(GL_DEPTH_TEST); + + glStencilFunc(GL_EQUAL, 1, 0xffffffff); + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + + // Set light + QVector3D reflectionLightPos = lightPos; + reflectionLightPos.setY(-(lightPos.y())); + m_cachedScene->activeLight()->setPosition(reflectionLightPos); + + // Draw bar reflections + (void)drawBars(&selectedBar, depthProjectionViewMatrix, + projectionViewMatrix, viewMatrix, + startRow, stopRow, stepRow, + startBar, stopBar, stepBar, -1.0f); + + Abstract3DRenderer::drawCustomItems(RenderingNormal, m_customItemShader, + viewMatrix, projectionViewMatrix, + depthProjectionViewMatrix, m_depthTexture, + m_shadowQualityToShader, -1.0f); + + // Reset light + m_cachedScene->activeLight()->setPosition(lightPos); + + glDisable(GL_STENCIL_TEST); + + glCullFace(GL_BACK); + } + + // + // Draw the real scene + // + // Draw background + if (m_reflectionEnabled) { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + drawBackground(backgroundRotation, depthProjectionViewMatrix, projectionViewMatrix, + viewMatrix, true); + glDisable(GL_BLEND); + } else { + drawBackground(backgroundRotation, depthProjectionViewMatrix, projectionViewMatrix, + viewMatrix); + } + + // Draw bars + bool barSelectionFound = drawBars(&selectedBar, depthProjectionViewMatrix, + projectionViewMatrix, viewMatrix, + startRow, stopRow, stepRow, + startBar, stopBar, stepBar); + + // Draw grid lines + drawGridLines(depthProjectionViewMatrix, projectionViewMatrix, viewMatrix); + + // Draw custom items + Abstract3DRenderer::drawCustomItems(RenderingNormal, m_customItemShader, viewMatrix, + projectionViewMatrix, depthProjectionViewMatrix, + m_depthTexture, m_shadowQualityToShader); + + // Draw labels + drawLabels(false, activeCamera, viewMatrix, projectionMatrix); + + // Handle selected bar label generation + if (barSelectionFound) { + // Print value of selected bar + glDisable(GL_DEPTH_TEST); + // Draw the selection label + LabelItem &labelItem = selectionLabelItem(); + if (m_selectedBar != selectedBar || 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_selectedBar = selectedBar; + } + + Drawer::LabelPosition position = + m_selectedBar->height() >= 0 ? Drawer::LabelOver : Drawer::LabelBelow; + + m_drawer->drawLabel(*selectedBar, labelItem, viewMatrix, projectionMatrix, + zeroVector, identityQuaternion, selectedBar->height(), + m_cachedSelectionMode, m_labelShader, + m_labelObj, activeCamera, true, false, position); + + // Reset label update flag; they should have been updated when we get here + m_updateLabels = false; + + glEnable(GL_DEPTH_TEST); + } else { + m_selectedBar = 0; + } + + glDisable(GL_BLEND); + + // Release shader + glUseProgram(0); + m_selectionDirty = false; +} + +bool Bars3DRenderer::drawBars(BarRenderItem **selectedBar, + const QMatrix4x4 &depthProjectionViewMatrix, + const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &viewMatrix, + GLint startRow, GLint stopRow, GLint stepRow, + GLint startBar, GLint stopBar, GLint stepBar, GLfloat reflection) +{ + QVector3D lightPos = m_cachedScene->activeLight()->position(); + QVector4D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor()); + + bool rowMode = m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionRow); ShaderHelper *barShader = 0; GLuint gradientTexture = 0; @@ -1251,7 +1430,6 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) m_sliceTitleItem = 0; } - // Draw bars glEnable(GL_POLYGON_OFFSET_FILL); glPolygonOffset(0.5f, 1.0f); @@ -1302,12 +1480,12 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) } previousColorStyle = colorStyle; - for (int row = startRow; row != stopRow; row += stepRow) { BarRenderItemRow &renderRow = renderArray[row]; for (int bar = startBar; bar != stopBar; bar += stepBar) { BarRenderItem &item = renderRow[bar]; - if (item.height() < 0) + float adjustedHeight = reflection * item.height(); + if (adjustedHeight < 0) glCullFace(GL_FRONT); else glCullFace(GL_BACK); @@ -1316,13 +1494,13 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) QMatrix4x4 itModelMatrix; QMatrix4x4 MVPMatrix; - colPos = (bar + seriesPos) * (m_cachedBarSpacing.width()); - rowPos = (row + 0.5f) * (m_cachedBarSpacing.height()); + GLfloat colPos = (bar + seriesPos) * (m_cachedBarSpacing.width()); + GLfloat rowPos = (row + 0.5f) * (m_cachedBarSpacing.height()); modelMatrix.translate((colPos - m_rowWidth) / m_scaleFactor, - item.height(), + adjustedHeight, (m_columnDepth - rowPos) / m_scaleFactor); - modelScaler.setY(item.height()); + modelScaler.setY(adjustedHeight); if (!seriesRotation.isIdentity() || !item.rotation().isIdentity()) { QQuaternion totalRotation = seriesRotation * item.rotation(); modelMatrix.rotate(totalRotation); @@ -1357,8 +1535,8 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) // We have no ownership, don't delete the previous one if (!m_cachedIsSlicingActivated && m_selectedSeriesCache == cache) { - selectedBar = &item; - selectedBar->setPosition(QPoint(row, bar)); + *selectedBar = &item; + (*selectedBar)->setPosition(QPoint(row, bar)); item.setTranslation(modelMatrix.column(3).toVector3D()); barSelectionFound = true; } @@ -1438,8 +1616,15 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) } } - // Skip drawing of 0-height bars - if (item.height() != 0) { + if (item.height() == 0) { + continue; + } else if ((m_reflectionEnabled + && (reflection == 1.0f + || (reflection != 1.0f + && ((m_yFlipped && item.height() < 0.0) + || (!m_yFlipped && item.height() > 0.0))))) + || !m_reflectionEnabled) { + // Skip drawing of 0-height bars and reflections of bars on the "wrong side" // Set shader bindings barShader->setUniformValue(barShader->model(), modelMatrix); barShader->setUniformValue(barShader->nModel(), @@ -1452,8 +1637,10 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) qAbs(item.height()) / m_gradientFraction); } -#if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + if (((m_reflectionEnabled && reflection == 1.0f + && m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) + || m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) + && !m_isOpenGLES) { // Set shadow shader bindings QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; barShader->setUniformValue(barShader->shadowQ(), @@ -1465,13 +1652,15 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) // Draw the object m_drawer->drawObject(barShader, barObj, gradientTexture, m_depthTexture); - } else -#else - Q_UNUSED(shadowLightStrength); -#endif - { + } else { // Set shadowless shader bindings - barShader->setUniformValue(barShader->lightS(), lightStrength); + if (m_reflectionEnabled && reflection != 1.0f + && m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + barShader->setUniformValue(barShader->lightS(), + adjustedLightStrength); + } else { + barShader->setUniformValue(barShader->lightS(), lightStrength); + } // Draw the object m_drawer->drawObject(barShader, barObj, gradientTexture); @@ -1481,122 +1670,145 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) } } } - glDisable(GL_POLYGON_OFFSET_FILL); // Reset culling glCullFace(GL_BACK); - // Bind background shader - m_backgroundShader->bind(); + return barSelectionFound; +} +void Bars3DRenderer::drawBackground(GLfloat backgroundRotation, + const QMatrix4x4 &depthProjectionViewMatrix, + const QMatrix4x4 &projectionViewMatrix, + const QMatrix4x4 &viewMatrix, bool reflectingDraw, + bool drawingSelectionBuffer) +{ // Draw background if (m_cachedTheme->isBackgroundEnabled() && m_backgroundObj) { + QVector3D lightPos = m_cachedScene->activeLight()->position(); + QVector4D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor()); + GLfloat adjustedLightStrength = m_cachedTheme->lightStrength() / 10.0f; + ShaderHelper *shader = 0; + + // Bind background shader + if (drawingSelectionBuffer) + shader = m_selectionShader; // Use single color shader when drawing to selection buffer + else + shader = m_backgroundShader; + shader->bind(); + QMatrix4x4 modelMatrix; QMatrix4x4 MVPMatrix; QMatrix4x4 itModelMatrix; - QVector3D backgroundScaler(rowScaleFactor, 1.0f, columnScaleFactor); - modelMatrix.translate(0.0f, m_backgroundAdjustment, 0.0f); + QVector3D backgroundScaler(m_scaleXWithBackground, m_scaleYWithBackground, + m_scaleZWithBackground); + QVector4D backgroundColor = Utils::vectorFromColor(m_cachedTheme->backgroundColor()); + if (m_reflectionEnabled) + backgroundColor.setW(backgroundColor.w() * m_reflectivity); + // Set shader bindings + shader->setUniformValue(shader->lightP(), lightPos); + shader->setUniformValue(shader->view(), viewMatrix); + if (drawingSelectionBuffer) { + // Use selectionSkipColor for background when drawing to selection buffer + shader->setUniformValue(shader->color(), selectionSkipColor); + } else { + shader->setUniformValue(shader->color(), backgroundColor); + } + shader->setUniformValue(shader->ambientS(), + m_cachedTheme->ambientLightStrength() * 2.0f); + shader->setUniformValue(shader->lightColor(), lightColor); + + // Draw floor modelMatrix.scale(backgroundScaler); - itModelMatrix.scale(backgroundScaler); - modelMatrix.rotate(backgroundRotation, 0.0f, 1.0f, 0.0f); - itModelMatrix.rotate(backgroundRotation, 0.0f, 1.0f, 0.0f); + + if (m_yFlipped) + modelMatrix.rotate(m_xRightAngleRotation); + else + modelMatrix.rotate(m_xRightAngleRotationNeg); + + itModelMatrix = modelMatrix; #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); + // Set changed shader bindings + shader->setUniformValue(shader->model(), modelMatrix); + shader->setUniformValue(shader->nModel(), itModelMatrix.inverted().transposed()); + shader->setUniformValue(shader->MVP(), MVPMatrix); -#if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + 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(), - adjustedLightStrength); - + shader->setUniformValue(shader->depth(), depthMVPMatrix); // Draw the object - m_drawer->drawObject(m_backgroundShader, m_backgroundObj, 0, m_depthTexture); - } else -#endif - { - // Set shadowless shader bindings - m_backgroundShader->setUniformValue(m_backgroundShader->lightS(), - m_cachedTheme->lightStrength()); - + m_drawer->drawObject(shader, m_gridLineObj, 0, m_depthTexture); + } else { // Draw the object - m_drawer->drawObject(m_backgroundShader, m_backgroundObj); + m_drawer->drawObject(shader, m_gridLineObj); } - // Draw floor + // Draw walls modelMatrix = QMatrix4x4(); itModelMatrix = QMatrix4x4(); + modelMatrix.translate(0.0f, m_backgroundAdjustment, 0.0f); modelMatrix.scale(backgroundScaler); - - if (m_yFlipped) - modelMatrix.rotate(90.0f, 1.0f, 0.0f, 0.0f); - else - modelMatrix.rotate(-90.0f, 1.0f, 0.0f, 0.0f); - - itModelMatrix = modelMatrix; + itModelMatrix.scale(backgroundScaler); + modelMatrix.rotate(backgroundRotation, 0.0f, 1.0f, 0.0f); + itModelMatrix.rotate(backgroundRotation, 0.0f, 1.0f, 0.0f); #ifdef SHOW_DEPTH_TEXTURE_SCENE MVPMatrix = depthProjectionViewMatrix * modelMatrix; #else MVPMatrix = projectionViewMatrix * modelMatrix; #endif + // Set changed shader bindings - m_backgroundShader->setUniformValue(m_backgroundShader->model(), modelMatrix); - m_backgroundShader->setUniformValue(m_backgroundShader->nModel(), - itModelMatrix.inverted().transposed()); - m_backgroundShader->setUniformValue(m_backgroundShader->MVP(), MVPMatrix); + shader->setUniformValue(shader->model(), modelMatrix); + shader->setUniformValue(shader->nModel(), itModelMatrix.inverted().transposed()); + shader->setUniformValue(shader->MVP(), MVPMatrix); + if (!m_reflectionEnabled || (m_reflectionEnabled && reflectingDraw)) { + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) { + // Set shadow shader bindings + QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; + shader->setUniformValue(shader->shadowQ(), m_shadowQualityToShader); + shader->setUniformValue(shader->depth(), depthMVPMatrix); + shader->setUniformValue(shader->lightS(), adjustedLightStrength); -#if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { - // Set shadow shader bindings - QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; - m_backgroundShader->setUniformValue(m_backgroundShader->depth(), depthMVPMatrix); - // Draw the object - m_drawer->drawObject(m_backgroundShader, m_gridLineObj, 0, m_depthTexture); - } else -#endif - { - // Draw the object - m_drawer->drawObject(m_backgroundShader, m_gridLineObj); + // Draw the object + m_drawer->drawObject(shader, m_backgroundObj, 0, m_depthTexture); + } else { + // Set shadowless shader bindings + shader->setUniformValue(shader->lightS(), m_cachedTheme->lightStrength()); + + // Draw the object + m_drawer->drawObject(shader, m_backgroundObj); + } } } +} - // Disable textures - glDisable(GL_TEXTURE_2D); - - // Draw grid lines +void Bars3DRenderer::drawGridLines(const QMatrix4x4 &depthProjectionViewMatrix, + const QMatrix4x4 &projectionViewMatrix, + const QMatrix4x4 &viewMatrix) +{ if (m_cachedTheme->isGridEnabled()) { -#if !(defined QT_OPENGL_ES_2) - ShaderHelper *lineShader = m_backgroundShader; -#else - ShaderHelper *lineShader = m_selectionShader; // Plain color shader for GL_LINES -#endif + ShaderHelper *lineShader; + if (m_isOpenGLES) + lineShader = m_selectionShader; // Plain color shader for GL_LINES + else + lineShader = m_backgroundShader; + QQuaternion lineRotation; + QVector3D lightPos = m_cachedScene->activeLight()->position(); + QVector4D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor()); + // Bind bar shader lineShader->bind(); @@ -1607,15 +1819,12 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) lineShader->setUniformValue(lineShader->color(), barColor); lineShader->setUniformValue(lineShader->ambientS(), m_cachedTheme->ambientLightStrength()); lineShader->setUniformValue(lineShader->lightColor(), lightColor); -#if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + 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 -#endif - { + } else { // Set shadowless shader bindings lineShader->setUniformValue(lineShader->lightS(), m_cachedTheme->lightStrength() / 2.5f); @@ -1625,12 +1834,12 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) if (m_yFlipped) yFloorLinePosition = -yFloorLinePosition; - QVector3D gridLineScaler(rowScaleFactor, gridLineWidth, gridLineWidth); + QVector3D gridLineScaler(m_scaleXWithBackground, gridLineWidth, gridLineWidth); if (m_yFlipped) - lineRotation = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, 90.0f); + lineRotation = m_xRightAngleRotation; else - lineRotation = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, -90.0f); + lineRotation = m_xRightAngleRotationNeg; // Floor lines: rows for (GLfloat row = 0.0f; row <= m_cachedRowCount; row++) { @@ -1638,7 +1847,7 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) QMatrix4x4 MVPMatrix; QMatrix4x4 itModelMatrix; - rowPos = row * m_cachedBarSpacing.height(); + GLfloat rowPos = row * m_cachedBarSpacing.height(); modelMatrix.translate(0.0f, yFloorLinePosition, (m_columnDepth - rowPos) / m_scaleFactor); modelMatrix.scale(gridLineScaler); @@ -1654,33 +1863,32 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) itModelMatrix.inverted().transposed()); lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); -#if !defined(QT_OPENGL_ES_2) - 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); + if (m_isOpenGLES) { + m_drawer->drawLine(lineShader); } else { - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj); + 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); -#endif } // Floor lines: columns -#if defined(QT_OPENGL_ES_2) - lineRotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, 90.0f); -#endif - gridLineScaler = QVector3D(gridLineWidth, gridLineWidth, columnScaleFactor); + if (m_isOpenGLES) + lineRotation = m_yRightAngleRotation; + gridLineScaler = QVector3D(gridLineWidth, gridLineWidth, m_scaleZWithBackground); for (GLfloat bar = 0.0f; bar <= m_cachedColumnCount; bar++) { QMatrix4x4 modelMatrix; QMatrix4x4 MVPMatrix; QMatrix4x4 itModelMatrix; - colPos = bar * m_cachedBarSpacing.width(); + GLfloat colPos = bar * m_cachedBarSpacing.width(); modelMatrix.translate((m_rowWidth - colPos) / m_scaleFactor, yFloorLinePosition, 0.0f); modelMatrix.scale(gridLineScaler); @@ -1696,31 +1904,31 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) itModelMatrix.inverted().transposed()); lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); -#if !defined(QT_OPENGL_ES_2) - 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); + if (m_isOpenGLES) { + m_drawer->drawLine(lineShader); } else { - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj); + 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); -#endif } if (m_axisCacheY.segmentCount() > 0) { // Wall lines: back wall int gridLineCount = m_axisCacheY.gridLineCount(); - GLfloat zWallLinePosition = -columnScaleFactor + gridLineOffset; + GLfloat zWallLinePosition = -m_scaleZWithBackground + gridLineOffset; if (m_zFlipped) zWallLinePosition = -zWallLinePosition; - gridLineScaler = QVector3D(rowScaleFactor, gridLineWidth, gridLineWidth); + gridLineScaler = QVector3D(m_scaleXWithBackground, gridLineWidth, gridLineWidth); for (int line = 0; line < gridLineCount; line++) { QMatrix4x4 modelMatrix; QMatrix4x4 MVPMatrix; @@ -1732,8 +1940,8 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) modelMatrix.scale(gridLineScaler); itModelMatrix.scale(gridLineScaler); if (m_zFlipped) { - modelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f); - itModelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f); + modelMatrix.rotate(m_xFlipRotation); + itModelMatrix.rotate(m_xFlipRotation); } MVPMatrix = projectionViewMatrix * modelMatrix; @@ -1744,33 +1952,33 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) itModelMatrix.inverted().transposed()); lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); -#if !defined(QT_OPENGL_ES_2) - 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); + if (m_isOpenGLES) { + m_drawer->drawLine(lineShader); } else { - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj); + 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); -#endif } // Wall lines: side wall - GLfloat xWallLinePosition = -rowScaleFactor + gridLineOffset; + GLfloat xWallLinePosition = -m_scaleXWithBackground + gridLineOffset; if (m_xFlipped) xWallLinePosition = -xWallLinePosition; if (m_xFlipped) - lineRotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, -90.0f); + lineRotation = m_yRightAngleRotationNeg; else - lineRotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, 90.0f); + lineRotation = m_yRightAngleRotation; - gridLineScaler = QVector3D(gridLineWidth, gridLineWidth, columnScaleFactor); + gridLineScaler = QVector3D(gridLineWidth, gridLineWidth, m_scaleZWithBackground); for (int line = 0; line < gridLineCount; line++) { QMatrix4x4 modelMatrix; QMatrix4x4 MVPMatrix; @@ -1792,76 +2000,27 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) itModelMatrix.inverted().transposed()); lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); -#if !defined(QT_OPENGL_ES_2) - 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); + if (m_isOpenGLES) { + m_drawer->drawLine(lineShader); } else { - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj); + 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); -#endif } } } - - Abstract3DRenderer::drawCustomItems(RenderingNormal, m_customItemShader, viewMatrix, - projectionViewMatrix, depthProjectionViewMatrix, - m_depthTexture, m_shadowQualityToShader); - - drawLabels(false, activeCamera, viewMatrix, projectionMatrix, rowScaleFactor, - columnScaleFactor); - - // Handle selected bar label generation - if (barSelectionFound) { - // Print value of selected bar - glDisable(GL_DEPTH_TEST); - // Draw the selection label - LabelItem &labelItem = selectionLabelItem(); - if (m_selectedBar != selectedBar || 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_selectedBar = selectedBar; - } - - Drawer::LabelPosition position = - m_selectedBar->height() >= 0 ? Drawer::LabelOver : Drawer::LabelBelow; - - m_drawer->drawLabel(*selectedBar, labelItem, viewMatrix, projectionMatrix, - zeroVector, identityQuaternion, selectedBar->height(), - m_cachedSelectionMode, m_labelShader, - m_labelObj, activeCamera, true, false, position); - - // Reset label update flag; they should have been updated when we get here - m_updateLabels = false; - - glEnable(GL_DEPTH_TEST); - } else { - m_selectedBar = 0; - } - - glDisable(GL_TEXTURE_2D); - glDisable(GL_BLEND); - - // Release shader - glUseProgram(0); - m_selectionDirty = false; } void Bars3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCamera, - const QMatrix4x4 &viewMatrix, const QMatrix4x4 &projectionMatrix, - GLfloat rowScaleFactor, GLfloat columnScaleFactor) { + const QMatrix4x4 &viewMatrix, const QMatrix4x4 &projectionMatrix) { ShaderHelper *shader = 0; GLfloat alphaForValueSelection = labelValueAlpha / 255.0f; GLfloat alphaForRowSelection = labelRowAlpha / 255.0f; @@ -1873,7 +2032,6 @@ void Bars3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCamer shader = m_labelShader; shader->bind(); - glEnable(GL_TEXTURE_2D); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } @@ -1894,8 +2052,8 @@ void Bars3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCamer int labelCount = m_axisCacheY.labelCount(); GLfloat labelMarginXTrans = labelMargin; GLfloat labelMarginZTrans = labelMargin; - GLfloat labelXTrans = rowScaleFactor; - GLfloat labelZTrans = columnScaleFactor; + GLfloat labelXTrans = m_scaleXWithBackground; + GLfloat labelZTrans = m_scaleZWithBackground; 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; @@ -1999,10 +2157,8 @@ void Bars3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCamer fractionCamY = activeCamera->yRotation() * labelAngleFraction; fractionCamX = activeCamera->xRotation() * labelAngleFraction; GLfloat labelYAdjustment = 0.005f; - GLfloat scaledRowWidth = rowScaleFactor; - GLfloat scaledColumnDepth = columnScaleFactor; - GLfloat colPosValue = scaledRowWidth + labelMargin; - GLfloat rowPosValue = scaledColumnDepth + labelMargin; + GLfloat colPosValue = m_scaleXWithBackground + labelMargin; + GLfloat rowPosValue = m_scaleZWithBackground + labelMargin; GLfloat rowPos = 0.0f; GLfloat colPos = 0.0f; Qt::AlignmentFlag alignment = (m_xFlipped == m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight; @@ -2296,14 +2452,8 @@ void Bars3DRenderer::updateAxisRange(QAbstract3DAxis::AxisOrientation orientatio { Abstract3DRenderer::updateAxisRange(orientation, min, max); - if (orientation == QAbstract3DAxis::AxisOrientationY) { - // Check if we have negative values - if (min < 0) - m_hasNegativeValues = true; - else if (min >= 0) - m_hasNegativeValues = false; + if (orientation == QAbstract3DAxis::AxisOrientationY) calculateHeightAdjustment(); - } } void Bars3DRenderer::updateAxisReversed(QAbstract3DAxis::AxisOrientation orientation, bool enable) @@ -2384,10 +2534,12 @@ void Bars3DRenderer::updateShadowQuality(QAbstract3DGraph::ShadowQuality quality handleShadowQualityChange(); -#if !defined(QT_OPENGL_ES_2) // Re-init depth buffer updateDepthBuffer(); -#endif + + // Redraw to handle both reflections and shadows on background + if (m_reflectionEnabled) + needRender(); } void Bars3DRenderer::loadBackgroundMesh() @@ -2398,6 +2550,8 @@ void Bars3DRenderer::loadBackgroundMesh() void Bars3DRenderer::updateTextures() { + Abstract3DRenderer::updateTextures(); + // Drawer has changed; this flag needs to be checked when checking if we need to update labels m_updateLabels = true; } @@ -2420,30 +2574,60 @@ void Bars3DRenderer::calculateSceneScalingFactors() m_maxDimension = qMax(m_rowWidth, m_columnDepth); m_scaleFactor = qMin((m_cachedColumnCount * (m_maxDimension / m_maxSceneSize)), (m_cachedRowCount * (m_maxDimension / m_maxSceneSize))); + + // Single bar scaling m_scaleX = m_cachedBarThickness.width() / m_scaleFactor; m_scaleZ = m_cachedBarThickness.height() / m_scaleFactor; + + // Whole graph scale factors + m_xScaleFactor = m_rowWidth / m_scaleFactor; + m_zScaleFactor = m_columnDepth / m_scaleFactor; + + if (m_requestedMargin < 0.0f) { + m_hBackgroundMargin = 0.0f; + m_vBackgroundMargin = 0.0f; + } else { + m_hBackgroundMargin = m_requestedMargin; + m_vBackgroundMargin = m_requestedMargin; + } + + m_scaleXWithBackground = m_xScaleFactor + m_hBackgroundMargin; + m_scaleYWithBackground = 1.0f + m_vBackgroundMargin; + m_scaleZWithBackground = m_zScaleFactor + m_hBackgroundMargin; + + updateCameraViewport(); + updateCustomItemPositions(); } void Bars3DRenderer::calculateHeightAdjustment() { + float min = m_axisCacheY.min(); + float max = m_axisCacheY.max(); GLfloat newAdjustment = 1.0f; - GLfloat maxAbs = qFabs(m_axisCacheY.max()); - - if (m_axisCacheY.max() < 0.0f) { - m_heightNormalizer = GLfloat(qFabs(m_axisCacheY.min()) - qFabs(m_axisCacheY.max())); - maxAbs = qFabs(m_axisCacheY.max()) - qFabs(m_axisCacheY.min()); + m_actualFloorLevel = qBound(min, m_floorLevel, max); + GLfloat maxAbs = qFabs(max - m_actualFloorLevel); + + // Check if we have negative values + if (min < m_actualFloorLevel) + m_hasNegativeValues = true; + else if (min >= m_actualFloorLevel) + m_hasNegativeValues = false; + + if (max < m_actualFloorLevel) { + m_heightNormalizer = GLfloat(qFabs(min) - qFabs(max)); + maxAbs = qFabs(max) - qFabs(min); } else { - m_heightNormalizer = GLfloat(m_axisCacheY.max() - m_axisCacheY.min()); + m_heightNormalizer = GLfloat(max - min); } // Height fractions are used in gradient calculations and are therefore doubled // Note that if max or min is exactly zero, we still consider it outside the range - if (m_axisCacheY.max() <= 0.0f || m_axisCacheY.min() >= 0.0f) { + if (max <= m_actualFloorLevel || min >= m_actualFloorLevel) { m_noZeroInRange = true; m_gradientFraction = 2.0f; } else { m_noZeroInRange = false; - GLfloat minAbs = qFabs(m_axisCacheY.min()); + GLfloat minAbs = qFabs(min - m_actualFloorLevel); m_gradientFraction = qMax(minAbs, maxAbs) / m_heightNormalizer * 2.0f; } @@ -2551,13 +2735,13 @@ void Bars3DRenderer::updateSlicingActive(bool isSlicing) m_cachedIsSlicingActivated = isSlicing; - if (!m_cachedIsSlicingActivated) - initSelectionBuffer(); // We need to re-init selection buffer in case there has been a resize + if (!m_cachedIsSlicingActivated) { + // We need to re-init selection buffer in case there has been a resize + initSelectionBuffer(); + initCursorPositionBuffer(); + } -#if !defined(QT_OPENGL_ES_2) updateDepthBuffer(); // Re-init depth buffer as well -#endif - m_selectionDirty = true; } @@ -2598,32 +2782,35 @@ void Bars3DRenderer::initSelectionBuffer() m_selectionDepthBuffer); } -#if !defined(QT_OPENGL_ES_2) void Bars3DRenderer::initDepthShader() { - if (m_depthShader) - delete m_depthShader; - m_depthShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexDepth"), - QStringLiteral(":/shaders/fragmentDepth")); - m_depthShader->initialize(); + if (!m_isOpenGLES) { + if (m_depthShader) + delete m_depthShader; + m_depthShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexDepth"), + QStringLiteral(":/shaders/fragmentDepth")); + m_depthShader->initialize(); + } } void Bars3DRenderer::updateDepthBuffer() { - m_textureHelper->deleteTexture(&m_depthTexture); + if (!m_isOpenGLES) { + m_textureHelper->deleteTexture(&m_depthTexture); - if (m_primarySubViewport.size().isEmpty()) - return; + 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(); + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + m_depthTexture = + m_textureHelper->createDepthTextureFrameBuffer(m_primarySubViewport.size(), + m_depthFrameBuffer, + m_shadowQualityMultiplier); + if (!m_depthTexture) + lowerShadowQuality(); + } } } -#endif void Bars3DRenderer::initBackgroundShaders(const QString &vertexShader, const QString &fragmentShader) @@ -2634,14 +2821,6 @@ void Bars3DRenderer::initBackgroundShaders(const QString &vertexShader, m_backgroundShader->initialize(); } -void Bars3DRenderer::initLabelShaders(const QString &vertexShader, const QString &fragmentShader) -{ - if (m_labelShader) - delete m_labelShader; - m_labelShader = new ShaderHelper(this, vertexShader, fragmentShader); - m_labelShader->initialize(); -} - QVector3D Bars3DRenderer::convertPositionToTranslation(const QVector3D &position, bool isAbsolute) { float xTrans = 0.0f; @@ -2649,15 +2828,15 @@ QVector3D Bars3DRenderer::convertPositionToTranslation(const QVector3D &position float zTrans = 0.0f; if (!isAbsolute) { // Convert row and column to translation on graph - xTrans = (((position.x() + 0.5f) * m_cachedBarSpacing.width()) - m_rowWidth) - / m_scaleFactor; - zTrans = (m_columnDepth - ((position.z() + 0.5f) * m_cachedBarSpacing.height())) - / m_scaleFactor; + xTrans = (((position.x() - m_axisCacheX.min() + 0.5f) * m_cachedBarSpacing.width()) + - m_rowWidth) / m_scaleFactor; + zTrans = (m_columnDepth - ((position.z() - m_axisCacheZ.min() + 0.5f) + * m_cachedBarSpacing.height())) / m_scaleFactor; yTrans = m_axisCacheY.positionAt(position.y()); } else { - xTrans = position.x() * m_scaleX; + xTrans = position.x() * m_xScaleFactor; yTrans = position.y() + m_backgroundAdjustment; - zTrans = position.z() * m_scaleZ; + zTrans = position.z() * -m_zScaleFactor; } return QVector3D(xTrans, yTrans, zTrans); } @@ -2667,4 +2846,18 @@ void Bars3DRenderer::updateAspectRatio(float ratio) Q_UNUSED(ratio) } +void Bars3DRenderer::updateFloorLevel(float level) +{ + foreach (SeriesRenderCache *cache, m_renderCacheList) + cache->setDataDirty(true); + m_floorLevel = level; + calculateHeightAdjustment(); +} + +void Bars3DRenderer::updateMargin(float margin) +{ + Abstract3DRenderer::updateMargin(margin); + calculateSceneScalingFactors(); +} + QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/engine/bars3drenderer_p.h b/src/datavisualization/engine/bars3drenderer_p.h index 3a0ab3b8..094b8fd9 100644 --- a/src/datavisualization/engine/bars3drenderer_p.h +++ b/src/datavisualization/engine/bars3drenderer_p.h @@ -66,9 +66,7 @@ private: ShaderHelper *m_depthShader; ShaderHelper *m_selectionShader; ShaderHelper *m_backgroundShader; - ShaderHelper *m_labelShader; GLuint m_bgrTexture; - GLuint m_depthTexture; GLuint m_selectionTexture; GLuint m_depthFrameBuffer; GLuint m_selectionFrameBuffer; @@ -86,7 +84,6 @@ private: GLfloat m_scaleFactor; GLfloat m_maxSceneSize; QPoint m_visualSelectedBarPos; - bool m_resetCameraBaseOrientation; QPoint m_selectedBarPos; BarSeriesRenderCache *m_selectedSeriesCache; BarRenderItem m_dummyBarRenderItem; @@ -100,6 +97,10 @@ private: bool m_haveUniformColorSeries; bool m_haveGradientSeries; float m_zeroPosition; + float m_xScaleFactor; + float m_zScaleFactor; + float m_floorLevel; + float m_actualFloorLevel; public: explicit Bars3DRenderer(Bars3DController *controller); @@ -116,9 +117,13 @@ public: QVector3D convertPositionToTranslation(const QVector3D &position, bool isAbsolute); void updateAspectRatio(float ratio); + void updateFloorLevel(float level); + void updateMargin(float margin); protected: virtual void initializeOpenGL(); + virtual void fixCameraTarget(QVector3D &target); + virtual void getVisibleItemBounds(QVector3D &minBounds, QVector3D &maxBounds); public slots: void updateMultiSeriesScaling(bool uniform); @@ -146,18 +151,25 @@ private: void drawSlicedScene(); void drawScene(GLuint defaultFboHandle); void drawLabels(bool drawSelection, const Q3DCamera *activeCamera, - const QMatrix4x4 &viewMatrix, const QMatrix4x4 &projectionMatrix, - GLfloat rowScaleFactor, GLfloat columnScaleFactor); + const QMatrix4x4 &viewMatrix, const QMatrix4x4 &projectionMatrix); + + bool drawBars(BarRenderItem **selectedBar, const QMatrix4x4 &depthProjectionViewMatrix, + const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &viewMatrix, + GLint startRow, GLint stopRow, GLint stepRow, + GLint startBar, GLint stopBar, GLint stepBar, GLfloat reflection = 1.0f); + void drawBackground(GLfloat backgroundRotation, const QMatrix4x4 &depthProjectionViewMatrix, + const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &viewMatrix, + bool reflectingDraw = false, bool drawingSelectionBuffer = false); + void drawGridLines(const QMatrix4x4 &depthProjectionViewMatrix, + const QMatrix4x4 &projectionViewMatrix, + const QMatrix4x4 &viewMatrix); void loadBackgroundMesh(); void initSelectionShader(); void initBackgroundShaders(const QString &vertexShader, const QString &fragmentShader); - void initLabelShaders(const QString &vertexShader, const QString &fragmentShader); void initSelectionBuffer(); -#if !defined(QT_OPENGL_ES_2) void initDepthShader(); void updateDepthBuffer(); -#endif void calculateSceneScalingFactors(); void calculateHeightAdjustment(); Abstract3DController::SelectionType isSelected(int row, int bar, diff --git a/src/datavisualization/engine/drawer.cpp b/src/datavisualization/engine/drawer.cpp index b8726840..d4352702 100644 --- a/src/datavisualization/engine/drawer.cpp +++ b/src/datavisualization/engine/drawer.cpp @@ -69,10 +69,9 @@ Drawer::~Drawer() void Drawer::initializeOpenGL() { - if (!m_textureHelper) { - initializeOpenGLFunctions(); + initializeOpenGLFunctions(); + if (!m_textureHelper) m_textureHelper = new TextureHelper(); - } } void Drawer::setTheme(Q3DTheme *theme) @@ -93,8 +92,11 @@ QFont Drawer::font() const } void Drawer::drawObject(ShaderHelper *shader, AbstractObjectHelper *object, GLuint textureId, - GLuint depthTextureId) + GLuint depthTextureId, GLuint textureId3D) { +#if defined(QT_OPENGL_ES_2) + Q_UNUSED(textureId3D) +#endif if (textureId) { // Activate texture glActiveTexture(GL_TEXTURE0); @@ -108,6 +110,14 @@ void Drawer::drawObject(ShaderHelper *shader, AbstractObjectHelper *object, GLui glBindTexture(GL_TEXTURE_2D, depthTextureId); shader->setUniformValue(shader->shadow(), 1); } +#if !defined(QT_OPENGL_ES_2) + if (textureId3D) { + // Activate texture + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_3D, textureId3D); + shader->setUniformValue(shader->texture(), 2); + } +#endif // 1st attribute buffer : vertices glEnableVertexAttribArray(shader->posAtt()); @@ -115,14 +125,18 @@ void Drawer::drawObject(ShaderHelper *shader, AbstractObjectHelper *object, GLui glVertexAttribPointer(shader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0, (void*)0); // 2nd attribute buffer : normals - glEnableVertexAttribArray(shader->normalAtt()); - glBindBuffer(GL_ARRAY_BUFFER, object->normalBuf()); - glVertexAttribPointer(shader->normalAtt(), 3, GL_FLOAT, GL_FALSE, 0, (void*)0); + if (shader->normalAtt() >= 0) { + glEnableVertexAttribArray(shader->normalAtt()); + glBindBuffer(GL_ARRAY_BUFFER, object->normalBuf()); + glVertexAttribPointer(shader->normalAtt(), 3, GL_FLOAT, GL_FALSE, 0, (void*)0); + } // 3rd attribute buffer : UVs - glEnableVertexAttribArray(shader->uvAtt()); - glBindBuffer(GL_ARRAY_BUFFER, object->uvBuf()); - glVertexAttribPointer(shader->uvAtt(), 2, GL_FLOAT, GL_FALSE, 0, (void*)0); + if (shader->uvAtt() >= 0) { + glEnableVertexAttribArray(shader->uvAtt()); + glBindBuffer(GL_ARRAY_BUFFER, object->uvBuf()); + glVertexAttribPointer(shader->uvAtt(), 2, GL_FLOAT, GL_FALSE, 0, (void*)0); + } // Index buffer glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, object->elementBuf()); @@ -134,11 +148,19 @@ void Drawer::drawObject(ShaderHelper *shader, AbstractObjectHelper *object, GLui glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - glDisableVertexAttribArray(shader->uvAtt()); - glDisableVertexAttribArray(shader->normalAtt()); + if (shader->uvAtt() >= 0) + glDisableVertexAttribArray(shader->uvAtt()); + if (shader->normalAtt() >= 0) + glDisableVertexAttribArray(shader->normalAtt()); glDisableVertexAttribArray(shader->posAtt()); // Release textures +#if !defined(QT_OPENGL_ES_2) + if (textureId3D) { + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_3D, 0); + } +#endif if (depthTextureId) { glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, 0); @@ -206,13 +228,27 @@ void Drawer::drawPoint(ShaderHelper *shader) glDisableVertexAttribArray(shader->posAtt()); } -void Drawer::drawPoints(ShaderHelper *shader, ScatterPointBufferHelper *object) +void Drawer::drawPoints(ShaderHelper *shader, ScatterPointBufferHelper *object, GLuint textureId) { + if (textureId) { + // Activate texture + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, textureId); + shader->setUniformValue(shader->texture(), 0); + } + // 1st attribute buffer : vertices glEnableVertexAttribArray(shader->posAtt()); glBindBuffer(GL_ARRAY_BUFFER, object->pointBuf()); glVertexAttribPointer(shader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0, (void*)0); + // 2nd attribute buffer : UVs + if (textureId) { + glEnableVertexAttribArray(shader->uvAtt()); + glBindBuffer(GL_ARRAY_BUFFER, object->uvBuf()); + glVertexAttribPointer(shader->uvAtt(), 2, GL_FLOAT, GL_FALSE, 0, (void*)0); + } + // Draw the points glDrawArrays(GL_POINTS, 0, object->indexCount()); @@ -220,6 +256,12 @@ void Drawer::drawPoints(ShaderHelper *shader, ScatterPointBufferHelper *object) glBindBuffer(GL_ARRAY_BUFFER, 0); glDisableVertexAttribArray(shader->posAtt()); + + if (textureId) { + glDisableVertexAttribArray(shader->uvAtt()); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, 0); + } } void Drawer::drawLine(ShaderHelper *shader) diff --git a/src/datavisualization/engine/drawer_p.h b/src/datavisualization/engine/drawer_p.h index ffcea315..0c96724c 100644 --- a/src/datavisualization/engine/drawer_p.h +++ b/src/datavisualization/engine/drawer_p.h @@ -75,11 +75,11 @@ public: inline GLfloat scaledFontSize() const { return m_scaledFontSize; } void drawObject(ShaderHelper *shader, AbstractObjectHelper *object, GLuint textureId = 0, - GLuint depthTextureId = 0); + GLuint depthTextureId = 0, GLuint textureId3D = 0); void drawSelectionObject(ShaderHelper *shader, AbstractObjectHelper *object); void drawSurfaceGrid(ShaderHelper *shader, SurfaceObject *object); void drawPoint(ShaderHelper *shader); - void drawPoints(ShaderHelper *shader, ScatterPointBufferHelper *object); + void drawPoints(ShaderHelper *shader, ScatterPointBufferHelper *object, GLuint textureId); void drawLine(ShaderHelper *shader); void drawLabel(const AbstractRenderItem &item, const LabelItem &labelItem, const QMatrix4x4 &viewmatrix, const QMatrix4x4 &projectionmatrix, diff --git a/src/datavisualization/engine/engine.pri b/src/datavisualization/engine/engine.pri index 96fa7fa9..60703e96 100644 --- a/src/datavisualization/engine/engine.pri +++ b/src/datavisualization/engine/engine.pri @@ -57,3 +57,5 @@ SOURCES += $$PWD/qabstract3dgraph.cpp \ RESOURCES += engine/engine.qrc OTHER_FILES += $$PWD/meshes/* $$PWD/shaders/* + +INCLUDEPATH += $$PWD diff --git a/src/datavisualization/engine/engine.qrc b/src/datavisualization/engine/engine.qrc index 673b6ee0..0ef169c9 100644 --- a/src/datavisualization/engine/engine.qrc +++ b/src/datavisualization/engine/engine.qrc @@ -57,5 +57,18 @@ <file alias="fragmentTexture">shaders/texture.frag</file> <file alias="fragmentTextureES2">shaders/texture_ES2.frag</file> <file alias="vertexTexture">shaders/texture.vert</file> + <file alias="fragmentTexturedSurfaceShadowFlat">shaders/surfaceTexturedShadowFlat.frag</file> + <file alias="fragmentSurfaceTexturedFlat">shaders/surfaceTexturedFlat.frag</file> + <file alias="fragmentTexture3D">shaders/texture3d.frag</file> + <file alias="vertexTexture3D">shaders/texture3d.vert</file> + <file alias="fragmentTexture3DSlice">shaders/texture3dslice.frag</file> + <file alias="vertexShadowNoMatrices">shaders/shadowNoMatrices.vert</file> + <file alias="vertexNoMatrices">shaders/defaultNoMatrices.vert</file> + <file alias="fragmentTexture3DLowDef">shaders/texture3dlowdef.frag</file> + <file alias="vertexPointES2_UV">shaders/point_ES2_UV.vert</file> + <file alias="fragment3DSliceFrames">shaders/3dsliceframes.frag</file> + <file alias="vertexPosition">shaders/position.vert</file> + <file alias="fragmentPositionMap">shaders/positionmap.frag</file> + <file alias="fragmentTexturedSurfaceShadow">shaders/surfaceTexturedShadow.frag</file> </qresource> </RCC> diff --git a/src/datavisualization/engine/q3dbars.cpp b/src/datavisualization/engine/q3dbars.cpp index bb7aca89..d42c11b7 100644 --- a/src/datavisualization/engine/q3dbars.cpp +++ b/src/datavisualization/engine/q3dbars.cpp @@ -331,6 +331,26 @@ QBar3DSeries *Q3DBars::selectedSeries() const } /*! + * \property Q3DBars::floorLevel + * + * The desired floor level for the bar graph in Y-axis data coordinates. + * The actual floor level cannot go below Y-axis minimum or above Y-axis maximum. + * Defaults to zero. + */ +void Q3DBars::setFloorLevel(float level) +{ + if (level != floorLevel()) { + dptr()->m_shared->setFloorLevel(level); + emit floorLevelChanged(level); + } +} + +float Q3DBars::floorLevel() const +{ + return dptrc()->m_shared->floorLevel(); +} + +/*! * Adds \a axis to the graph. The axes added via addAxis are not yet taken to use, * addAxis is simply used to give the ownership of the \a axis to the graph. * The \a axis must not be null or added to another graph. diff --git a/src/datavisualization/engine/q3dbars.h b/src/datavisualization/engine/q3dbars.h index 7f9c981f..df1c846e 100644 --- a/src/datavisualization/engine/q3dbars.h +++ b/src/datavisualization/engine/q3dbars.h @@ -40,6 +40,7 @@ class QT_DATAVISUALIZATION_EXPORT Q3DBars : public QAbstract3DGraph Q_PROPERTY(QValue3DAxis *valueAxis READ valueAxis WRITE setValueAxis NOTIFY valueAxisChanged) Q_PROPERTY(QBar3DSeries *primarySeries READ primarySeries WRITE setPrimarySeries NOTIFY primarySeriesChanged) Q_PROPERTY(QBar3DSeries *selectedSeries READ selectedSeries NOTIFY selectedSeriesChanged) + Q_PROPERTY(float floorLevel READ floorLevel WRITE setFloorLevel NOTIFY floorLevelChanged) public: explicit Q3DBars(const QSurfaceFormat *format = 0, QWindow *parent = 0); @@ -75,6 +76,8 @@ public: QList<QAbstract3DAxis *> axes() const; QBar3DSeries *selectedSeries() const; + void setFloorLevel(float level); + float floorLevel() const; signals: void multiSeriesUniformChanged(bool uniform); @@ -86,6 +89,7 @@ signals: void valueAxisChanged(QValue3DAxis *axis); void primarySeriesChanged(QBar3DSeries *series); void selectedSeriesChanged(QBar3DSeries *series); + void floorLevelChanged(float level); private: Q3DBarsPrivate *dptr(); diff --git a/src/datavisualization/engine/q3dcamera.cpp b/src/datavisualization/engine/q3dcamera.cpp index 06fd570c..897c3779 100644 --- a/src/datavisualization/engine/q3dcamera.cpp +++ b/src/datavisualization/engine/q3dcamera.cpp @@ -110,8 +110,36 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION /*! * \qmlproperty float Camera3D::zoomLevel * - * This property contains the the camera zoom level in percentage. 100.0 means there is no zoom - * in or out set in the camera. + * This property contains the the camera zoom level in percentage. The default value of \c{100.0} + * means there is no zoom in or out set in the camera. + * The value is limited within the bounds defined by minZoomLevel and maxZoomLevel properties. + * + * \sa minZoomLevel, maxZoomLevel + */ + +/*! + * \qmlproperty float Camera3D::minZoomLevel + * + * This property contains the the minimum allowed camera zoom level. + * If the new minimum level is more than the existing maximum level, the maximum level is + * adjusted to the new minimum as well. + * If current zoomLevel is outside the new bounds, it is adjusted as well. + * The minZoomLevel cannot be set below \c{1.0}. + * Defaults to \c{10.0}. + * + * \sa zoomLevel, maxZoomLevel + */ + +/*! + * \qmlproperty float Camera3D::maxZoomLevel + * + * This property contains the the maximum allowed camera zoom level. + * If the new maximum level is less than the existing minimum level, the minimum level is + * adjusted to the new maximum as well. + * If current zoomLevel is outside the new bounds, it is adjusted as well. + * Defaults to \c{500.0f}. + * + * \sa zoomLevel, minZoomLevel */ /*! @@ -137,6 +165,19 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION */ /*! + * \qmlproperty vector3d Camera3D::target + * \since QtDataVisualization 1.2 + * + * Holds the camera \a target as a vector3d. Defaults to \c {vector3d(0.0, 0.0, 0.0)}. + * + * Valid coordinate values are between \c{-1.0...1.0}, where the edge values indicate + * the edges of the corresponding axis range. Any values outside this range are clamped to the edge. + * + * \note For bar graphs, the Y-coordinate is ignored and camera always targets a point on + * the horizontal background. + */ + +/*! * Constructs a new 3D camera with position set to origin, up direction facing towards the Y-axis * and looking at origin by default. An optional \a parent parameter can be given and is then passed * to QObject constructor. @@ -160,22 +201,11 @@ Q3DCamera::~Q3DCamera() */ void Q3DCamera::copyValuesFrom(const Q3DObject &source) { - Q3DObject::copyValuesFrom(source); + // Note: Do not copy values from parent, as we are handling the position internally const Q3DCamera &sourceCamera = static_cast<const Q3DCamera &>(source); - d_ptr->m_target.setX(sourceCamera.d_ptr->m_target.x()); - d_ptr->m_target.setY(sourceCamera.d_ptr->m_target.y()); - d_ptr->m_target.setZ(sourceCamera.d_ptr->m_target.z()); - - d_ptr->m_up.setX(sourceCamera.d_ptr->m_up.x()); - d_ptr->m_up.setY(sourceCamera.d_ptr->m_up.y()); - d_ptr->m_up.setZ(sourceCamera.d_ptr->m_up.z()); - - float *values = new float[16]; - sourceCamera.d_ptr->m_viewMatrix.copyDataTo(values); - d_ptr->m_viewMatrix = QMatrix4x4(values); - delete[] values; + d_ptr->m_requestedTarget = sourceCamera.d_ptr->m_requestedTarget; d_ptr->m_xRotation = sourceCamera.d_ptr->m_xRotation; d_ptr->m_yRotation = sourceCamera.d_ptr->m_yRotation; @@ -189,6 +219,8 @@ void Q3DCamera::copyValuesFrom(const Q3DObject &source) d_ptr->m_wrapYRotation = sourceCamera.d_ptr->m_wrapYRotation; d_ptr->m_zoomLevel = sourceCamera.d_ptr->m_zoomLevel; + d_ptr->m_minZoomLevel = sourceCamera.d_ptr->m_minZoomLevel; + d_ptr->m_maxZoomLevel = sourceCamera.d_ptr->m_maxZoomLevel; d_ptr->m_activePreset = sourceCamera.d_ptr->m_activePreset; } @@ -389,6 +421,9 @@ void Q3DCamera::setCameraPreset(CameraPreset preset) break; } + // All presets target the center of the graph + setTarget(zeroVector); + if (d_ptr->m_activePreset != preset) { d_ptr->m_activePreset = preset; setDirty(true); @@ -399,8 +434,11 @@ void Q3DCamera::setCameraPreset(CameraPreset preset) /*! * \property Q3DCamera::zoomLevel * - * This property contains the the camera zoom level in percentage. \c 100.0f means there is no zoom - * in or out set in the camera. + * This property contains the the camera zoom level in percentage. The default value of \c{100.0f} + * means there is no zoom in or out set in the camera. + * The value is limited within the bounds defined by minZoomLevel and maxZoomLevel properties. + * + * \sa minZoomLevel, maxZoomLevel */ float Q3DCamera::zoomLevel() const { @@ -409,10 +447,73 @@ float Q3DCamera::zoomLevel() const void Q3DCamera::setZoomLevel(float zoomLevel) { - if (d_ptr->m_zoomLevel != zoomLevel) { - d_ptr->m_zoomLevel = zoomLevel; + float newZoomLevel = qBound(d_ptr->m_minZoomLevel, zoomLevel, d_ptr->m_maxZoomLevel); + + if (d_ptr->m_zoomLevel != newZoomLevel) { + d_ptr->m_zoomLevel = newZoomLevel; + setDirty(true); + emit zoomLevelChanged(newZoomLevel); + } +} + +/*! + * \property Q3DCamera::minZoomLevel + * + * This property contains the the minimum allowed camera zoom level. + * If the new minimum level is more than the existing maximum level, the maximum level is + * adjusted to the new minimum as well. + * If current zoomLevel is outside the new bounds, it is adjusted as well. + * The minZoomLevel cannot be set below \c{1.0f}. + * Defaults to \c{10.0f}. + * + * \sa zoomLevel, maxZoomLevel + */ +float Q3DCamera::minZoomLevel() const +{ + return d_ptr->m_minZoomLevel; +} + +void Q3DCamera::setMinZoomLevel(float zoomLevel) +{ + // Don't allow minimum to be below one, as that can cause zoom to break. + float newMinLevel = qMax(zoomLevel, 1.0f); + if (d_ptr->m_minZoomLevel != newMinLevel) { + d_ptr->m_minZoomLevel = newMinLevel; + if (d_ptr->m_maxZoomLevel < newMinLevel) + setMaxZoomLevel(newMinLevel); + setZoomLevel(d_ptr->m_zoomLevel); setDirty(true); - emit zoomLevelChanged(zoomLevel); + emit minZoomLevelChanged(newMinLevel); + } +} + +/*! + * \property Q3DCamera::maxZoomLevel + * + * This property contains the the maximum allowed camera zoom level. + * If the new maximum level is less than the existing minimum level, the minimum level is + * adjusted to the new maximum as well. + * If current zoomLevel is outside the new bounds, it is adjusted as well. + * Defaults to \c{500.0f}. + * + * \sa zoomLevel, minZoomLevel + */ +float Q3DCamera::maxZoomLevel() const +{ + return d_ptr->m_maxZoomLevel; +} + +void Q3DCamera::setMaxZoomLevel(float zoomLevel) +{ + // Don't allow maximum to be below one, as that can cause zoom to break. + float newMaxLevel = qMax(zoomLevel, 1.0f); + if (d_ptr->m_maxZoomLevel != newMaxLevel) { + d_ptr->m_maxZoomLevel = newMaxLevel; + if (d_ptr->m_minZoomLevel > newMaxLevel) + setMinZoomLevel(newMaxLevel); + setZoomLevel(d_ptr->m_zoomLevel); + setDirty(true); + emit maxZoomLevelChanged(newMaxLevel); } } @@ -459,16 +560,61 @@ void Q3DCamera::setWrapYRotation(bool isEnabled) /*! * Utility function that sets the camera rotations and distance.\a horizontal and \a vertical * define the camera rotations to be used. - * Optional \a zoom parameter can be given to set the zoom percentage of the camera in range of - * \c{10.0f - 500.0f}. + * Optional \a zoom parameter can be given to set the zoom percentage of the camera within + * the bounds defined by minZoomLevel and maxZoomLevel properties. */ void Q3DCamera::setCameraPosition(float horizontal, float vertical, float zoom) { - setZoomLevel(qBound(10.0f, zoom, 500.0f)); + setZoomLevel(zoom); setXRotation(horizontal); setYRotation(vertical); } +/*! + * \property Q3DCamera::target + * \since QtDataVisualization 1.2 + * + * Holds the camera \a target as a QVector3D. Defaults to \c {QVector3D(0.0, 0.0, 0.0)}. + * + * Valid coordinate values are between \c{-1.0...1.0}, where the edge values indicate + * the edges of the corresponding axis range. Any values outside this range are clamped to the edge. + * + * \note For bar graphs, the Y-coordinate is ignored and camera always targets a point on + * the horizontal background. + */ +QVector3D Q3DCamera::target() const +{ + return d_ptr->m_requestedTarget; +} + +void Q3DCamera::setTarget(const QVector3D &target) +{ + QVector3D newTarget = target; + + if (newTarget.x() < -1.0f) + newTarget.setX(-1.0f); + else if (newTarget.x() > 1.0f) + newTarget.setX(1.0f); + + if (newTarget.y() < -1.0f) + newTarget.setY(-1.0f); + else if (newTarget.y() > 1.0f) + newTarget.setY(1.0f); + + if (newTarget.z() < -1.0f) + newTarget.setZ(-1.0f); + else if (newTarget.z() > 1.0f) + newTarget.setZ(1.0f); + + if (d_ptr->m_requestedTarget != newTarget) { + if (d_ptr->m_activePreset != CameraPresetNone) + d_ptr->m_activePreset = CameraPresetNone; + d_ptr->m_requestedTarget = newTarget; + setDirty(true); + emit targetChanged(newTarget); + } +} + Q3DCameraPrivate::Q3DCameraPrivate(Q3DCamera *q) : q_ptr(q), m_isViewMatrixUpdateActive(true), @@ -479,6 +625,8 @@ Q3DCameraPrivate::Q3DCameraPrivate(Q3DCamera *q) : m_maxXRotation(180.0f), m_maxYRotation(90.0f), m_zoomLevel(100.0f), + m_minZoomLevel(10.0f), + m_maxZoomLevel(500.0f), m_wrapXRotation(true), m_wrapYRotation(false), m_activePreset(Q3DCamera::CameraPresetNone) @@ -645,9 +793,9 @@ void Q3DCameraPrivate::updateViewMatrix(float zoomAdjustment) QMatrix4x4 viewMatrix; // Apply to view matrix - viewMatrix.lookAt(q_ptr->position(), m_target, m_up); + viewMatrix.lookAt(q_ptr->position(), m_actualTarget, m_up); // Compensate for translation (if d_ptr->m_target is off origin) - viewMatrix.translate(m_target.x(), m_target.y(), m_target.z()); + viewMatrix.translate(m_actualTarget.x(), m_actualTarget.y(), m_actualTarget.z()); // Apply rotations // Handle x and z rotation when y -angle is other than 0 viewMatrix.rotate(m_xRotation, 0, qCos(qDegreesToRadians(m_yRotation)), @@ -657,7 +805,7 @@ void Q3DCameraPrivate::updateViewMatrix(float zoomAdjustment) // handle zoom by scaling viewMatrix.scale(zoom / 100.0f); // Compensate for translation (if d_ptr->m_target is off origin) - viewMatrix.translate(-m_target.x(), -m_target.y(), -m_target.z()); + viewMatrix.translate(-m_actualTarget.x(), -m_actualTarget.y(), -m_actualTarget.z()); setViewMatrix(viewMatrix); } @@ -713,9 +861,9 @@ void Q3DCameraPrivate::setBaseOrientation(const QVector3D &basePosition, const QVector3D &target, const QVector3D &baseUp) { - if (q_ptr->position() != basePosition || m_target != target || m_up != baseUp) { + if (q_ptr->position() != basePosition || m_actualTarget != target || m_up != baseUp) { q_ptr->setPosition(basePosition); - m_target = target; + m_actualTarget = target; m_up = baseUp; q_ptr->setDirty(true); } @@ -737,20 +885,33 @@ QVector3D Q3DCameraPrivate::calculatePositionRelativeToCamera(const QVector3D &r float distanceModifier) const { // Move the position with camera - GLfloat radiusFactor = cameraDistance * (1.5f + distanceModifier); - GLfloat xAngle; - GLfloat yAngle; + const float radiusFactor = cameraDistance * (1.5f + distanceModifier); + float xAngle; + float yAngle; + if (!fixedRotation) { xAngle = qDegreesToRadians(m_xRotation); - yAngle = qDegreesToRadians(m_yRotation); + float yRotation = m_yRotation; + // Light must not be paraller to eye vector, so fudge the y rotation a bit. + // Note: This needs redoing if we ever allow arbitrary light positioning. + const float yMargin = 0.1f; // Smaller margins cause weird shadow artifacts on tops of bars + const float absYRotation = qAbs(yRotation); + if (absYRotation < 90.0f + yMargin && absYRotation > 90.0f - yMargin) { + if (yRotation < 0.0f) + yRotation = -90.0f + yMargin; + else + yRotation = 90.0f - yMargin; + } + yAngle = qDegreesToRadians(yRotation); } else { xAngle = qDegreesToRadians(fixedRotation); yAngle = 0; } - GLfloat radius = (radiusFactor + relativePosition.y()); // set radius to match the highest height of the position - GLfloat zPos = radius * qCos(xAngle) * qCos(yAngle); - GLfloat xPos = radius * qSin(xAngle) * qCos(yAngle); - GLfloat yPos = (radiusFactor + relativePosition.y()) * qSin(yAngle); + // Set radius to match the highest height of the position + const float radius = (radiusFactor + relativePosition.y()); + const float zPos = radius * qCos(xAngle) * qCos(yAngle); + const float xPos = radius * qSin(xAngle) * qCos(yAngle); + const float yPos = radius * qSin(yAngle); // Keep in the set position in relation to camera return QVector3D(-xPos + relativePosition.x(), diff --git a/src/datavisualization/engine/q3dcamera.h b/src/datavisualization/engine/q3dcamera.h index e9ad6dd2..5b539ccf 100644 --- a/src/datavisualization/engine/q3dcamera.h +++ b/src/datavisualization/engine/q3dcamera.h @@ -35,6 +35,9 @@ class QT_DATAVISUALIZATION_EXPORT Q3DCamera : public Q3DObject Q_PROPERTY(CameraPreset cameraPreset READ cameraPreset WRITE setCameraPreset NOTIFY cameraPresetChanged) Q_PROPERTY(bool wrapXRotation READ wrapXRotation WRITE setWrapXRotation NOTIFY wrapXRotationChanged) Q_PROPERTY(bool wrapYRotation READ wrapYRotation WRITE setWrapYRotation NOTIFY wrapYRotationChanged) + Q_PROPERTY(QVector3D target READ target WRITE setTarget NOTIFY targetChanged REVISION 1) + Q_PROPERTY(float minZoomLevel READ minZoomLevel WRITE setMinZoomLevel NOTIFY minZoomLevelChanged REVISION 1) + Q_PROPERTY(float maxZoomLevel READ maxZoomLevel WRITE setMaxZoomLevel NOTIFY maxZoomLevelChanged REVISION 1) public: enum CameraPreset { @@ -86,9 +89,16 @@ public: float zoomLevel() const; void setZoomLevel(float zoomLevel); + float minZoomLevel() const; + void setMinZoomLevel(float zoomLevel); + float maxZoomLevel() const; + void setMaxZoomLevel(float zoomLevel); void setCameraPosition(float horizontal, float vertical, float zoom = 100.0f); + QVector3D target() const; + void setTarget(const QVector3D &target); + signals: void xRotationChanged(float rotation); void yRotationChanged(float rotation); @@ -96,6 +106,9 @@ signals: void cameraPresetChanged(Q3DCamera::CameraPreset preset); void wrapXRotationChanged(bool isEnabled); void wrapYRotationChanged(bool isEnabled); + Q_REVISION(1) void targetChanged(const QVector3D &target); + Q_REVISION(1) void minZoomLevelChanged(float zoomLevel); + Q_REVISION(1) void maxZoomLevelChanged(float zoomLevel); private: QScopedPointer<Q3DCameraPrivate> d_ptr; diff --git a/src/datavisualization/engine/q3dcamera_p.h b/src/datavisualization/engine/q3dcamera_p.h index 01e7a508..b7826a5b 100644 --- a/src/datavisualization/engine/q3dcamera_p.h +++ b/src/datavisualization/engine/q3dcamera_p.h @@ -84,22 +84,25 @@ signals: public: Q3DCamera *q_ptr; - QVector3D m_target; + QVector3D m_actualTarget; QVector3D m_up; QMatrix4x4 m_viewMatrix; bool m_isViewMatrixUpdateActive; - GLfloat m_xRotation; - GLfloat m_yRotation; - GLfloat m_minXRotation; - GLfloat m_minYRotation; - GLfloat m_maxXRotation; - GLfloat m_maxYRotation; - GLfloat m_zoomLevel; + float m_xRotation; + float m_yRotation; + float m_minXRotation; + float m_minYRotation; + float m_maxXRotation; + float m_maxYRotation; + float m_zoomLevel; + float m_minZoomLevel; + float m_maxZoomLevel; bool m_wrapXRotation; bool m_wrapYRotation; Q3DCamera::CameraPreset m_activePreset; + QVector3D m_requestedTarget; friend class Bars3DRenderer; friend class Surface3DRenderer; diff --git a/src/datavisualization/engine/q3dlight.cpp b/src/datavisualization/engine/q3dlight.cpp index 03f094cb..92b7bd07 100644 --- a/src/datavisualization/engine/q3dlight.cpp +++ b/src/datavisualization/engine/q3dlight.cpp @@ -68,13 +68,8 @@ Q3DLightPrivate::~Q3DLightPrivate() void Q3DLightPrivate::sync(Q3DLight &other) { - // Copies changed values from this light to the other light. If the other light had same changes, - // those changes are discarded. - if (q_ptr->isDirty()) { - other.copyValuesFrom(*q_ptr); - q_ptr->setDirty(false); - other.setDirty(false); - } + Q_UNUSED(other); + // Do nothing. Only value light has to sync is the position, which we handle internally. } QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/engine/q3dobject.cpp b/src/datavisualization/engine/q3dobject.cpp index 56edb098..6b442ef8 100644 --- a/src/datavisualization/engine/q3dobject.cpp +++ b/src/datavisualization/engine/q3dobject.cpp @@ -53,9 +53,7 @@ Q3DObject::~Q3DObject() */ void Q3DObject::copyValuesFrom(const Q3DObject &source) { - d_ptr->m_position.setX(source.d_ptr->m_position.x()); - d_ptr->m_position.setY(source.d_ptr->m_position.y()); - d_ptr->m_position.setZ(source.d_ptr->m_position.z()); + d_ptr->m_position = source.d_ptr->m_position; setDirty(true); } @@ -74,6 +72,9 @@ Q3DScene *Q3DObject::parentScene() * \property Q3DObject::position * * This property contains the 3D position of the object. + * + * \note Currently setting this property has no effect, as the positions of Q3DObjects in the + * scene are handled internally. */ QVector3D Q3DObject::position() const { @@ -97,7 +98,7 @@ void Q3DObject::setDirty(bool dirty) { d_ptr->m_isDirty = dirty; if (parentScene()) - parentScene()->d_ptr->m_sceneDirty = true; + parentScene()->d_ptr->markDirty(); } /*! diff --git a/src/datavisualization/engine/q3dscene.cpp b/src/datavisualization/engine/q3dscene.cpp index 9464bc8d..e4015c2b 100644 --- a/src/datavisualization/engine/q3dscene.cpp +++ b/src/datavisualization/engine/q3dscene.cpp @@ -96,9 +96,33 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION */ /*! + * \qmlproperty point Scene3D::graphPositionQuery + * + * This property contains the coordinates for the user input that should be processed + * by the scene as a graph position query. If this is set to value other than + * invalidSelectionPoint, the graph tries to match a graph position to the given \a point + * within the primary viewport. + * After the rendering pass this property is returned to its default state of + * invalidSelectionPoint. The queried graph position can be read from + * AbstractGraph3D::queriedGraphPosition property after the next render pass. + * + * There isn't a single correct 3D coordinate to match to each specific screen position, so to be + * consistent, the queries are always done against the inner sides of an invisible box surrounding + * the graph. + * + * \note Bar graphs allow graph position queries only at the graph floor level. + * + * \sa AbstractGraph3D::queriedGraphPosition + */ + +/*! * \qmlproperty bool Scene3D::slicingActive * - * This property contains whether 2D slicing view is currently active or not. + * This property contains whether 2D slicing view is currently active or not. If setting it, you + * must make sure AbstractGraph3D::selectionMode has either + * \l{QAbstract3DGraph::SelectionRow}{AbstractGraph3D.SelectionRow} or + * \l{QAbstract3DGraph::SelectionColumn}{AbstractGraph3D.SelectionColumn} flag set, and there is a + * valid selection. * \note Not all visualizations support the 2D slicing view. */ @@ -135,7 +159,6 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION * A constant property providing an invalid point for selection. */ - /*! * Constructs a basic scene with one light and one camera in it. An * optional \a parent parameter can be given and is then passed to QObject constructor. @@ -195,33 +218,35 @@ void Q3DScene::setPrimarySubViewport(const QRect &primarySubViewport) /*! * Returns whether the given \a point resides inside the primary subview or not. * \return \c true if the point is inside the primary subview. + * \note If subviews are superimposed, and the given \a point resides inside both, result is + * \c true only when the primary subview is on top. */ bool Q3DScene::isPointInPrimarySubView(const QPoint &point) { int x = point.x(); int y = point.y(); - int areaMinX = d_ptr->m_primarySubViewport.x(); - int areaMaxX = d_ptr->m_primarySubViewport.x() + d_ptr->m_primarySubViewport.width(); - int areaMinY = d_ptr->m_primarySubViewport.y(); - int areaMaxY = d_ptr->m_primarySubViewport.y() + d_ptr->m_primarySubViewport.height(); - - return ( x > areaMinX && x < areaMaxX && y > areaMinY && y < areaMaxY ); + bool isInSecondary = d_ptr->isInArea(d_ptr->m_secondarySubViewport, x, y); + if (!isInSecondary || (isInSecondary && !d_ptr->m_isSecondarySubviewOnTop)) + return d_ptr->isInArea(d_ptr->m_primarySubViewport, x, y); + else + return false; } /*! * Returns whether the given \a point resides inside the secondary subview or not. * \return \c true if the point is inside the secondary subview. + * \note If subviews are superimposed, and the given \a point resides inside both, result is + * \c true only when the secondary subview is on top. */ bool Q3DScene::isPointInSecondarySubView(const QPoint &point) { int x = point.x(); int y = point.y(); - int areaMinX = d_ptr->m_secondarySubViewport.x(); - int areaMaxX = d_ptr->m_secondarySubViewport.x() + d_ptr->m_secondarySubViewport.width(); - int areaMinY = d_ptr->m_secondarySubViewport.y(); - int areaMaxY = d_ptr->m_secondarySubViewport.y() + d_ptr->m_secondarySubViewport.height(); - - return ( x > areaMinX && x < areaMaxX && y > areaMinY && y < areaMaxY ); + bool isInPrimary = d_ptr->isInArea(d_ptr->m_primarySubViewport, x, y); + if (!isInPrimary || (isInPrimary && d_ptr->m_isSecondarySubviewOnTop)) + return d_ptr->isInArea(d_ptr->m_secondarySubViewport, x, y); + else + return false; } /*! @@ -254,7 +279,7 @@ void Q3DScene::setSecondarySubViewport(const QRect &secondarySubViewport) * \property Q3DScene::selectionQueryPosition * * This property contains the coordinates for the user input that should be processed - * by the scene as selection. If this is set to value other than invalidSelectionPoint() the + * by the scene as a selection. If this is set to value other than invalidSelectionPoint(), the * graph tries to select a data item, axis label, or a custom item at the given \a point within * the primary viewport. * After the rendering pass the property is returned to its default state of @@ -289,9 +314,47 @@ QPoint Q3DScene::invalidSelectionPoint() } /*! + * \property Q3DScene::graphPositionQuery + * + * This property contains the coordinates for the user input that should be processed + * by the scene as a graph position query. If this is set to value other than + * invalidSelectionPoint(), the graph tries to match a graph position to the given \a point + * within the primary viewport. + * After the rendering pass this property is returned to its default state of + * invalidSelectionPoint(). The queried graph position can be read from + * QAbstract3DGraph::queriedGraphPosition property after the next render pass. + * + * There isn't a single correct 3D coordinate to match to each specific screen position, so to be + * consistent, the queries are always done against the inner sides of an invisible box surrounding + * the graph. + * + * \note Bar graphs allow graph position queries only at the graph floor level. + * + * \sa QAbstract3DGraph::queriedGraphPosition + */ +void Q3DScene::setGraphPositionQuery(const QPoint &point) +{ + if (point != d_ptr->m_graphPositionQueryPosition) { + d_ptr->m_graphPositionQueryPosition = point; + d_ptr->m_changeTracker.graphPositionQueryPositionChanged = true; + d_ptr->m_sceneDirty = true; + + emit graphPositionQueryChanged(point); + emit d_ptr->needRender(); + } +} + +QPoint Q3DScene::graphPositionQuery() const +{ + return d_ptr->m_graphPositionQueryPosition; +} + +/*! * \property Q3DScene::slicingActive * - * This property contains whether 2D slicing view is currently active or not. + * This property contains whether 2D slicing view is currently active or not. If setting it, you + * must make sure QAbstract3DGraph::selectionMode has either QAbstract3DGraph::SelectionRow or + * QAbstract3DGraph::SelectionColumn flag set, and there is a valid selection. * \note Not all visualizations support the 2D slicing view. */ bool Q3DScene::isSlicingActive() const @@ -306,6 +369,10 @@ void Q3DScene::setSlicingActive(bool isSlicing) d_ptr->m_changeTracker.slicingActivatedChanged = true; d_ptr->m_sceneDirty = true; + // Set secondary subview behind primary to achieve default functionality (= clicking on + // primary disables slice) + setSecondarySubviewOnTop(!isSlicing); + d_ptr->calculateSubViewports(); emit slicingActiveChanged(isSlicing); emit d_ptr->needRender(); @@ -447,7 +514,9 @@ Q3DScenePrivate::Q3DScenePrivate(Q3DScene *q) : m_light(), m_isUnderSideCameraEnabled(false), m_isSlicingActive(false), - m_selectionQueryPosition(Q3DScene::invalidSelectionPoint()) + m_selectionQueryPosition(Q3DScene::invalidSelectionPoint()), + m_graphPositionQueryPosition(Q3DScene::invalidSelectionPoint()), + m_sceneDirty(true) { } @@ -491,6 +560,11 @@ void Q3DScenePrivate::sync(Q3DScenePrivate &other) m_changeTracker.selectionQueryPositionChanged = false; other.m_changeTracker.selectionQueryPositionChanged = false; } + if (m_changeTracker.graphPositionQueryPositionChanged) { + other.q_ptr->setGraphPositionQuery(q_ptr->graphPositionQuery()); + m_changeTracker.graphPositionQueryPositionChanged = false; + other.m_changeTracker.graphPositionQueryPositionChanged = false; + } if (m_changeTracker.cameraChanged) { m_camera->setDirty(true); m_changeTracker.cameraChanged = false; @@ -649,4 +723,19 @@ void Q3DScenePrivate::setLightPositionRelativeToCamera(const QVector3D &relative distanceModifier)); } +void Q3DScenePrivate::markDirty() +{ + m_sceneDirty = true; + emit needRender(); +} + +bool Q3DScenePrivate::isInArea(const QRect &area, int x, int y) const +{ + int areaMinX = area.x(); + int areaMaxX = area.x() + area.width(); + int areaMinY = area.y(); + int areaMaxY = area.y() + area.height(); + return ( x >= areaMinX && x <= areaMaxX && y >= areaMinY && y <= areaMaxY ); +} + QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/engine/q3dscene.h b/src/datavisualization/engine/q3dscene.h index 1699b125..a46b4d7b 100644 --- a/src/datavisualization/engine/q3dscene.h +++ b/src/datavisualization/engine/q3dscene.h @@ -41,6 +41,7 @@ class QT_DATAVISUALIZATION_EXPORT Q3DScene : public QObject Q_PROPERTY(Q3DCamera* activeCamera READ activeCamera WRITE setActiveCamera NOTIFY activeCameraChanged) Q_PROPERTY(Q3DLight* activeLight READ activeLight WRITE setActiveLight NOTIFY activeLightChanged) Q_PROPERTY(float devicePixelRatio READ devicePixelRatio WRITE setDevicePixelRatio NOTIFY devicePixelRatioChanged) + Q_PROPERTY(QPoint graphPositionQuery READ graphPositionQuery WRITE setGraphPositionQuery NOTIFY graphPositionQueryChanged REVISION 1) public: Q3DScene(QObject *parent = 0); @@ -60,6 +61,9 @@ public: QPoint selectionQueryPosition() const; static QPoint invalidSelectionPoint(); + void setGraphPositionQuery(const QPoint &point); + QPoint graphPositionQuery() const; + void setSlicingActive(bool isSlicing); bool isSlicingActive() const; @@ -85,6 +89,7 @@ signals: void activeLightChanged(Q3DLight *light); void devicePixelRatioChanged(float pixelRatio); void selectionQueryPositionChanged(const QPoint &position); + Q_REVISION(1) void graphPositionQueryChanged(const QPoint &position); private: QScopedPointer<Q3DScenePrivate> d_ptr; diff --git a/src/datavisualization/engine/q3dscene_p.h b/src/datavisualization/engine/q3dscene_p.h index 2c69e5e0..d0f6e735 100644 --- a/src/datavisualization/engine/q3dscene_p.h +++ b/src/datavisualization/engine/q3dscene_p.h @@ -47,6 +47,7 @@ struct Q3DSceneChangeBitField { bool slicingActivatedChanged : 1; bool devicePixelRatioChanged : 1; bool selectionQueryPositionChanged : 1; + bool graphPositionQueryPositionChanged : 1; bool windowSizeChanged : 1; Q3DSceneChangeBitField() @@ -59,6 +60,7 @@ struct Q3DSceneChangeBitField { slicingActivatedChanged(true), devicePixelRatioChanged(true), selectionQueryPositionChanged(false), + graphPositionQueryPositionChanged(false), windowSizeChanged(true) { } @@ -89,6 +91,10 @@ public: float fixedRotation = 0.0f, float distanceModifier = 0.0f); + void markDirty(); + + bool isInArea(const QRect &area, int x, int y) const; + signals: void needRender(); @@ -106,6 +112,7 @@ public: bool m_isUnderSideCameraEnabled; bool m_isSlicingActive; QPoint m_selectionQueryPosition; + QPoint m_graphPositionQueryPosition; QSize m_windowSize; QRect m_glViewport; QRect m_glPrimarySubViewport; diff --git a/src/datavisualization/engine/q3dsurface.cpp b/src/datavisualization/engine/q3dsurface.cpp index c5ce29d7..ce3e7f4c 100644 --- a/src/datavisualization/engine/q3dsurface.cpp +++ b/src/datavisualization/engine/q3dsurface.cpp @@ -98,6 +98,8 @@ Q3DSurface::Q3DSurface(const QSurfaceFormat *format, QWindow *parent) dptr()->m_shared->initializeOpenGL(); QObject::connect(dptr()->m_shared, &Surface3DController::selectedSeriesChanged, this, &Q3DSurface::selectedSeriesChanged); + QObject::connect(dptr()->m_shared, &Surface3DController::flipHorizontalGridChanged, + this, &Q3DSurface::flipHorizontalGridChanged); } /*! @@ -230,6 +232,31 @@ QSurface3DSeries *Q3DSurface::selectedSeries() const } /*! + * \property Q3DSurface::flipHorizontalGrid + * \since QtDataVisualization 1.2 + * + * In some use cases the horizontal axis grid is mostly covered by the surface, so it can be more + * useful to display the horizontal axis grid on top of the graph rather than on the bottom. + * A typical use case for this is showing 2D spectrograms using orthoGraphic projection with + * a top-down viewpoint. + * + * If \c{false}, the horizontal axis grid and labels are drawn on the horizontal background + * of the graph. + * If \c{true}, the horizontal axis grid and labels are drawn on the opposite side of the graph + * from the horizontal background. + * Defaults to \c{false}. + */ +void Q3DSurface::setFlipHorizontalGrid(bool flip) +{ + dptr()->m_shared->setFlipHorizontalGrid(flip); +} + +bool Q3DSurface::flipHorizontalGrid() const +{ + return dptrc()->m_shared->flipHorizontalGrid(); +} + +/*! * Adds \a axis to the graph. The axes added via addAxis are not yet taken to use, * addAxis is simply used to give the ownership of the \a axis to the graph. * The \a axis must not be null or added to another graph. diff --git a/src/datavisualization/engine/q3dsurface.h b/src/datavisualization/engine/q3dsurface.h index 9868c844..86740519 100644 --- a/src/datavisualization/engine/q3dsurface.h +++ b/src/datavisualization/engine/q3dsurface.h @@ -34,6 +34,7 @@ class QT_DATAVISUALIZATION_EXPORT Q3DSurface : public QAbstract3DGraph Q_PROPERTY(QValue3DAxis *axisY READ axisY WRITE setAxisY NOTIFY axisYChanged) Q_PROPERTY(QValue3DAxis *axisZ READ axisZ WRITE setAxisZ NOTIFY axisZChanged) Q_PROPERTY(QSurface3DSeries *selectedSeries READ selectedSeries NOTIFY selectedSeriesChanged) + Q_PROPERTY(bool flipHorizontalGrid READ flipHorizontalGrid WRITE setFlipHorizontalGrid NOTIFY flipHorizontalGridChanged) public: explicit Q3DSurface(const QSurfaceFormat *format = 0, QWindow *parent = 0); @@ -55,12 +56,15 @@ public: QList<QValue3DAxis *> axes() const; QSurface3DSeries *selectedSeries() const; + void setFlipHorizontalGrid(bool flip); + bool flipHorizontalGrid() const; signals: void axisXChanged(QValue3DAxis *axis); void axisYChanged(QValue3DAxis *axis); void axisZChanged(QValue3DAxis *axis); void selectedSeriesChanged(QSurface3DSeries *series); + void flipHorizontalGridChanged(bool flip); private: Q3DSurfacePrivate *dptr(); diff --git a/src/datavisualization/engine/qabstract3dgraph.cpp b/src/datavisualization/engine/qabstract3dgraph.cpp index b8fa92e8..e51d9ce4 100644 --- a/src/datavisualization/engine/qabstract3dgraph.cpp +++ b/src/datavisualization/engine/qabstract3dgraph.cpp @@ -22,6 +22,7 @@ #include "qabstract3dinputhandler_p.h" #include "q3dscene_p.h" #include "qutils.h" +#include "utils_p.h" #include <QtGui/QGuiApplication> #include <QtGui/QOpenGLContext> @@ -29,6 +30,9 @@ #include <QtGui/QPainter> #include <QtGui/QOpenGLFramebufferObject> #include <QtGui/QOffscreenSurface> +#if defined(Q_OS_OSX) +#include <qpa/qplatformnativeinterface.h> +#endif QT_BEGIN_NAMESPACE_DATAVISUALIZATION @@ -148,7 +152,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION \value OptimizationDefault Provides the full feature set at a reasonable performance. \value OptimizationStatic - Beta level feature. Optimizes the rendering of static data sets at the expense of some features. + Optimizes the rendering of static data sets at the expense of some features. */ /*! @@ -169,11 +173,7 @@ QAbstract3DGraph::QAbstract3DGraph(QAbstract3DGraphPrivate *d, const QSurfaceFor if (format) { surfaceFormat = *format; // Make sure renderable type is correct -#if !defined(QT_OPENGL_ES_2) - surfaceFormat.setRenderableType(QSurfaceFormat::OpenGL); -#else - surfaceFormat.setRenderableType(QSurfaceFormat::OpenGLES); -#endif + surfaceFormat.setRenderableType(QSurfaceFormat::DefaultRenderableType); } else { surfaceFormat = qDefaultSurfaceFormat(); } @@ -197,17 +197,24 @@ QAbstract3DGraph::QAbstract3DGraph(QAbstract3DGraphPrivate *d, const QSurfaceFor qDebug() << "GLSL version:" << (const char *)shaderVersion; #endif -#if !defined(QT_OPENGL_ES_2) - // If we have real OpenGL, GLSL version must be 1.2 or over. Quit if not. - QStringList splitversionstr = - QString::fromLatin1((const char *)shaderVersion).split(QChar::fromLatin1(' ')); - if (splitversionstr[0].toFloat() < 1.2) - qFatal("GLSL version must be 1.20 or higher. Try installing latest display drivers."); -#else - Q_UNUSED(shaderVersion) -#endif + if (!Utils::isOpenGLES()) { + // If we have real OpenGL, GLSL version must be 1.2 or over. Quit if not. + QStringList splitversionstr = + QString::fromLatin1((const char *)shaderVersion).split(QChar::fromLatin1(' ')); + if (splitversionstr[0].toFloat() < 1.2) + qFatal("GLSL version must be 1.20 or higher. Try installing latest display drivers."); + } d_ptr->renderLater(); + +#if defined(Q_OS_OSX) + // Enable touch events for Mac touchpads + typedef void * (*EnableTouch)(QWindow*, bool); + EnableTouch enableTouch = + (EnableTouch)QGuiApplication::platformNativeInterface()->nativeResourceFunctionForIntegration("registertouchwindow"); + if (enableTouch) + enableTouch(this, true); +#endif } /*! @@ -400,7 +407,7 @@ void QAbstract3DGraph::clearSelection() * \return index to the added item if add was successful, -1 if trying to add a null item, and * index of the item if trying to add an already added item. * - * \sa removeCustomItems(), removeCustomItem(), removeCustomItemAt() + * \sa removeCustomItems(), removeCustomItem(), removeCustomItemAt(), customItems() * * \since QtDataVisualization 1.1 */ @@ -455,6 +462,16 @@ void QAbstract3DGraph::releaseCustomItem(QCustom3DItem *item) } /*! + * \return list of all added custom items. + * \since QtDataVisualization 1.2 + * \sa addCustomItem() + */ +QList<QCustom3DItem *> QAbstract3DGraph::customItems() const +{ + return d_ptr->m_visualController->customItems(); +} + +/*! * Can be used to query the index of the selected label after receiving \c selectedElementChanged * signal with any label type. Selection is valid until the next \c selectedElementChanged signal. * @@ -545,6 +562,8 @@ QAbstract3DGraph::ElementType QAbstract3DGraph::selectedElement() const * \since QtDataVisualization 1.1 * * \return rendered image. + * + * \note OpenGL ES2 does not support anitialiasing, so \a msaaSamples is always forced to \c{0}. */ QImage QAbstract3DGraph::renderToImage(int msaaSamples, const QSize &imageSize) { @@ -591,8 +610,15 @@ qreal QAbstract3DGraph::currentFps() const * \property QAbstract3DGraph::orthoProjection * \since QtDataVisualization 1.1 * - * If \c {true}, orthographic projection will be used for displaying the graph. Defaults to \c{false}. + * If \c {true}, orthographic projection will be used for displaying the graph. + * \note Orthographic projection can be used to create 2D graphs by replacing the default input + * handler with one that doesn't allow rotating the graph and setting the camera to view the graph + * directly from the side or from the top. Also, axis labels typically need to be rotated when + * viewing the graph from the sides. + * Defaults to \c{false}. * \note Shadows will be disabled when set to \c{true}. + * + * \sa QAbstract3DAxis::labelAutoRotation, Q3DCamera::cameraPreset */ void QAbstract3DGraph::setOrthoProjection(bool enable) { @@ -608,14 +634,16 @@ bool QAbstract3DGraph::isOrthoProjection() const * \property QAbstract3DGraph::aspectRatio * \since QtDataVisualization 1.1 * - * Aspect ratio of the graph data. This is the ratio of data scaling between horizontal and - * vertical axes. Defaults to \c{2.0}. + * The aspect ratio is the ratio of the graph scaling between the longest axis on the horizontal + * plane and the Y-axis. Defaults to \c{2.0}. * * \note Has no effect on Q3DBars. + * + * \sa horizontalAspectRatio */ void QAbstract3DGraph::setAspectRatio(qreal ratio) { - d_ptr->m_visualController->setAspectRatio(float(ratio)); + d_ptr->m_visualController->setAspectRatio(ratio); } qreal QAbstract3DGraph::aspectRatio() const @@ -626,14 +654,20 @@ qreal QAbstract3DGraph::aspectRatio() const /*! * \property QAbstract3DGraph::optimizationHints * - * Defines if the rendering optimization is default or static. Default mode provides the full feature set at - * reasonable performance. Static is a beta level feature and currently supports only a subset of the - * features on the Scatter graph. Missing features are object gradient for mesh objects, both gradients - * for points, and diffuse and specular color on rotations. At this point static is intended just for - * introducing a new feature. It optimizes graph rendering and is ideal for large non-changing data - * sets. It is slower with dynamic data changes and item rotations. Selection is not optimized, so using it - * with massive data sets is not advisable. + * Defines if the rendering optimization is default or static. Default mode provides the full + * feature set at reasonable performance. Static mode optimizes graph rendering and is ideal for + * large non-changing data sets. It is slower with dynamic data changes and item rotations. + * Selection is not optimized, so using it with massive data sets is not advisable. + * Static works only on the Scatter graph. * Defaults to \c{OptimizationDefault}. + * + * \note On some environments, large graphs using static optimization may not render, because + * all of the items are rendered using a single draw call, and different graphics drivers have + * different maximum vertice counts per call that they support. + * This is mostly an issue on 32bit and/or OpenGL ES2 platforms. + * To work around this issue, choose an item mesh with low vertex count or use the point mesh. + * + * \sa QAbstract3DSeries::mesh */ void QAbstract3DGraph::setOptimizationHints(OptimizationHints hints) { @@ -646,6 +680,195 @@ QAbstract3DGraph::OptimizationHints QAbstract3DGraph::optimizationHints() const } /*! + * \property QAbstract3DGraph::polar + * \since QtDataVisualization 1.2 + * + * If \c {true}, the horizontal axes are changed into polar axes. The X axis becomes the + * angular axis and the Z axis becomes the radial axis. + * Polar mode is not available for bar graphs. + * + * Defaults to \c{false}. + * + * \sa orthoProjection, radialLabelOffset + */ +void QAbstract3DGraph::setPolar(bool enable) +{ + d_ptr->m_visualController->setPolar(enable); +} + +bool QAbstract3DGraph::isPolar() const +{ + return d_ptr->m_visualController->isPolar(); +} + +/*! + * \property QAbstract3DGraph::radialLabelOffset + * \since QtDataVisualization 1.2 + * + * This property specifies the normalized horizontal offset for the axis labels of the radial + * polar axis. The value 0.0 indicates the labels should be drawn next to the 0-angle angular + * axis grid line. The value 1.0 indicates the labels are drawn on their normal place at the edge + * of the graph background. + * This property is ignored if polar property value is \c{false}. Defaults to 1.0. + * + * \sa polar + */ +void QAbstract3DGraph::setRadialLabelOffset(float offset) +{ + d_ptr->m_visualController->setRadialLabelOffset(offset); +} + +float QAbstract3DGraph::radialLabelOffset() const +{ + return d_ptr->m_visualController->radialLabelOffset(); +} + +/*! + * \property QAbstract3DGraph::horizontalAspectRatio + * \since QtDataVisualization 1.2 + * + * The horizontal aspect ratio is the ratio of the graph scaling between the X and Z axes. + * Value of 0.0 indicates automatic scaling according to axis ranges. + * Defaults to \c{0.0}. + * + * \note Has no effect on Q3DBars, which handles scaling on the horizontal plane via + * \l{Q3DBars::barThickness}{barThickness} and \l{Q3DBars::barSpacing}{barSpacing} properties. + * Polar graphs also ignore this property. + * + * \sa aspectRatio, polar, Q3DBars::barThickness, Q3DBars::barSpacing + */ +void QAbstract3DGraph::setHorizontalAspectRatio(qreal ratio) +{ + d_ptr->m_visualController->setHorizontalAspectRatio(ratio); +} + +qreal QAbstract3DGraph::horizontalAspectRatio() const +{ + return d_ptr->m_visualController->horizontalAspectRatio(); +} + +/*! + * \property QAbstract3DGraph::reflection + * \since QtDataVisualization 1.2 + * + * Sets floor reflections on or off. Defaults to \c{false}. + * + * \note Affects only Q3DBars. + * + * \note In Q3DBars graphs holding both positive and negative values, reflections are not supported + * for custom items that intersect the floor plane. In that case, reflections should be turned off + * to avoid incorrect rendering. + * + * \note If using custom surface format, stencil buffer needs to be defined + * (QSurfaceFormat::setStencilBufferSize()) for reflections to work. + * + * \sa reflectivity + */ +void QAbstract3DGraph::setReflection(bool enable) +{ + d_ptr->m_visualController->setReflection(enable); +} + +bool QAbstract3DGraph::isReflection() const +{ + return d_ptr->m_visualController->reflection(); +} + +/*! + * \property QAbstract3DGraph::reflectivity + * \since QtDataVisualization 1.2 + * + * Adjusts floor reflectivity, larger number being more reflective. Valid range is \c{[0...1]}. + * Defaults to \c{0.5}. + * + * \note Affects only Q3DBars. + * + * \sa reflection + */ +void QAbstract3DGraph::setReflectivity(qreal reflectivity) +{ + d_ptr->m_visualController->setReflectivity(reflectivity); +} + +qreal QAbstract3DGraph::reflectivity() const +{ + return d_ptr->m_visualController->reflectivity(); +} + +/*! + * \property QAbstract3DGraph::locale + * \since QtDataVisualization 1.2 + * + * Sets the locale used for formatting various numeric labels. + * Defaults to \c{"C"} locale. + * + * \sa QValue3DAxis::labelFormat + */ +void QAbstract3DGraph::setLocale(const QLocale &locale) +{ + d_ptr->m_visualController->setLocale(locale); +} + +QLocale QAbstract3DGraph::locale() const +{ + return d_ptr->m_visualController->locale(); +} + +/*! + * \property QAbstract3DGraph::queriedGraphPosition + * \since QtDataVisualization 1.2 + * + * This read-only property contains the latest graph position values along each axis queried using + * Q3DScene::graphPositionQuery. The values are normalized to range \c{[-1, 1]}. + * If the queried position was outside the graph bounds, the values + * will not reflect the real position, but will instead be some undefined position outside + * the range \c{[-1, 1]}. The value will be undefined before any queries are made. + * + * There isn't a single correct 3D coordinate to match to each specific screen position, so to be + * consistent, the queries are always done against the inner sides of an invisible box surrounding + * the graph. + * + * \note Bar graphs only allow querying graph position at the graph floor level, + * so the Y-value is always zero for bar graphs and the valid queries can be only made at + * screen positions that contain the floor of the graph. + * + * \sa Q3DScene::graphPositionQuery + */ +QVector3D QAbstract3DGraph::queriedGraphPosition() const +{ + return d_ptr->m_visualController->queriedGraphPosition(); +} + +/*! + * \property QAbstract3DGraph::margin + * \since QtDataVisualization 1.2 + * + * This property contains the absolute value used for graph margin. The graph margin is the space + * left between the edge of the plottable graph area and the edge of the graph background. + * If the margin value is negative, the margins are determined automatically and can vary according + * to size of the items in the series and the type of the graph. + * The value is interpreted as a fraction of Y-axis range, provided the graph aspect ratios have + * not beed changed from the defaults. + * Defaults to \c{-1.0}. + * + * \note Having smaller than the automatically determined margin on scatter graph can cause + * the scatter items at the edges of the graph to overlap with the graph background. + * + * \note On scatter and surface graphs, if the margin is comparatively small to the axis label + * size, the positions of the edge labels of the axes are adjusted to avoid overlap with + * the edge labels of the neighboring axes. + */ +void QAbstract3DGraph::setMargin(qreal margin) +{ + d_ptr->m_visualController->setMargin(margin); +} + +qreal QAbstract3DGraph::margin() const +{ + return d_ptr->m_visualController->margin(); +} + +/*! * \internal */ bool QAbstract3DGraph::event(QEvent *event) @@ -795,6 +1018,23 @@ void QAbstract3DGraphPrivate::setVisualController(Abstract3DController *controll QObject::connect(m_visualController, &Abstract3DController::aspectRatioChanged, q_ptr, &QAbstract3DGraph::aspectRatioChanged); + QObject::connect(m_visualController, &Abstract3DController::polarChanged, q_ptr, + &QAbstract3DGraph::polarChanged); + QObject::connect(m_visualController, &Abstract3DController::radialLabelOffsetChanged, q_ptr, + &QAbstract3DGraph::radialLabelOffsetChanged); + QObject::connect(m_visualController, &Abstract3DController::horizontalAspectRatioChanged, q_ptr, + &QAbstract3DGraph::horizontalAspectRatioChanged); + + QObject::connect(m_visualController, &Abstract3DController::reflectionChanged, q_ptr, + &QAbstract3DGraph::reflectionChanged); + QObject::connect(m_visualController, &Abstract3DController::reflectivityChanged, q_ptr, + &QAbstract3DGraph::reflectivityChanged); + QObject::connect(m_visualController, &Abstract3DController::localeChanged, q_ptr, + &QAbstract3DGraph::localeChanged); + QObject::connect(m_visualController, &Abstract3DController::queriedGraphPositionChanged, q_ptr, + &QAbstract3DGraph::queriedGraphPositionChanged); + QObject::connect(m_visualController, &Abstract3DController::marginChanged, q_ptr, + &QAbstract3DGraph::marginChanged); } void QAbstract3DGraphPrivate::handleDevicePixelRatioChange() @@ -849,8 +1089,10 @@ QImage QAbstract3DGraphPrivate::renderToImage(int msaaSamples, const QSize &imag // Render the wanted frame offscreen m_context->makeCurrent(m_offscreenSurface); fboFormat.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); - fboFormat.setInternalTextureFormat(GL_RGB); - fboFormat.setSamples(msaaSamples); + if (!Utils::isOpenGLES()) { + fboFormat.setInternalTextureFormat(GL_RGB); + fboFormat.setSamples(msaaSamples); + } fbo = new QOpenGLFramebufferObject(imageSize, fboFormat); if (fbo->isValid()) { QRect originalViewport = m_visualController->m_scene->viewport(); diff --git a/src/datavisualization/engine/qabstract3dgraph.h b/src/datavisualization/engine/qabstract3dgraph.h index 59f61aae..a0c26a42 100644 --- a/src/datavisualization/engine/qabstract3dgraph.h +++ b/src/datavisualization/engine/qabstract3dgraph.h @@ -25,6 +25,7 @@ #include <QtDataVisualization/qabstract3dinputhandler.h> #include <QtGui/QWindow> #include <QtGui/QOpenGLFunctions> +#include <QtCore/QLocale> QT_BEGIN_NAMESPACE_DATAVISUALIZATION @@ -50,6 +51,14 @@ class QT_DATAVISUALIZATION_EXPORT QAbstract3DGraph : public QWindow, protected Q Q_PROPERTY(ElementType selectedElement READ selectedElement NOTIFY selectedElementChanged) Q_PROPERTY(qreal aspectRatio READ aspectRatio WRITE setAspectRatio NOTIFY aspectRatioChanged) Q_PROPERTY(OptimizationHints optimizationHints READ optimizationHints WRITE setOptimizationHints NOTIFY optimizationHintsChanged) + Q_PROPERTY(bool polar READ isPolar WRITE setPolar NOTIFY polarChanged) + Q_PROPERTY(float radialLabelOffset READ radialLabelOffset WRITE setRadialLabelOffset NOTIFY radialLabelOffsetChanged) + Q_PROPERTY(qreal horizontalAspectRatio READ horizontalAspectRatio WRITE setHorizontalAspectRatio NOTIFY horizontalAspectRatioChanged) + Q_PROPERTY(bool reflection READ isReflection WRITE setReflection NOTIFY reflectionChanged) + Q_PROPERTY(qreal reflectivity READ reflectivity WRITE setReflectivity NOTIFY reflectivityChanged) + Q_PROPERTY(QLocale locale READ locale WRITE setLocale NOTIFY localeChanged) + Q_PROPERTY(QVector3D queriedGraphPosition READ queriedGraphPosition NOTIFY queriedGraphPositionChanged) + Q_PROPERTY(qreal margin READ margin WRITE setMargin NOTIFY marginChanged) protected: explicit QAbstract3DGraph(QAbstract3DGraphPrivate *d, const QSurfaceFormat *format, @@ -126,6 +135,7 @@ public: void removeCustomItem(QCustom3DItem *item); void removeCustomItemAt(const QVector3D &position); void releaseCustomItem(QCustom3DItem *item); + QList<QCustom3DItem *> customItems() const; int selectedLabelIndex() const; QAbstract3DAxis *selectedAxis() const; @@ -150,6 +160,29 @@ public: void setOptimizationHints(OptimizationHints hints); OptimizationHints optimizationHints() const; + void setPolar(bool enable); + bool isPolar() const; + + void setRadialLabelOffset(float offset); + float radialLabelOffset() const; + + void setHorizontalAspectRatio(qreal ratio); + qreal horizontalAspectRatio() const; + + void setReflection(bool enable); + bool isReflection() const; + + void setReflectivity(qreal reflectivity); + qreal reflectivity() const; + + void setLocale(const QLocale &locale); + QLocale locale() const; + + QVector3D queriedGraphPosition() const; + + void setMargin(qreal margin); + qreal margin() const; + protected: bool event(QEvent *event); void resizeEvent(QResizeEvent *event); @@ -173,6 +206,14 @@ signals: void orthoProjectionChanged(bool enabled); void aspectRatioChanged(qreal ratio); void optimizationHintsChanged(QAbstract3DGraph::OptimizationHints hints); + void polarChanged(bool enabled); + void radialLabelOffsetChanged(float offset); + void horizontalAspectRatioChanged(qreal ratio); + void reflectionChanged(bool enabled); + void reflectivityChanged(qreal reflectivity); + void localeChanged(const QLocale &locale); + void queriedGraphPositionChanged(const QVector3D &data); + void marginChanged(qreal margin); private: Q_DISABLE_COPY(QAbstract3DGraph) diff --git a/src/datavisualization/engine/scatter3drenderer.cpp b/src/datavisualization/engine/scatter3drenderer.cpp index dd50188b..f6367153 100644 --- a/src/datavisualization/engine/scatter3drenderer.cpp +++ b/src/datavisualization/engine/scatter3drenderer.cpp @@ -33,12 +33,9 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION -//#define USE_UNIFORM_SCALING // Scale x and z uniformly, or based on autoscaled values - const GLfloat defaultMinSize = 0.01f; const GLfloat defaultMaxSize = 0.1f; const GLfloat itemScaler = 3.0f; -const GLfloat gridLineWidth = 0.005f; Scatter3DRenderer::Scatter3DRenderer(Scatter3DController *controller) : Abstract3DRenderer(controller), @@ -46,30 +43,27 @@ Scatter3DRenderer::Scatter3DRenderer(Scatter3DController *controller) m_updateLabels(false), m_dotShader(0), m_dotGradientShader(0), - #if defined(QT_OPENGL_ES_2) + m_staticSelectedItemGradientShader(0), + m_staticSelectedItemShader(0), m_pointShader(0), - #endif m_depthShader(0), m_selectionShader(0), m_backgroundShader(0), - m_labelShader(0), + m_staticGradientPointShader(0), m_bgrTexture(0), - m_depthTexture(0), m_selectionTexture(0), m_depthFrameBuffer(0), m_selectionFrameBuffer(0), m_selectionDepthBuffer(0), m_shadowQualityToShader(100.0f), m_shadowQualityMultiplier(3), - m_heightNormalizer(1.0f), - m_scaleFactor(0), + m_scaleX(0.0f), + m_scaleY(0.0f), + m_scaleZ(0.0f), m_selectedItemIndex(Scatter3DController::invalidSelectionIndex()), m_selectedSeriesCache(0), m_oldSelectedSeriesCache(0), - m_areaSize(QSizeF(0.0, 0.0)), m_dotSizeScale(1.0f), - m_hasHeightAdjustmentChanged(true), - m_backgroundMargin(defaultMaxSize), m_maxItemSize(0.0f), m_clickedIndex(Scatter3DController::invalidSelectionIndex()), m_havePointSeries(false), @@ -77,15 +71,13 @@ Scatter3DRenderer::Scatter3DRenderer(Scatter3DController *controller) m_haveUniformColorMeshSeries(false), m_haveGradientMeshSeries(false) { - m_axisCacheY.setScale(2.0f); - m_axisCacheY.setTranslate(-1.0f); - - initializeOpenGLFunctions(); initializeOpenGL(); } Scatter3DRenderer::~Scatter3DRenderer() { + fixContextBeforeDelete(); + if (QOpenGLContext::currentContext()) { m_textureHelper->glDeleteFramebuffers(1, &m_selectionFrameBuffer); m_textureHelper->glDeleteRenderbuffers(1, &m_selectionDepthBuffer); @@ -94,11 +86,13 @@ Scatter3DRenderer::~Scatter3DRenderer() 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_labelShader; + delete m_staticGradientPointShader; } void Scatter3DRenderer::initializeOpenGL() @@ -106,28 +100,17 @@ void Scatter3DRenderer::initializeOpenGL() Abstract3DRenderer::initializeOpenGL(); // Initialize shaders - initLabelShaders(QStringLiteral(":/shaders/vertexLabel"), - QStringLiteral(":/shaders/fragmentLabel")); -#if !defined(QT_OPENGL_ES_2) - // Init depth shader (for shadows). Init in any case, easier to handle shadow activation if done via api. - initDepthShader(); -#else - // Init point shader - initPointShader(); -#endif + if (!m_isOpenGLES) { + initDepthShader(); // For shadows + loadGridLineMesh(); + } else { + initPointShader(); + } // Init selection shader initSelectionShader(); -#if !defined(QT_OPENGL_ES_2) - // Load grid line mesh - loadGridLineMesh(); -#endif - - // Load label mesh - loadLabelMesh(); - // Set view port glViewport(m_primarySubViewport.x(), m_primarySubViewport.y(), @@ -138,6 +121,53 @@ void Scatter3DRenderer::initializeOpenGL() 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(); @@ -145,24 +175,31 @@ void Scatter3DRenderer::updateData() foreach (SeriesRenderCache *baseCache, m_renderCacheList) { ScatterSeriesRenderCache *cache = static_cast<ScatterSeriesRenderCache *>(baseCache); - if (cache->isVisible() && cache->dataDirty()) { + 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 (dataSize != renderArray.size()) - renderArray.resize(dataSize); + if (cache->dataDirty()) { + if (dataSize != renderArray.size()) + renderArray.resize(dataSize); + + for (int i = 0; i < dataSize; i++) + updateRenderItem(dataArray.at(i), renderArray[i]); - for (int i = 0; i < dataSize; i++) - updateRenderItem(dataArray.at(i), renderArray[i]); - cache->setDataDirty(false); + 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))), + m_dotSizeScale = GLfloat(qBound(defaultMinSize, + 2.0f / float(qSqrt(qreal(totalDataSize))), defaultMaxSize)); } @@ -179,6 +216,7 @@ void Scatter3DRenderer::updateData() points = new ScatterPointBufferHelper(); cache->setBufferPoints(points); } + points->setScaleY(m_scaleY); points->load(cache); } else { ScatterObjectBufferHelper *object = cache->bufferObject(); @@ -187,7 +225,9 @@ void Scatter3DRenderer::updateData() cache->setBufferObject(object); } if (renderArraySize != cache->oldArraySize() - || cache->object()->objectFile() != cache->oldMeshFileName()) { + || cache->object()->objectFile() != cache->oldMeshFileName() + || cache->staticBufferDirty()) { + object->setScaleY(m_scaleY); object->fullLoad(cache, m_dotSizeScale); cache->setOldArraySize(renderArraySize); cache->setOldMeshFileName(cache->object()->objectFile()); @@ -195,6 +235,8 @@ void Scatter3DRenderer::updateData() object->update(cache, m_dotSizeScale); } } + + cache->setStaticBufferDirty(false); } } } @@ -205,9 +247,28 @@ void Scatter3DRenderer::updateData() void Scatter3DRenderer::updateSeries(const QList<QAbstract3DSeries *> &seriesList) { + int seriesCount = seriesList.size(); + + // Check OptimizationStatic specific issues before populate marks changeTracker done + if (m_cachedOptimizationHint.testFlag(QAbstract3DGraph::OptimizationStatic)) { + for (int i = 0; i < seriesCount; i++) { + QScatter3DSeries *scatterSeries = static_cast<QScatter3DSeries *>(seriesList[i]); + if (scatterSeries->isVisible()) { + QAbstract3DSeriesChangeBitField &changeTracker = scatterSeries->d_ptr->m_changeTracker; + ScatterSeriesRenderCache *cache = + static_cast<ScatterSeriesRenderCache *>(m_renderCacheList.value(scatterSeries)); + if (cache) { + if (changeTracker.baseGradientChanged || changeTracker.colorStyleChanged) + cache->setStaticObjectUVDirty(true); + if (cache->itemSize() != scatterSeries->itemSize()) + cache->setStaticBufferDirty(true); + } + } + } + } + Abstract3DRenderer::updateSeries(seriesList); - int seriesCount = seriesList.size(); float maxItemSize = 0.0f; float itemSize = 0.0f; bool noSelection = true; @@ -243,13 +304,28 @@ void Scatter3DRenderer::updateSeries(const QList<QAbstract3DSeries *> &seriesLis 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; - if (maxItemSize > defaultMaxSize) - m_backgroundMargin = maxItemSize / itemScaler; - else - m_backgroundMargin = defaultMaxSize; + calculateSceneScalingFactors(); if (noSelection) { if (!selectionLabel().isEmpty()) @@ -268,6 +344,8 @@ void Scatter3DRenderer::updateItems(const QVector<Scatter3DController::ChangeIte 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; @@ -282,7 +360,43 @@ void Scatter3DRenderer::updateItems(const QVector<Scatter3DController::ChangeIte } if (cache->isVisible()) { const int index = item.index; - updateRenderItem(dataArray->at(index), cache->renderArray()[index]); + if (index >= cache->renderArray().size()) + continue; // Items removed from array for same render + bool oldVisibility; + ScatterRenderItem &item = cache->renderArray()[index]; + if (optimizationStatic) + oldVisibility = item.isVisible(); + updateRenderItem(dataArray->at(index), item); + if (optimizationStatic) { + if (!cache->visibilityChanged() && oldVisibility != item.isVisible()) + cache->setVisibilityChanged(true); + cache->updateIndices().append(index); + } + } + } + if (optimizationStatic) { + foreach (SeriesRenderCache *baseCache, m_renderCacheList) { + ScatterSeriesRenderCache *cache = static_cast<ScatterSeriesRenderCache *>(baseCache); + if (cache->isVisible() && cache->updateIndices().size()) { + if (cache->mesh() == QAbstract3DSeries::MeshPoint) { + cache->bufferPoints()->update(cache); + if (cache->colorStyle() == Q3DTheme::ColorStyleRangeGradient) + cache->bufferPoints()->updateUVs(cache); + } else { + if (cache->visibilityChanged()) { + // If any change changes item visibility, full load is needed to + // resize the buffers. + cache->updateIndices().clear(); + cache->bufferObject()->fullLoad(cache, m_dotSizeScale); + } else { + cache->bufferObject()->update(cache, m_dotSizeScale); + if (cache->colorStyle() == Q3DTheme::ColorStyleRangeGradient) + cache->bufferObject()->updateUVs(cache); + } + } + cache->updateIndices().clear(); + } + cache->setVisibilityChanged(false); } } } @@ -291,14 +405,46 @@ void Scatter3DRenderer::updateScene(Q3DScene *scene) { scene->activeCamera()->d_ptr->setMinYRotation(-90.0f); - if (m_hasHeightAdjustmentChanged) { - // Set initial camera position. Also update if height adjustment has changed. - scene->activeCamera()->d_ptr->setBaseOrientation(cameraDistanceVector, zeroVector, - upVector); - m_hasHeightAdjustmentChanged = false; + 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")); } +} - Abstract3DRenderer::updateScene(scene); +void Scatter3DRenderer::updateMargin(float margin) +{ + Abstract3DRenderer::updateMargin(margin); + calculateSceneScalingFactors(); } void Scatter3DRenderer::resetClickedStatus() @@ -374,6 +520,7 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) 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) @@ -389,169 +536,182 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) QVector3D lightPos = m_cachedScene->activeLight()->position(); // Introduce regardless of shadow quality to simplify logic - QMatrix4x4 depthViewMatrix; - QMatrix4x4 depthProjectionMatrix; 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); - - // Get the depth view matrix - // It may be possible to hack lightPos here if we want to make some tweaks to shadow - QVector3D depthLightPos = activeCamera->d_ptr->calculatePositionRelativeToCamera( - zeroVector, 0.0f, 2.5f / m_autoScaleAdjustment); - depthViewMatrix.lookAt(depthLightPos, zeroVector, upVector); - // Set the depth projection matrix - depthProjectionMatrix.perspective(15.0f, viewPortRatio, 3.0f, 100.0f); - depthProjectionViewMatrix = depthProjectionMatrix * depthViewMatrix; - - // Draw dots to depth buffer - foreach (SeriesRenderCache *baseCache, m_renderCacheList) { - if (baseCache->isVisible()) { - ScatterSeriesRenderCache *cache = - static_cast<ScatterSeriesRenderCache *>(baseCache); - ObjectHelper *dotObj = cache->object(); - QQuaternion seriesRotation(cache->meshRotation()); - const ScatterRenderItemArray &renderArray = cache->renderArray(); - const int renderArraySize = renderArray.size(); - bool drawingPoints = (cache->mesh() == QAbstract3DSeries::MeshPoint); - float itemSize = cache->itemSize() / itemScaler; - if (itemSize == 0.0f) - itemSize = m_dotSizeScale; - if (drawingPoints) { - // Scale points based on shadow quality for shadows, not by zoom level - glPointSize(itemSize * 100.0f * m_shadowQualityMultiplier); - } - QVector3D modelScaler(itemSize, itemSize, itemSize); + if (m_havePointSeries) { + glEnable(GL_POINT_SMOOTH); + glEnable(GL_PROGRAM_POINT_SIZE); + } - if (!optimizationDefault - && ((drawingPoints && cache->bufferPoints()->indexCount() == 0) - || (!drawingPoints && cache->bufferObject()->indexCount() == 0))) { - continue; - } + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + // Render scene into a depth texture for using with shadow mapping + // Bind depth shader + m_depthShader->bind(); + + // Set viewport for depth map rendering. Must match texture size. Larger values give smoother shadows. + glViewport(0, 0, + m_primarySubViewport.width() * m_shadowQualityMultiplier, + m_primarySubViewport.height() * m_shadowQualityMultiplier); + + // Enable drawing to framebuffer + glBindFramebuffer(GL_FRAMEBUFFER, m_depthFrameBuffer); + glClear(GL_DEPTH_BUFFER_BIT); + + // Set front face culling to reduce self-shadowing issues + glCullFace(GL_FRONT); + + QMatrix4x4 depthViewMatrix; + QMatrix4x4 depthProjectionMatrix; + + // Get the depth view matrix + // It may be possible to hack lightPos here if we want to make some tweaks to shadow + QVector3D depthLightPos = activeCamera->d_ptr->calculatePositionRelativeToCamera( + zeroVector, 0.0f, 2.5f / m_autoScaleAdjustment); + depthViewMatrix.lookAt(depthLightPos, zeroVector, upVector); + // Set the depth projection matrix + depthProjectionMatrix.perspective(15.0f, viewPortRatio, 3.0f, 100.0f); + depthProjectionViewMatrix = depthProjectionMatrix * depthViewMatrix; + + // Draw dots to depth buffer + foreach (SeriesRenderCache *baseCache, m_renderCacheList) { + if (baseCache->isVisible()) { + ScatterSeriesRenderCache *cache = + static_cast<ScatterSeriesRenderCache *>(baseCache); + ObjectHelper *dotObj = cache->object(); + QQuaternion seriesRotation(cache->meshRotation()); + const ScatterRenderItemArray &renderArray = cache->renderArray(); + const int renderArraySize = renderArray.size(); + bool drawingPoints = (cache->mesh() == QAbstract3DSeries::MeshPoint); + float itemSize = cache->itemSize() / itemScaler; + if (itemSize == 0.0f) + itemSize = m_dotSizeScale; + if (drawingPoints) { + // Scale points based on shadow quality for shadows, not by zoom level + m_funcs_2_1->glPointSize(itemSize * 100.0f * m_shadowQualityMultiplier); + } + QVector3D modelScaler(itemSize, itemSize, itemSize); - int loopCount = 1; - if (optimizationDefault) - loopCount = renderArraySize; - for (int dot = 0; dot < loopCount; dot++) { - const ScatterRenderItem &item = renderArray.at(dot); - if (!item.isVisible() && optimizationDefault) + if (!optimizationDefault + && ((drawingPoints && cache->bufferPoints()->indexCount() == 0) + || (!drawingPoints && cache->bufferObject()->indexCount() == 0))) { 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; + 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; - m_depthShader->setUniformValue(m_depthShader->MVP(), MVPMatrix); + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; - if (drawingPoints) { - if (optimizationDefault) - m_drawer->drawPoint(m_depthShader); - else - m_drawer->drawPoints(m_depthShader, cache->bufferPoints()); - } 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()); + modelMatrix.translate(item.translation()); + if (!drawingPoints) { + if (!seriesRotation.isIdentity() || !item.rotation().isIdentity()) + modelMatrix.rotate(seriesRotation * item.rotation()); + modelMatrix.scale(modelScaler); + } + } - // Draw the triangles - glDrawElements(GL_TRIANGLES, dotObj->indexCount(), GL_UNSIGNED_SHORT, - (void *)0); + MVPMatrix = depthProjectionViewMatrix * modelMatrix; - // Free buffers - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - glBindBuffer(GL_ARRAY_BUFFER, 0); + m_depthShader->setUniformValue(m_depthShader->MVP(), MVPMatrix); - glDisableVertexAttribArray(m_depthShader->posAtt()); + if (drawingPoints) { + if (optimizationDefault) + m_drawer->drawPoint(m_depthShader); + else + m_drawer->drawPoints(m_depthShader, cache->bufferPoints(), 0); } 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(), - object->indicesType(), (void *)0); - - // Free buffers - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - glBindBuffer(GL_ARRAY_BUFFER, 0); - - glDisableVertexAttribArray(m_depthShader->posAtt()); + 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_SHORT, + (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(), + object->indicesType(), (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); + Abstract3DRenderer::drawCustomItems(RenderingDepth, m_depthShader, viewMatrix, + projectionViewMatrix, + depthProjectionViewMatrix, m_depthTexture, + m_shadowQualityToShader); - // Disable drawing to framebuffer (= enable drawing to screen) - glBindFramebuffer(GL_FRAMEBUFFER, defaultFboHandle); + // Disable drawing to framebuffer (= enable drawing to screen) + glBindFramebuffer(GL_FRAMEBUFFER, defaultFboHandle); - // Reset culling to normal - glCullFace(GL_BACK); + // Reset culling to normal + glCullFace(GL_BACK); - // Revert to original viewport - glViewport(m_primarySubViewport.x(), - m_primarySubViewport.y(), - m_primarySubViewport.width(), - m_primarySubViewport.height()); + // 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 *pointSelectionShader = m_selectionShader; -#else - ShaderHelper *pointSelectionShader = m_pointShader; -#endif 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_visibleSeriesCount > 0 || !m_customRenderCache.isEmpty()) + && m_selectionTexture) { // Draw dots to selection buffer glBindFramebuffer(GL_FRAMEBUFFER, m_selectionFrameBuffer); glViewport(0, 0, @@ -578,8 +738,8 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) if (itemSize == 0.0f) itemSize = m_dotSizeScale; #if !defined(QT_OPENGL_ES_2) - if (drawingPoints) - glPointSize(itemSize * activeCamera->zoomLevel()); // Scale points based on zoom + if (drawingPoints && !m_isOpenGLES) + m_funcs_2_1->glPointSize(itemSize * activeCamera->zoomLevel()); #endif QVector3D modelScaler(itemSize, itemSize, itemSize); @@ -627,9 +787,10 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) } } - Abstract3DRenderer::drawCustomItems(RenderingSelection, m_selectionShader, viewMatrix, - projectionViewMatrix, depthProjectionViewMatrix, - m_depthTexture, m_shadowQualityToShader); + Abstract3DRenderer::drawCustomItems(RenderingSelection, m_selectionShader, + viewMatrix, projectionViewMatrix, + depthProjectionViewMatrix, m_depthTexture, + m_shadowQualityToShader); drawLabels(true, activeCamera, viewMatrix, projectionMatrix); @@ -639,6 +800,7 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) QVector4D clickedColor = Utils::getSelection(m_inputPosition, m_viewport.height()); selectionColorToSeriesAndIndex(clickedColor, m_clickedIndex, m_clickedSeries); + m_clickResolved = true; emit needRender(); @@ -683,13 +845,12 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) previousMeshColorStyle = Q3DTheme::ColorStyleRangeGradient; m_dotGradientShader->setUniformValue(m_dotGradientShader->gradientHeight(), 0.0f); } - glEnable(GL_TEXTURE_2D); } else { dotShader = pointSelectionShader; - previousDrawingPoints = true; - dotShader->bind(); } + float rangeGradientYScaler = 0.5f / m_scaleY; + foreach (SeriesRenderCache *baseCache, m_renderCacheList) { if (baseCache->isVisible()) { ScatterSeriesRenderCache *cache = @@ -710,14 +871,16 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) if (itemSize == 0.0f) itemSize = m_dotSizeScale; #if !defined(QT_OPENGL_ES_2) - if (drawingPoints) - glPointSize(itemSize * activeCamera->zoomLevel()); // Scale points based on zoom + 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))) { + || (!drawingPoints && cache->bufferObject()->indexCount() == 0))) { continue; } @@ -725,10 +888,18 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) if (drawingPoints != previousDrawingPoints || (!drawingPoints && (colorStyleIsUniform != (previousMeshColorStyle - == Q3DTheme::ColorStyleUniform)))) { + == Q3DTheme::ColorStyleUniform))) + || (!optimizationDefault && drawingPoints)) { previousDrawingPoints = drawingPoints; if (drawingPoints) { - dotShader = pointSelectionShader; + if (!optimizationDefault && rangeGradientPoints) { + if (m_isOpenGLES) + dotShader = m_staticGradientPointShader; + else + dotShader = m_labelShader; + } else { + dotShader = pointSelectionShader; + } } else { if (colorStyleIsUniform) dotShader = m_dotShader; @@ -740,13 +911,13 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) if (!drawingPoints && !colorStyleIsUniform && previousMeshColorStyle != colorStyle) { if (colorStyle == Q3DTheme::ColorStyleObjectGradient) { - m_dotGradientShader->setUniformValue(m_dotGradientShader->gradientMin(), 0.0f); - m_dotGradientShader->setUniformValue(m_dotGradientShader->gradientHeight(), - 0.5f); + dotShader->setUniformValue(dotShader->gradientMin(), 0.0f); + dotShader->setUniformValue(dotShader->gradientHeight(), + 0.5f); } else { // Each dot is of uniform color according to its Y-coordinate - m_dotGradientShader->setUniformValue(m_dotGradientShader->gradientHeight(), - 0.0f); + dotShader->setUniformValue(dotShader->gradientHeight(), + 0.0f); } } @@ -760,6 +931,7 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) int loopCount = 1; if (optimizationDefault) loopCount = renderArraySize; + for (int i = 0; i < loopCount; i++) { ScatterRenderItem &item = renderArray[i]; if (!item.isVisible() && optimizationDefault) @@ -791,7 +963,8 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) if (rangeGradientPoints) { // Drawing points with range gradient // Get color from gradient based on items y position converted to percent - int position = int(item.translation().y() * 50.0f) + 50; + 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 { @@ -801,6 +974,9 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) gradientTexture = cache->baseGradientTexture(); } + if (!optimizationDefault && rangeGradientPoints) + gradientTexture = cache->baseGradientTexture(); + GLfloat lightStrength = m_cachedTheme->lightStrength(); if (optimizationDefault && selectedSeries && (m_selectedItemIndex == i)) { if (useColor) @@ -808,13 +984,12 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) else gradientTexture = cache->singleHighlightGradientTexture(); lightStrength = m_cachedTheme->highlightLightStrength(); - // Insert data to ScatterRenderItem - // We don't have ownership, so don't delete the previous one + // 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_cachedTheme->font().pointSizeF() / 500.0f); + selectedItemSize = itemSize + m_drawer->scaledFontSize() - 0.05f; } if (!drawingPoints) { @@ -829,10 +1004,10 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) dotShader->setUniformValue(dotShader->color(), dotColor); } else if (colorStyle == Q3DTheme::ColorStyleRangeGradient) { dotShader->setUniformValue(dotShader->gradientMin(), - (item.translation().y() + 1.0f) / 2.0f); + (item.translation().y() + m_scaleY) + * rangeGradientYScaler); } -#if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) { if (!drawingPoints) { // Set shadow shader bindings QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; @@ -853,11 +1028,9 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) if (optimizationDefault) m_drawer->drawPoint(dotShader); else - m_drawer->drawPoints(dotShader, cache->bufferPoints()); + m_drawer->drawPoints(dotShader, cache->bufferPoints(), gradientTexture); } - } else -#endif - { + } else { if (!drawingPoints) { // Set shadowless shader bindings dotShader->setUniformValue(dotShader->lightS(), lightStrength); @@ -871,100 +1044,139 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) if (optimizationDefault) m_drawer->drawPoint(dotShader); else - m_drawer->drawPoints(dotShader, cache->bufferPoints()); + 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]; - ObjectHelper *dotObj = cache->object(); + if (item.isVisible()) { + ShaderHelper *selectionShader; + if (drawingPoints) { + selectionShader = pointSelectionShader; + } else { + if (colorStyleIsUniform) + selectionShader = m_staticSelectedItemShader; + else + selectionShader = m_staticSelectedItemGradientShader; + } + selectionShader->bind(); - QMatrix4x4 modelMatrix; - QMatrix4x4 itModelMatrix; + ObjectHelper *dotObj = cache->object(); - modelMatrix.translate(item.translation()); - if (!drawingPoints) { - if (!seriesRotation.isIdentity() || !item.rotation().isIdentity()) { - QQuaternion totalRotation = seriesRotation * item.rotation(); - modelMatrix.rotate(totalRotation); - itModelMatrix.rotate(totalRotation); + 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); } - modelMatrix.scale(modelScaler); - itModelMatrix.scale(modelScaler); - } - QMatrix4x4 MVPMatrix; + QMatrix4x4 MVPMatrix; #ifdef SHOW_DEPTH_TEXTURE_SCENE - MVPMatrix = depthProjectionViewMatrix * modelMatrix; + MVPMatrix = depthProjectionViewMatrix * modelMatrix; #else - MVPMatrix = projectionViewMatrix * modelMatrix; + 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 on label drawing - selectedItem = &item; - dotSelectionFound = true; - // Save selected item size (adjusted with font size) for selection label - // positioning - selectedItemSize = itemSize + (m_cachedTheme->font().pointSizeF() / 500.0f); - - if (!drawingPoints) { - // Set shader bindings - dotShader->setUniformValue(dotShader->model(), modelMatrix); - dotShader->setUniformValue(dotShader->nModel(), - itModelMatrix.inverted().transposed()); - } + 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; - dotShader->setUniformValue(dotShader->MVP(), MVPMatrix); - if (useColor) { - dotShader->setUniformValue(dotShader->color(), dotColor); - } else if (colorStyle == Q3DTheme::ColorStyleRangeGradient) { - dotShader->setUniformValue(dotShader->gradientMin(), - (item.translation().y() + 1.0f) / 2.0f); - } + 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); + } + } + } - if (!drawingPoints) { - glEnable(GL_POLYGON_OFFSET_FILL); - glPolygonOffset(-1.0f, 1.0f); - } + selectionShader->setUniformValue(selectionShader->MVP(), MVPMatrix); + if (useColor) + selectionShader->setUniformValue(selectionShader->color(), dotColor); -#if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { if (!drawingPoints) { - // Set shadow shader bindings - QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; - dotShader->setUniformValue(dotShader->depth(), depthMVPMatrix); - dotShader->setUniformValue(dotShader->lightS(), lightStrength / 10.0f); - - // Draw the object - m_drawer->drawObject(dotShader, dotObj, gradientTexture, m_depthTexture); - } else { - // Draw the object - m_drawer->drawPoint(dotShader); + glEnable(GL_POLYGON_OFFSET_FILL); + glPolygonOffset(-1.0f, 1.0f); } - } else -#endif - { - if (!drawingPoints) { - // Set shadowless shader bindings - dotShader->setUniformValue(dotShader->lightS(), lightStrength); - // Draw the object - m_drawer->drawObject(dotShader, dotObj, gradientTexture); + + 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 { - // Draw the object - m_drawer->drawPoint(dotShader); + 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); + if (!drawingPoints) + glDisable(GL_POLYGON_OFFSET_FILL); + } + dotShader->bind(); } } } @@ -987,25 +1199,12 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) QMatrix4x4 MVPMatrix; QMatrix4x4 itModelMatrix; -#ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z - GLfloat xScale = (m_graphAspectRatio * m_areaSize.width()) / m_scaleFactor - + m_backgroundMargin; - GLfloat zScale = (m_graphAspectRatio * m_areaSize.height()) / m_scaleFactor - + m_backgroundMargin; - if (m_maxItemSize > xScale) - xScale = m_maxItemSize; - if (m_maxItemSize > zScale) - zScale = m_maxItemSize; - QVector3D bgScale(xScale, 1.0f + m_backgroundMargin, zScale); -#else // ..and this if we want uniform scaling based on largest dimension - QVector3D bgScale((m_graphAspectRatio + m_backgroundMargin), - 1.0f + m_backgroundMargin, - (m_graphAspectRatio + m_backgroundMargin)); -#endif + 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(180.0f, 1.0, 0.0, 0.0); + 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); @@ -1031,8 +1230,7 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) m_cachedTheme->ambientLightStrength() * 2.0f); m_backgroundShader->setUniformValue(m_backgroundShader->lightColor(), lightColor); -#if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) { // Set shadow shader bindings QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; m_backgroundShader->setUniformValue(m_backgroundShader->shadowQ(), @@ -1043,9 +1241,7 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) // Draw the object m_drawer->drawObject(m_backgroundShader, m_backgroundObj, 0, m_depthTexture); - } else -#endif - { + } else { // Set shadowless shader bindings m_backgroundShader->setUniformValue(m_backgroundShader->lightS(), m_cachedTheme->lightStrength()); @@ -1055,16 +1251,17 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) } } - // Disable textures - glDisable(GL_TEXTURE_2D); - // 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()) { -#if !(defined QT_OPENGL_ES_2) - ShaderHelper *lineShader = m_backgroundShader; -#else - ShaderHelper *lineShader = m_selectionShader; // Plain color shader for GL_LINES -#endif + ShaderHelper *lineShader; + if (m_isOpenGLES) + lineShader = m_selectionShader; // Plain color shader for GL_LINES + else + lineShader = m_backgroundShader; // Bind line shader lineShader->bind(); @@ -1076,15 +1273,12 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) lineShader->setUniformValue(lineShader->color(), lineColor); lineShader->setUniformValue(lineShader->ambientS(), m_cachedTheme->ambientLightStrength()); lineShader->setUniformValue(lineShader->lightColor(), lightColor); -#if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + 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 -#endif - { + } else { // Set shadowless shader bindings lineShader->setUniformValue(lineShader->lightS(), m_cachedTheme->lightStrength() / 2.5f); @@ -1094,242 +1288,213 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) QQuaternion lineXRotation; if (m_xFlipped) - lineYRotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, -90.0f); + lineYRotation = m_yRightAngleRotationNeg; else - lineYRotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, 90.0f); + lineYRotation = m_yRightAngleRotation; - if (m_yFlipped) - lineXRotation = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, 90.0f); + if (m_yFlippedForGrid) + lineXRotation = m_xRightAngleRotation; else - lineXRotation = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, -90.0f); + lineXRotation = m_xRightAngleRotationNeg; - GLfloat yFloorLinePosition = -1.0f - m_backgroundMargin + gridLineOffset; - if (m_yFlipped) + 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; -#ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z - GLfloat xScale = (m_graphAspectRatio * m_areaSize.width()) / m_scaleFactor - + m_backgroundMargin; - if (m_maxItemSize > xScale) - xScale = m_maxItemSize; - QVector3D gridLineScaler(xScale, gridLineWidth, gridLineWidth); -#else // ..and this if we want uniform scaling based on largest dimension - QVector3D gridLineScaler((m_graphAspectRatio + m_backgroundMargin), - gridLineWidth, gridLineWidth); -#endif + modelMatrix.translate(0.0f, yFloorLinePosition, + m_axisCacheZ.gridLinePosition(line)); - 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.scale(gridLineScaler); - itModelMatrix.scale(gridLineScaler); + modelMatrix.rotate(lineXRotation); + itModelMatrix.rotate(lineXRotation); - modelMatrix.rotate(lineXRotation); - itModelMatrix.rotate(lineXRotation); + MVPMatrix = projectionViewMatrix * modelMatrix; - 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); - // Set the rest of the shader bindings - lineShader->setUniformValue(lineShader->model(), modelMatrix); - lineShader->setUniformValue(lineShader->nModel(), - itModelMatrix.inverted().transposed()); - lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); - -#if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > 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); + 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); + } + } } -#else - m_drawer->drawLine(lineShader); -#endif - } - // Side wall lines - gridLineScaler = QVector3D(gridLineWidth, 1.0f + m_backgroundMargin, gridLineWidth); -#ifndef USE_UNIFORM_SCALING - GLfloat lineXTrans = (m_graphAspectRatio * m_areaSize.width()) - / m_scaleFactor - gridLineOffset + m_backgroundMargin; - if (m_maxItemSize > lineXTrans) - lineXTrans = m_maxItemSize - gridLineOffset; -#else - GLfloat lineXTrans = m_graphAspectRatio + m_backgroundMargin - gridLineOffset; -#endif - if (!m_xFlipped) - lineXTrans = -lineXTrans; + // Side wall lines + GLfloat lineXTrans = m_scaleXWithBackground - gridLineOffset; - for (int line = 0; line < gridLineCount; line++) { - QMatrix4x4 modelMatrix; - QMatrix4x4 MVPMatrix; - QMatrix4x4 itModelMatrix; + if (!m_xFlipped) + lineXTrans = -lineXTrans; - modelMatrix.translate(lineXTrans, 0.0f, m_axisCacheZ.gridLinePosition(line)); + for (int line = 0; line < gridLineCount; line++) { + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; + QMatrix4x4 itModelMatrix; - modelMatrix.scale(gridLineScaler); - itModelMatrix.scale(gridLineScaler); + modelMatrix.translate(lineXTrans, 0.0f, m_axisCacheZ.gridLinePosition(line)); -#if !defined(QT_OPENGL_ES_2) - modelMatrix.rotate(lineYRotation); - itModelMatrix.rotate(lineYRotation); -#else - modelMatrix.rotate(90.0f, 0.0f, 0.0f, 1.0f); - itModelMatrix.rotate(90.0f, 0.0f, 0.0f, 1.0f); -#endif + modelMatrix.scale(gridLineScaleY); + itModelMatrix.scale(gridLineScaleY); - MVPMatrix = projectionViewMatrix * modelMatrix; + if (m_isOpenGLES) { + modelMatrix.rotate(m_zRightAngleRotation); + itModelMatrix.rotate(m_zRightAngleRotation); + } else { + modelMatrix.rotate(lineYRotation); + itModelMatrix.rotate(lineYRotation); + } - // Set the rest of the shader bindings - lineShader->setUniformValue(lineShader->model(), modelMatrix); - lineShader->setUniformValue(lineShader->nModel(), - itModelMatrix.inverted().transposed()); - lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); + MVPMatrix = projectionViewMatrix * modelMatrix; -#if !defined(QT_OPENGL_ES_2) - 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); + // 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); + } } -#else - m_drawer->drawLine(lineShader); -#endif } } // Columns (= X) if (m_axisCacheX.segmentCount() > 0) { -#if defined(QT_OPENGL_ES_2) - lineXRotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, 90.0f); -#endif + if (m_isOpenGLES) + lineXRotation = m_yRightAngleRotation; // Floor lines int gridLineCount = m_axisCacheX.gridLineCount(); -#ifndef USE_UNIFORM_SCALING - GLfloat zScale = (m_graphAspectRatio * m_areaSize.height()) / m_scaleFactor - + m_backgroundMargin; - if (m_maxItemSize > zScale) - zScale = m_maxItemSize; - QVector3D gridLineScaler(gridLineWidth, gridLineWidth, zScale); -#else - QVector3D gridLineScaler(gridLineWidth, gridLineWidth, - m_graphAspectRatio + m_backgroundMargin); -#endif - - for (int line = 0; line < gridLineCount; line++) { - QMatrix4x4 modelMatrix; - QMatrix4x4 MVPMatrix; - QMatrix4x4 itModelMatrix; - - modelMatrix.translate(m_axisCacheX.gridLinePosition(line), yFloorLinePosition, - 0.0f); + if (m_polarGraph) { + drawAngularGrid(lineShader, yFloorLinePosition, projectionViewMatrix, + depthProjectionViewMatrix); + } else { + for (int line = 0; line < gridLineCount; line++) { + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; + QMatrix4x4 itModelMatrix; - modelMatrix.scale(gridLineScaler); - itModelMatrix.scale(gridLineScaler); + modelMatrix.translate(m_axisCacheX.gridLinePosition(line), yFloorLinePosition, + 0.0f); - modelMatrix.rotate(lineXRotation); - itModelMatrix.rotate(lineXRotation); + modelMatrix.scale(gridLineScaleZ); + itModelMatrix.scale(gridLineScaleZ); - MVPMatrix = projectionViewMatrix * modelMatrix; + modelMatrix.rotate(lineXRotation); + itModelMatrix.rotate(lineXRotation); - // Set the rest of the shader bindings - lineShader->setUniformValue(lineShader->model(), modelMatrix); - lineShader->setUniformValue(lineShader->nModel(), - itModelMatrix.inverted().transposed()); - lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); + MVPMatrix = projectionViewMatrix * modelMatrix; -#if !defined(QT_OPENGL_ES_2) - 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); + // 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); + } } -#else - m_drawer->drawLine(lineShader); -#endif - } - - // Back wall lines -#ifndef USE_UNIFORM_SCALING - GLfloat lineZTrans = (m_graphAspectRatio * m_areaSize.height()) - / m_scaleFactor - gridLineOffset + m_backgroundMargin; - if (m_maxItemSize > lineZTrans) - lineZTrans = m_maxItemSize - gridLineOffset; -#else - GLfloat lineZTrans = m_graphAspectRatio + m_backgroundMargin - gridLineOffset; -#endif - if (!m_zFlipped) - lineZTrans = -lineZTrans; - gridLineScaler = QVector3D(gridLineWidth, 1.0f + m_backgroundMargin, gridLineWidth); + // Back wall lines + GLfloat lineZTrans = m_scaleZWithBackground - gridLineOffset; - for (int line = 0; line < gridLineCount; line++) { - QMatrix4x4 modelMatrix; - QMatrix4x4 MVPMatrix; - QMatrix4x4 itModelMatrix; + if (!m_zFlipped) + lineZTrans = -lineZTrans; - modelMatrix.translate(m_axisCacheX.gridLinePosition(line), 0.0f, lineZTrans); + for (int line = 0; line < gridLineCount; line++) { + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; + QMatrix4x4 itModelMatrix; - modelMatrix.scale(gridLineScaler); - itModelMatrix.scale(gridLineScaler); + modelMatrix.translate(m_axisCacheX.gridLinePosition(line), 0.0f, lineZTrans); -#if !defined(QT_OPENGL_ES_2) - if (m_zFlipped) { - modelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f); - itModelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f); - } -#else - modelMatrix.rotate(90.0f, 0.0f, 0.0f, 1.0f); - itModelMatrix.rotate(90.0f, 0.0f, 0.0f, 1.0f); -#endif + modelMatrix.scale(gridLineScaleY); + itModelMatrix.scale(gridLineScaleY); - MVPMatrix = projectionViewMatrix * modelMatrix; + if (m_isOpenGLES) { + modelMatrix.rotate(m_zRightAngleRotation); + itModelMatrix.rotate(m_zRightAngleRotation); + } else { + if (m_zFlipped) { + modelMatrix.rotate(m_xFlipRotation); + itModelMatrix.rotate(m_xFlipRotation); + } + } - // Set the rest of the shader bindings - lineShader->setUniformValue(lineShader->model(), modelMatrix); - lineShader->setUniformValue(lineShader->nModel(), - itModelMatrix.inverted().transposed()); - lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); + MVPMatrix = projectionViewMatrix * modelMatrix; -#if !defined(QT_OPENGL_ES_2) - 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); + // 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); + } } -#else - m_drawer->drawLine(lineShader); -#endif } } @@ -1338,21 +1503,8 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) // Back wall int gridLineCount = m_axisCacheY.gridLineCount(); -#ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z - GLfloat lineZTrans = (m_graphAspectRatio * m_areaSize.height()) - / m_scaleFactor - gridLineOffset + m_backgroundMargin; - if (m_maxItemSize > lineZTrans) - lineZTrans = m_maxItemSize - gridLineOffset; - GLfloat xScale = (m_graphAspectRatio * m_areaSize.width()) / m_scaleFactor - + m_backgroundMargin; - if (m_maxItemSize > xScale) - xScale = m_maxItemSize; - QVector3D gridLineScaler(xScale, gridLineWidth, gridLineWidth); -#else // ..and this if we want uniform scaling based on largest dimension - GLfloat lineZTrans = m_graphAspectRatio + m_backgroundMargin - gridLineOffset; - QVector3D gridLineScaler((m_graphAspectRatio + m_backgroundMargin), - gridLineWidth, gridLineWidth); -#endif + GLfloat lineZTrans = m_scaleZWithBackground - gridLineOffset; + if (!m_zFlipped) lineZTrans = -lineZTrans; @@ -1363,12 +1515,12 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) modelMatrix.translate(0.0f, m_axisCacheY.gridLinePosition(line), lineZTrans); - modelMatrix.scale(gridLineScaler); - itModelMatrix.scale(gridLineScaler); + modelMatrix.scale(gridLineScaleX); + itModelMatrix.scale(gridLineScaleX); if (m_zFlipped) { - modelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f); - itModelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f); + modelMatrix.rotate(m_xFlipRotation); + itModelMatrix.rotate(m_xFlipRotation); } MVPMatrix = projectionViewMatrix * modelMatrix; @@ -1379,38 +1531,25 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) itModelMatrix.inverted().transposed()); lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); -#if !defined(QT_OPENGL_ES_2) - 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); + 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 { - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj); + m_drawer->drawLine(lineShader); } -#else - m_drawer->drawLine(lineShader); -#endif } // Side wall -#ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z - GLfloat lineXTrans = (m_graphAspectRatio * m_areaSize.width()) - / m_scaleFactor - gridLineOffset + m_backgroundMargin; - if (m_maxItemSize > lineXTrans) - lineXTrans = m_maxItemSize - gridLineOffset; - GLfloat zScale = (m_graphAspectRatio * m_areaSize.height()) - / m_scaleFactor + m_backgroundMargin; - if (m_maxItemSize > zScale) - zScale = m_maxItemSize; - gridLineScaler = QVector3D(gridLineWidth, gridLineWidth, zScale); -#else // ..and this if we want uniform scaling based on largest dimension - GLfloat lineXTrans = m_graphAspectRatio + m_backgroundMargin - gridLineOffset; - gridLineScaler = QVector3D(gridLineWidth, gridLineWidth, - m_graphAspectRatio + m_backgroundMargin); -#endif + GLfloat lineXTrans = m_scaleXWithBackground - gridLineOffset; + if (!m_xFlipped) lineXTrans = -lineXTrans; @@ -1421,8 +1560,8 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) modelMatrix.translate(lineXTrans, m_axisCacheY.gridLinePosition(line), 0.0f); - modelMatrix.scale(gridLineScaler); - itModelMatrix.scale(gridLineScaler); + modelMatrix.scale(gridLineScaleZ); + itModelMatrix.scale(gridLineScaleZ); modelMatrix.rotate(lineYRotation); itModelMatrix.rotate(lineYRotation); @@ -1435,20 +1574,20 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) itModelMatrix.inverted().transposed()); lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); -#if !defined(QT_OPENGL_ES_2) - 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); + 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 { - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj); + m_drawer->drawLine(lineShader); } -#else - m_drawer->drawLine(lineShader); -#endif } } } @@ -1489,7 +1628,6 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) glEnable(GL_DEPTH_TEST); } - glDisable(GL_TEXTURE_2D); glDisable(GL_BLEND); // Release shader @@ -1512,7 +1650,6 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa shader = m_labelShader; shader->bind(); - glEnable(GL_TEXTURE_2D); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } @@ -1532,15 +1669,14 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa // Z Labels if (m_axisCacheZ.segmentCount() > 0) { int labelCount = m_axisCacheZ.labelCount(); -#ifndef USE_UNIFORM_SCALING - GLfloat labelXTrans = (m_graphAspectRatio * m_areaSize.width()) - / m_scaleFactor + labelMargin + m_backgroundMargin; - if (m_maxItemSize > labelXTrans) - labelXTrans = m_maxItemSize + labelMargin; -#else - GLfloat labelXTrans = m_graphAspectRatio + m_backgroundMargin + labelMargin; -#endif - GLfloat labelYTrans = -1.0f - m_backgroundMargin; + 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) @@ -1550,7 +1686,7 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa if (labelAutoAngle == 0.0f) { if (m_zFlipped) labelRotation.setY(180.0f); - if (m_yFlipped) { + if (m_yFlippedForGrid) { if (m_zFlipped) labelRotation.setY(180.0f); else @@ -1562,7 +1698,7 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa } else { if (m_zFlipped) labelRotation.setY(180.0f); - if (m_yFlipped) { + if (m_yFlippedForGrid) { if (m_zFlipped) { if (m_xFlipped) { labelRotation.setX(90.0f - (labelAutoAngle - fractionCamX) @@ -1621,13 +1757,35 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa } float offsetValue = 0.0f; for (int label = startIndex; label != endIndex; label = label + indexStep) { - labelTrans.setZ(m_axisCacheZ.labelPosition(label)); - 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); - const LabelItem &axisLabelItem = *m_axisCacheZ.labelItems().at(label); if (drawSelection) { QVector4D labelColor = QVector4D(label / 255.0f, 0.0f, 0.0f, @@ -1642,7 +1800,14 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa labelsMaxWidth = qMax(labelsMaxWidth, float(axisLabelItem.size().width())); } if (!drawSelection && m_axisCacheZ.isTitleVisible()) { - labelTrans.setZ(0.0f); + 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); } @@ -1656,15 +1821,13 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa fractionCamY = activeCamera->yRotation() * labelAngleFraction; fractionCamX = activeCamera->xRotation() * labelAngleFraction; int labelCount = m_axisCacheX.labelCount(); -#ifndef USE_UNIFORM_SCALING - GLfloat labelZTrans = (m_graphAspectRatio * m_areaSize.height()) - / m_scaleFactor + labelMargin + m_backgroundMargin; - if (m_maxItemSize > labelZTrans) - labelZTrans = m_maxItemSize + labelMargin; -#else - GLfloat labelZTrans = m_graphAspectRatio + m_backgroundMargin + labelMargin; -#endif - GLfloat labelYTrans = -1.0f - m_backgroundMargin; + 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) @@ -1675,7 +1838,7 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa labelRotation = QVector3D(-90.0f, 90.0f, 0.0f); if (m_xFlipped) labelRotation.setY(-90.0f); - if (m_yFlipped) { + if (m_yFlippedForGrid) { if (m_xFlipped) labelRotation.setY(-90.0f); else @@ -1687,7 +1850,7 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa labelRotation.setY(-90.0f); else labelRotation.setY(90.0f); - if (m_yFlipped) { + if (m_yFlippedForGrid) { if (m_zFlipped) { if (m_xFlipped) { labelRotation.setX(90.0f - (2.0f * labelAutoAngle - fractionCamX) @@ -1735,6 +1898,14 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa } 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; @@ -1746,14 +1917,64 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa indexStep = 1; } float offsetValue = 0.0f; - for (int label = startIndex; label != endIndex; label = label + indexStep) { - labelTrans.setX(m_axisCacheX.labelPosition(label)); + bool showLastLabel = false; + QVector<float> &labelPositions = m_axisCacheX.formatter()->labelPositions(); + int lastLabelPosIndex = labelPositions.size() - 1; + if (labelPositions.size() + && (labelPositions.at(lastLabelPosIndex) != 1.0f || labelPositions.at(0) != 0.0f)) { + // Avoid overlapping first and last label if they would get on same position + showLastLabel = true; + } + for (int label = startIndex; label != endIndex; label = label + indexStep) { glPolygonOffset(offsetValue++ / -10.0f, 1.0f); - // Draw the label here - m_dummyRenderItem.setTranslation(labelTrans); + 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, @@ -1769,8 +1990,20 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa } 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); + activeCamera, labelsMaxWidth, viewMatrix, projectionMatrix, shader, + radial); } } @@ -1782,22 +2015,13 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa fractionCamY = activeCamera->yRotation() * labelAngleFraction; fractionCamX = activeCamera->xRotation() * labelAngleFraction; int labelCount = m_axisCacheY.labelCount(); -#ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z - GLfloat labelXTrans = (m_graphAspectRatio* m_areaSize.width()) - / m_scaleFactor + m_backgroundMargin; - GLfloat labelZTrans = (m_graphAspectRatio * m_areaSize.height()) - / m_scaleFactor + m_backgroundMargin; - if (m_maxItemSize > labelXTrans) - labelXTrans = m_maxItemSize; - if (m_maxItemSize > labelZTrans) - labelZTrans = m_maxItemSize; -#else // ..and this if we want uniform scaling based on largest dimension - GLfloat labelXTrans = m_graphAspectRatio + m_backgroundMargin; - GLfloat labelZTrans = labelXTrans; -#endif + + float labelXTrans = m_scaleXWithBackground; + float labelZTrans = m_scaleZWithBackground; + // Back & side wall - GLfloat labelMarginXTrans = labelMargin; - GLfloat labelMarginZTrans = labelMargin; + 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 = @@ -1854,7 +2078,7 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa float offsetValue = 0.0f; for (int label = startIndex; label != endIndex; label = label + indexStep) { const LabelItem &axisLabelItem = *m_axisCacheY.labelItems().at(label); - const GLfloat labelYTrans = m_axisCacheY.labelPosition(label); + float labelYTrans = m_axisCacheY.labelPosition(label); glPolygonOffset(offsetValue++ / -10.0f, 1.0f); @@ -1864,6 +2088,21 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa 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); @@ -1958,10 +2197,8 @@ void Scatter3DRenderer::updateShadowQuality(QAbstract3DGraph::ShadowQuality qual handleShadowQualityChange(); -#if !defined(QT_OPENGL_ES_2) // Re-init depth buffer updateDepthBuffer(); -#endif } void Scatter3DRenderer::loadBackgroundMesh() @@ -1972,8 +2209,13 @@ void Scatter3DRenderer::loadBackgroundMesh() 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) @@ -1991,35 +2233,83 @@ void Scatter3DRenderer::calculateTranslation(ScatterRenderItem &item) { // We need to normalize translations const QVector3D &pos = item.position(); - float xTrans = m_axisCacheX.positionAt(pos.x()); + float xTrans; float yTrans = m_axisCacheY.positionAt(pos.y()); - float zTrans = m_axisCacheZ.positionAt(pos.z()); + 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() { - m_heightNormalizer = GLfloat(m_axisCacheY.max() - m_axisCacheY.min()) / 2.0f; - m_areaSize.setHeight((m_axisCacheZ.max() - m_axisCacheZ.min()) / 2.0f); - m_areaSize.setWidth((m_axisCacheX.max() - m_axisCacheX.min()) / 2.0f); - m_scaleFactor = qMax(m_areaSize.width(), m_areaSize.height()); - -#ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z - float factorScaler = 2.0f * m_graphAspectRatio / m_scaleFactor; - m_axisCacheX.setScale(factorScaler * m_areaSize.width()); - m_axisCacheZ.setScale(-factorScaler * m_areaSize.height()); -#else // ..and this if we want uniform scaling based on largest dimension - m_axisCacheX.setScale(2.0f * m_graphAspectRatio); - m_axisCacheZ.setScale(-m_axisCacheX.scale()); -#endif - m_axisCacheX.setTranslate(-m_axisCacheX.scale() / 2.0f); - m_axisCacheZ.setTranslate(-m_axisCacheZ.scale() / 2.0f); + 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) { - if (m_dotShader) - delete m_dotShader; + delete m_dotShader; m_dotShader = new ShaderHelper(this, vertexShader, fragmentShader); m_dotShader->initialize(); } @@ -2027,16 +2317,30 @@ void Scatter3DRenderer::initShaders(const QString &vertexShader, const QString & void Scatter3DRenderer::initGradientShaders(const QString &vertexShader, const QString &fragmentShader) { - if (m_dotGradientShader) - delete m_dotGradientShader; + 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() { - if (m_selectionShader) - delete m_selectionShader; + delete m_selectionShader; m_selectionShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexPlainColor"), QStringLiteral(":/shaders/fragmentPlainColor")); m_selectionShader->initialize(); @@ -2054,41 +2358,45 @@ void Scatter3DRenderer::initSelectionBuffer() m_selectionDepthBuffer); } -#if !defined(QT_OPENGL_ES_2) void Scatter3DRenderer::initDepthShader() { - if (m_depthShader) - delete m_depthShader; - m_depthShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexDepth"), - QStringLiteral(":/shaders/fragmentDepth")); - m_depthShader->initialize(); + 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() { - m_textureHelper->deleteTexture(&m_depthTexture); + if (!m_isOpenGLES) { + m_textureHelper->deleteTexture(&m_depthTexture); - if (m_primarySubViewport.size().isEmpty()) - return; + 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(); + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + m_depthTexture = m_textureHelper->createDepthTextureFrameBuffer(m_primarySubViewport.size(), + m_depthFrameBuffer, + m_shadowQualityMultiplier); + if (!m_depthTexture) + lowerShadowQuality(); + } } } -#else + void Scatter3DRenderer::initPointShader() { - if (m_pointShader) - delete m_pointShader; - m_pointShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexPointES2"), - QStringLiteral(":/shaders/fragmentPlainColor")); - m_pointShader->initialize(); + if (m_isOpenGLES) { + if (m_pointShader) + delete m_pointShader; + m_pointShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexPointES2"), + QStringLiteral(":/shaders/fragmentPlainColor")); + m_pointShader->initialize(); + } } -#endif void Scatter3DRenderer::initBackgroundShaders(const QString &vertexShader, const QString &fragmentShader) @@ -2099,12 +2407,13 @@ void Scatter3DRenderer::initBackgroundShaders(const QString &vertexShader, m_backgroundShader->initialize(); } -void Scatter3DRenderer::initLabelShaders(const QString &vertexShader, const QString &fragmentShader) +void Scatter3DRenderer::initStaticPointShaders(const QString &vertexShader, + const QString &fragmentShader) { - if (m_labelShader) - delete m_labelShader; - m_labelShader = new ShaderHelper(this, vertexShader, fragmentShader); - m_labelShader->initialize(); + if (m_staticGradientPointShader) + delete m_staticGradientPointShader; + m_staticGradientPointShader = new ShaderHelper(this, vertexShader, fragmentShader); + m_staticGradientPointShader->initialize(); } void Scatter3DRenderer::selectionColorToSeriesAndIndex(const QVector4D &color, @@ -2190,13 +2499,17 @@ QVector3D Scatter3DRenderer::convertPositionToTranslation(const QVector3D &posit float yTrans = 0.0f; float zTrans = 0.0f; if (!isAbsolute) { - xTrans = m_axisCacheX.positionAt(position.x()); + 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()); - zTrans = m_axisCacheZ.positionAt(position.z()); } else { - xTrans = position.x() * m_axisCacheX.scale() / 2.0f; - yTrans = position.y(); - zTrans = position.z() * m_axisCacheZ.scale() / 2.0f; + xTrans = position.x() * m_scaleX; + yTrans = position.y() * m_scaleY; + zTrans = position.z() * -m_scaleZ; } return QVector3D(xTrans, yTrans, zTrans); } diff --git a/src/datavisualization/engine/scatter3drenderer_p.h b/src/datavisualization/engine/scatter3drenderer_p.h index 7f213179..b45b31a2 100644 --- a/src/datavisualization/engine/scatter3drenderer_p.h +++ b/src/datavisualization/engine/scatter3drenderer_p.h @@ -53,31 +53,28 @@ private: bool m_updateLabels; ShaderHelper *m_dotShader; ShaderHelper *m_dotGradientShader; -#if defined(QT_OPENGL_ES_2) + ShaderHelper *m_staticSelectedItemGradientShader; + ShaderHelper *m_staticSelectedItemShader; ShaderHelper *m_pointShader; -#endif ShaderHelper *m_depthShader; ShaderHelper *m_selectionShader; ShaderHelper *m_backgroundShader; - ShaderHelper *m_labelShader; + ShaderHelper *m_staticGradientPointShader; GLuint m_bgrTexture; - GLuint m_depthTexture; GLuint m_selectionTexture; GLuint m_depthFrameBuffer; GLuint m_selectionFrameBuffer; GLuint m_selectionDepthBuffer; GLfloat m_shadowQualityToShader; GLint m_shadowQualityMultiplier; - GLfloat m_heightNormalizer; - GLfloat m_scaleFactor; + float m_scaleX; + float m_scaleY; + float m_scaleZ; int m_selectedItemIndex; ScatterSeriesRenderCache *m_selectedSeriesCache; ScatterSeriesRenderCache *m_oldSelectedSeriesCache; - QSizeF m_areaSize; GLfloat m_dotSizeScale; - bool m_hasHeightAdjustmentChanged; ScatterRenderItem m_dummyRenderItem; - GLfloat m_backgroundMargin; GLfloat m_maxItemSize; int m_clickedIndex; bool m_havePointSeries; @@ -94,6 +91,12 @@ public: SeriesRenderCache *createNewCache(QAbstract3DSeries *series); void updateItems(const QVector<Scatter3DController::ChangeItem> &items); void updateScene(Q3DScene *scene); + void updateAxisLabels(QAbstract3DAxis::AxisOrientation orientation, + const QStringList &labels); + void updateAxisTitleVisibility(QAbstract3DAxis::AxisOrientation orientation, + bool visible); + void updateOptimizationHint(QAbstract3DGraph::OptimizationHints hint); + void updateMargin(float margin); QVector3D convertPositionToTranslation(const QVector3D &position, bool isAbsolute); @@ -107,10 +110,16 @@ public slots: protected: virtual void initializeOpenGL(); + virtual void fixCameraTarget(QVector3D &target); + virtual void getVisibleItemBounds(QVector3D &minBounds, QVector3D &maxBounds); private: virtual void initShaders(const QString &vertexShader, const QString &fragmentShader); virtual void initGradientShaders(const QString &vertexShader, const QString &fragmentShader); + virtual void initStaticSelectedItemShaders(const QString &vertexShader, + const QString &fragmentShader, + const QString &gradientVertexShader, + const QString &gradientFragmentShader); virtual void updateShadowQuality(QAbstract3DGraph::ShadowQuality quality); virtual void updateTextures(); virtual void fixMeshFileName(QString &fileName, QAbstract3DSeries::Mesh mesh); @@ -122,14 +131,11 @@ private: void loadBackgroundMesh(); void initSelectionShader(); void initBackgroundShaders(const QString &vertexShader, const QString &fragmentShader); - void initLabelShaders(const QString &vertexShader, const QString &fragmentShader); + void initStaticPointShaders(const QString &vertexShader, const QString &fragmentShader); void initSelectionBuffer(); -#if !defined(QT_OPENGL_ES_2) void initDepthShader(); void updateDepthBuffer(); -#else void initPointShader(); -#endif void calculateTranslation(ScatterRenderItem &item); void calculateSceneScalingFactors(); diff --git a/src/datavisualization/engine/scatterseriesrendercache.cpp b/src/datavisualization/engine/scatterseriesrendercache.cpp index e8888d19..4930dde1 100644 --- a/src/datavisualization/engine/scatterseriesrendercache.cpp +++ b/src/datavisualization/engine/scatterseriesrendercache.cpp @@ -27,10 +27,12 @@ ScatterSeriesRenderCache::ScatterSeriesRenderCache(QAbstract3DSeries *series, : SeriesRenderCache(series, renderer), m_itemSize(0.0f), m_selectionIndexOffset(0), + m_staticBufferDirty(false), m_oldRenderArraySize(0), m_oldMeshFileName(QString()), m_scatterBufferObj(0), - m_scatterBufferPoints(0) + m_scatterBufferPoints(0), + m_visibilityChanged(false) { } diff --git a/src/datavisualization/engine/scatterseriesrendercache_p.h b/src/datavisualization/engine/scatterseriesrendercache_p.h index 490e21fb..9c6e8e8f 100644 --- a/src/datavisualization/engine/scatterseriesrendercache_p.h +++ b/src/datavisualization/engine/scatterseriesrendercache_p.h @@ -53,6 +53,8 @@ public: inline float itemSize() const { return m_itemSize; } inline void setSelectionIndexOffset(int offset) { m_selectionIndexOffset = offset; } inline int selectionIndexOffset() const { return m_selectionIndexOffset; } + inline void setStaticBufferDirty(bool state) { m_staticBufferDirty = state; } + inline bool staticBufferDirty() const { return m_staticBufferDirty; } inline int oldArraySize() const { return m_oldRenderArraySize; } inline void setOldArraySize(int size) { m_oldRenderArraySize = size; } inline const QString &oldMeshFileName() const { return m_oldMeshFileName; } @@ -61,15 +63,23 @@ public: inline ScatterObjectBufferHelper *bufferObject() const { return m_scatterBufferObj; } inline void setBufferPoints(ScatterPointBufferHelper *object) { m_scatterBufferPoints = object; } inline ScatterPointBufferHelper *bufferPoints() const { return m_scatterBufferPoints; } + inline QVector<int> &updateIndices() { return m_updateIndices; } + inline QVector<int> &bufferIndices() { return m_bufferIndices; } + inline void setVisibilityChanged(bool changed) { m_visibilityChanged = changed; } + inline bool visibilityChanged() const { return m_visibilityChanged; } protected: ScatterRenderItemArray m_renderArray; float m_itemSize; int m_selectionIndexOffset; // Temporarily cached value for selection color calculations + bool m_staticBufferDirty; int m_oldRenderArraySize; // Used to detect if full buffer change needed QString m_oldMeshFileName; // Used to detect if full buffer change needed ScatterObjectBufferHelper *m_scatterBufferObj; ScatterPointBufferHelper *m_scatterBufferPoints; + QVector<int> m_updateIndices; // Used as temporary cache during item updates + QVector<int> m_bufferIndices; // Cache for mapping renderarray to mesh buffer + bool m_visibilityChanged; // Used to detect if full buffer change needed }; QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/engine/selectionpointer.cpp b/src/datavisualization/engine/selectionpointer.cpp index 183d3f8e..b57ec511 100644 --- a/src/datavisualization/engine/selectionpointer.cpp +++ b/src/datavisualization/engine/selectionpointer.cpp @@ -122,9 +122,6 @@ void SelectionPointer::render(GLuint defaultFboHandle, bool useOrtho) MVPMatrix = projectionMatrix * viewMatrix * modelMatrix; - // Enable texturing - glEnable(GL_TEXTURE_2D); - QVector3D lightPos = m_cachedScene->activeLight()->position(); // @@ -186,9 +183,6 @@ void SelectionPointer::render(GLuint defaultFboHandle, bool useOrtho) // Release shader glUseProgram(0); - // Disable textures - glDisable(GL_TEXTURE_2D); - // Disable transparency glDisable(GL_BLEND); @@ -217,10 +211,12 @@ void SelectionPointer::setRotation(const QQuaternion &rotation) m_rotation = rotation; } -void SelectionPointer::setLabel(const QString &label) +void SelectionPointer::setLabel(const QString &label, bool themeChange) { - m_label = label; - m_drawer->generateLabelItem(m_labelItem, m_label); + if (themeChange || m_label != label) { + m_label = label; + m_drawer->generateLabelItem(m_labelItem, m_label); + } } void SelectionPointer::setPointerObject(ObjectHelper *object) @@ -236,7 +232,7 @@ void SelectionPointer::setLabelObject(ObjectHelper *object) void SelectionPointer::handleDrawerChange() { m_cachedTheme = m_drawer->theme(); - setLabel(m_label); + setLabel(m_label, true); } void SelectionPointer::updateBoundingRect(const QRect &rect) @@ -256,15 +252,16 @@ void SelectionPointer::initShaders() // The shader for the small point ball if (m_pointShader) delete m_pointShader; -#if !defined(QT_OPENGL_ES_2) - m_pointShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"), - QStringLiteral(":/shaders/fragment")); -#else - m_pointShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"), - QStringLiteral(":/shaders/fragmentES2")); -#endif - m_pointShader->initialize(); + if (Utils::isOpenGLES()) { + m_pointShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"), + QStringLiteral(":/shaders/fragmentES2")); + } else { + m_pointShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"), + QStringLiteral(":/shaders/fragment")); + } + + m_pointShader->initialize(); } QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/engine/selectionpointer_p.h b/src/datavisualization/engine/selectionpointer_p.h index 7dc28024..08382b9f 100644 --- a/src/datavisualization/engine/selectionpointer_p.h +++ b/src/datavisualization/engine/selectionpointer_p.h @@ -49,7 +49,7 @@ public: void render(GLuint defaultFboHandle = 0, bool useOrtho = false); void setPosition(const QVector3D &position); - void setLabel(const QString &label); + void setLabel(const QString &label, bool themeChange = false); void setPointerObject(ObjectHelper *object); void setLabelObject(ObjectHelper *object); void handleDrawerChange(); diff --git a/src/datavisualization/engine/seriesrendercache.cpp b/src/datavisualization/engine/seriesrendercache.cpp index dc4b9db3..77af31c7 100644 --- a/src/datavisualization/engine/seriesrendercache.cpp +++ b/src/datavisualization/engine/seriesrendercache.cpp @@ -37,7 +37,8 @@ SeriesRenderCache::SeriesRenderCache(QAbstract3DSeries *series, Abstract3DRender m_valid(false), m_visible(false), m_renderer(renderer), - m_objectDirty(true) + m_objectDirty(true), + m_staticObjectUVDirty(false) { } @@ -91,9 +92,8 @@ void SeriesRenderCache::populate(bool newSeries) meshFileName = QStringLiteral(":/defaultMeshes/arrow"); break; case QAbstract3DSeries::MeshPoint: -#if defined(QT_OPENGL_ES_2) - qWarning("QAbstract3DSeries::MeshPoint is not fully supported on OpenGL ES2"); -#endif + if (Utils::isOpenGLES()) + qWarning("QAbstract3DSeries::MeshPoint is not fully supported on OpenGL ES2"); break; default: // Default to cube diff --git a/src/datavisualization/engine/seriesrendercache_p.h b/src/datavisualization/engine/seriesrendercache_p.h index 96b61b87..5047d671 100644 --- a/src/datavisualization/engine/seriesrendercache_p.h +++ b/src/datavisualization/engine/seriesrendercache_p.h @@ -71,6 +71,8 @@ public: inline bool isVisible() const { return m_visible; } inline void setDataDirty(bool state) { m_objectDirty = state; } inline bool dataDirty() const { return m_objectDirty; } + inline void setStaticObjectUVDirty(bool state) { m_staticObjectUVDirty = state; } + inline bool staticObjectUVDirty() { return m_staticObjectUVDirty; } protected: QAbstract3DSeries *m_series; @@ -94,6 +96,7 @@ protected: bool m_visible; Abstract3DRenderer *m_renderer; bool m_objectDirty; + bool m_staticObjectUVDirty; }; QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/engine/shaders/3dsliceframes.frag b/src/datavisualization/engine/shaders/3dsliceframes.frag new file mode 100644 index 00000000..44c080dc --- /dev/null +++ b/src/datavisualization/engine/shaders/3dsliceframes.frag @@ -0,0 +1,15 @@ +#version 120 + +uniform highp vec4 color_mdl; +uniform highp vec2 sliceFrameWidth; + +varying highp vec3 pos; + +void main() { + highp vec2 absPos = min(vec2(1.0, 1.0), abs(pos.xy)); + if (absPos.x > sliceFrameWidth.x || absPos.y > sliceFrameWidth.y) + gl_FragColor = color_mdl; + else + discard; +} + diff --git a/src/datavisualization/engine/shaders/default.frag b/src/datavisualization/engine/shaders/default.frag index d16055a3..0276ed95 100644 --- a/src/datavisualization/engine/shaders/default.frag +++ b/src/datavisualization/engine/shaders/default.frag @@ -32,6 +32,7 @@ void main() { materialAmbientColor + materialDiffuseColor * lightStrength * pow(cosTheta, 2) / distance + materialSpecularColor * lightStrength * pow(cosAlpha, 5) / distance; - gl_FragColor.a = 1.0; + gl_FragColor.a = color_mdl.a; + gl_FragColor = clamp(gl_FragColor, 0.0, 1.0); } diff --git a/src/datavisualization/engine/shaders/defaultNoMatrices.vert b/src/datavisualization/engine/shaders/defaultNoMatrices.vert new file mode 100644 index 00000000..cef10c19 --- /dev/null +++ b/src/datavisualization/engine/shaders/defaultNoMatrices.vert @@ -0,0 +1,26 @@ +attribute highp vec3 vertexPosition_mdl; +attribute highp vec2 vertexUV; +attribute highp vec3 vertexNormal_mdl; + +uniform highp mat4 MVP; +uniform highp mat4 V; +uniform highp vec3 lightPosition_wrld; + +varying highp vec3 lightPosition_wrld_frag; +varying highp vec3 position_wrld; +varying highp vec3 normal_cmr; +varying highp vec3 eyeDirection_cmr; +varying highp vec3 lightDirection_cmr; +varying highp vec2 coords_mdl; + +void main() { + gl_Position = MVP * vec4(vertexPosition_mdl, 1.0); + coords_mdl = vertexPosition_mdl.xy; + position_wrld = vertexPosition_mdl; + vec3 vertexPosition_cmr = vec4(V * vec4(vertexPosition_mdl, 1.0)).xyz; + eyeDirection_cmr = vec3(0.0, 0.0, 0.0) - vertexPosition_cmr; + vec3 lightPosition_cmr = vec4(V * vec4(lightPosition_wrld, 1.0)).xyz; + lightDirection_cmr = lightPosition_cmr + eyeDirection_cmr; + normal_cmr = vec4(V * vec4(vertexNormal_mdl, 0.0)).xyz; + lightPosition_wrld_frag = lightPosition_wrld; +} diff --git a/src/datavisualization/engine/shaders/default_ES2.frag b/src/datavisualization/engine/shaders/default_ES2.frag index 73d66d5b..60fa3c43 100644 --- a/src/datavisualization/engine/shaders/default_ES2.frag +++ b/src/datavisualization/engine/shaders/default_ES2.frag @@ -34,6 +34,6 @@ void main() { materialAmbientColor + materialDiffuseColor * lightStrength * (cosTheta * cosTheta) / distance + materialSpecularColor * lightStrength * (cosAlpha * cosAlpha * cosAlpha * cosAlpha * cosAlpha) / distance; - gl_FragColor.a = 1.0; + gl_FragColor.a = color_mdl.a; } diff --git a/src/datavisualization/engine/shaders/point_ES2_UV.vert b/src/datavisualization/engine/shaders/point_ES2_UV.vert new file mode 100644 index 00000000..f181db4f --- /dev/null +++ b/src/datavisualization/engine/shaders/point_ES2_UV.vert @@ -0,0 +1,12 @@ +uniform highp mat4 MVP; + +attribute highp vec3 vertexPosition_mdl; +attribute highp vec2 vertexUV; + +varying highp vec2 UV; + +void main() { + gl_PointSize = 5.0; + gl_Position = MVP * vec4(vertexPosition_mdl, 1.0); + UV = vertexUV; +} diff --git a/src/datavisualization/engine/shaders/position.vert b/src/datavisualization/engine/shaders/position.vert new file mode 100644 index 00000000..34849eae --- /dev/null +++ b/src/datavisualization/engine/shaders/position.vert @@ -0,0 +1,10 @@ +uniform highp mat4 MVP; + +attribute highp vec3 vertexPosition_mdl; + +varying highp vec3 pos; + +void main() { + gl_Position = MVP * vec4(vertexPosition_mdl, 1.0); + pos = vertexPosition_mdl; +} diff --git a/src/datavisualization/engine/shaders/positionmap.frag b/src/datavisualization/engine/shaders/positionmap.frag new file mode 100644 index 00000000..9a277ab8 --- /dev/null +++ b/src/datavisualization/engine/shaders/positionmap.frag @@ -0,0 +1,11 @@ +varying highp vec3 pos; + +void main() { + // This shader encodes the axis position into the vertex color, assuming the object + // is a cube filling the entire data area of the graph + gl_FragColor = vec4((pos.x + 1.0) / 2.0, + (pos.y + 1.0) / 2.0, + (-pos.z + 1.0) / 2.0, + 0.0); +} + diff --git a/src/datavisualization/engine/shaders/shadowNoMatrices.vert b/src/datavisualization/engine/shaders/shadowNoMatrices.vert new file mode 100644 index 00000000..31748503 --- /dev/null +++ b/src/datavisualization/engine/shaders/shadowNoMatrices.vert @@ -0,0 +1,36 @@ +#version 120 + +uniform highp mat4 MVP; +uniform highp mat4 V; +uniform highp mat4 M; +uniform highp mat4 depthMVP; +uniform highp vec3 lightPosition_wrld; + +attribute highp vec3 vertexPosition_mdl; +attribute highp vec3 vertexNormal_mdl; +attribute highp vec2 vertexUV; + +varying highp vec2 UV; +varying highp vec3 position_wrld; +varying highp vec3 normal_cmr; +varying highp vec3 eyeDirection_cmr; +varying highp vec3 lightDirection_cmr; +varying highp vec4 shadowCoord; +varying highp vec2 coords_mdl; + +const highp mat4 bias = mat4(0.5, 0.0, 0.0, 0.0, + 0.0, 0.5, 0.0, 0.0, + 0.0, 0.0, 0.5, 0.0, + 0.5, 0.5, 0.5, 1.0); + +void main() { + gl_Position = MVP * vec4(vertexPosition_mdl, 1.0); + coords_mdl = vertexPosition_mdl.xy; + shadowCoord = bias * depthMVP * vec4(vertexPosition_mdl, 1.0); + position_wrld = vertexPosition_mdl; + vec3 vertexPosition_cmr = vec4(V * vec4(vertexPosition_mdl, 1.0)).xyz; + eyeDirection_cmr = vec3(0.0, 0.0, 0.0) - vertexPosition_cmr; + lightDirection_cmr = vec4(V * vec4(lightPosition_wrld, 0.0)).xyz; + normal_cmr = vec4(V * vec4(vertexNormal_mdl, 0.0)).xyz; + UV = vertexUV; +} diff --git a/src/datavisualization/engine/shaders/shadowNoTex.frag b/src/datavisualization/engine/shaders/shadowNoTex.frag index b2e7adfc..84e2f209 100644 --- a/src/datavisualization/engine/shaders/shadowNoTex.frag +++ b/src/datavisualization/engine/shaders/shadowNoTex.frag @@ -61,6 +61,6 @@ void main() { (materialAmbientColor + materialDiffuseColor * lightStrength * cosTheta + materialSpecularColor * lightStrength * pow(cosAlpha, 10)); - gl_FragColor.a = 1.0; + gl_FragColor.a = color_mdl.a; gl_FragColor.rgb = visibility * clamp(gl_FragColor.rgb, 0.0, 1.0); } diff --git a/src/datavisualization/engine/shaders/surface.frag b/src/datavisualization/engine/shaders/surface.frag index f17dd73e..238e5fed 100644 --- a/src/datavisualization/engine/shaders/surface.frag +++ b/src/datavisualization/engine/shaders/surface.frag @@ -11,9 +11,11 @@ uniform highp vec3 lightPosition_wrld; uniform highp float lightStrength; uniform highp float ambientStrength; uniform highp vec4 lightColor; +uniform highp float gradMin; +uniform highp float gradHeight; void main() { - highp vec2 gradientUV = vec2(0.0, (coords_mdl.y + 1.0) / 2.0); + highp vec2 gradientUV = vec2(0.0, gradMin + coords_mdl.y * gradHeight); highp vec3 materialDiffuseColor = texture2D(textureSampler, gradientUV).xyz; highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor; highp vec3 materialSpecularColor = lightColor.rgb; diff --git a/src/datavisualization/engine/shaders/surfaceFlat.frag b/src/datavisualization/engine/shaders/surfaceFlat.frag index 748fb3dd..1a0bbdeb 100644 --- a/src/datavisualization/engine/shaders/surfaceFlat.frag +++ b/src/datavisualization/engine/shaders/surfaceFlat.frag @@ -13,9 +13,11 @@ uniform highp vec3 lightPosition_wrld; uniform highp float lightStrength; uniform highp float ambientStrength; uniform highp vec4 lightColor; +uniform highp float gradMin; +uniform highp float gradHeight; void main() { - highp vec2 gradientUV = vec2(0.0, (coords_mdl.y + 1.0) / 2.0); + highp vec2 gradientUV = vec2(0.0, gradMin + coords_mdl.y * gradHeight); highp vec3 materialDiffuseColor = texture2D(textureSampler, gradientUV).xyz; highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor; highp vec3 materialSpecularColor = lightColor.rgb; diff --git a/src/datavisualization/engine/shaders/surfaceFlat.vert b/src/datavisualization/engine/shaders/surfaceFlat.vert index 102bea78..0f953f4c 100644 --- a/src/datavisualization/engine/shaders/surfaceFlat.vert +++ b/src/datavisualization/engine/shaders/surfaceFlat.vert @@ -4,6 +4,7 @@ attribute highp vec3 vertexPosition_mdl; attribute highp vec3 vertexNormal_mdl; +attribute highp vec2 vertexUV; uniform highp mat4 MVP; uniform highp mat4 V; @@ -11,6 +12,7 @@ uniform highp mat4 M; uniform highp mat4 itM; uniform highp vec3 lightPosition_wrld; +varying highp vec2 UV; varying highp vec3 position_wrld; flat varying highp vec3 normal_cmr; varying highp vec3 eyeDirection_cmr; @@ -26,4 +28,5 @@ void main() { vec3 lightPosition_cmr = vec4(V * vec4(lightPosition_wrld, 1.0)).xyz; lightDirection_cmr = lightPosition_cmr + eyeDirection_cmr; normal_cmr = vec4(V * itM * vec4(vertexNormal_mdl, 0.0)).xyz; + UV = vertexUV; } diff --git a/src/datavisualization/engine/shaders/surfaceShadowFlat.frag b/src/datavisualization/engine/shaders/surfaceShadowFlat.frag index 0613a40c..7eaba7f5 100644 --- a/src/datavisualization/engine/shaders/surfaceShadowFlat.frag +++ b/src/datavisualization/engine/shaders/surfaceShadowFlat.frag @@ -2,20 +2,22 @@ #extension GL_EXT_gpu_shader4 : require -varying highp vec3 coords_mdl; +varying highp vec2 coords_mdl; varying highp vec3 position_wrld; flat varying highp vec3 normal_cmr; varying highp vec3 eyeDirection_cmr; varying highp vec3 lightDirection_cmr; +varying highp vec4 shadowCoord; uniform highp sampler2DShadow shadowMap; uniform sampler2D textureSampler; -varying highp vec4 shadowCoord; uniform highp vec3 lightPosition_wrld; uniform highp float lightStrength; uniform highp float ambientStrength; uniform highp float shadowQuality; uniform highp vec4 lightColor; +uniform highp float gradMin; +uniform highp float gradHeight; highp vec2 poissonDisk[16] = vec2[16](vec2(-0.94201624, -0.39906216), vec2(0.94558609, -0.76890725), @@ -35,7 +37,7 @@ highp vec2 poissonDisk[16] = vec2[16](vec2(-0.94201624, -0.39906216), vec2(0.14383161, -0.14100790)); void main() { - highp vec2 gradientUV = vec2(0.0, (coords_mdl.y + 1.0) / 2.0); + highp vec2 gradientUV = vec2(0.0, gradMin + coords_mdl.y * gradHeight); highp vec3 materialDiffuseColor = texture2D(textureSampler, gradientUV).xyz; highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor; highp vec3 materialSpecularColor = lightColor.rgb; @@ -49,7 +51,7 @@ void main() { highp float cosAlpha = clamp(dot(E, R), 0.0, 1.0); highp float bias = 0.005 * tan(acos(cosTheta)); - bias = clamp(bias, 0.0, 0.01); + bias = clamp(bias, 0.001, 0.01); vec4 shadCoords = shadowCoord; shadCoords.z -= bias; diff --git a/src/datavisualization/engine/shaders/surfaceShadowFlat.vert b/src/datavisualization/engine/shaders/surfaceShadowFlat.vert index 8da7b196..98fdde3f 100644 --- a/src/datavisualization/engine/shaders/surfaceShadowFlat.vert +++ b/src/datavisualization/engine/shaders/surfaceShadowFlat.vert @@ -2,9 +2,6 @@ #extension GL_EXT_gpu_shader4 : require -attribute highp vec3 vertexPosition_mdl; -attribute highp vec3 vertexNormal_mdl; - uniform highp mat4 MVP; uniform highp mat4 V; uniform highp mat4 M; @@ -12,12 +9,17 @@ uniform highp mat4 itM; uniform highp mat4 depthMVP; uniform highp vec3 lightPosition_wrld; +attribute highp vec3 vertexPosition_mdl; +attribute highp vec3 vertexNormal_mdl; +attribute highp vec2 vertexUV; + +varying highp vec2 UV; varying highp vec3 position_wrld; flat varying highp vec3 normal_cmr; varying highp vec3 eyeDirection_cmr; varying highp vec3 lightDirection_cmr; varying highp vec4 shadowCoord; -varying highp vec3 coords_mdl; +varying highp vec2 coords_mdl; const highp mat4 bias = mat4(0.5, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.0, @@ -26,12 +28,12 @@ const highp mat4 bias = mat4(0.5, 0.0, 0.0, 0.0, void main() { gl_Position = MVP * vec4(vertexPosition_mdl, 1.0); - coords_mdl = vertexPosition_mdl; + coords_mdl = vertexPosition_mdl.xy; shadowCoord = bias * depthMVP * vec4(vertexPosition_mdl, 1.0); position_wrld = vec4(M * vec4(vertexPosition_mdl, 1.0)).xyz; vec3 vertexPosition_cmr = vec4(V * M * vec4(vertexPosition_mdl, 1.0)).xyz; eyeDirection_cmr = vec3(0.0, 0.0, 0.0) - vertexPosition_cmr; - vec3 lightPosition_cmr = vec4(V * vec4(lightPosition_wrld, 1.0)).xyz; - lightDirection_cmr = lightPosition_cmr + eyeDirection_cmr; + lightDirection_cmr = vec4(V * vec4(lightPosition_wrld, 0.0)).xyz; normal_cmr = vec4(V * itM * vec4(vertexNormal_mdl, 0.0)).xyz; + UV = vertexUV; } diff --git a/src/datavisualization/engine/shaders/surfaceShadowNoTex.frag b/src/datavisualization/engine/shaders/surfaceShadowNoTex.frag index 1acf8f69..985214be 100644 --- a/src/datavisualization/engine/shaders/surfaceShadowNoTex.frag +++ b/src/datavisualization/engine/shaders/surfaceShadowNoTex.frag @@ -5,15 +5,17 @@ varying highp vec3 position_wrld; varying highp vec3 normal_cmr; varying highp vec3 eyeDirection_cmr; varying highp vec3 lightDirection_cmr; +varying highp vec4 shadowCoord; uniform highp sampler2DShadow shadowMap; uniform sampler2D textureSampler; -varying highp vec4 shadowCoord; uniform highp vec3 lightPosition_wrld; uniform highp float lightStrength; uniform highp float ambientStrength; uniform highp float shadowQuality; uniform highp vec4 lightColor; +uniform highp float gradMin; +uniform highp float gradHeight; highp vec2 poissonDisk[16] = vec2[16](vec2(-0.94201624, -0.39906216), vec2(0.94558609, -0.76890725), @@ -33,7 +35,7 @@ highp vec2 poissonDisk[16] = vec2[16](vec2(-0.94201624, -0.39906216), vec2(0.14383161, -0.14100790)); void main() { - highp vec2 gradientUV = vec2(0.0, (coords_mdl.y + 1.0) / 2.0); + highp vec2 gradientUV = vec2(0.0, gradMin + coords_mdl.y * gradHeight); highp vec3 materialDiffuseColor = texture2D(textureSampler, gradientUV).xyz; highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor; highp vec3 materialSpecularColor = lightColor.rgb; @@ -47,7 +49,7 @@ void main() { highp float cosAlpha = clamp(dot(E, R), 0.0, 1.0); highp float bias = 0.005 * tan(acos(cosTheta)); - bias = clamp(bias, 0.0, 0.01); + bias = clamp(bias, 0.001, 0.01); vec4 shadCoords = shadowCoord; shadCoords.z -= bias; diff --git a/src/datavisualization/engine/shaders/surfaceTexturedFlat.frag b/src/datavisualization/engine/shaders/surfaceTexturedFlat.frag new file mode 100644 index 00000000..7c654e0c --- /dev/null +++ b/src/datavisualization/engine/shaders/surfaceTexturedFlat.frag @@ -0,0 +1,39 @@ +#version 120 + +#extension GL_EXT_gpu_shader4 : require + +varying highp vec3 coords_mdl; +varying highp vec3 position_wrld; +flat varying highp vec3 normal_cmr; +varying highp vec3 eyeDirection_cmr; +varying highp vec3 lightDirection_cmr; +varying highp vec2 UV; + +uniform sampler2D textureSampler; +uniform highp vec3 lightPosition_wrld; +uniform highp float lightStrength; +uniform highp float ambientStrength; +uniform highp vec4 lightColor; + +void main() { + highp vec3 materialDiffuseColor = texture2D(textureSampler, UV).xyz; + highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor; + highp vec3 materialSpecularColor = lightColor.rgb; + + highp float distance = length(lightPosition_wrld - position_wrld); + + highp vec3 n = normalize(normal_cmr); + highp vec3 l = normalize(lightDirection_cmr); + highp float cosTheta = clamp(dot(n, l), 0.0, 1.0); + + highp vec3 E = normalize(eyeDirection_cmr); + highp vec3 R = reflect(-l, n); + highp float cosAlpha = clamp(dot(E, R), 0.0, 1.0); + + gl_FragColor.rgb = + materialAmbientColor + + materialDiffuseColor * lightStrength * pow(cosTheta, 2) / distance + + materialSpecularColor * lightStrength * pow(cosAlpha, 10) / distance; + gl_FragColor.a = 1.0; +} + diff --git a/src/datavisualization/engine/shaders/surfaceTexturedShadow.frag b/src/datavisualization/engine/shaders/surfaceTexturedShadow.frag new file mode 100644 index 00000000..a4259565 --- /dev/null +++ b/src/datavisualization/engine/shaders/surfaceTexturedShadow.frag @@ -0,0 +1,67 @@ +#version 120 + +uniform highp float lightStrength; +uniform highp float ambientStrength; +uniform highp float shadowQuality; +uniform highp sampler2D textureSampler; +uniform highp sampler2DShadow shadowMap; +uniform highp vec4 lightColor; + +varying highp vec4 shadowCoord; +varying highp vec2 UV; +varying highp vec3 position_wrld; +varying highp vec3 normal_cmr; +varying highp vec3 eyeDirection_cmr; +varying highp vec3 lightDirection_cmr; + +highp vec2 poissonDisk[16] = vec2[16](vec2(-0.94201624, -0.39906216), + vec2(0.94558609, -0.76890725), + vec2(-0.094184101, -0.92938870), + vec2(0.34495938, 0.29387760), + vec2(-0.91588581, 0.45771432), + vec2(-0.81544232, -0.87912464), + vec2(-0.38277543, 0.27676845), + vec2(0.97484398, 0.75648379), + vec2(0.44323325, -0.97511554), + vec2(0.53742981, -0.47373420), + vec2(-0.26496911, -0.41893023), + vec2(0.79197514, 0.19090188), + vec2(-0.24188840, 0.99706507), + vec2(-0.81409955, 0.91437590), + vec2(0.19984126, 0.78641367), + vec2(0.14383161, -0.14100790)); + +void main() { + highp vec3 materialDiffuseColor = texture2D(textureSampler, UV).rgb; + highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor; + highp vec3 materialSpecularColor = lightColor.rgb * 0.2; + + highp vec3 n = normalize(normal_cmr); + highp vec3 l = normalize(lightDirection_cmr); + highp float cosTheta = clamp(dot(n, l), 0.0, 1.0); + + highp vec3 E = normalize(eyeDirection_cmr); + highp vec3 R = reflect(-l, n); + highp float cosAlpha = clamp(dot(E, R), 0.0, 1.0); + + highp float bias = 0.005 * tan(acos(cosTheta)); + bias = clamp(bias, 0.001, 0.01); + + vec4 shadCoords = shadowCoord; + shadCoords.z -= bias; + + highp float visibility = 0.6; + for (int i = 0; i < 15; i++) { + vec4 shadCoordsPD = shadCoords; + shadCoordsPD.x += cos(poissonDisk[i].x) / shadowQuality; + shadCoordsPD.y += sin(poissonDisk[i].y) / shadowQuality; + visibility += 0.025 * shadow2DProj(shadowMap, shadCoordsPD).r; + } + + gl_FragColor.rgb = + (materialAmbientColor + + materialDiffuseColor * lightStrength * cosTheta + + materialSpecularColor * lightStrength * pow(cosAlpha, 10)); + gl_FragColor.a = texture2D(textureSampler, UV).a; + gl_FragColor.rgb = visibility * clamp(gl_FragColor.rgb, 0.0, 1.0); +} diff --git a/src/datavisualization/engine/shaders/surfaceTexturedShadowFlat.frag b/src/datavisualization/engine/shaders/surfaceTexturedShadowFlat.frag new file mode 100644 index 00000000..496f4777 --- /dev/null +++ b/src/datavisualization/engine/shaders/surfaceTexturedShadowFlat.frag @@ -0,0 +1,72 @@ +#version 120 + +#extension GL_EXT_gpu_shader4 : require + +varying highp vec3 coords_mdl; +varying highp vec3 position_wrld; +flat varying highp vec3 normal_cmr; +varying highp vec3 eyeDirection_cmr; +varying highp vec3 lightDirection_cmr; +varying highp vec2 UV; + +uniform highp sampler2DShadow shadowMap; +uniform sampler2D textureSampler; +varying highp vec4 shadowCoord; +uniform highp vec3 lightPosition_wrld; +uniform highp float lightStrength; +uniform highp float ambientStrength; +uniform highp float shadowQuality; +uniform highp vec4 lightColor; + +highp vec2 poissonDisk[16] = vec2[16](vec2(-0.94201624, -0.39906216), + vec2(0.94558609, -0.76890725), + vec2(-0.094184101, -0.92938870), + vec2(0.34495938, 0.29387760), + vec2(-0.91588581, 0.45771432), + vec2(-0.81544232, -0.87912464), + vec2(-0.38277543, 0.27676845), + vec2(0.97484398, 0.75648379), + vec2(0.44323325, -0.97511554), + vec2(0.53742981, -0.47373420), + vec2(-0.26496911, -0.41893023), + vec2(0.79197514, 0.19090188), + vec2(-0.24188840, 0.99706507), + vec2(-0.81409955, 0.91437590), + vec2(0.19984126, 0.78641367), + vec2(0.14383161, -0.14100790)); + +void main() { + highp vec3 materialDiffuseColor = texture2D(textureSampler, UV).xyz; + highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor; + highp vec3 materialSpecularColor = lightColor.rgb; + + highp vec3 n = normalize(normal_cmr); + highp vec3 l = normalize(lightDirection_cmr); + highp float cosTheta = clamp(dot(n, l), 0.0, 1.0); + + highp vec3 E = normalize(eyeDirection_cmr); + highp vec3 R = reflect(-l, n); + highp float cosAlpha = clamp(dot(E, R), 0.0, 1.0); + + highp float bias = 0.005 * tan(acos(cosTheta)); + bias = clamp(bias, 0.001, 0.01); + + vec4 shadCoords = shadowCoord; + shadCoords.z -= bias; + + highp float visibility = 0.6; + for (int i = 0; i < 15; i++) { + vec4 shadCoordsPD = shadCoords; + shadCoordsPD.x += cos(poissonDisk[i].x) / shadowQuality; + shadCoordsPD.y += sin(poissonDisk[i].y) / shadowQuality; + visibility += 0.025 * shadow2DProj(shadowMap, shadCoordsPD).r; + } + + gl_FragColor.rgb = + (materialAmbientColor + + materialDiffuseColor * lightStrength * cosTheta + + materialSpecularColor * lightStrength * pow(cosAlpha, 10)); + gl_FragColor.a = 1.0; + gl_FragColor.rgb = visibility * clamp(gl_FragColor.rgb, 0.0, 1.0); +} + diff --git a/src/datavisualization/engine/shaders/surface_ES2.frag b/src/datavisualization/engine/shaders/surface_ES2.frag index 58d13834..1d1bdc3e 100644 --- a/src/datavisualization/engine/shaders/surface_ES2.frag +++ b/src/datavisualization/engine/shaders/surface_ES2.frag @@ -10,9 +10,11 @@ uniform sampler2D textureSampler; uniform highp float lightStrength; uniform highp float ambientStrength; uniform highp vec4 lightColor; +uniform highp float gradMin; +uniform highp float gradHeight; void main() { - highp vec2 gradientUV = vec2(0.0, (coords_mdl.y + 1.0) / 2.0); + highp vec2 gradientUV = vec2(0.0, gradMin + coords_mdl.y * gradHeight); highp vec3 materialDiffuseColor = texture2D(textureSampler, gradientUV).xyz; highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor; highp vec3 materialSpecularColor = lightColor.rgb; diff --git a/src/datavisualization/engine/shaders/texture3d.frag b/src/datavisualization/engine/shaders/texture3d.frag new file mode 100644 index 00000000..3f9c42ff --- /dev/null +++ b/src/datavisualization/engine/shaders/texture3d.frag @@ -0,0 +1,155 @@ +#version 120 + +varying highp vec3 pos; +varying highp vec3 rayDir; + +uniform highp sampler3D textureSampler; +uniform highp vec4 colorIndex[256]; +uniform highp int color8Bit; +uniform highp vec3 textureDimensions; +uniform highp int sampleCount; // This is the maximum sample count +uniform highp float alphaMultiplier; +uniform highp int preserveOpacity; +uniform highp vec3 minBounds; +uniform highp vec3 maxBounds; + +// Ray traveling straight through a single 'alpha thickness' applies 100% of the encountered alpha. +// Rays traveling shorter distances apply a fraction. This is used to normalize the alpha over +// entire volume, regardless of texture dimensions +const highp float alphaThicknesses = 32.0; + +void main() { + vec3 rayStart = pos; + + highp vec3 startBounds = minBounds; + highp vec3 endBounds = maxBounds; + if (rayDir.x < 0.0) { + startBounds.x = maxBounds.x; + endBounds.x = minBounds.x; + } + if (rayDir.y > 0.0) { + startBounds.y = maxBounds.y; + endBounds.y = minBounds.y; + } + if (rayDir.z > 0.0) { + startBounds.z = maxBounds.z; + endBounds.z = minBounds.z; + } + + // Calculate ray intersection endpoint + highp vec3 rayStop; + highp vec3 invRayDir = 1.0 / rayDir; + highp vec3 t = (endBounds - rayStart) * invRayDir; + highp float endT = min(t.x, min(t.y, t.z)); + rayStop = rayStart + endT * rayDir; + if (endT <= 0.0) + discard; + + // Convert intersections to texture coords + rayStart = 0.5 * (rayStart + 1.0); + rayStop = 0.5 * (rayStop + 1.0); + + highp vec3 ray = rayStop - rayStart; + + highp vec3 absRay = abs(ray); + highp vec3 invAbsRay = 1.0 / absRay; + highp float fullDist = length(ray); + highp vec3 curPos = rayStart; + + highp vec4 curColor = vec4(0, 0, 0, 0); + highp float curAlpha = 0.0; + highp float curLen = 0.0; + highp vec3 curRgb = vec3(0, 0, 0); + + highp vec4 destColor = vec4(0, 0, 0, 0); + highp float totalOpacity = 1.0; + + highp float extraAlphaMultiplier = fullDist * alphaThicknesses * alphaMultiplier; + + // nextEdges vector indicates the next edges of the texel boundaries along each axis that + // the ray is about to cross. The first edges are offset by a fraction of a texel to + // avoid artifacts from rounding errors later. + highp vec3 nextEdges = vec3(floor(curPos.x / textureDimensions.x) * textureDimensions.x, + floor(curPos.y / textureDimensions.y) * textureDimensions.y, + floor(curPos.z / textureDimensions.z) * textureDimensions.z); + + highp vec3 textureSteps = textureDimensions; + highp vec3 textureOffset = textureDimensions * 0.001; + if (ray.x > 0) { + nextEdges.x += textureDimensions.x + textureOffset.x; + } else { + nextEdges.x -= textureOffset.x; + textureSteps.x = -textureDimensions.x; + } + if (ray.y > 0) { + nextEdges.y += textureDimensions.y + textureOffset.y; + } else { + nextEdges.y -= textureOffset.y; + textureSteps.y = -textureDimensions.y; + } + if (ray.z > 0) { + nextEdges.z += textureDimensions.z + textureOffset.z; + } else { + nextEdges.z -= textureOffset.z; + textureSteps.z = -textureDimensions.z; + } + + // Raytrace into volume, need to sample pixels along the eye ray until we hit opacity 1 + for (int i = 0; i < sampleCount; i++) { + curColor = texture3D(textureSampler, curPos); + if (color8Bit != 0) + curColor = colorIndex[int(curColor.r * 255.0)]; + + // Find which dimension has least to go to figure out the next step distance + highp vec3 delta = abs(nextEdges - curPos); + highp vec3 modDelta = delta * invAbsRay; + highp float minDelta = min(modDelta.x, min(modDelta.y, modDelta.z)); + highp float stepSize; + if (minDelta == modDelta.x) { + nextEdges.x += textureSteps.x; + stepSize = delta.x * invAbsRay.x; + } + if (minDelta == modDelta.y) { + nextEdges.y += textureSteps.y; + stepSize = delta.y * invAbsRay.y; + } + if (minDelta == modDelta.z) { + nextEdges.z += textureSteps.z; + stepSize = delta.z * invAbsRay.z; + } + + curPos += stepSize * ray; + curLen += stepSize; + + if (curColor.a >= 0.0) { + if (curColor.a == 1.0 && (preserveOpacity == 1 || alphaMultiplier >= 1.0)) + curAlpha = 1.0; + else + curAlpha = curColor.a * extraAlphaMultiplier * stepSize; + highp float nextOpacity = totalOpacity - curAlpha; + // If opacity goes beyond full opacity, we need to adjust current alpha according + // to the fraction of the distance the material is visible, so that we don't get + // box artifacts around texels. + if (nextOpacity < 0.0) { + curAlpha *= totalOpacity / curAlpha; + nextOpacity = 0.0; + } + curRgb = curColor.rgb * curAlpha * (totalOpacity + nextOpacity); + totalOpacity = nextOpacity; + destColor.rgb += curRgb; + } + + if (curLen >= 1.0 || totalOpacity <= 0.0) + break; + } + + if (totalOpacity == 1.0) + discard; + + // Brighten up the final color if there is some transparency left + if (totalOpacity >= 0.0 && totalOpacity < 1.0) + destColor *= (1.0 - (totalOpacity * 0.5)) / (1.0 - totalOpacity); + + destColor.a = (1.0 - totalOpacity); + gl_FragColor = clamp(destColor, 0.0, 1.0); +} diff --git a/src/datavisualization/engine/shaders/texture3d.vert b/src/datavisualization/engine/shaders/texture3d.vert new file mode 100644 index 00000000..ef3f1b25 --- /dev/null +++ b/src/datavisualization/engine/shaders/texture3d.vert @@ -0,0 +1,36 @@ +uniform highp mat4 MVP; +uniform highp vec3 minBounds; +uniform highp vec3 maxBounds; +uniform highp vec3 cameraPositionRelativeToModel; + +attribute highp vec3 vertexPosition_mdl; +attribute highp vec2 vertexUV; +attribute highp vec3 vertexNormal_mdl; + +varying highp vec3 pos; +varying highp vec3 rayDir; + +void main() { + gl_Position = MVP * vec4(vertexPosition_mdl, 1.0); + + highp vec3 minBoundsNorm = minBounds; + highp vec3 maxBoundsNorm = maxBounds; + + // Y and Z are flipped in bounds to be directly usable in texture calculations, + // so flip them back to normal for position calculations + minBoundsNorm.yz = -minBoundsNorm.yz; + maxBoundsNorm.yz = -maxBoundsNorm.yz; + + minBoundsNorm = 0.5 * (minBoundsNorm + 1.0); + maxBoundsNorm = 0.5 * (maxBoundsNorm + 1.0); + + pos = vertexPosition_mdl + + ((1.0 - vertexPosition_mdl) * minBoundsNorm) + - ((1.0 + vertexPosition_mdl) * (1.0 - maxBoundsNorm)); + + rayDir = -(cameraPositionRelativeToModel - pos); + + // Flip Y and Z so QImage bits work directly for texture and first image is in the front + rayDir.yz = -rayDir.yz; + pos.yz = -pos.yz; +} diff --git a/src/datavisualization/engine/shaders/texture3dlowdef.frag b/src/datavisualization/engine/shaders/texture3dlowdef.frag new file mode 100644 index 00000000..ed0d41ce --- /dev/null +++ b/src/datavisualization/engine/shaders/texture3dlowdef.frag @@ -0,0 +1,109 @@ +#version 120 + +varying highp vec3 pos; +varying highp vec3 rayDir; + +uniform highp sampler3D textureSampler; +uniform highp vec4 colorIndex[256]; +uniform highp int color8Bit; +uniform highp vec3 textureDimensions; +uniform highp int sampleCount; // This is the maximum sample count +uniform highp float alphaMultiplier; +uniform highp int preserveOpacity; +uniform highp vec3 minBounds; +uniform highp vec3 maxBounds; + +// Ray traveling straight through a single 'alpha thickness' applies 100% of the encountered alpha. +// Rays traveling shorter distances apply a fraction. This is used to normalize the alpha over +// entire volume, regardless of texture dimensions +const highp float alphaThicknesses = 32.0; +const highp float SQRT3 = 1.73205081; + +void main() { + vec3 rayStart = pos; + highp vec3 startBounds = minBounds; + highp vec3 endBounds = maxBounds; + if (rayDir.x < 0.0) { + startBounds.x = maxBounds.x; + endBounds.x = minBounds.x; + } + if (rayDir.y > 0.0) { + startBounds.y = maxBounds.y; + endBounds.y = minBounds.y; + } + if (rayDir.z > 0.0) { + startBounds.z = maxBounds.z; + endBounds.z = minBounds.z; + } + + // Calculate ray intersection endpoint + highp vec3 rayStop; + highp vec3 invRayDir = 1.0 / rayDir; + highp vec3 t = (endBounds - rayStart) * invRayDir; + highp float endT = min(t.x, min(t.y, t.z)); + if (endT <= 0.0) + discard; + rayStop = rayStart + endT * rayDir; + + // Convert intersections to texture coords + rayStart = 0.5 * (rayStart + 1.0); + rayStop = 0.5 * (rayStop + 1.0); + + highp vec3 ray = rayStop - rayStart; + + highp float fullDist = length(ray); + highp float stepSize = SQRT3 / sampleCount; + highp vec3 step = (SQRT3 * normalize(ray)) / sampleCount; + + rayStart += (step * 0.001); + + highp vec3 curPos = rayStart; + highp float curLen = 0.0; + highp vec4 curColor = vec4(0, 0, 0, 0); + highp float curAlpha = 0.0; + highp vec3 curRgb = vec3(0, 0, 0); + highp vec4 destColor = vec4(0, 0, 0, 0); + highp float totalOpacity = 1.0; + + highp float extraAlphaMultiplier = stepSize * alphaThicknesses * alphaMultiplier; + + // Raytrace into volume, need to sample pixels along the eye ray until we hit opacity 1 + for (int i = 0; i < sampleCount; i++) { + curColor = texture3D(textureSampler, curPos); + if (color8Bit != 0) + curColor = colorIndex[int(curColor.r * 255.0)]; + + if (curColor.a >= 0.0) { + if (curColor.a == 1.0 && (preserveOpacity == 1 || alphaMultiplier >= 1.0)) + curAlpha = 1.0; + else + curAlpha = curColor.a * extraAlphaMultiplier; + highp float nextOpacity = totalOpacity - curAlpha; + // If opacity goes beyond full opacity, we need to adjust current alpha according + // to the fraction of the distance the material is visible, so that we don't get + // box artifacts around texels. + if (nextOpacity < 0.0) { + curAlpha *= totalOpacity / curAlpha; + nextOpacity = 0.0; + } + + curRgb = curColor.rgb * curAlpha * (totalOpacity + nextOpacity); + destColor.rgb += curRgb; + totalOpacity = nextOpacity; + } + curPos += step; + curLen += stepSize; + if (curLen >= fullDist || totalOpacity <= 0.0) + break; + } + + if (totalOpacity == 1.0) + discard; + + // Brighten up the final color if there is some transparency left + if (totalOpacity >= 0.0 && totalOpacity < 1.0) + destColor *= (1.0 - (totalOpacity * 0.5)) / (1.0 - totalOpacity); + + destColor.a = (1.0 - totalOpacity); + gl_FragColor = clamp(destColor, 0.0, 1.0); +} diff --git a/src/datavisualization/engine/shaders/texture3dslice.frag b/src/datavisualization/engine/shaders/texture3dslice.frag new file mode 100644 index 00000000..c555af98 --- /dev/null +++ b/src/datavisualization/engine/shaders/texture3dslice.frag @@ -0,0 +1,155 @@ +#version 120 + +varying highp vec3 pos; +varying highp vec3 rayDir; + +uniform highp sampler3D textureSampler; +uniform highp vec3 volumeSliceIndices; +uniform highp vec4 colorIndex[256]; +uniform highp int color8Bit; +uniform highp float alphaMultiplier; +uniform highp int preserveOpacity; +uniform highp vec3 minBounds; +uniform highp vec3 maxBounds; + +const highp vec3 xPlaneNormal = vec3(1.0, 0, 0); +const highp vec3 yPlaneNormal = vec3(0, 1.0, 0); +const highp vec3 zPlaneNormal = vec3(0, 0, 1.0); + +void main() { + // Find out where ray intersects the slice planes + vec3 normRayDir = normalize(rayDir); + highp vec3 rayStart = pos; + highp float minT = 2.0f; + if (normRayDir.x != 0.0 && normRayDir.y != 0.0 && normRayDir.z != 0.0) { + highp vec3 boxBounds = vec3(1.0, 1.0, 1.0); + highp vec3 invRayDir = 1.0 / normRayDir; + if (normRayDir.x < 0) + boxBounds.x = -1.0; + if (normRayDir.y < 0) + boxBounds.y = -1.0; + if (normRayDir.z < 0) + boxBounds.z = -1.0; + highp vec3 t = (boxBounds - rayStart) * invRayDir; + minT = min(t.x, min(t.y, t.z)); + } + + highp vec3 xPoint = vec3(volumeSliceIndices.x, 0, 0); + highp vec3 yPoint = vec3(0, volumeSliceIndices.y, 0); + highp vec3 zPoint = vec3(0, 0, volumeSliceIndices.z); + highp float firstD = minT + 1.0; + highp float secondD = firstD; + highp float thirdD = firstD; + if (volumeSliceIndices.x >= -1.0) { + highp float dx = dot(xPoint - rayStart, xPlaneNormal) / dot(normRayDir, xPlaneNormal); + if (dx >= 0.0 && dx <= minT) + firstD = min(dx, firstD); + } + if (volumeSliceIndices.y >= -1.0) { + highp float dy = dot(yPoint - rayStart, yPlaneNormal) / dot(normRayDir, yPlaneNormal); + if (dy >= 0.0 && dy <= minT) { + if (dy < firstD) { + secondD = firstD; + firstD = dy; + } else { + secondD = dy; + } + } + } + if (volumeSliceIndices.z >= -1.0) { + highp float dz = dot(zPoint - rayStart, zPlaneNormal) / dot(normRayDir, zPlaneNormal); + if (dz >= 0.0) { + if (dz < firstD && dz <= minT) { + thirdD = secondD; + secondD = firstD; + firstD = dz; + } else if (dz < secondD){ + thirdD = secondD; + secondD = dz; + } else { + thirdD = dz; + } + } + } + + highp vec4 destColor = vec4(0.0, 0.0, 0.0, 0.0); + highp vec4 curColor = vec4(0.0, 0.0, 0.0, 0.0); + highp float totalAlpha = 0.0; + highp vec3 curRgb = vec3(0, 0, 0); + highp float curAlpha = 0.0; + + // Convert intersection to texture coords + + if (firstD <= minT) { + highp vec3 texelVec = rayStart + normRayDir * firstD; + if (clamp(texelVec.x, minBounds.x, maxBounds.x) == texelVec.x + && clamp(texelVec.y, maxBounds.y, minBounds.y) == texelVec.y + && clamp(texelVec.z, maxBounds.z, minBounds.z) == texelVec.z) { + texelVec = 0.5 * (texelVec + 1.0); + curColor = texture3D(textureSampler, texelVec); + if (color8Bit != 0) + curColor = colorIndex[int(curColor.r * 255.0)]; + + if (curColor.a > 0.0) { + curAlpha = curColor.a; + if (curColor.a == 1.0 && preserveOpacity != 0) + curAlpha = 1.0; + else + curAlpha = clamp(curColor.a * alphaMultiplier, 0.0, 1.0); + destColor.rgb = curColor.rgb * curAlpha; + totalAlpha = curAlpha; + } + } + if (secondD <= minT && totalAlpha < 1.0) { + texelVec = rayStart + normRayDir * secondD; + if (clamp(texelVec.x, minBounds.x, maxBounds.x) == texelVec.x + && clamp(texelVec.y, maxBounds.y, minBounds.y) == texelVec.y + && clamp(texelVec.z, maxBounds.z, minBounds.z) == texelVec.z) { + texelVec = 0.5 * (texelVec + 1.0); + curColor = texture3D(textureSampler, texelVec); + if (color8Bit != 0) + curColor = colorIndex[int(curColor.r * 255.0)]; + if (curColor.a > 0.0) { + if (curColor.a == 1.0 && preserveOpacity != 0) + curAlpha = 1.0; + else + curAlpha = clamp(curColor.a * alphaMultiplier, 0.0, 1.0); + curRgb = curColor.rgb * curAlpha * (1.0 - totalAlpha); + destColor.rgb += curRgb; + totalAlpha += curAlpha; + } + } + if (thirdD <= minT && totalAlpha < 1.0) { + texelVec = rayStart + normRayDir * thirdD; + if (clamp(texelVec.x, minBounds.x, maxBounds.x) == texelVec.x + && clamp(texelVec.y, maxBounds.y, minBounds.y) == texelVec.y + && clamp(texelVec.z, maxBounds.z, minBounds.z) == texelVec.z) { + texelVec = 0.5 * (texelVec + 1.0); + curColor = texture3D(textureSampler, texelVec); + if (curColor.a > 0.0) { + if (color8Bit != 0) + curColor = colorIndex[int(curColor.r * 255.0)]; + if (curColor.a == 1.0 && preserveOpacity != 0) + curAlpha = 1.0; + else + curAlpha = clamp(curColor.a * alphaMultiplier, 0.0, 1.0); + curRgb = curColor.rgb * curAlpha * (1.0 - totalAlpha); + destColor.rgb += curRgb; + totalAlpha += curAlpha; + } + } + } + } + } + + if (totalAlpha == 0.0) + discard; + + // Brighten up the final color if there is some transparency left + if (totalAlpha > 0.0 && totalAlpha < 1.0) + destColor *= 1.0 / totalAlpha; + + destColor.a = totalAlpha; + gl_FragColor = clamp(destColor, 0.0, 1.0); +} + diff --git a/src/datavisualization/engine/surface3dcontroller.cpp b/src/datavisualization/engine/surface3dcontroller.cpp index c03bafd8..a6c086da 100644 --- a/src/datavisualization/engine/surface3dcontroller.cpp +++ b/src/datavisualization/engine/surface3dcontroller.cpp @@ -29,7 +29,8 @@ Surface3DController::Surface3DController(QRect rect, Q3DScene *scene) m_renderer(0), m_selectedPoint(invalidSelectionPosition()), m_selectedSeries(0), - m_flatShadingSupported(true) + m_flatShadingSupported(true), + m_flipHorizontalGrid(false) { // Setting a null axis creates a new default axis according to orientation and graph type. // Note: these cannot be set in the Abstract3DController constructor, as they will call virtual @@ -79,6 +80,17 @@ void Surface3DController::synchDataToRenderer() m_renderer->updateSelectedPoint(m_selectedPoint, m_selectedSeries); m_changeTracker.selectedPointChanged = false; } + + if (m_changeTracker.flipHorizontalGridChanged) { + m_renderer->updateFlipHorizontalGrid(m_flipHorizontalGrid); + m_changeTracker.flipHorizontalGridChanged = false; + } + + if (m_changeTracker.surfaceTextureChanged) { + m_renderer->updateSurfaceTextures(m_changedTextures); + m_changeTracker.surfaceTextureChanged = false; + m_changedTextures.clear(); + } } void Surface3DController::handleAxisAutoAdjustRangeChangedInOrientation( @@ -140,6 +152,9 @@ void Surface3DController::addSeries(QAbstract3DSeries *series) QSurface3DSeries *surfaceSeries = static_cast<QSurface3DSeries *>(series); if (surfaceSeries->selectedPoint() != invalidSelectionPosition()) setSelectedPoint(surfaceSeries->selectedPoint(), surfaceSeries, false); + + if (!surfaceSeries->texture().isNull()) + updateSurfaceTexture(surfaceSeries); } void Surface3DController::removeSeries(QAbstract3DSeries *series) @@ -168,6 +183,21 @@ QList<QSurface3DSeries *> Surface3DController::surfaceSeriesList() return surfaceSeriesList; } +void Surface3DController::setFlipHorizontalGrid(bool flip) +{ + if (m_flipHorizontalGrid != flip) { + m_flipHorizontalGrid = flip; + m_changeTracker.flipHorizontalGridChanged = true; + emit flipHorizontalGridChanged(flip); + emitNeedRender(); + } +} + +bool Surface3DController::flipHorizontalGrid() const +{ + return m_flipHorizontalGrid; +} + void Surface3DController::setSelectionMode(QAbstract3DGraph::SelectionFlags mode) { // Currently surface only supports row and column modes when also slicing @@ -429,6 +459,16 @@ void Surface3DController::handleRowsRemoved(int startIndex, int count) emitNeedRender(); } +void Surface3DController::updateSurfaceTexture(QSurface3DSeries *series) +{ + m_changeTracker.surfaceTextureChanged = true; + + if (!m_changedTextures.contains(series)) + m_changedTextures.append(series); + + emitNeedRender(); +} + void Surface3DController::adjustAxisRanges() { QValue3DAxis *valueAxisX = static_cast<QValue3DAxis *>(m_axisX); diff --git a/src/datavisualization/engine/surface3dcontroller_p.h b/src/datavisualization/engine/surface3dcontroller_p.h index 2be74f35..8bcdea91 100644 --- a/src/datavisualization/engine/surface3dcontroller_p.h +++ b/src/datavisualization/engine/surface3dcontroller_p.h @@ -38,14 +38,18 @@ class Surface3DRenderer; class QSurface3DSeries; struct Surface3DChangeBitField { - bool selectedPointChanged : 1; - bool rowsChanged : 1; - bool itemChanged : 1; + bool selectedPointChanged : 1; + bool rowsChanged : 1; + bool itemChanged : 1; + bool flipHorizontalGridChanged : 1; + bool surfaceTextureChanged : 1; Surface3DChangeBitField() : selectedPointChanged(true), rowsChanged(false), - itemChanged(false) + itemChanged(false), + flipHorizontalGridChanged(true), + surfaceTextureChanged(true) { } }; @@ -73,6 +77,8 @@ private: bool m_flatShadingSupported; QVector<ChangeItem> m_changedItems; QVector<ChangeRow> m_changedRows; + bool m_flipHorizontalGrid; + QVector<QSurface3DSeries *> m_changedTextures; public: explicit Surface3DController(QRect rect, Q3DScene *scene = 0); @@ -101,6 +107,11 @@ public: virtual void removeSeries(QAbstract3DSeries *series); virtual QList<QSurface3DSeries *> surfaceSeriesList(); + void setFlipHorizontalGrid(bool flip); + bool flipHorizontalGrid() const; + + void updateSurfaceTexture(QSurface3DSeries *series); + public slots: void handleArrayReset(); void handleRowsAdded(int startIndex, int count); @@ -113,6 +124,7 @@ public slots: signals: void selectedSeriesChanged(QSurface3DSeries *series); + void flipHorizontalGridChanged(bool flip); private: Q_DISABLE_COPY(Surface3DController) diff --git a/src/datavisualization/engine/surface3drenderer.cpp b/src/datavisualization/engine/surface3drenderer.cpp index 38c8d8fe..37d6b463 100644 --- a/src/datavisualization/engine/surface3drenderer.cpp +++ b/src/datavisualization/engine/surface3drenderer.cpp @@ -30,10 +30,6 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION //#define SHOW_DEPTH_TEXTURE_SCENE -// Margin for background (1.10 make it 10% larger to avoid -// selection ball being drawn inside background) -const GLfloat backgroundMargin = 1.1f; -const GLfloat gridLineWidth = 0.005f; const GLfloat sliceZScale = 0.1f; const GLfloat sliceUnits = 2.5f; const uint greenMultiplier = 256; @@ -47,19 +43,16 @@ Surface3DRenderer::Surface3DRenderer(Surface3DController *controller) m_backgroundShader(0), m_surfaceFlatShader(0), m_surfaceSmoothShader(0), + m_surfaceTexturedSmoothShader(0), + m_surfaceTexturedFlatShader(0), m_surfaceGridShader(0), m_surfaceSliceFlatShader(0), m_surfaceSliceSmoothShader(0), m_selectionShader(0), - m_labelShader(0), m_heightNormalizer(0.0f), - m_scaleFactor(0.0f), m_scaleX(0.0f), + m_scaleY(0.0f), m_scaleZ(0.0f), - m_scaleXWithBackground(0.0f), - m_scaleZWithBackground(0.0f), - m_depthTexture(0), - m_depthModelTexture(0), m_depthFrameBuffer(0), m_selectionFrameBuffer(0), m_selectionDepthBuffer(0), @@ -68,16 +61,12 @@ Surface3DRenderer::Surface3DRenderer(Surface3DController *controller) m_flatSupported(true), m_selectionActive(false), m_shadowQualityMultiplier(3), - m_hasHeightAdjustmentChanged(true), m_selectedPoint(Surface3DController::invalidSelectionPosition()), m_selectedSeries(0), m_clickedPosition(Surface3DController::invalidSelectionPosition()), m_selectionTexturesDirty(false), m_noShadowTexture(0) { - m_axisCacheY.setScale(2.0f); - m_axisCacheY.setTranslate(-1.0f); - // Check if flat feature is supported ShaderHelper tester(this, QStringLiteral(":/shaders/vertexSurfaceFlat"), QStringLiteral(":/shaders/fragmentSurfaceFlat")); @@ -90,12 +79,13 @@ Surface3DRenderer::Surface3DRenderer(Surface3DController *controller) " Requires at least GLSL version 1.2 with GL_EXT_gpu_shader4 extension."; } - initializeOpenGLFunctions(); initializeOpenGL(); } Surface3DRenderer::~Surface3DRenderer() { + fixContextBeforeDelete(); + if (QOpenGLContext::currentContext()) { m_textureHelper->glDeleteFramebuffers(1, &m_depthFrameBuffer); m_textureHelper->glDeleteRenderbuffers(1, &m_selectionDepthBuffer); @@ -103,7 +93,6 @@ Surface3DRenderer::~Surface3DRenderer() m_textureHelper->deleteTexture(&m_noShadowTexture); m_textureHelper->deleteTexture(&m_depthTexture); - m_textureHelper->deleteTexture(&m_depthModelTexture); m_textureHelper->deleteTexture(&m_selectionResultTexture); } delete m_depthShader; @@ -111,10 +100,11 @@ Surface3DRenderer::~Surface3DRenderer() delete m_selectionShader; delete m_surfaceFlatShader; delete m_surfaceSmoothShader; + delete m_surfaceTexturedSmoothShader; + delete m_surfaceTexturedFlatShader; delete m_surfaceGridShader; delete m_surfaceSliceFlatShader; delete m_surfaceSliceSmoothShader; - delete m_labelShader; } void Surface3DRenderer::initializeOpenGL() @@ -123,25 +113,15 @@ void Surface3DRenderer::initializeOpenGL() // Initialize shaders initSurfaceShaders(); - initLabelShaders(QStringLiteral(":/shaders/vertexLabel"), - QStringLiteral(":/shaders/fragmentLabel")); -#if !defined(QT_OPENGL_ES_2) - // Init depth shader (for shadows). Init in any case, easier to handle shadow activation if done via api. - initDepthShader(); -#endif + if (!m_isOpenGLES) { + initDepthShader(); // For shadows + loadGridLineMesh(); + } // Init selection shader initSelectionShaders(); -#if !(defined QT_OPENGL_ES_2) - // Load grid line mesh - loadGridLineMesh(); -#endif - - // Load label mesh - loadLabelMesh(); - // Resize in case we've missed resize events // Resize calls initSelectionBuffer and initDepthBuffer, so they don't need to be called here handleResize(); @@ -155,6 +135,53 @@ void Surface3DRenderer::initializeOpenGL() m_noShadowTexture = m_textureHelper->create2DTexture(image, false, true, false, true); } +void Surface3DRenderer::fixCameraTarget(QVector3D &target) +{ + target.setX(target.x() * m_scaleX); + target.setY(target.y() * m_scaleY); + target.setZ(target.z() * -m_scaleZ); +} + +void Surface3DRenderer::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 Surface3DRenderer::updateData() { calculateSceneScalingFactors(); @@ -264,6 +291,33 @@ void Surface3DRenderer::updateSeries(const QList<QAbstract3DSeries *> &seriesLis } } +void Surface3DRenderer::updateSurfaceTextures(QVector<QSurface3DSeries *> seriesList) +{ + foreach (QSurface3DSeries *series, seriesList) { + SurfaceSeriesRenderCache *cache = + static_cast<SurfaceSeriesRenderCache *>(m_renderCacheList.value(series)); + if (cache) { + GLuint oldTexture = cache->surfaceTexture(); + m_textureHelper->deleteTexture(&oldTexture); + cache->setSurfaceTexture(0); + + const QSurface3DSeries *currentSeries = cache->series(); + QSurfaceDataProxy *dataProxy = currentSeries->dataProxy(); + const QSurfaceDataArray &array = *dataProxy->array(); + + if (!series->texture().isNull()) { + cache->setSurfaceTexture(m_textureHelper->create2DTexture( + series->texture(), true, true, true)); + + if (cache->isFlatShadingEnabled()) + cache->surfaceObject()->coarseUVs(array, cache->dataArray()); + else + cache->surfaceObject()->smoothUVs(array, cache->dataArray()); + } + } + } +} + SeriesRenderCache *Surface3DRenderer::createNewCache(QAbstract3DSeries *series) { m_selectionTexturesDirty = true; @@ -301,10 +355,13 @@ void Surface3DRenderer::updateRows(const QVector<Surface3DController::ChangeRow> srcArray->at(row)->at(j + sampleSpace.x()); } - if (cache->isFlatShadingEnabled()) - cache->surfaceObject()->updateCoarseRow(dstArray, row - sampleSpace.y()); - else - cache->surfaceObject()->updateSmoothRow(dstArray, row - sampleSpace.y()); + if (cache->isFlatShadingEnabled()) { + cache->surfaceObject()->updateCoarseRow(dstArray, row - sampleSpace.y(), + m_polarGraph); + } else { + cache->surfaceObject()->updateSmoothRow(dstArray, row - sampleSpace.y(), + m_polarGraph); + } } if (updateBuffers) cache->surfaceObject()->uploadBuffers(); @@ -343,9 +400,9 @@ void Surface3DRenderer::updateItems(const QVector<Surface3DController::ChangeIte (*(dstArray.at(y)))[x] = srcArray->at(point.x())->at(point.y()); if (cache->isFlatShadingEnabled()) - cache->surfaceObject()->updateCoarseItem(dstArray, y, x); + cache->surfaceObject()->updateCoarseItem(dstArray, y, x, m_polarGraph); else - cache->surfaceObject()->updateSmoothItem(dstArray, y, x); + cache->surfaceObject()->updateSmoothItem(dstArray, y, x, m_polarGraph); } if (updateBuffers) cache->surfaceObject()->uploadBuffers(); @@ -538,10 +595,12 @@ void Surface3DRenderer::updateSliceObject(SurfaceSeriesRenderCache *cache, const QRect sliceRect(0, 0, sliceRow->size(), 2); if (sliceRow->size() > 0) { - if (cache->isFlatShadingEnabled()) - cache->sliceSurfaceObject()->setUpData(sliceDataArray, sliceRect, true, flipZX); - else - cache->sliceSurfaceObject()->setUpSmoothData(sliceDataArray, sliceRect, true, flipZX); + if (cache->isFlatShadingEnabled()) { + cache->sliceSurfaceObject()->setUpData(sliceDataArray, sliceRect, true, false, flipZX); + } else { + cache->sliceSurfaceObject()->setUpSmoothData(sliceDataArray, sliceRect, true, false, + flipZX); + } } } @@ -664,15 +723,6 @@ QRect Surface3DRenderer::calculateSampleRect(const QSurfaceDataArray &array) void Surface3DRenderer::updateScene(Q3DScene *scene) { - // Set initial camera position - // X must be 0 for rotation to work - we can use "setCameraRotation" for setting it later - if (m_hasHeightAdjustmentChanged) { - scene->activeCamera()->d_ptr->setBaseOrientation(cameraDistanceVector, zeroVector, - upVector); - // For now this is used just to make things once. Proper use will come - m_hasHeightAdjustmentChanged = false; - } - Abstract3DRenderer::updateScene(scene); if (m_selectionActive @@ -716,6 +766,14 @@ void Surface3DRenderer::render(GLuint defaultFboHandle) void Surface3DRenderer::drawSlicedScene() { + if (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionRow) + == m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionColumn)) { + qWarning("Invalid selection mode. Either QAbstract3DGraph::SelectionRow or" + " QAbstract3DGraph::SelectionColumn must be set before calling" + " setSlicingActive(true)."); + return; + } + QVector3D lightPos; QVector4D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor()); @@ -751,6 +809,19 @@ void Surface3DRenderer::drawSlicedScene() AxisRenderCache &sliceCache = rowMode ? m_axisCacheX : m_axisCacheZ; GLfloat scaleXBackground = 0.0f; + if (rowMode) { + // Don't use the regular margin for polar, as the graph is not going to be to scale anyway, + // and polar graphs often have quite a bit of margin, resulting in ugly slices. + if (m_polarGraph) + scaleXBackground = m_scaleX + 0.1f; + else + scaleXBackground = m_scaleXWithBackground; + } else { + if (m_polarGraph) + scaleXBackground = m_scaleZ + 0.1f; + else + scaleXBackground = m_scaleZWithBackground; + } // Disable culling to avoid ugly conditionals with reversed axes and data glDisable(GL_CULL_FACE); @@ -767,11 +838,6 @@ void Surface3DRenderer::drawSlicedScene() drawGrid = true; } - if (rowMode) - scaleXBackground = m_scaleXWithBackground; - else - scaleXBackground = m_scaleZWithBackground; - QMatrix4x4 MVPMatrix; QMatrix4x4 modelMatrix; QMatrix4x4 itModelMatrix; @@ -790,9 +856,27 @@ void Surface3DRenderer::drawSlicedScene() surfaceShader->bind(); - GLuint colorTexture = cache->baseUniformTexture();; - if (cache->colorStyle() != Q3DTheme::ColorStyleUniform) + GLuint colorTexture = cache->baseUniformTexture(); + if (cache->colorStyle() == Q3DTheme::ColorStyleUniform) { + colorTexture = cache->baseUniformTexture(); + surfaceShader->setUniformValue(surfaceShader->gradientMin(), 0.0f); + surfaceShader->setUniformValue(surfaceShader->gradientHeight(), 0.0f); + } else { colorTexture = cache->baseGradientTexture(); + if (cache->colorStyle() == Q3DTheme::ColorStyleObjectGradient) { + float objMin = cache->surfaceObject()->minYValue(); + float objMax = cache->surfaceObject()->maxYValue(); + float objRange = objMax - objMin; + surfaceShader->setUniformValue(surfaceShader->gradientMin(), + -(objMin / objRange)); + surfaceShader->setUniformValue(surfaceShader->gradientHeight(), + 1.0f / objRange); + } else { + surfaceShader->setUniformValue(surfaceShader->gradientMin(), 0.5f); + surfaceShader->setUniformValue(surfaceShader->gradientHeight(), + 1.0f / (m_scaleY * 2.0f)); + } + } // Set shader bindings surfaceShader->setUniformValue(surfaceShader->lightP(), lightPos); @@ -801,9 +885,10 @@ void Surface3DRenderer::drawSlicedScene() surfaceShader->setUniformValue(surfaceShader->nModel(), itModelMatrix.inverted().transposed()); surfaceShader->setUniformValue(surfaceShader->MVP(), MVPMatrix); - surfaceShader->setUniformValue(surfaceShader->lightS(), 0.15f); + surfaceShader->setUniformValue(surfaceShader->lightS(), 0.0f); surfaceShader->setUniformValue(surfaceShader->ambientS(), - m_cachedTheme->ambientLightStrength() * 2.3f); + m_cachedTheme->ambientLightStrength() + + m_cachedTheme->lightStrength() / 10.0f); surfaceShader->setUniformValue(surfaceShader->lightColor(), lightColor); m_drawer->drawObject(surfaceShader, cache->sliceSurfaceObject(), colorTexture); @@ -830,19 +915,16 @@ void Surface3DRenderer::drawSlicedScene() } } - // Disable textures - glDisable(GL_TEXTURE_2D); - glEnable(GL_CULL_FACE); glCullFace(GL_BACK); // Grid lines - if (m_cachedTheme->isGridEnabled() && m_heightNormalizer) { -#if !(defined QT_OPENGL_ES_2) - ShaderHelper *lineShader = m_backgroundShader; -#else - ShaderHelper *lineShader = m_selectionShader; // Plain color shader for GL_LINES -#endif + 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(); @@ -853,7 +935,8 @@ void Surface3DRenderer::drawSlicedScene() lineShader->setUniformValue(lineShader->view(), viewMatrix); lineShader->setUniformValue(lineShader->color(), lineColor); lineShader->setUniformValue(lineShader->ambientS(), - m_cachedTheme->ambientLightStrength() * 2.3f); + m_cachedTheme->ambientLightStrength() + + m_cachedTheme->lightStrength() / 10.0f); lineShader->setUniformValue(lineShader->lightS(), 0.0f); lineShader->setUniformValue(lineShader->lightColor(), lightColor); @@ -881,16 +964,15 @@ void Surface3DRenderer::drawSlicedScene() lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); // Draw the object -#if !(defined QT_OPENGL_ES_2) - m_drawer->drawObject(lineShader, m_gridLineObj); -#else - m_drawer->drawLine(lineShader); -#endif + if (m_isOpenGLES) + m_drawer->drawLine(lineShader); + else + m_drawer->drawObject(lineShader, m_gridLineObj); } } // Vertical lines - QVector3D gridLineScaleY(gridLineWidth, backgroundMargin, gridLineWidth); + QVector3D gridLineScaleY(gridLineWidth, m_scaleYWithBackground, gridLineWidth); gridLineCount = sliceCache.gridLineCount(); for (int line = 0; line < gridLineCount; line++) { @@ -901,10 +983,11 @@ void Surface3DRenderer::drawSlicedScene() modelMatrix.translate(sliceCache.gridLinePosition(line), 0.0f, -1.0f); modelMatrix.scale(gridLineScaleY); itModelMatrix.scale(gridLineScaleY); -#if (defined QT_OPENGL_ES_2) - modelMatrix.rotate(90.0f, 0.0f, 0.0f, 1.0f); - itModelMatrix.rotate(90.0f, 0.0f, 0.0f, 1.0f); -#endif + + if (m_isOpenGLES) { + modelMatrix.rotate(m_zRightAngleRotation); + itModelMatrix.rotate(m_zRightAngleRotation); + } MVPMatrix = projectionViewMatrix * modelMatrix; @@ -915,17 +998,15 @@ void Surface3DRenderer::drawSlicedScene() lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); // Draw the object -#if !(defined QT_OPENGL_ES_2) - m_drawer->drawObject(lineShader, m_gridLineObj); -#else - m_drawer->drawLine(lineShader); -#endif + if (m_isOpenGLES) + m_drawer->drawLine(lineShader); + else + m_drawer->drawObject(lineShader, m_gridLineObj); } } // Draw labels m_labelShader->bind(); - glEnable(GL_TEXTURE_2D); glDisable(GL_DEPTH_TEST); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); @@ -959,12 +1040,15 @@ void Surface3DRenderer::drawSlicedScene() labelNbr = 0; positionComp.setY(-0.1f); - labelTrans.setY(-backgroundMargin); + labelTrans.setY(-m_scaleYWithBackground); labelCount = sliceCache.labelCount(); for (int label = 0; label < labelCount; label++) { if (countLabelItems > labelNbr) { // Draw the label here - labelTrans.setX(sliceCache.labelPosition(label)); + if (rowMode) + labelTrans.setX(sliceCache.labelPosition(label)); + else + labelTrans.setX(-sliceCache.labelPosition(label)); m_dummyRenderItem.setTranslation(labelTrans); @@ -998,7 +1082,6 @@ void Surface3DRenderer::drawSlicedScene() m_cachedSelectionMode, m_labelShader, m_labelObj, activeCamera, false, false, Drawer::LabelMid, Qt::AlignBottom); - glDisable(GL_TEXTURE_2D); glEnable(GL_DEPTH_TEST); glDisable(GL_BLEND); @@ -1048,6 +1131,21 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) else m_xFlipped = true; + m_yFlippedForGrid = m_yFlipped; + if (m_flipHorizontalGrid) { + if (!m_useOrthoProjection) { + // Need to determine if camera is below graph top + float distanceToCenter = activeCamera->position().length() + / activeCamera->zoomLevel() / m_autoScaleAdjustment * 100.0f; + qreal cameraAngle = qreal(activeCamera->yRotation()) / 180.0 * M_PI; + float cameraYPos = float(qSin(cameraAngle)) * distanceToCenter; + m_yFlippedForGrid = cameraYPos < (m_scaleYWithBackground - m_oldCameraTarget.y()); + } else if (m_useOrthoProjection && activeCamera->yRotation() == 0.0f) { + // With ortho we only need to flip at angle zero, to fix label autorotation angles + m_yFlippedForGrid = !m_yFlipped; + } + } + // calculate background rotation based on view matrix rotation if (viewMatrix.row(0).x() > 0 && viewMatrix.row(0).z() <= 0) backgroundRotation = 270.0f; @@ -1065,9 +1163,8 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) QMatrix4x4 depthProjectionViewMatrix; // Draw depth buffer -#if !defined(QT_OPENGL_ES_2) GLfloat adjustedLightStrength = m_cachedTheme->lightStrength() / 10.0f; - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && + if (!m_isOpenGLES && m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && (!m_renderCacheList.isEmpty() || !m_customRenderCache.isEmpty())) { // Render scene into a depth texture for using with shadow mapping // Enable drawing to depth framebuffer @@ -1097,6 +1194,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) / (GLfloat)m_primarySubViewport.height(), 3.0f, 100.0f); depthProjectionViewMatrix = depthProjectionMatrix * depthViewMatrix; + // Surface is not closed, so don't cull anything glDisable(GL_CULL_FACE); foreach (SeriesRenderCache *baseCache, m_renderCacheList) { @@ -1104,45 +1202,9 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) SurfaceObject *object = cache->surfaceObject(); if (object->indexCount() && cache->surfaceVisible() && cache->isVisible() && cache->sampleSpace().width() >= 2 && cache->sampleSpace().height() >= 2) { - QMatrix4x4 modelMatrix; - QMatrix4x4 MVPMatrix; - - MVPMatrix = depthProjectionViewMatrix * modelMatrix; - cache->setMVPMatrix(MVPMatrix); - m_depthShader->setUniformValue(m_depthShader->MVP(), MVPMatrix); - - // 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(), - object->indicesType(), (void *)0); - } - } - - glEnable(GL_CULL_FACE); - glCullFace(GL_FRONT); - - Abstract3DRenderer::drawCustomItems(RenderingDepth, m_depthShader, viewMatrix, - projectionViewMatrix, depthProjectionViewMatrix, - m_depthTexture, m_shadowQualityToShader); - - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, - m_depthModelTexture, 0); - glClear(GL_DEPTH_BUFFER_BIT); - - foreach (SeriesRenderCache *baseCache, m_renderCacheList) { - SurfaceSeriesRenderCache *cache = static_cast<SurfaceSeriesRenderCache *>(baseCache); - SurfaceObject *object = cache->surfaceObject(); - if (object->indexCount() && cache->surfaceVisible() && cache->isVisible() - && cache->sampleSpace().width() >= 2 && cache->sampleSpace().height() >= 2) { - m_depthShader->setUniformValue(m_depthShader->MVP(), cache->MVPMatrix()); + // No translation nor scaling for surfaces, therefore no modelMatrix + // Use directly projectionViewMatrix + m_depthShader->setUniformValue(m_depthShader->MVP(), depthProjectionViewMatrix); // 1st attribute buffer : vertices glEnableVertexAttribArray(m_depthShader->posAtt()); @@ -1165,9 +1227,13 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) glDisableVertexAttribArray(m_depthShader->posAtt()); + glEnable(GL_CULL_FACE); + glCullFace(GL_FRONT); + Abstract3DRenderer::drawCustomItems(RenderingDepth, m_depthShader, viewMatrix, - projectionViewMatrix, depthProjectionViewMatrix, - m_depthTexture, m_shadowQualityToShader); + projectionViewMatrix, + depthProjectionViewMatrix, m_depthTexture, + m_shadowQualityToShader); // Disable drawing to depth framebuffer (= enable drawing to screen) glBindFramebuffer(GL_FRAMEBUFFER, defaultFboHandle); @@ -1182,15 +1248,20 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) glEnable(GL_CULL_FACE); glCullFace(GL_BACK); } -#endif - // Enable texturing - glEnable(GL_TEXTURE_2D); + + // Do position mapping when necessary + if (m_graphPositionQueryPending) { + QVector3D graphDimensions(m_scaleX, m_scaleY, m_scaleZ); + queriedGraphPosition(projectionViewMatrix, graphDimensions, defaultFboHandle); + emit needRender(); + } // Draw selection buffer if (!m_cachedIsSlicingActivated && (!m_renderCacheList.isEmpty() || !m_customRenderCache.isEmpty()) && m_selectionState == SelectOnScene - && m_cachedSelectionMode > QAbstract3DGraph::SelectionNone) { + && m_cachedSelectionMode > QAbstract3DGraph::SelectionNone + && m_selectionResultTexture) { m_selectionShader->bind(); glBindFramebuffer(GL_FRAMEBUFFER, m_selectionFrameBuffer); glViewport(0, @@ -1208,18 +1279,17 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) foreach (SeriesRenderCache *baseCache, m_renderCacheList) { SurfaceSeriesRenderCache *cache = static_cast<SurfaceSeriesRenderCache *>(baseCache); if (cache->surfaceObject()->indexCount() && cache->renderable()) { - QMatrix4x4 modelMatrix; - QMatrix4x4 MVPMatrix; + m_selectionShader->setUniformValue(m_selectionShader->MVP(), projectionViewMatrix); - MVPMatrix = projectionViewMatrix * modelMatrix; - m_selectionShader->setUniformValue(m_selectionShader->MVP(), MVPMatrix); + cache->surfaceObject()->activateSurfaceTexture(false); m_drawer->drawObject(m_selectionShader, cache->surfaceObject(), cache->selectionTexture()); } } m_surfaceGridShader->bind(); - Abstract3DRenderer::drawCustomItems(RenderingSelection, m_surfaceGridShader, viewMatrix, + Abstract3DRenderer::drawCustomItems(RenderingSelection, m_surfaceGridShader, + viewMatrix, projectionViewMatrix, depthProjectionViewMatrix, m_depthTexture, m_shadowQualityToShader); drawLabels(true, activeCamera, viewMatrix, projectionMatrix); @@ -1237,6 +1307,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) + uint(clickedColor.w()) * alphaMultiplier; m_clickedPosition = selectionIdToSurfacePoint(selectionId); + m_clickResolved = true; emit needRender(); @@ -1249,7 +1320,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) // Draw the surface if (!m_renderCacheList.isEmpty()) { - // For surface we can see climpses from underneath + // For surface we can see glimpses from underneath glDisable(GL_CULL_FACE); bool drawGrid = false; @@ -1261,9 +1332,9 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) QMatrix4x4 itModelMatrix; #ifdef SHOW_DEPTH_TEXTURE_SCENE - MVPMatrix = depthProjectionViewMatrix * modelMatrix; + MVPMatrix = depthProjectionViewMatrix; #else - MVPMatrix = projectionViewMatrix * modelMatrix; + MVPMatrix = projectionViewMatrix; #endif cache->setMVPMatrix(MVPMatrix); @@ -1279,8 +1350,13 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) if (cache->surfaceVisible()) { ShaderHelper *shader = m_surfaceFlatShader; - if (!cache->isFlatShadingEnabled()) + if (cache->surfaceTexture()) + shader = m_surfaceTexturedFlatShader; + if (!cache->isFlatShadingEnabled()) { shader = m_surfaceSmoothShader; + if (cache->surfaceTexture()) + shader = m_surfaceTexturedSmoothShader; + } shader->bind(); // Set shader bindings @@ -1294,14 +1370,35 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) m_cachedTheme->ambientLightStrength()); shader->setUniformValue(shader->lightColor(), lightColor); - GLuint gradientTexture; - if (cache->colorStyle() == Q3DTheme::ColorStyleUniform) - gradientTexture = cache->baseUniformTexture(); - else - gradientTexture = cache->baseGradientTexture(); + // Set the surface texturing + cache->surfaceObject()->activateSurfaceTexture(false); + GLuint texture; + if (cache->surfaceTexture()) { + texture = cache->surfaceTexture(); + cache->surfaceObject()->activateSurfaceTexture(true); + } else { + if (cache->colorStyle() == Q3DTheme::ColorStyleUniform) { + texture = cache->baseUniformTexture(); + shader->setUniformValue(shader->gradientMin(), 0.0f); + shader->setUniformValue(shader->gradientHeight(), 0.0f); + } else { + texture = cache->baseGradientTexture(); + if (cache->colorStyle() == Q3DTheme::ColorStyleObjectGradient) { + float objMin = cache->surfaceObject()->minYValue(); + float objMax = cache->surfaceObject()->maxYValue(); + float objRange = objMax - objMin; + shader->setUniformValue(shader->gradientMin(), -(objMin / objRange)); + shader->setUniformValue(shader->gradientHeight(), 1.0f / objRange); + } else { + shader->setUniformValue(shader->gradientMin(), 0.5f); + shader->setUniformValue(shader->gradientHeight(), + 1.0f / (m_scaleY * 2.0f)); + } + } + } -#if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + if (!m_isOpenGLES && + m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { // Set shadow shader bindings QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; shader->setUniformValue(shader->shadowQ(), m_shadowQualityToShader); @@ -1309,17 +1406,13 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) shader->setUniformValue(shader->lightS(), adjustedLightStrength); // Draw the objects - m_drawer->drawObject(shader, cache->surfaceObject(), gradientTexture, - m_depthModelTexture); - } else -#endif - { + m_drawer->drawObject(shader, cache->surfaceObject(), texture, + m_depthTexture); + } else { // Set shadowless shader bindings - shader->setUniformValue(shader->lightS(), - m_cachedTheme->lightStrength()); - + shader->setUniformValue(shader->lightS(), m_cachedTheme->lightStrength()); // Draw the objects - m_drawer->drawObject(shader, cache->surfaceObject(), gradientTexture); + m_drawer->drawObject(shader, cache->surfaceObject(), texture); } } } @@ -1359,12 +1452,12 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) QMatrix4x4 MVPMatrix; QMatrix4x4 itModelMatrix; - QVector3D bgScale(m_scaleXWithBackground, backgroundMargin, m_scaleZWithBackground); + 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(180.0f, 1.0f, 0.0f, 0.0f); + 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); @@ -1392,8 +1485,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) m_cachedTheme->ambientLightStrength() * 2.0f); m_backgroundShader->setUniformValue(m_backgroundShader->lightColor(), lightColor); -#if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) { // Set shadow shader bindings QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; m_backgroundShader->setUniformValue(m_backgroundShader->shadowQ(), @@ -1406,11 +1498,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) m_drawer->drawObject(m_backgroundShader, m_backgroundObj, 0, m_noShadowTexture); else m_drawer->drawObject(m_backgroundShader, m_backgroundObj, 0, m_depthTexture); - } else -#else - Q_UNUSED(noShadows); -#endif - { + } else { // Set shadowless shader bindings m_backgroundShader->setUniformValue(m_backgroundShader->lightS(), m_cachedTheme->lightStrength()); @@ -1423,14 +1511,14 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) // Draw grid lines QVector3D gridLineScaleX(m_scaleXWithBackground, gridLineWidth, gridLineWidth); QVector3D gridLineScaleZ(gridLineWidth, gridLineWidth, m_scaleZWithBackground); - QVector3D gridLineScaleY(gridLineWidth, backgroundMargin, gridLineWidth); + QVector3D gridLineScaleY(gridLineWidth, m_scaleYWithBackground, gridLineWidth); - if (m_cachedTheme->isGridEnabled() && m_heightNormalizer) { -#if !(defined QT_OPENGL_ES_2) - ShaderHelper *lineShader = m_backgroundShader; -#else - ShaderHelper *lineShader = m_selectionShader; // Plain color shader for GL_LINES -#endif + if (m_cachedTheme->isGridEnabled()) { + ShaderHelper *lineShader; + if (m_isOpenGLES) + lineShader = m_surfaceGridShader; // Plain color shader for GL_LINES + else + lineShader = m_backgroundShader; // Bind line shader lineShader->bind(); @@ -1442,15 +1530,12 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) lineShader->setUniformValue(lineShader->color(), lineColor); lineShader->setUniformValue(lineShader->ambientS(), m_cachedTheme->ambientLightStrength()); lineShader->setUniformValue(lineShader->lightColor(), lightColor); -#if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + 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 -#endif - { + } else { // Set shadowless shader bindings lineShader->setUniformValue(lineShader->lightS(), m_cachedTheme->lightStrength() / 2.5f); @@ -1460,205 +1545,211 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) QQuaternion lineXRotation; if (m_xFlipped) - lineYRotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, -90.0f); + lineYRotation = m_yRightAngleRotationNeg; else - lineYRotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, 90.0f); + lineYRotation = m_yRightAngleRotation; - if (m_yFlipped) - lineXRotation = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, 90.0f); + if (m_yFlippedForGrid) + lineXRotation = m_xRightAngleRotation; else - lineXRotation = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, -90.0f); + lineXRotation = m_xRightAngleRotationNeg; - GLfloat yFloorLinePosition = -backgroundMargin + gridLineOffset; - if (m_yFlipped) + float yFloorLinePosition = -m_scaleYWithBackground + gridLineOffset; + if (m_yFlipped != m_flipHorizontalGrid) yFloorLinePosition = -yFloorLinePosition; // Rows (= Z) if (m_axisCacheZ.segmentCount() > 0) { - // Floor lines int gridLineCount = m_axisCacheZ.gridLineCount(); - - 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 !defined(QT_OPENGL_ES_2) - 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); + // Floor lines + 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) { + 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); + } } -#else - m_drawer->drawLine(lineShader); -#endif - } + // Side wall lines + GLfloat lineXTrans = m_scaleXWithBackground - gridLineOffset; - // Side wall lines - GLfloat lineXTrans = m_scaleXWithBackground - gridLineOffset; + if (!m_xFlipped) + lineXTrans = -lineXTrans; - if (!m_xFlipped) - lineXTrans = -lineXTrans; + for (int line = 0; line < gridLineCount; line++) { + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; + QMatrix4x4 itModelMatrix; - for (int line = 0; line < gridLineCount; line++) { - QMatrix4x4 modelMatrix; - QMatrix4x4 MVPMatrix; - QMatrix4x4 itModelMatrix; - - modelMatrix.translate(lineXTrans, 0.0f, m_axisCacheZ.gridLinePosition(line)); + modelMatrix.translate(lineXTrans, 0.0f, m_axisCacheZ.gridLinePosition(line)); - modelMatrix.scale(gridLineScaleY); - itModelMatrix.scale(gridLineScaleY); + modelMatrix.scale(gridLineScaleY); + itModelMatrix.scale(gridLineScaleY); -#if !defined(QT_OPENGL_ES_2) - modelMatrix.rotate(lineYRotation); - itModelMatrix.rotate(lineYRotation); -#else - modelMatrix.rotate(90.0f, 0.0f, 0.0f, 1.0f); - itModelMatrix.rotate(90.0f, 0.0f, 0.0f, 1.0f); -#endif - - 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) { + modelMatrix.rotate(m_zRightAngleRotation); + itModelMatrix.rotate(m_zRightAngleRotation); + } else { + modelMatrix.rotate(lineYRotation); + itModelMatrix.rotate(lineYRotation); + } -#if !defined(QT_OPENGL_ES_2) - 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); + 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); + } } -#else - m_drawer->drawLine(lineShader); -#endif } } // Columns (= X) if (m_axisCacheX.segmentCount() > 0) { -#if defined(QT_OPENGL_ES_2) - lineXRotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, 90.0f); -#endif + if (m_isOpenGLES) + lineXRotation = m_yRightAngleRotation; + // Floor lines int gridLineCount = m_axisCacheX.gridLineCount(); - 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 !defined(QT_OPENGL_ES_2) - 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); + 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); + } } -#else - m_drawer->drawLine(lineShader); -#endif - } - // Back wall lines - GLfloat lineZTrans = m_scaleZWithBackground - gridLineOffset; + // 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); + if (!m_zFlipped) + lineZTrans = -lineZTrans; - modelMatrix.scale(gridLineScaleY); - itModelMatrix.scale(gridLineScaleY); + for (int line = 0; line < gridLineCount; line++) { + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; + QMatrix4x4 itModelMatrix; -#if !defined(QT_OPENGL_ES_2) - if (m_zFlipped) { - modelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f); - itModelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f); - } -#else - modelMatrix.rotate(90.0f, 0.0f, 0.0f, 1.0f); - itModelMatrix.rotate(90.0f, 0.0f, 0.0f, 1.0f); -#endif + modelMatrix.translate(m_axisCacheX.gridLinePosition(line), 0.0f, lineZTrans); - MVPMatrix = projectionViewMatrix * modelMatrix; + modelMatrix.scale(gridLineScaleY); + itModelMatrix.scale(gridLineScaleY); - // 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) { + modelMatrix.rotate(m_zRightAngleRotation); + itModelMatrix.rotate(m_zRightAngleRotation); + } else if (m_zFlipped) { + modelMatrix.rotate(m_xFlipRotation); + itModelMatrix.rotate(m_xFlipRotation); + } -#if !defined(QT_OPENGL_ES_2) - 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); + 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); + } } -#else - m_drawer->drawLine(lineShader); -#endif } } @@ -1683,8 +1774,8 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) itModelMatrix.scale(gridLineScaleX); if (m_zFlipped) { - modelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f); - itModelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f); + modelMatrix.rotate(m_xFlipRotation); + itModelMatrix.rotate(m_xFlipRotation); } MVPMatrix = projectionViewMatrix * modelMatrix; @@ -1695,20 +1786,20 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) itModelMatrix.inverted().transposed()); lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); -#if !defined(QT_OPENGL_ES_2) - 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); + 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 { - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj); + m_drawer->drawLine(lineShader); } -#else - m_drawer->drawLine(lineShader); -#endif } // Side wall @@ -1738,20 +1829,20 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) itModelMatrix.inverted().transposed()); lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); -#if !defined(QT_OPENGL_ES_2) - 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); + 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 { - // Draw the object - m_drawer->drawObject(lineShader, m_gridLineObj); + m_drawer->drawLine(lineShader); } -#else - m_drawer->drawLine(lineShader); -#endif } } } @@ -1811,7 +1902,7 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa } else { shader = m_labelShader; shader->bind(); - glEnable(GL_TEXTURE_2D); + glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } @@ -1832,8 +1923,14 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa QVector3D positionZComp(0.0f, 0.0f, 0.0f); if (m_axisCacheZ.segmentCount() > 0) { int labelCount = m_axisCacheZ.labelCount(); - GLfloat labelXTrans = m_scaleXWithBackground + labelMargin; - GLfloat labelYTrans = -backgroundMargin; + float labelXTrans = m_scaleXWithBackground + labelMargin; + float labelYTrans = m_flipHorizontalGrid ? m_scaleYWithBackground : -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) @@ -1843,7 +1940,7 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa if (labelAutoAngle == 0.0f) { if (m_zFlipped) labelRotation.setY(180.0f); - if (m_yFlipped) { + if (m_yFlippedForGrid) { if (m_zFlipped) labelRotation.setY(180.0f); else @@ -1855,7 +1952,7 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa } else { if (m_zFlipped) labelRotation.setY(180.0f); - if (m_yFlipped) { + if (m_yFlippedForGrid) { if (m_zFlipped) { if (m_xFlipped) { labelRotation.setX(90.0f - (labelAutoAngle - fractionCamX) @@ -1920,10 +2017,34 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa 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 - labelTrans.setZ(m_axisCacheZ.labelPosition(label)); + 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); - const LabelItem &axisLabelItem = *m_axisCacheZ.labelItems().at(label); if (drawSelection) { QVector4D labelColor = QVector4D(label / 255.0f, 0.0f, 0.0f, @@ -1938,7 +2059,14 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa labelsMaxWidth = qMax(labelsMaxWidth, float(axisLabelItem.size().width())); } if (!drawSelection && m_axisCacheZ.isTitleVisible()) { - labelTrans.setZ(0.0f); + 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); } @@ -1952,8 +2080,13 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa fractionCamX = activeCamera->xRotation() * labelAngleFraction; int labelCount = m_axisCacheX.labelCount(); - GLfloat labelZTrans = m_scaleZWithBackground + labelMargin; - GLfloat labelYTrans = -backgroundMargin; + float labelZTrans = 0.0f; + float labelYTrans = m_flipHorizontalGrid ? m_scaleYWithBackground : -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) @@ -1964,7 +2097,7 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa labelRotation = QVector3D(-90.0f, 90.0f, 0.0f); if (m_xFlipped) labelRotation.setY(-90.0f); - if (m_yFlipped) { + if (m_yFlippedForGrid) { if (m_xFlipped) labelRotation.setY(-90.0f); else @@ -1976,7 +2109,7 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa labelRotation.setY(-90.0f); else labelRotation.setY(90.0f); - if (m_yFlipped) { + if (m_yFlippedForGrid) { if (m_zFlipped) { if (m_xFlipped) { labelRotation.setX(90.0f - (2.0f * labelAutoAngle - fractionCamX) @@ -2022,7 +2155,16 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa } } } + 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, @@ -2038,12 +2180,64 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa indexStep = 1; } float offsetValue = 0.0f; + bool showLastLabel = false; + QVector<float> &labelPositions = m_axisCacheX.formatter()->labelPositions(); + int lastLabelPosIndex = labelPositions.size() - 1; + if (labelPositions.size() + && (labelPositions.at(lastLabelPosIndex) != 1.0f || labelPositions.at(0) != 0.0f)) { + // Avoid overlapping first and last label if they would get on same position + showLastLabel = true; + } + for (int label = startIndex; label != endIndex; label = label + indexStep) { glPolygonOffset(offsetValue++ / -10.0f, 1.0f); // Draw the label here - labelTrans.setX(m_axisCacheX.labelPosition(label)); - m_dummyRenderItem.setTranslation(labelTrans); + 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, @@ -2059,8 +2253,20 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa } 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); + activeCamera, labelsMaxWidth, viewMatrix, projectionMatrix, shader, + radial); } } // Y Labels @@ -2072,12 +2278,12 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa fractionCamX = activeCamera->xRotation() * labelAngleFraction; int labelCount = m_axisCacheY.labelCount(); - GLfloat labelXTrans = m_scaleXWithBackground; - GLfloat labelZTrans = m_scaleZWithBackground; + float labelXTrans = m_scaleXWithBackground; + float labelZTrans = m_scaleZWithBackground; // Back & side wall - GLfloat labelMarginXTrans = labelMargin; - GLfloat labelMarginZTrans = labelMargin; + 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 = @@ -2134,7 +2340,7 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa float offsetValue = 0.0f; for (int label = startIndex; label != endIndex; label = label + indexStep) { const LabelItem &axisLabelItem = *m_axisCacheY.labelItems().at(label); - const GLfloat labelYTrans = m_axisCacheY.labelPosition(label); + float labelYTrans = m_axisCacheY.labelPosition(label); glPolygonOffset(offsetValue++ / -10.0f, 1.0f); @@ -2144,6 +2350,21 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa 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); @@ -2174,10 +2395,8 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa } glDisable(GL_POLYGON_OFFSET_FILL); - if (!drawSelection) { - glDisable(GL_TEXTURE_2D); + if (!drawSelection) glDisable(GL_BLEND); - } } void Surface3DRenderer::updateSelectionMode(QAbstract3DGraph::SelectionFlags mode) @@ -2205,11 +2424,12 @@ void Surface3DRenderer::updateSelectionTextures() void Surface3DRenderer::createSelectionTexture(SurfaceSeriesRenderCache *cache, uint &lastSelectionId) { - // Create the selection ID image. Each grid corner gets 2x2 pixel area of - // ID color so that each vertex (data point) has 4x4 pixel area of ID color + // Create the selection ID image. Each grid corner gets 1 pixel area of + // ID color so that each vertex (data point) has 2x2 pixel area of ID color, + // except the vertices on the edges. const QRect &sampleSpace = cache->sampleSpace(); - int idImageWidth = (sampleSpace.width() - 1) * 4; - int idImageHeight = (sampleSpace.height() - 1) * 4; + int idImageWidth = (sampleSpace.width() - 1) * 2; + int idImageHeight = (sampleSpace.height() - 1) * 2; if (idImageHeight <= 0 || idImageWidth <= 0) { cache->setSelectionIdRange(-1, -1); @@ -2221,21 +2441,21 @@ void Surface3DRenderer::createSelectionTexture(SurfaceSeriesRenderCache *cache, uint idStart = lastSelectionId; uchar *bits = new uchar[idImageWidth * idImageHeight * 4 * sizeof(uchar)]; - for (int i = 0; i < idImageHeight; i += 4) { - for (int j = 0; j < idImageWidth; j += 4) { + for (int i = 0; i < idImageHeight; i += 2) { + for (int j = 0; j < idImageWidth; j += 2) { int p = (i * idImageWidth + j) * 4; uchar r, g, b, a; idToRGBA(lastSelectionId, &r, &g, &b, &a); - fillIdCorner(&bits[p], r, g, b, a, stride); + fillIdCorner(&bits[p], r, g, b, a); idToRGBA(lastSelectionId + 1, &r, &g, &b, &a); - fillIdCorner(&bits[p + 8], r, g, b, a, stride); + fillIdCorner(&bits[p + 4], r, g, b, a); idToRGBA(lastSelectionId + sampleSpace.width(), &r, &g, &b, &a); - fillIdCorner(&bits[p + 2 * stride], r, g, b, a, stride); + fillIdCorner(&bits[p + stride], r, g, b, a); idToRGBA(lastSelectionId + sampleSpace.width() + 1, &r, &g, &b, &a); - fillIdCorner(&bits[p + 2 * stride + 8], r, g, b, a, stride); + fillIdCorner(&bits[p + stride + 4], r, g, b, a); lastSelectionId++; } @@ -2263,24 +2483,12 @@ void Surface3DRenderer::initSelectionBuffer() m_selectionDepthBuffer); } -void Surface3DRenderer::fillIdCorner(uchar *p, uchar r, uchar g, uchar b, uchar a, int stride) +void Surface3DRenderer::fillIdCorner(uchar *p, uchar r, uchar g, uchar b, uchar a) { p[0] = r; p[1] = g; p[2] = b; p[3] = a; - p[4] = r; - p[5] = g; - p[6] = b; - p[7] = a; - p[stride + 0] = r; - p[stride + 1] = g; - p[stride + 2] = b; - p[stride + 3] = a; - p[stride + 4] = r; - p[stride + 5] = g; - p[stride + 6] = b; - p[stride + 7] = a; } void Surface3DRenderer::idToRGBA(uint id, uchar *r, uchar *g, uchar *b, uchar *a) @@ -2293,21 +2501,66 @@ void Surface3DRenderer::idToRGBA(uint id, uchar *r, uchar *g, uchar *b, uchar *a void Surface3DRenderer::calculateSceneScalingFactors() { + // Margin for background (the default 0.10 makes it 10% larger to avoid + // selection ball being drawn inside background) + if (m_requestedMargin < 0.0f) { + m_hBackgroundMargin = 0.1f; + m_vBackgroundMargin = 0.1f; + } else { + m_hBackgroundMargin = m_requestedMargin; + m_vBackgroundMargin = m_requestedMargin; + } + if (m_polarGraph) { + float polarMargin = calculatePolarBackgroundMargin(); + m_hBackgroundMargin = qMax(m_hBackgroundMargin, polarMargin); + } + // Calculate scene scaling and translation factors m_heightNormalizer = GLfloat(m_axisCacheY.max() - m_axisCacheY.min()); - m_areaSize.setHeight(m_axisCacheZ.max() - m_axisCacheZ.min()); - m_areaSize.setWidth(m_axisCacheX.max() - m_axisCacheX.min()); - m_scaleFactor = qMax(m_areaSize.width(), m_areaSize.height()); - m_scaleX = m_graphAspectRatio * m_areaSize.width() / m_scaleFactor; - m_scaleZ = m_graphAspectRatio * m_areaSize.height() / m_scaleFactor; - m_scaleXWithBackground = m_scaleX + backgroundMargin - 1.0f; - m_scaleZWithBackground = m_scaleZ + backgroundMargin - 1.0f; - - float factorScaler = 2.0f * m_graphAspectRatio / m_scaleFactor; - m_axisCacheX.setScale(factorScaler * m_areaSize.width()); - m_axisCacheZ.setScale(-factorScaler * m_areaSize.height()); - m_axisCacheX.setTranslate(-m_axisCacheX.scale() / 2.0f); - m_axisCacheZ.setTranslate(-m_axisCacheZ.scale() / 2.0f); + + 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 Surface3DRenderer::checkFlatSupport(SurfaceSeriesRenderCache *cache) @@ -2326,10 +2579,20 @@ void Surface3DRenderer::updateObjects(SurfaceSeriesRenderCache *cache, bool dime QSurfaceDataArray &dataArray = cache->dataArray(); const QRect &sampleSpace = cache->sampleSpace(); - if (cache->isFlatShadingEnabled()) - cache->surfaceObject()->setUpData(dataArray, sampleSpace, dimensionChanged); - else - cache->surfaceObject()->setUpSmoothData(dataArray, sampleSpace, dimensionChanged); + const QSurface3DSeries *currentSeries = cache->series(); + QSurfaceDataProxy *dataProxy = currentSeries->dataProxy(); + const QSurfaceDataArray &array = *dataProxy->array(); + + if (cache->isFlatShadingEnabled()) { + cache->surfaceObject()->setUpData(dataArray, sampleSpace, dimensionChanged, m_polarGraph); + if (cache->surfaceTexture()) + cache->surfaceObject()->coarseUVs(array, dataArray); + } else { + cache->surfaceObject()->setUpSmoothData(dataArray, sampleSpace, dimensionChanged, + m_polarGraph); + if (cache->surfaceTexture()) + cache->surfaceObject()->smoothUVs(array, dataArray); + } } void Surface3DRenderer::updateSelectedPoint(const QPoint &position, QSurface3DSeries *series) @@ -2339,6 +2602,11 @@ void Surface3DRenderer::updateSelectedPoint(const QPoint &position, QSurface3DSe m_selectionDirty = true; } +void Surface3DRenderer::updateFlipHorizontalGrid(bool flip) +{ + m_flipHorizontalGrid = flip; +} + void Surface3DRenderer::resetClickedStatus() { m_clickedPosition = Surface3DController::invalidSelectionPosition(); @@ -2539,14 +2807,15 @@ void Surface3DRenderer::updateShadowQuality(QAbstract3DGraph::ShadowQuality qual handleShadowQualityChange(); -#if !defined(QT_OPENGL_ES_2) updateDepthBuffer(); -#endif } void Surface3DRenderer::updateTextures() { - // Do nothing, but required as it is pure virtual on parent + Abstract3DRenderer::updateTextures(); + + if (m_polarGraph) + calculateSceneScalingFactors(); } void Surface3DRenderer::updateSlicingActive(bool isSlicing) @@ -2556,12 +2825,13 @@ void Surface3DRenderer::updateSlicingActive(bool isSlicing) m_cachedIsSlicingActivated = isSlicing; - if (!m_cachedIsSlicingActivated) - initSelectionBuffer(); // We need to re-init selection buffer in case there has been a resize + if (!m_cachedIsSlicingActivated) { + // We need to re-init selection buffer in case there has been a resize + initSelectionBuffer(); + initCursorPositionBuffer(); + } -#if !defined(QT_OPENGL_ES_2) updateDepthBuffer(); // Re-init depth buffer as well -#endif m_selectionDirty = true; @@ -2577,55 +2847,68 @@ void Surface3DRenderer::initShaders(const QString &vertexShader, const QString & Q_UNUSED(vertexShader); Q_UNUSED(fragmentShader); - // draw the shader for the surface according to smooth status, shadow and uniform color - if (m_surfaceFlatShader) - delete m_surfaceFlatShader; - if (m_surfaceSmoothShader) - delete m_surfaceSmoothShader; - if (m_surfaceSliceFlatShader) - delete m_surfaceSliceFlatShader; - if (m_surfaceSliceSmoothShader) - delete m_surfaceSliceSmoothShader; - -#if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { - m_surfaceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexShadow"), - QStringLiteral(":/shaders/fragmentSurfaceShadowNoTex")); - } else { - m_surfaceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"), - QStringLiteral(":/shaders/fragmentSurface")); - } - m_surfaceSliceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"), - QStringLiteral(":/shaders/fragmentSurface")); - if (m_flatSupported) { + delete m_surfaceFlatShader; + delete m_surfaceSmoothShader; + delete m_surfaceTexturedSmoothShader; + delete m_surfaceTexturedFlatShader; + delete m_surfaceSliceFlatShader; + delete m_surfaceSliceSmoothShader; + + if (!m_isOpenGLES) { if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { - m_surfaceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceShadowFlat"), - QStringLiteral(":/shaders/fragmentSurfaceShadowFlat")); + m_surfaceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexShadow"), + QStringLiteral(":/shaders/fragmentSurfaceShadowNoTex")); + m_surfaceTexturedSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexShadow"), + QStringLiteral(":/shaders/fragmentTexturedSurfaceShadow")); + } else { + m_surfaceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"), + QStringLiteral(":/shaders/fragmentSurface")); + m_surfaceTexturedSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexTexture"), + QStringLiteral(":/shaders/fragmentTexture")); + } + m_surfaceSliceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"), + QStringLiteral(":/shaders/fragmentSurface")); + if (m_flatSupported) { + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + m_surfaceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceShadowFlat"), + QStringLiteral(":/shaders/fragmentSurfaceShadowFlat")); + m_surfaceTexturedFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceShadowFlat"), + QStringLiteral(":/shaders/fragmentTexturedSurfaceShadowFlat")); + } else { + m_surfaceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceFlat"), + QStringLiteral(":/shaders/fragmentSurfaceFlat")); + m_surfaceTexturedFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceFlat"), + QStringLiteral(":/shaders/fragmentSurfaceTexturedFlat")); + } + m_surfaceSliceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceFlat"), + QStringLiteral(":/shaders/fragmentSurfaceFlat")); } else { - m_surfaceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceFlat"), - QStringLiteral(":/shaders/fragmentSurfaceFlat")); + m_surfaceFlatShader = 0; + m_surfaceSliceFlatShader = 0; + m_surfaceTexturedFlatShader = 0; } - m_surfaceSliceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceFlat"), - QStringLiteral(":/shaders/fragmentSurfaceFlat")); } else { - m_surfaceFlatShader = 0; - m_surfaceSliceFlatShader = 0; + m_surfaceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"), + QStringLiteral(":/shaders/fragmentSurfaceES2")); + m_surfaceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"), + QStringLiteral(":/shaders/fragmentSurfaceES2")); + m_surfaceTexturedSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexTexture"), + QStringLiteral(":/shaders/fragmentTextureES2")); + m_surfaceTexturedFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexTexture"), + QStringLiteral(":/shaders/fragmentTextureES2")); + m_surfaceSliceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"), + QStringLiteral(":/shaders/fragmentSurfaceES2")); + m_surfaceSliceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"), + QStringLiteral(":/shaders/fragmentSurfaceES2")); } -#else - m_surfaceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"), - QStringLiteral(":/shaders/fragmentSurfaceES2")); - m_surfaceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"), - QStringLiteral(":/shaders/fragmentSurfaceES2")); - m_surfaceSliceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"), - QStringLiteral(":/shaders/fragmentSurfaceES2")); - m_surfaceSliceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"), - QStringLiteral(":/shaders/fragmentSurfaceES2")); -#endif + m_surfaceSmoothShader->initialize(); m_surfaceSliceSmoothShader->initialize(); + m_surfaceTexturedSmoothShader->initialize(); if (m_flatSupported) { m_surfaceFlatShader->initialize(); m_surfaceSliceFlatShader->initialize(); + m_surfaceTexturedFlatShader->initialize(); } } @@ -2660,45 +2943,33 @@ void Surface3DRenderer::initSurfaceShaders() handleShadowQualityChange(); } -void Surface3DRenderer::initLabelShaders(const QString &vertexShader, const QString &fragmentShader) -{ - if (m_labelShader) - delete m_labelShader; - m_labelShader = new ShaderHelper(this, vertexShader, fragmentShader); - m_labelShader->initialize(); -} - -#if !defined(QT_OPENGL_ES_2) void Surface3DRenderer::initDepthShader() { - if (m_depthShader) + if (!m_isOpenGLES) { delete m_depthShader; - m_depthShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexDepth"), - QStringLiteral(":/shaders/fragmentDepth")); - m_depthShader->initialize(); + m_depthShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexDepth"), + QStringLiteral(":/shaders/fragmentDepth")); + m_depthShader->initialize(); + } } void Surface3DRenderer::updateDepthBuffer() { - m_textureHelper->deleteTexture(&m_depthTexture); - m_textureHelper->deleteTexture(&m_depthModelTexture); + if (!m_isOpenGLES) { + m_textureHelper->deleteTexture(&m_depthTexture); - if (m_primarySubViewport.size().isEmpty()) - return; + if (m_primarySubViewport.size().isEmpty()) + return; - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { - m_depthTexture = m_textureHelper->createDepthTextureFrameBuffer(m_primarySubViewport.size(), - m_depthFrameBuffer, - m_shadowQualityMultiplier); - m_textureHelper->fillDepthTexture(m_depthTexture, m_primarySubViewport.size(), - m_shadowQualityMultiplier, 1.0f); - m_depthModelTexture = m_textureHelper->createDepthTexture(m_primarySubViewport.size(), - m_shadowQualityMultiplier); - if (!m_depthTexture || !m_depthModelTexture) - lowerShadowQuality(); + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + m_depthTexture = m_textureHelper->createDepthTextureFrameBuffer(m_primarySubViewport.size(), + m_depthFrameBuffer, + m_shadowQualityMultiplier); + if (!m_depthTexture) + lowerShadowQuality(); + } } } -#endif QVector3D Surface3DRenderer::convertPositionToTranslation(const QVector3D &position, bool isAbsolute) @@ -2707,15 +2978,44 @@ QVector3D Surface3DRenderer::convertPositionToTranslation(const QVector3D &posit float yTrans = 0.0f; float zTrans = 0.0f; if (!isAbsolute) { - xTrans = m_axisCacheX.positionAt(position.x()); + 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()); - zTrans = m_axisCacheZ.positionAt(position.z()); } else { xTrans = position.x() * m_scaleX; - yTrans = position.y(); - zTrans = position.z() * m_scaleZ; + yTrans = position.y() * m_scaleY; + zTrans = position.z() * -m_scaleZ; } return QVector3D(xTrans, yTrans, zTrans); } +void Surface3DRenderer::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 Surface3DRenderer::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 Surface3DRenderer::updateMargin(float margin) +{ + Abstract3DRenderer::updateMargin(margin); + calculateSceneScalingFactors(); +} + QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/engine/surface3drenderer_p.h b/src/datavisualization/engine/surface3drenderer_p.h index efa8ff7e..57b6f9e6 100644 --- a/src/datavisualization/engine/surface3drenderer_p.h +++ b/src/datavisualization/engine/surface3drenderer_p.h @@ -51,19 +51,16 @@ private: ShaderHelper *m_backgroundShader; ShaderHelper *m_surfaceFlatShader; ShaderHelper *m_surfaceSmoothShader; + ShaderHelper *m_surfaceTexturedSmoothShader; + ShaderHelper *m_surfaceTexturedFlatShader; ShaderHelper *m_surfaceGridShader; ShaderHelper *m_surfaceSliceFlatShader; ShaderHelper *m_surfaceSliceSmoothShader; ShaderHelper *m_selectionShader; - ShaderHelper *m_labelShader; - GLfloat m_heightNormalizer; - GLfloat m_scaleFactor; - GLfloat m_scaleX; - GLfloat m_scaleZ; - GLfloat m_scaleXWithBackground; - GLfloat m_scaleZWithBackground; - GLuint m_depthTexture; - GLuint m_depthModelTexture; + float m_heightNormalizer; + float m_scaleX; + float m_scaleY; + float m_scaleZ; GLuint m_depthFrameBuffer; GLuint m_selectionFrameBuffer; GLuint m_selectionDepthBuffer; @@ -73,13 +70,12 @@ private: bool m_selectionActive; AbstractRenderItem m_dummyRenderItem; GLint m_shadowQualityMultiplier; - QSizeF m_areaSize; - bool m_hasHeightAdjustmentChanged; QPoint m_selectedPoint; QSurface3DSeries *m_selectedSeries; QPoint m_clickedPosition; bool m_selectionTexturesDirty; GLuint m_noShadowTexture; + bool m_flipHorizontalGrid; public: explicit Surface3DRenderer(Surface3DController *controller); @@ -87,6 +83,7 @@ public: void updateData(); void updateSeries(const QList<QAbstract3DSeries *> &seriesList); + void updateSurfaceTextures(QVector<QSurface3DSeries *> seriesList); SeriesRenderCache *createNewCache(QAbstract3DSeries *series); void cleanCache(SeriesRenderCache *cache); void updateSelectionMode(QAbstract3DGraph::SelectionFlags mode); @@ -95,14 +92,22 @@ public: void updateScene(Q3DScene *scene); void updateSlicingActive(bool isSlicing); void updateSelectedPoint(const QPoint &position, QSurface3DSeries *series); + void updateFlipHorizontalGrid(bool flip); inline QPoint clickedPosition() const { return m_clickedPosition; } void resetClickedStatus(); QVector3D convertPositionToTranslation(const QVector3D &position, bool isAbsolute); + void updateAxisLabels(QAbstract3DAxis::AxisOrientation orientation, + const QStringList &labels); + void updateAxisTitleVisibility(QAbstract3DAxis::AxisOrientation orientation, + bool visible); + void updateMargin(float margin); void render(GLuint defaultFboHandle = 0); protected: void initializeOpenGL(); + virtual void fixCameraTarget(QVector3D &target); + virtual void getVisibleItemBounds(QVector3D &minBounds, QVector3D &maxBounds); signals: void flatShadingSupportedChanged(bool supported); @@ -128,7 +133,6 @@ private: void calculateSceneScalingFactors(); void initBackgroundShaders(const QString &vertexShader, const QString &fragmentShader); - void initLabelShaders(const QString &vertexShader, const QString &fragmentShader); void initSelectionShaders(); void initSurfaceShaders(); void initSelectionBuffer(); @@ -136,13 +140,11 @@ private: void updateSelectionTextures(); void createSelectionTexture(SurfaceSeriesRenderCache *cache, uint &lastSelectionId); void idToRGBA(uint id, uchar *r, uchar *g, uchar *b, uchar *a); - void fillIdCorner(uchar *p, uchar r, uchar g, uchar b, uchar a, int stride); + void fillIdCorner(uchar *p, uchar r, uchar g, uchar b, uchar a); void surfacePointSelected(const QPoint &point); void updateSelectionPoint(SurfaceSeriesRenderCache *cache, const QPoint &point, bool label); QPoint selectionIdToSurfacePoint(uint id); -#if !defined(QT_OPENGL_ES_2) void updateDepthBuffer(); -#endif void emitSelectedPointChanged(QPoint position); Q_DISABLE_COPY(Surface3DRenderer) diff --git a/src/datavisualization/engine/surfaceseriesrendercache.cpp b/src/datavisualization/engine/surfaceseriesrendercache.cpp index 1cce5288..f1ad752a 100644 --- a/src/datavisualization/engine/surfaceseriesrendercache.cpp +++ b/src/datavisualization/engine/surfaceseriesrendercache.cpp @@ -39,7 +39,8 @@ SurfaceSeriesRenderCache::SurfaceSeriesRenderCache(QAbstract3DSeries *series, m_sliceSelectionPointer(0), m_mainSelectionPointer(0), m_slicePointerActive(false), - m_mainPointerActive(false) + m_mainPointerActive(false), + m_surfaceTexture(0) { } @@ -62,8 +63,10 @@ void SurfaceSeriesRenderCache::populate(bool newSeries) void SurfaceSeriesRenderCache::cleanup(TextureHelper *texHelper) { - if (QOpenGLContext::currentContext()) + if (QOpenGLContext::currentContext()) { texHelper->deleteTexture(&m_selectionTexture); + texHelper->deleteTexture(&m_surfaceTexture); + } delete m_surfaceObj; delete m_sliceSurfaceObj; diff --git a/src/datavisualization/engine/surfaceseriesrendercache_p.h b/src/datavisualization/engine/surfaceseriesrendercache_p.h index b6254a75..4d04149f 100644 --- a/src/datavisualization/engine/surfaceseriesrendercache_p.h +++ b/src/datavisualization/engine/surfaceseriesrendercache_p.h @@ -85,6 +85,8 @@ public: inline bool slicePointerActive() const { return m_slicePointerActive; } inline void setMainPointerActivity(bool activity) { m_mainPointerActive = activity; } inline bool mainPointerActive() const { return m_mainPointerActive; } + inline void setSurfaceTexture(GLuint texture) { m_surfaceTexture = texture; } + inline GLuint surfaceTexture() const { return m_surfaceTexture; } protected: bool m_surfaceVisible; @@ -105,6 +107,7 @@ protected: SelectionPointer *m_mainSelectionPointer; bool m_slicePointerActive; bool m_mainPointerActive; + GLuint m_surfaceTexture; }; QT_END_NAMESPACE_DATAVISUALIZATION |