diff options
Diffstat (limited to 'src/datavisualization/engine')
65 files changed, 6298 insertions, 3157 deletions
diff --git a/src/datavisualization/engine/abstract3dcontroller.cpp b/src/datavisualization/engine/abstract3dcontroller.cpp index ec18a850..8658d463 100644 --- a/src/datavisualization/engine/abstract3dcontroller.cpp +++ b/src/datavisualization/engine/abstract3dcontroller.cpp @@ -17,22 +17,16 @@ ****************************************************************************/ #include "abstract3dcontroller_p.h" -#include "camerahelper_p.h" #include "qabstract3daxis_p.h" -#include "qvalue3daxis.h" -#include "qcategory3daxis.h" +#include "qvalue3daxis_p.h" #include "abstract3drenderer_p.h" -#include "q3dcamera.h" -#include "q3dlight.h" -#include "qabstractdataproxy_p.h" #include "qabstract3dinputhandler_p.h" #include "qtouch3dinputhandler.h" -#include "qabstract3dseries_p.h" #include "thememanager_p.h" #include "q3dtheme_p.h" -#include "q3dscene_p.h" -#include "q3dscene.h" +#include "qcustom3ditem_p.h" #include <QtCore/QThread> +#include <QtGui/QOpenGLFramebufferObject> QT_BEGIN_NAMESPACE_DATAVISUALIZATION @@ -42,6 +36,9 @@ Abstract3DController::Abstract3DController(QRect initialViewport, Q3DScene *scen m_themeManager(new ThemeManager(this)), m_selectionMode(QAbstract3DGraph::SelectionItem), m_shadowQuality(QAbstract3DGraph::ShadowQualityMedium), + m_useOrthoProjection(false), + m_aspectRatio(2.0f), + m_optimizationHints(QAbstract3DGraph::OptimizationDefault), m_scene(scene), m_activeInputHandler(0), m_axisX(0), @@ -49,9 +46,13 @@ Abstract3DController::Abstract3DController(QRect initialViewport, Q3DScene *scen m_axisZ(0), m_renderer(0), m_isDataDirty(true), - m_isSeriesVisibilityDirty(true), + m_isCustomDataDirty(true), + m_isCustomItemDirty(true), m_isSeriesVisualsDirty(true), - m_renderPending(false) + m_renderPending(false), + m_measureFps(false), + m_numFrames(0), + m_currentFps(0.0) { if (!m_scene) m_scene = new Q3DScene; @@ -84,6 +85,9 @@ Abstract3DController::~Abstract3DController() destroyRenderer(); delete m_scene; delete m_themeManager; + foreach (QCustom3DItem *item, m_customItems) + delete item; + m_customItems.clear(); } void Abstract3DController::destroyRenderer() @@ -142,7 +146,7 @@ void Abstract3DController::removeSeries(QAbstract3DSeries *series) this, &Abstract3DController::handleSeriesVisibilityChanged); series->d_ptr->setController(0); m_isDataDirty = true; - m_isSeriesVisibilityDirty = true; + m_isSeriesVisualsDirty = true; emitNeedRender(); } } @@ -182,6 +186,46 @@ void Abstract3DController::synchDataToRenderer() m_changeTracker.selectionModeChanged = false; } + if (m_changeTracker.projectionChanged) { + m_renderer->m_useOrthoProjection = m_useOrthoProjection; + m_changeTracker.projectionChanged = false; + } + + if (m_changeTracker.aspectRatioChanged) { + m_renderer->updateAspectRatio(m_aspectRatio); + m_changeTracker.aspectRatioChanged = false; + } + + if (m_changeTracker.optimizationHintChanged) { + m_renderer->updateOptimizationHint(m_optimizationHints); + m_changeTracker.optimizationHintChanged = false; + } + + if (m_changeTracker.axisXFormatterChanged) { + m_changeTracker.axisXFormatterChanged = false; + if (m_axisX->type() & QAbstract3DAxis::AxisTypeValue) { + QValue3DAxis *valueAxisX = static_cast<QValue3DAxis *>(m_axisX); + m_renderer->updateAxisFormatter(QAbstract3DAxis::AxisOrientationX, + valueAxisX->formatter()); + } + } + if (m_changeTracker.axisYFormatterChanged) { + m_changeTracker.axisYFormatterChanged = false; + if (m_axisY->type() & QAbstract3DAxis::AxisTypeValue) { + QValue3DAxis *valueAxisY = static_cast<QValue3DAxis *>(m_axisY); + m_renderer->updateAxisFormatter(QAbstract3DAxis::AxisOrientationY, + valueAxisY->formatter()); + } + } + if (m_changeTracker.axisZFormatterChanged) { + m_changeTracker.axisZFormatterChanged = false; + if (m_axisZ->type() & QAbstract3DAxis::AxisTypeValue) { + QValue3DAxis *valueAxisZ = static_cast<QValue3DAxis *>(m_axisZ); + m_renderer->updateAxisFormatter(QAbstract3DAxis::AxisOrientationZ, + valueAxisZ->formatter()); + } + } + if (m_changeTracker.axisXTypeChanged) { m_renderer->updateAxisType(QAbstract3DAxis::AxisOrientationX, m_axisX->type()); m_changeTracker.axisXTypeChanged = false; @@ -325,9 +369,89 @@ void Abstract3DController::synchDataToRenderer() } } - if (m_isSeriesVisibilityDirty || m_isSeriesVisualsDirty) { - m_renderer->updateSeries(m_seriesList, m_isSeriesVisibilityDirty); - m_isSeriesVisibilityDirty = false; + if (m_changeTracker.axisXReversedChanged) { + m_changeTracker.axisXReversedChanged = false; + if (m_axisX->type() & QAbstract3DAxis::AxisTypeValue) { + QValue3DAxis *valueAxisX = static_cast<QValue3DAxis *>(m_axisX); + m_renderer->updateAxisReversed(QAbstract3DAxis::AxisOrientationX, + valueAxisX->reversed()); + } + } + + if (m_changeTracker.axisYReversedChanged) { + m_changeTracker.axisYReversedChanged = false; + if (m_axisY->type() & QAbstract3DAxis::AxisTypeValue) { + QValue3DAxis *valueAxisY = static_cast<QValue3DAxis *>(m_axisY); + m_renderer->updateAxisReversed(QAbstract3DAxis::AxisOrientationY, + valueAxisY->reversed()); + } + } + + if (m_changeTracker.axisZReversedChanged) { + m_changeTracker.axisZReversedChanged = false; + if (m_axisZ->type() & QAbstract3DAxis::AxisTypeValue) { + QValue3DAxis *valueAxisZ = static_cast<QValue3DAxis *>(m_axisZ); + m_renderer->updateAxisReversed(QAbstract3DAxis::AxisOrientationZ, + valueAxisZ->reversed()); + } + } + + if (m_changeTracker.axisXLabelAutoRotationChanged) { + m_renderer->updateAxisLabelAutoRotation(QAbstract3DAxis::AxisOrientationX, + m_axisX->labelAutoRotation()); + m_changeTracker.axisXLabelAutoRotationChanged = false; + } + + if (m_changeTracker.axisYLabelAutoRotationChanged) { + m_renderer->updateAxisLabelAutoRotation(QAbstract3DAxis::AxisOrientationY, + m_axisY->labelAutoRotation()); + m_changeTracker.axisYLabelAutoRotationChanged = false; + } + + if (m_changeTracker.axisZLabelAutoRotationChanged) { + m_renderer->updateAxisLabelAutoRotation(QAbstract3DAxis::AxisOrientationZ, + m_axisZ->labelAutoRotation()); + m_changeTracker.axisZLabelAutoRotationChanged = false; + } + + if (m_changeTracker.axisXTitleVisibilityChanged) { + m_renderer->updateAxisTitleVisibility(QAbstract3DAxis::AxisOrientationX, + m_axisX->isTitleVisible()); + m_changeTracker.axisXTitleVisibilityChanged = false; + } + if (m_changeTracker.axisYTitleVisibilityChanged) { + m_renderer->updateAxisTitleVisibility(QAbstract3DAxis::AxisOrientationY, + m_axisY->isTitleVisible()); + m_changeTracker.axisYTitleVisibilityChanged = false; + } + if (m_changeTracker.axisZTitleVisibilityChanged) { + m_renderer->updateAxisTitleVisibility(QAbstract3DAxis::AxisOrientationZ, + m_axisZ->isTitleVisible()); + m_changeTracker.axisZTitleVisibilityChanged = false; + } + if (m_changeTracker.axisXTitleFixedChanged) { + m_renderer->updateAxisTitleFixed(QAbstract3DAxis::AxisOrientationX, + m_axisX->isTitleFixed()); + m_changeTracker.axisXTitleFixedChanged = false; + } + if (m_changeTracker.axisYTitleFixedChanged) { + m_renderer->updateAxisTitleFixed(QAbstract3DAxis::AxisOrientationY, + m_axisY->isTitleFixed()); + m_changeTracker.axisYTitleFixedChanged = false; + } + if (m_changeTracker.axisZTitleFixedChanged) { + m_renderer->updateAxisTitleFixed(QAbstract3DAxis::AxisOrientationZ, + m_axisZ->isTitleFixed()); + m_changeTracker.axisZTitleFixedChanged = false; + } + + if (m_changedSeriesList.size()) { + m_renderer->modifiedSeriesList(m_changedSeriesList); + m_changedSeriesList.clear(); + } + + if (m_isSeriesVisualsDirty) { + m_renderer->updateSeries(m_seriesList); m_isSeriesVisualsDirty = false; } @@ -337,6 +461,16 @@ void Abstract3DController::synchDataToRenderer() m_renderer->updateData(); m_isDataDirty = false; } + + if (m_isCustomDataDirty) { + m_renderer->updateCustomData(m_customItems); + m_isCustomDataDirty = false; + } + + if (m_isCustomItemDirty) { + m_renderer->updateCustomItems(); + m_isCustomItemDirty = false; + } } void Abstract3DController::render(const GLuint defaultFboHandle) @@ -347,12 +481,21 @@ void Abstract3DController::render(const GLuint defaultFboHandle) if (!m_renderer) return; - m_renderer->render(defaultFboHandle); + if (m_measureFps) { + // Measure speed (as milliseconds per frame) + m_numFrames++; + int elapsed = m_frameTimer.elapsed(); + if (elapsed >= 1000) { + m_currentFps = qreal(m_numFrames) * 1000.0 / qreal(elapsed); + emit currentFpsChanged(m_currentFps); + m_numFrames = 0; + m_frameTimer.restart(); + } + // To get meaningful framerate, don't just do render on demand. + emitNeedRender(); + } -#ifdef DISPLAY_RENDER_SPEED - // To get meaningful framerate, don't just do render on demand. - emitNeedRender(); -#endif + m_renderer->render(defaultFboHandle); } void Abstract3DController::mouseDoubleClickEvent(QMouseEvent *event) @@ -445,7 +588,8 @@ void Abstract3DController::handleThemeSingleHighlightColorChanged(const QColor & markSeriesVisualsDirty(); } -void Abstract3DController::handleThemeSingleHighlightGradientChanged(const QLinearGradient &gradient) +void Abstract3DController::handleThemeSingleHighlightGradientChanged( + const QLinearGradient &gradient) { // Set value for series that have not explicitly set this value foreach (QAbstract3DSeries *series, m_seriesList) { @@ -503,7 +647,7 @@ void Abstract3DController::setAxisX(QAbstract3DAxis *axis) } } -QAbstract3DAxis *Abstract3DController::axisX() +QAbstract3DAxis *Abstract3DController::axisX() const { return m_axisX; } @@ -517,7 +661,7 @@ void Abstract3DController::setAxisY(QAbstract3DAxis *axis) } } -QAbstract3DAxis *Abstract3DController::axisY() +QAbstract3DAxis *Abstract3DController::axisY() const { return m_axisY; } @@ -531,7 +675,7 @@ void Abstract3DController::setAxisZ(QAbstract3DAxis *axis) } } -QAbstract3DAxis *Abstract3DController::axisZ() +QAbstract3DAxis *Abstract3DController::axisZ() const { return m_axisZ; } @@ -648,19 +792,6 @@ QList<QAbstract3DInputHandler *> Abstract3DController::inputHandlers() const return m_inputHandlers; } -int Abstract3DController::zoomLevel() -{ - return m_scene->activeCamera()->zoomLevel(); -} - -void Abstract3DController::setZoomLevel(int zoomLevel) -{ - m_scene->activeCamera()->setZoomLevel(zoomLevel); - - m_changeTracker.zoomLevelChanged = true; - emitNeedRender(); -} - void Abstract3DController::addTheme(Q3DTheme *theme) { m_themeManager->addTheme(theme); @@ -675,12 +806,13 @@ void Abstract3DController::releaseTheme(Q3DTheme *theme) if (oldTheme != m_themeManager->activeTheme()) emit activeThemeChanged(m_themeManager->activeTheme()); } + QList<Q3DTheme *> Abstract3DController::themes() const { return m_themeManager->themes(); } -void Abstract3DController::setActiveTheme(Q3DTheme *theme) +void Abstract3DController::setActiveTheme(Q3DTheme *theme, bool force) { if (theme != m_themeManager->activeTheme()) { m_themeManager->setActiveTheme(theme); @@ -689,7 +821,7 @@ void Abstract3DController::setActiveTheme(Q3DTheme *theme) Q3DTheme *newActiveTheme = m_themeManager->activeTheme(); // Reset all attached series to the new theme for (int i = 0; i < m_seriesList.size(); i++) - m_seriesList.at(i)->d_ptr->resetToTheme(*newActiveTheme, i, true); + m_seriesList.at(i)->d_ptr->resetToTheme(*newActiveTheme, i, force); markSeriesVisualsDirty(); emit activeThemeChanged(newActiveTheme); } @@ -717,6 +849,12 @@ QAbstract3DGraph::SelectionFlags Abstract3DController::selectionMode() const void Abstract3DController::setShadowQuality(QAbstract3DGraph::ShadowQuality quality) { + if (!m_useOrthoProjection) + doSetShadowQuality(quality); +} + +void Abstract3DController::doSetShadowQuality(QAbstract3DGraph::ShadowQuality quality) +{ if (quality != m_shadowQuality) { m_shadowQuality = quality; m_changeTracker.shadowQualityChanged = true; @@ -730,6 +868,22 @@ QAbstract3DGraph::ShadowQuality Abstract3DController::shadowQuality() const return m_shadowQuality; } +void Abstract3DController::setOptimizationHints(QAbstract3DGraph::OptimizationHints hints) +{ + if (hints != m_optimizationHints) { + m_optimizationHints = hints; + m_changeTracker.optimizationHintChanged = true; + m_isDataDirty = true; + emit optimizationHintsChanged(hints); + emitNeedRender(); + } +} + +QAbstract3DGraph::OptimizationHints Abstract3DController::optimizationHints() const +{ + return m_optimizationHints; +} + bool Abstract3DController::shadowsSupported() const { #if defined(QT_OPENGL_ES_2) @@ -747,7 +901,6 @@ bool Abstract3DController::isSlicingActive() const void Abstract3DController::setSlicingActive(bool isSlicing) { m_scene->setSlicingActive(isSlicing); - emitNeedRender(); } Q3DScene *Abstract3DController::scene() @@ -758,6 +911,8 @@ Q3DScene *Abstract3DController::scene() void Abstract3DController::markDataDirty() { m_isDataDirty = true; + + markSeriesItemLabelsDirty(); emitNeedRender(); } @@ -767,6 +922,79 @@ void Abstract3DController::markSeriesVisualsDirty() emitNeedRender(); } +void Abstract3DController::requestRender(QOpenGLFramebufferObject *fbo) +{ + m_renderer->render(fbo->handle()); +} + +int Abstract3DController::addCustomItem(QCustom3DItem *item) +{ + if (!item) + return -1; + + int index = m_customItems.indexOf(item); + + if (index != -1) + return index; + + item->setParent(this); + connect(item->d_ptr.data(), &QCustom3DItemPrivate::needUpdate, + this, &Abstract3DController::updateCustomItem); + m_customItems.append(item); + item->d_ptr->resetDirtyBits(); + m_isCustomDataDirty = true; + emitNeedRender(); + return m_customItems.count() - 1; +} + +void Abstract3DController::deleteCustomItems() +{ + foreach (QCustom3DItem *item, m_customItems) + delete item; + m_customItems.clear(); + m_isCustomDataDirty = true; + emitNeedRender(); +} + +void Abstract3DController::deleteCustomItem(QCustom3DItem *item) +{ + if (!item) + return; + + m_customItems.removeOne(item); + delete item; + item = 0; + m_isCustomDataDirty = true; + emitNeedRender(); +} + +void Abstract3DController::deleteCustomItem(const QVector3D &position) +{ + // Get the item for the position + foreach (QCustom3DItem *item, m_customItems) { + if (item->position() == position) + deleteCustomItem(item); + } +} + +void Abstract3DController::releaseCustomItem(QCustom3DItem *item) +{ + if (item && m_customItems.contains(item)) { + disconnect(item->d_ptr.data(), &QCustom3DItemPrivate::needUpdate, + this, &Abstract3DController::updateCustomItem); + m_customItems.removeOne(item); + item->setParent(0); + m_isCustomDataDirty = true; + emitNeedRender(); + } +} + +void Abstract3DController::updateCustomItem() +{ + m_isCustomItemDirty = true; + emitNeedRender(); +} + void Abstract3DController::handleAxisTitleChanged(const QString &title) { Q_UNUSED(title) @@ -783,6 +1011,8 @@ void Abstract3DController::handleAxisTitleChangedBySender(QObject *sender) m_changeTracker.axisZTitleChanged = true; else qWarning() << __FUNCTION__ << "invoked for invalid axis"; + + markSeriesItemLabelsDirty(); emitNeedRender(); } @@ -801,6 +1031,8 @@ void Abstract3DController::handleAxisLabelsChangedBySender(QObject *sender) m_changeTracker.axisZLabelsChanged = true; else qWarning() << __FUNCTION__ << "invoked for invalid axis"; + + markSeriesItemLabelsDirty(); emitNeedRender(); } @@ -882,6 +1114,35 @@ void Abstract3DController::handleAxisLabelFormatChanged(const QString &format) handleAxisLabelFormatChangedBySender(sender()); } +void Abstract3DController::handleAxisReversedChanged(bool enable) +{ + Q_UNUSED(enable) + handleAxisReversedChangedBySender(sender()); +} + +void Abstract3DController::handleAxisFormatterDirty() +{ + handleAxisFormatterDirtyBySender(sender()); +} + +void Abstract3DController::handleAxisLabelAutoRotationChanged(float angle) +{ + Q_UNUSED(angle) + handleAxisLabelAutoRotationChangedBySender(sender()); +} + +void Abstract3DController::handleAxisTitleVisibilityChanged(bool visible) +{ + Q_UNUSED(visible) + handleAxisTitleVisibilityChangedBySender(sender()); +} + +void Abstract3DController::handleAxisTitleFixedChanged(bool fixed) +{ + Q_UNUSED(fixed) + handleAxisTitleFixedChangedBySender(sender()); +} + void Abstract3DController::handleInputViewChanged(QAbstract3DInputHandler::InputView view) { // When in automatic slicing mode, input view change to primary disables slice mode @@ -890,15 +1151,12 @@ void Abstract3DController::handleInputViewChanged(QAbstract3DInputHandler::Input setSlicingActive(false); } - m_changeTracker.inputViewChanged = true; emitNeedRender(); } void Abstract3DController::handleInputPositionChanged(const QPoint &position) { Q_UNUSED(position) - - m_changeTracker.inputPositionChanged = true; emitNeedRender(); } @@ -914,6 +1172,21 @@ void Abstract3DController::handleRequestShadowQuality(QAbstract3DGraph::ShadowQu setShadowQuality(quality); } +void Abstract3DController::setMeasureFps(bool enable) +{ + if (m_measureFps != enable) { + m_measureFps = enable; + m_currentFps = 0.0; + + if (enable) { + m_frameTimer.start(); + m_numFrames = -1; + emitNeedRender(); + } + emit measureFpsChanged(enable); + } +} + void Abstract3DController::handleAxisLabelFormatChangedBySender(QObject *sender) { // Label format changing needs to dirty the data so that labels are reset. @@ -932,15 +1205,104 @@ void Abstract3DController::handleAxisLabelFormatChangedBySender(QObject *sender) emitNeedRender(); } +void Abstract3DController::handleAxisReversedChangedBySender(QObject *sender) +{ + // Reversing change needs to dirty the data so item positions are recalculated + if (sender == m_axisX) { + m_isDataDirty = true; + m_changeTracker.axisXReversedChanged = true; + } else if (sender == m_axisY) { + m_isDataDirty = true; + m_changeTracker.axisYReversedChanged = true; + } else if (sender == m_axisZ) { + m_isDataDirty = true; + m_changeTracker.axisZReversedChanged = true; + } else { + qWarning() << __FUNCTION__ << "invoked for invalid axis"; + } + emitNeedRender(); +} + +void Abstract3DController::handleAxisFormatterDirtyBySender(QObject *sender) +{ + // Sender is QValue3DAxisPrivate + QValue3DAxis *valueAxis = static_cast<QValue3DAxisPrivate *>(sender)->qptr(); + if (valueAxis == m_axisX) { + m_isDataDirty = true; + m_changeTracker.axisXFormatterChanged = true; + } else if (valueAxis == m_axisY) { + m_isDataDirty = true; + m_changeTracker.axisYFormatterChanged = true; + } else if (valueAxis == m_axisZ) { + m_isDataDirty = true; + m_changeTracker.axisZFormatterChanged = true; + } else { + qWarning() << __FUNCTION__ << "invoked for invalid axis"; + } + emitNeedRender(); +} + +void Abstract3DController::handleAxisLabelAutoRotationChangedBySender(QObject *sender) +{ + if (sender == m_axisX) + m_changeTracker.axisXLabelAutoRotationChanged = true; + else if (sender == m_axisY) + m_changeTracker.axisYLabelAutoRotationChanged = true; + else if (sender == m_axisZ) + m_changeTracker.axisZLabelAutoRotationChanged = true; + else + qWarning() << __FUNCTION__ << "invoked for invalid axis"; + + emitNeedRender(); +} + +void Abstract3DController::handleAxisTitleVisibilityChangedBySender(QObject *sender) +{ + if (sender == m_axisX) + m_changeTracker.axisXTitleVisibilityChanged = true; + else if (sender == m_axisY) + m_changeTracker.axisYTitleVisibilityChanged = true; + else if (sender == m_axisZ) + m_changeTracker.axisZTitleVisibilityChanged = true; + else + qWarning() << __FUNCTION__ << "invoked for invalid axis"; + + emitNeedRender(); +} + +void Abstract3DController::handleAxisTitleFixedChangedBySender(QObject *sender) +{ + if (sender == m_axisX) + m_changeTracker.axisXTitleFixedChanged = true; + else if (sender == m_axisY) + m_changeTracker.axisYTitleFixedChanged = true; + else if (sender == m_axisZ) + m_changeTracker.axisZTitleFixedChanged = true; + else + qWarning() << __FUNCTION__ << "invoked for invalid axis"; + + emitNeedRender(); +} + void Abstract3DController::handleSeriesVisibilityChangedBySender(QObject *sender) { - Q_UNUSED(sender) + QAbstract3DSeries *series = static_cast<QAbstract3DSeries *>(sender); + series->d_ptr->m_changeTracker.visibilityChanged = true; m_isDataDirty = true; - m_isSeriesVisibilityDirty = true; + m_isSeriesVisualsDirty = true; + + adjustAxisRanges(); + emitNeedRender(); } +void Abstract3DController::markSeriesItemLabelsDirty() +{ + for (int i = 0; i < m_seriesList.size(); i++) + m_seriesList.at(i)->d_ptr->markItemLabelDirty(); +} + void Abstract3DController::setAxisHelper(QAbstract3DAxis::AxisOrientation orientation, QAbstract3DAxis *axis, QAbstract3DAxis **axisPtr) { @@ -978,6 +1340,12 @@ void Abstract3DController::setAxisHelper(QAbstract3DAxis::AxisOrientation orient this, &Abstract3DController::handleAxisRangeChanged); QObject::connect(axis, &QAbstract3DAxis::autoAdjustRangeChanged, this, &Abstract3DController::handleAxisAutoAdjustRangeChanged); + QObject::connect(axis, &QAbstract3DAxis::labelAutoRotationChanged, + this, &Abstract3DController::handleAxisLabelAutoRotationChanged); + QObject::connect(axis, &QAbstract3DAxis::titleVisibilityChanged, + this, &Abstract3DController::handleAxisTitleVisibilityChanged); + QObject::connect(axis, &QAbstract3DAxis::titleFixedChanged, + this, &Abstract3DController::handleAxisTitleFixedChanged); if (orientation == QAbstract3DAxis::AxisOrientationX) m_changeTracker.axisXTypeChanged = true; @@ -991,6 +1359,9 @@ void Abstract3DController::setAxisHelper(QAbstract3DAxis::AxisOrientation orient handleAxisRangeChangedBySender(axis); handleAxisAutoAdjustRangeChangedInOrientation(axis->orientation(), axis->isAutoAdjustRange()); + handleAxisLabelAutoRotationChangedBySender(axis); + handleAxisTitleVisibilityChangedBySender(axis); + handleAxisTitleFixedChangedBySender(axis); if (axis->type() & QAbstract3DAxis::AxisTypeValue) { QValue3DAxis *valueAxis = static_cast<QValue3DAxis *>(axis); @@ -1000,14 +1371,21 @@ void Abstract3DController::setAxisHelper(QAbstract3DAxis::AxisOrientation orient this, &Abstract3DController::handleAxisSubSegmentCountChanged); QObject::connect(valueAxis, &QValue3DAxis::labelFormatChanged, this, &Abstract3DController::handleAxisLabelFormatChanged); + QObject::connect(valueAxis, &QValue3DAxis::reversedChanged, + this, &Abstract3DController::handleAxisReversedChanged); + QObject::connect(valueAxis->dptr(), &QValue3DAxisPrivate::formatterDirty, + this, &Abstract3DController::handleAxisFormatterDirty); handleAxisSegmentCountChangedBySender(valueAxis); handleAxisSubSegmentCountChangedBySender(valueAxis); handleAxisLabelFormatChangedBySender(valueAxis); + handleAxisReversedChangedBySender(valueAxis); + handleAxisFormatterDirtyBySender(valueAxis->dptr()); } } -QAbstract3DAxis *Abstract3DController::createDefaultAxis(QAbstract3DAxis::AxisOrientation orientation) +QAbstract3DAxis *Abstract3DController::createDefaultAxis( + QAbstract3DAxis::AxisOrientation orientation) { Q_UNUSED(orientation) @@ -1047,4 +1425,97 @@ void Abstract3DController::emitNeedRender() } } +void Abstract3DController::handlePendingClick() +{ + QAbstract3DGraph::ElementType type = m_renderer->clickedType(); + emit elementSelected(type); +} + +int Abstract3DController::selectedLabelIndex() const +{ + int index = m_renderer->m_selectedLabelIndex; + QAbstract3DAxis *axis = selectedAxis(); + if (axis && axis->labels().count() <= index) + index = -1; + return index; +} + +QAbstract3DAxis *Abstract3DController::selectedAxis() const +{ + QAbstract3DAxis *axis = 0; + QAbstract3DGraph::ElementType type = m_renderer->clickedType(); + switch (type) { + case QAbstract3DGraph::ElementAxisXLabel: + axis = axisX(); + break; + case QAbstract3DGraph::ElementAxisYLabel: + axis = axisY(); + break; + case QAbstract3DGraph::ElementAxisZLabel: + axis = axisZ(); + break; + default: + axis = 0; + break; + } + + return axis; +} + +int Abstract3DController::selectedCustomItemIndex() const +{ + int index = m_renderer->m_selectedCustomItemIndex; + if (m_customItems.count() <= index) + index = -1; + return index; +} + +QCustom3DItem *Abstract3DController::selectedCustomItem() const +{ + QCustom3DItem *item = 0; + int index = selectedCustomItemIndex(); + if (index >= 0) + item = m_customItems[index]; + return item; +} + +QAbstract3DGraph::ElementType Abstract3DController::selectedElement() const +{ + return m_renderer->clickedType(); +} + +void Abstract3DController::setOrthoProjection(bool enable) +{ + if (enable != m_useOrthoProjection) { + m_useOrthoProjection = enable; + m_changeTracker.projectionChanged = true; + emit orthoProjectionChanged(m_useOrthoProjection); + // If changed to ortho, disable shadows + if (m_useOrthoProjection) + doSetShadowQuality(QAbstract3DGraph::ShadowQualityNone); + emitNeedRender(); + } +} + +bool Abstract3DController::isOrthoProjection() const +{ + return m_useOrthoProjection; +} + +void Abstract3DController::setAspectRatio(float ratio) +{ + if (m_aspectRatio != ratio) { + m_aspectRatio = ratio; + m_changeTracker.aspectRatioChanged = true; + emit aspectRatioChanged(m_aspectRatio); + m_isDataDirty = true; + emitNeedRender(); + } +} + +float Abstract3DController::aspectRatio() +{ + return m_aspectRatio; +} + QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/engine/abstract3dcontroller_p.h b/src/datavisualization/engine/abstract3dcontroller_p.h index 06be450e..0e4d1add 100644 --- a/src/datavisualization/engine/abstract3dcontroller_p.h +++ b/src/datavisualization/engine/abstract3dcontroller_p.h @@ -33,25 +33,25 @@ #include "qabstract3daxis.h" #include "drawer_p.h" #include "qabstract3dinputhandler.h" -#include "qabstractdataproxy.h" +#include "qabstract3dgraph.h" #include "q3dscene_p.h" +#include "qcustom3ditem.h" #include <QtGui/QLinearGradient> +#include <QtCore/QTime> -class QFont; +class QOpenGLFramebufferObject; QT_BEGIN_NAMESPACE_DATAVISUALIZATION -class CameraHelper; class Abstract3DRenderer; class QAbstract3DSeries; class ThemeManager; struct Abstract3DChangeBitField { - bool zoomLevelChanged : 1; bool themeChanged : 1; bool shadowQualityChanged : 1; bool selectionModeChanged : 1; - bool objFileChanged : 1; + bool optimizationHintChanged : 1; bool axisXTypeChanged : 1; bool axisYTypeChanged : 1; bool axisZTypeChanged : 1; @@ -73,15 +73,29 @@ struct Abstract3DChangeBitField { bool axisXLabelFormatChanged : 1; bool axisYLabelFormatChanged : 1; bool axisZLabelFormatChanged : 1; - bool inputViewChanged : 1; - bool inputPositionChanged : 1; + bool axisXReversedChanged : 1; + bool axisYReversedChanged : 1; + bool axisZReversedChanged : 1; + bool axisXFormatterChanged : 1; + bool axisYFormatterChanged : 1; + bool axisZFormatterChanged : 1; + bool projectionChanged : 1; + bool axisXLabelAutoRotationChanged : 1; + bool axisYLabelAutoRotationChanged : 1; + bool axisZLabelAutoRotationChanged : 1; + bool aspectRatioChanged : 1; + bool axisXTitleVisibilityChanged : 1; + bool axisYTitleVisibilityChanged : 1; + bool axisZTitleVisibilityChanged : 1; + bool axisXTitleFixedChanged : 1; + bool axisYTitleFixedChanged : 1; + bool axisZTitleFixedChanged : 1; Abstract3DChangeBitField() : - zoomLevelChanged(true), themeChanged(true), shadowQualityChanged(true), selectionModeChanged(true), - objFileChanged(true), + optimizationHintChanged(true), axisXTypeChanged(true), axisYTypeChanged(true), axisZTypeChanged(true), @@ -102,7 +116,24 @@ struct Abstract3DChangeBitField { axisZSubSegmentCountChanged(true), axisXLabelFormatChanged(true), axisYLabelFormatChanged(true), - axisZLabelFormatChanged(true) + axisZLabelFormatChanged(true), + axisXReversedChanged(true), + axisYReversedChanged(true), + axisZReversedChanged(true), + axisXFormatterChanged(true), + axisYFormatterChanged(true), + axisZFormatterChanged(true), + projectionChanged(true), + axisXLabelAutoRotationChanged(true), + axisYLabelAutoRotationChanged(true), + axisZLabelAutoRotationChanged(true), + aspectRatioChanged(true), + axisXTitleVisibilityChanged(true), + axisYTitleVisibilityChanged(true), + axisZTitleVisibilityChanged(true), + axisXTitleFixedChanged(true), + axisYTitleFixedChanged(true), + axisZTitleFixedChanged(true) { } }; @@ -119,28 +150,19 @@ public: SelectionColumn }; - enum MouseState { - MouseNone = 0, - MouseOnScene, - MouseOnOverview, - MouseOnZoom, - MouseRotating, - MouseOnPinch - }; - private: Abstract3DChangeBitField m_changeTracker; - GLfloat m_horizontalRotation; - GLfloat m_verticalRotation; ThemeManager *m_themeManager; QAbstract3DGraph::SelectionFlags m_selectionMode; QAbstract3DGraph::ShadowQuality m_shadowQuality; + bool m_useOrthoProjection; + float m_aspectRatio; + QAbstract3DGraph::OptimizationHints m_optimizationHints; protected: Q3DScene *m_scene; QList<QAbstract3DInputHandler *> m_inputHandlers; // List of all added input handlers QAbstract3DInputHandler *m_activeInputHandler; - CameraHelper *m_cameraHelper; // Active axes QAbstract3DAxis *m_axisX; QAbstract3DAxis *m_axisY; @@ -149,12 +171,22 @@ protected: QList<QAbstract3DAxis *> m_axes; // List of all added axes Abstract3DRenderer *m_renderer; bool m_isDataDirty; - bool m_isSeriesVisibilityDirty; + bool m_isCustomDataDirty; + bool m_isCustomItemDirty; bool m_isSeriesVisualsDirty; bool m_renderPending; QList<QAbstract3DSeries *> m_seriesList; + bool m_measureFps; + QTime m_frameTimer; + int m_numFrames; + qreal m_currentFps; + + QVector<QAbstract3DSeries *> m_changedSeriesList; + + QList<QCustom3DItem *> m_customItems; + explicit Abstract3DController(QRect initialViewport, Q3DScene *scene, QObject *parent = 0); public: @@ -173,11 +205,11 @@ public: QList<QAbstract3DSeries *> seriesList(); virtual void setAxisX(QAbstract3DAxis *axis); - virtual QAbstract3DAxis *axisX(); + virtual QAbstract3DAxis *axisX() const; virtual void setAxisY(QAbstract3DAxis *axis); - virtual QAbstract3DAxis *axisY(); + virtual QAbstract3DAxis *axisY() const; virtual void setAxisZ(QAbstract3DAxis *axis); - virtual QAbstract3DAxis *axisZ(); + virtual QAbstract3DAxis *axisZ() const; virtual void addAxis(QAbstract3DAxis *axis); virtual void releaseAxis(QAbstract3DAxis *axis); virtual QList<QAbstract3DAxis *> axes() const; // Omits default axes @@ -188,12 +220,9 @@ public: virtual QAbstract3DInputHandler *activeInputHandler(); virtual QList<QAbstract3DInputHandler *> inputHandlers() const; - virtual int zoomLevel(); - virtual void setZoomLevel(int zoomLevel); - virtual void addTheme(Q3DTheme *theme); virtual void releaseTheme(Q3DTheme *theme); - virtual void setActiveTheme(Q3DTheme *theme); + virtual void setActiveTheme(Q3DTheme *theme, bool force = true); virtual Q3DTheme *activeTheme() const; virtual QList<Q3DTheme *> themes() const; @@ -201,9 +230,13 @@ public: virtual QAbstract3DGraph::SelectionFlags selectionMode() const; virtual void setShadowQuality(QAbstract3DGraph::ShadowQuality quality); + virtual void doSetShadowQuality(QAbstract3DGraph::ShadowQuality quality); virtual QAbstract3DGraph::ShadowQuality shadowQuality() const; virtual bool shadowsSupported() const; + void setOptimizationHints(QAbstract3DGraph::OptimizationHints hints); + QAbstract3DGraph::OptimizationHints optimizationHints() const; + bool isSlicingActive() const; void setSlicingActive(bool isSlicing); @@ -212,6 +245,22 @@ public: void markDataDirty(); void markSeriesVisualsDirty(); + void requestRender(QOpenGLFramebufferObject *fbo); + + int addCustomItem(QCustom3DItem *item); + void deleteCustomItems(); + void deleteCustomItem(QCustom3DItem *item); + void deleteCustomItem(const QVector3D &position); + void releaseCustomItem(QCustom3DItem *item); + + int selectedLabelIndex() const; + QAbstract3DAxis *selectedAxis() const; + int selectedCustomItemIndex() const; + QCustom3DItem *selectedCustomItem() const; + + void setOrthoProjection(bool enable); + bool isOrthoProjection() const; + void emitNeedRender(); virtual void clearSelection() = 0; @@ -231,8 +280,16 @@ public: virtual void handleAxisAutoAdjustRangeChangedInOrientation( QAbstract3DAxis::AxisOrientation orientation, bool autoAdjust) = 0; virtual void handleAxisLabelFormatChangedBySender(QObject *sender); + virtual void handleAxisReversedChangedBySender(QObject *sender); + virtual void handleAxisFormatterDirtyBySender(QObject *sender); + virtual void handleAxisLabelAutoRotationChangedBySender(QObject *sender); + virtual void handleAxisTitleVisibilityChangedBySender(QObject *sender); + virtual void handleAxisTitleFixedChangedBySender(QObject *sender); virtual void handleSeriesVisibilityChangedBySender(QObject *sender); virtual void handlePendingClick() = 0; + virtual void adjustAxisRanges() = 0; + + void markSeriesItemLabelsDirty(); public slots: void handleAxisTitleChanged(const QString &title); @@ -242,6 +299,11 @@ public slots: void handleAxisSubSegmentCountChanged(int count); void handleAxisAutoAdjustRangeChanged(bool autoAdjust); void handleAxisLabelFormatChanged(const QString &format); + void handleAxisReversedChanged(bool enable); + void handleAxisFormatterDirty(); + void handleAxisLabelAutoRotationChanged(float angle); + void handleAxisTitleVisibilityChanged(bool visible); + void handleAxisTitleFixedChanged(bool fixed); void handleInputViewChanged(QAbstract3DInputHandler::InputView view); void handleInputPositionChanged(const QPoint &position); void handleSeriesVisibilityChanged(bool visible); @@ -258,6 +320,17 @@ 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); @@ -267,6 +340,12 @@ signals: void axisXChanged(QAbstract3DAxis *axis); void axisYChanged(QAbstract3DAxis *axis); void axisZChanged(QAbstract3DAxis *axis); + void elementSelected(QAbstract3DGraph::ElementType type); + void measureFpsChanged(bool enabled); + void currentFpsChanged(qreal fps); + void orthoProjectionChanged(bool enabled); + void aspectRatioChanged(float ratio); + void optimizationHintsChanged(QAbstract3DGraph::OptimizationHints hints); protected: virtual QAbstract3DAxis *createDefaultAxis(QAbstract3DAxis::AxisOrientation orientation); @@ -278,7 +357,9 @@ private: void setAxisHelper(QAbstract3DAxis::AxisOrientation orientation, QAbstract3DAxis *axis, QAbstract3DAxis **axisPtr); + friend class AbstractDeclarative; friend class Bars3DController; + friend class QAbstract3DGraphPrivate; }; QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/engine/abstract3drenderer.cpp b/src/datavisualization/engine/abstract3drenderer.cpp index 3122cf76..5d97a6ca 100644 --- a/src/datavisualization/engine/abstract3drenderer.cpp +++ b/src/datavisualization/engine/abstract3drenderer.cpp @@ -17,15 +17,13 @@ ****************************************************************************/ #include "abstract3drenderer_p.h" -#include "qvalue3daxis.h" #include "texturehelper_p.h" -#include "utils_p.h" -#include "q3dscene_p.h" #include "q3dcamera_p.h" -#include "q3dlight_p.h" -#include "qabstract3dseries_p.h" #include "q3dtheme_p.h" -#include "objecthelper_p.h" +#include "qvalue3daxisformatter_p.h" +#include "shaderhelper_p.h" +#include "qcustom3ditem_p.h" +#include "qcustom3dlabel_p.h" QT_BEGIN_NAMESPACE_DATAVISUALIZATION @@ -37,6 +35,7 @@ Abstract3DRenderer::Abstract3DRenderer(Abstract3DController *controller) m_cachedShadowQuality(QAbstract3DGraph::ShadowQualityMedium), m_autoScaleAdjustment(1.0f), m_cachedSelectionMode(QAbstract3DGraph::SelectionNone), + m_cachedOptimizationHint(QAbstract3DGraph::OptimizationDefault), m_textureHelper(0), m_cachedScene(new Q3DScene()), m_selectionDirty(true), @@ -45,12 +44,18 @@ Abstract3DRenderer::Abstract3DRenderer(Abstract3DController *controller) m_selectionLabelDirty(true), m_clickPending(false), m_clickedSeries(0), - m_selectionLabelItem(0) -#ifdef DISPLAY_RENDER_SPEED - , m_isFirstFrame(true), - m_numFrames(0) -#endif - + m_clickedType(QAbstract3DGraph::ElementNone), + m_selectionLabelItem(0), + m_visibleSeriesCount(0), + m_customItemShader(0), + m_useOrthoProjection(false), + m_xFlipped(false), + m_yFlipped(false), + m_zFlipped(false), + m_backgroundObj(0), + m_gridLineObj(0), + m_labelObj(0), + m_graphAspectRatio(2.0f) { QObject::connect(m_drawer, &Drawer::drawerChanged, this, &Abstract3DRenderer::updateTextures); QObject::connect(this, &Abstract3DRenderer::needRender, controller, @@ -61,14 +66,30 @@ Abstract3DRenderer::Abstract3DRenderer(Abstract3DController *controller) Abstract3DRenderer::~Abstract3DRenderer() { - for (int i = 0; i < m_visibleSeriesList.size(); i++) - m_visibleSeriesList[i].cleanup(m_textureHelper); - delete m_drawer; - delete m_textureHelper; delete m_cachedScene; delete m_cachedTheme; delete m_selectionLabelItem; + delete m_customItemShader; + + foreach (SeriesRenderCache *cache, m_renderCacheList) { + cache->cleanup(m_textureHelper); + delete cache; + } + m_renderCacheList.clear(); + + foreach (CustomRenderItem *item, m_customRenderCache) { + GLuint texture = item->texture(); + m_textureHelper->deleteTexture(&texture); + delete item; + } + m_customRenderCache.clear(); + + ObjectHelper::releaseObjectHelper(this, m_backgroundObj); + ObjectHelper::releaseObjectHelper(this, m_gridLineObj); + ObjectHelper::releaseObjectHelper(this, m_labelObj); + + delete m_textureHelper; } void Abstract3DRenderer::initializeOpenGL() @@ -95,23 +116,6 @@ void Abstract3DRenderer::initializeOpenGL() void Abstract3DRenderer::render(const GLuint defaultFboHandle) { -#ifdef DISPLAY_RENDER_SPEED - // For speed computation - if (m_isFirstFrame) { - m_lastFrameTime.start(); - m_isFirstFrame = false; - } - - // Measure speed (as milliseconds per frame) - m_numFrames++; - if (m_lastFrameTime.elapsed() >= 1000) { // print only if last measurement was more than 1s ago - qDebug() << float(m_lastFrameTime.elapsed()) / float(m_numFrames) << "ms/frame (=" - << float(m_numFrames) << "fps)"; - m_numFrames = 0; - m_lastFrameTime.restart(); - } -#endif - if (defaultFboHandle) { glDepthMask(true); glEnable(GL_DEPTH_TEST); @@ -131,20 +135,12 @@ void Abstract3DRenderer::render(const GLuint defaultFboHandle) m_viewport.width(), m_viewport.height()); glEnable(GL_SCISSOR_TEST); - QVector3D clearColor = Utils::vectorFromColor(m_cachedTheme->windowColor()); + 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); glDisable(GL_SCISSOR_TEST); } -QString Abstract3DRenderer::generateValueLabel(const QString &format, float value) -{ - QString valueLabelFormat = format; - Utils::ParamType valueParamType = Utils::findFormatParamType(valueLabelFormat); - QByteArray valueFormatArray = valueLabelFormat.toUtf8(); - return Utils::formatLabel(valueFormatArray, valueParamType, value); -} - void Abstract3DRenderer::updateSelectionState(SelectionState state) { m_selectionState = state; @@ -163,6 +159,15 @@ void Abstract3DRenderer::initGradientShaders(const QString &vertexShader, Q_UNUSED(fragmentShader) } +void Abstract3DRenderer::initCustomItemShaders(const QString &vertexShader, + const QString &fragmentShader) +{ + if (m_customItemShader) + delete m_customItemShader; + m_customItemShader = new ShaderHelper(this, vertexShader, fragmentShader); + m_customItemShader->initialize(); +} + void Abstract3DRenderer::updateTheme(Q3DTheme *theme) { // Synchronize the controller theme with renderer @@ -229,6 +234,8 @@ void Abstract3DRenderer::reInitShaders() 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")); @@ -236,6 +243,8 @@ void Abstract3DRenderer::reInitShaders() QStringLiteral(":/shaders/fragment")); initBackgroundShaders(QStringLiteral(":/shaders/vertex"), QStringLiteral(":/shaders/fragment")); + initCustomItemShaders(QStringLiteral(":/shaders/vertexTexture"), + QStringLiteral(":/shaders/fragmentTexture")); } #else initGradientShaders(QStringLiteral(":/shaders/vertex"), @@ -244,6 +253,8 @@ void Abstract3DRenderer::reInitShaders() QStringLiteral(":/shaders/fragmentES2")); initBackgroundShaders(QStringLiteral(":/shaders/vertex"), QStringLiteral(":/shaders/fragmentES2")); + initCustomItemShaders(QStringLiteral(":/shaders/vertexTexture"), + QStringLiteral(":/shaders/fragmentTextureES2")); #endif } @@ -266,18 +277,28 @@ void Abstract3DRenderer::updateSelectionMode(QAbstract3DGraph::SelectionFlags mo m_selectionDirty = true; } +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::updateOptimizationHint(QAbstract3DGraph::OptimizationHints hint) +{ + m_cachedOptimizationHint = hint; +} + void Abstract3DRenderer::handleResize() { if (m_primarySubViewport.width() == 0 || m_primarySubViewport.height() == 0) return; - // Calculate zoom level based on aspect ratio - GLfloat div; - GLfloat zoomAdjustment; - div = qMin(m_primarySubViewport.width(), m_primarySubViewport.height()); - zoomAdjustment = defaultRatio * ((m_primarySubViewport.width() / div) - / (m_primarySubViewport.height() / div)); - m_autoScaleAdjustment = qMin(zoomAdjustment, 1.0f); // clamp to 1.0f + // Recalculate zoom + calculateZoomLevel(); // Re-init selection buffer initSelectionBuffer(); @@ -288,6 +309,18 @@ void Abstract3DRenderer::handleResize() #endif } +void Abstract3DRenderer::calculateZoomLevel() +{ + // Calculate zoom level based on aspect ratio + GLfloat div; + GLfloat zoomAdjustment; + div = qMin(m_primarySubViewport.width(), m_primarySubViewport.height()); + zoomAdjustment = 2.0f * defaultRatio + * ((m_primarySubViewport.width() / div) + / (m_primarySubViewport.height() / div)) / m_graphAspectRatio; + m_autoScaleAdjustment = qMin(zoomAdjustment, 1.0f); // clamp to 1.0f +} + void Abstract3DRenderer::updateAxisType(QAbstract3DAxis::AxisOrientation orientation, QAbstract3DAxis::AxisType type) { @@ -312,18 +345,25 @@ void Abstract3DRenderer::updateAxisRange(QAbstract3DAxis::AxisOrientation orient AxisRenderCache &cache = axisCacheForOrientation(orientation); cache.setMin(min); cache.setMax(max); + + foreach (SeriesRenderCache *cache, m_renderCacheList) + cache->setDataDirty(true); + + updateCustomItemPositions(); } void Abstract3DRenderer::updateAxisSegmentCount(QAbstract3DAxis::AxisOrientation orientation, int count) { - axisCacheForOrientation(orientation).setSegmentCount(count); + AxisRenderCache &cache = axisCacheForOrientation(orientation); + cache.setSegmentCount(count); } void Abstract3DRenderer::updateAxisSubSegmentCount(QAbstract3DAxis::AxisOrientation orientation, int count) { - axisCacheForOrientation(orientation).setSubSegmentCount(count); + AxisRenderCache &cache = axisCacheForOrientation(orientation); + cache.setSubSegmentCount(count); } void Abstract3DRenderer::updateAxisLabelFormat(QAbstract3DAxis::AxisOrientation orientation, @@ -332,6 +372,67 @@ void Abstract3DRenderer::updateAxisLabelFormat(QAbstract3DAxis::AxisOrientation axisCacheForOrientation(orientation).setLabelFormat(format); } +void Abstract3DRenderer::updateAxisReversed(QAbstract3DAxis::AxisOrientation orientation, + bool enable) +{ + axisCacheForOrientation(orientation).setReversed(enable); + foreach (SeriesRenderCache *cache, m_renderCacheList) + cache->setDataDirty(true); + + updateCustomItemPositions(); +} + +void Abstract3DRenderer::updateAxisFormatter(QAbstract3DAxis::AxisOrientation orientation, + QValue3DAxisFormatter *formatter) +{ + AxisRenderCache &cache = axisCacheForOrientation(orientation); + if (cache.ctrlFormatter() != formatter) { + delete cache.formatter(); + cache.setFormatter(formatter->createNewInstance()); + cache.setCtrlFormatter(formatter); + } + formatter->d_ptr->populateCopy(*(cache.formatter())); + cache.markPositionsDirty(); + + foreach (SeriesRenderCache *cache, m_renderCacheList) + cache->setDataDirty(true); + + updateCustomItemPositions(); +} + +void Abstract3DRenderer::updateAxisLabelAutoRotation(QAbstract3DAxis::AxisOrientation orientation, + float angle) +{ + AxisRenderCache &cache = axisCacheForOrientation(orientation); + if (cache.labelAutoRotation() != angle) + cache.setLabelAutoRotation(angle); +} + +void Abstract3DRenderer::updateAxisTitleVisibility(QAbstract3DAxis::AxisOrientation orientation, + bool visible) +{ + AxisRenderCache &cache = axisCacheForOrientation(orientation); + if (cache.isTitleVisible() != visible) + cache.setTitleVisible(visible); +} + +void Abstract3DRenderer::updateAxisTitleFixed(QAbstract3DAxis::AxisOrientation orientation, + bool fixed) +{ + AxisRenderCache &cache = axisCacheForOrientation(orientation); + if (cache.isTitleFixed() != fixed) + cache.setTitleFixed(fixed); +} + +void Abstract3DRenderer::modifiedSeriesList(const QVector<QAbstract3DSeries *> &seriesList) +{ + foreach (QAbstract3DSeries *series, seriesList) { + SeriesRenderCache *cache = m_renderCacheList.value(series, 0); + if (cache) + cache->setDataDirty(true); + } +} + void Abstract3DRenderer::fixMeshFileName(QString &fileName, QAbstract3DSeries::Mesh mesh) { // Default implementation does nothing. @@ -339,41 +440,84 @@ void Abstract3DRenderer::fixMeshFileName(QString &fileName, QAbstract3DSeries::M Q_UNUSED(mesh) } -void Abstract3DRenderer::updateSeries(const QList<QAbstract3DSeries *> &seriesList, - bool updateVisibility) +void Abstract3DRenderer::updateSeries(const QList<QAbstract3DSeries *> &seriesList) { - int visibleCount = 0; - if (updateVisibility) { - int oldSize = m_visibleSeriesList.size(); - foreach (QAbstract3DSeries *current, seriesList) { - if (current->isVisible()) - visibleCount++; - } + foreach (SeriesRenderCache *cache, m_renderCacheList) + cache->setValid(false); - // Clean up series caches that are about to be permanently deleted. - // Can't just use cache destructor, as resize will call that to all items. - if (visibleCount < oldSize) { - for (int i = visibleCount; i < oldSize; i++) - m_visibleSeriesList[i].cleanup(m_textureHelper); + m_visibleSeriesCount = 0; + int seriesCount = seriesList.size(); + for (int i = 0; i < seriesCount; i++) { + QAbstract3DSeries *series = seriesList.at(i); + SeriesRenderCache *cache = m_renderCacheList.value(series); + bool newSeries = false; + if (!cache) { + cache = createNewCache(series); + m_renderCacheList[series] = cache; + newSeries = true; } + cache->setValid(true); + cache->populate(newSeries); + if (cache->isVisible()) + m_visibleSeriesCount++; + } + + // Remove non-valid objects from the cache list + foreach (SeriesRenderCache *cache, m_renderCacheList) { + if (!cache->isValid()) + cleanCache(cache); + } +} + +void Abstract3DRenderer::updateCustomData(const QList<QCustom3DItem *> &customItems) +{ + if (customItems.isEmpty() && m_customRenderCache.isEmpty()) + return; - if (visibleCount != oldSize) - m_visibleSeriesList.resize(visibleCount); + foreach (CustomRenderItem *item, m_customRenderCache) + item->setValid(false); - visibleCount = 0; + int itemCount = customItems.size(); + // Check custom item list for items that are not yet in render item cache + for (int i = 0; i < itemCount; i++) { + QCustom3DItem *item = customItems.at(i); + CustomRenderItem *renderItem = m_customRenderCache.value(item); + if (!renderItem) + renderItem = addCustomItem(item); + renderItem->setValid(true); + renderItem->setIndex(i); // always update index, as it must match the custom item index } - foreach (QAbstract3DSeries *current, seriesList) { - if (current->isVisible()) { - // Item selection label may need update - if (current->d_ptr->m_changeTracker.nameChanged - || current->d_ptr->m_changeTracker.itemLabelFormatChanged) { - m_selectionLabelDirty = true; - } - m_visibleSeriesList[visibleCount++].populate(current, this); + + // Check render item cache and remove items that are not in customItems list anymore + foreach (CustomRenderItem *renderItem, m_customRenderCache) { + if (!renderItem->isValid()) { + m_customRenderCache.remove(renderItem->itemPointer()); + GLuint texture = renderItem->texture(); + m_textureHelper->deleteTexture(&texture); + delete renderItem; } } } +void Abstract3DRenderer::updateCustomItems() +{ + // Check all items + foreach (CustomRenderItem *item, m_customRenderCache) + updateCustomItem(item); +} + +SeriesRenderCache *Abstract3DRenderer::createNewCache(QAbstract3DSeries *series) +{ + return new SeriesRenderCache(series, this); +} + +void Abstract3DRenderer::cleanCache(SeriesRenderCache *cache) +{ + m_renderCacheList.remove(cache->series()); + cache->cleanup(m_textureHelper); + delete cache; +} + AxisRenderCache &Abstract3DRenderer::axisCacheForOrientation( QAbstract3DAxis::AxisOrientation orientation) { @@ -428,26 +572,243 @@ void Abstract3DRenderer::lowerShadowQuality() updateShadowQuality(newQuality); } -void Abstract3DRenderer::generateBaseColorTexture(const QColor &color, GLuint *texture) +void Abstract3DRenderer::drawAxisTitleY(const QVector3D &sideLabelRotation, + const QVector3D &backLabelRotation, + const QVector3D &sideLabelTrans, + const QVector3D &backLabelTrans, + const QQuaternion &totalSideRotation, + const QQuaternion &totalBackRotation, + AbstractRenderItem &dummyItem, + const Q3DCamera *activeCamera, + float labelsMaxWidth, + const QMatrix4x4 &viewMatrix, + const QMatrix4x4 &projectionMatrix, + ShaderHelper *shader) { - if (*texture) { - m_textureHelper->deleteTexture(texture); - *texture = 0; + float scaleFactor = m_drawer->scaledFontSize() / m_axisCacheY.titleItem().size().height(); + float titleOffset = 2.0f * (labelMargin + (labelsMaxWidth * scaleFactor)); + float yRotation; + QVector3D titleTrans; + QQuaternion totalRotation; + if (m_xFlipped == m_zFlipped) { + yRotation = backLabelRotation.y(); + titleTrans = backLabelTrans; + totalRotation = totalBackRotation; + } else { + yRotation = sideLabelRotation.y(); + titleTrans = sideLabelTrans; + totalRotation = totalSideRotation; + } + + QQuaternion offsetRotator = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, yRotation); + QVector3D titleOffsetVector = + offsetRotator.rotatedVector(QVector3D(-titleOffset, 0.0f, 0.0f)); + + 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); + } else { + titleRotation = totalRotation + * QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, 90.0f); } + dummyItem.setTranslation(titleTrans + titleOffsetVector); + + m_drawer->drawLabel(dummyItem, m_axisCacheY.titleItem(), viewMatrix, + projectionMatrix, zeroVector, titleRotation, 0, + m_cachedSelectionMode, shader, m_labelObj, activeCamera, + true, true, Drawer::LabelMid, Qt::AlignBottom); +} +void Abstract3DRenderer::drawAxisTitleX(const QVector3D &labelRotation, + const QVector3D &labelTrans, + const QQuaternion &totalRotation, + AbstractRenderItem &dummyItem, + const Q3DCamera *activeCamera, + float labelsMaxWidth, + const QMatrix4x4 &viewMatrix, + const QMatrix4x4 &projectionMatrix, + ShaderHelper *shader) +{ + float scaleFactor = m_drawer->scaledFontSize() / m_axisCacheX.titleItem().size().height(); + float 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) { + alignment = Qt::AlignBottom; + if (m_zFlipped) { + zRotation = 180.0f; + titleOffset = -titleOffset; + if (m_xFlipped) { + offsetRotation = -offsetRotation; + extraRotation = -extraRotation; + } else { + xRotation = -90.0f - labelRotation.z(); + } + } else { + zRotation = 180.0f; + yRotation = 180.0f; + if (m_xFlipped) { + offsetRotation = -offsetRotation; + xRotation = -90.0f - labelRotation.z(); + } else { + extraRotation = -extraRotation; + } + } + } else { + if (m_zFlipped) { + titleOffset = -titleOffset; + if (m_xFlipped) { + yRotation = 180.0f; + offsetRotation = -offsetRotation; + } else { + yRotation = 180.0f; + xRotation = -90.0f - labelRotation.z(); + extraRotation = -extraRotation; + } + } else { + if (m_xFlipped) { + offsetRotation = -offsetRotation; + xRotation = -90.0f - labelRotation.z(); + extraRotation = -extraRotation; + } + } + } + + if (offsetRotation == 180.0f || offsetRotation == -180.0f) + offsetRotation = 0.0f; + QQuaternion offsetRotator = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, offsetRotation); + QVector3D titleOffsetVector = + offsetRotator.rotatedVector(QVector3D(0.0f, 0.0f, titleOffset)); + + QQuaternion titleRotation; + if (m_axisCacheX.isTitleFixed()) { + titleRotation = QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, zRotation) + * QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, yRotation) + * QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, xRotation); + } else { + titleRotation = totalRotation + * QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, extraRotation); + } + dummyItem.setTranslation(labelTrans + titleOffsetVector); + + m_drawer->drawLabel(dummyItem, m_axisCacheX.titleItem(), viewMatrix, + projectionMatrix, zeroVector, titleRotation, 0, + m_cachedSelectionMode, shader, m_labelObj, activeCamera, + true, true, Drawer::LabelMid, alignment); +} + +void Abstract3DRenderer::drawAxisTitleZ(const QVector3D &labelRotation, + const QVector3D &labelTrans, + const QQuaternion &totalRotation, + AbstractRenderItem &dummyItem, + const Q3DCamera *activeCamera, + float labelsMaxWidth, + const QMatrix4x4 &viewMatrix, + const QMatrix4x4 &projectionMatrix, + ShaderHelper *shader) +{ + float scaleFactor = m_drawer->scaledFontSize() / m_axisCacheZ.titleItem().size().height(); + float titleOffset = 2.0f * (labelMargin + (labelsMaxWidth * scaleFactor)); + float zRotation = labelRotation.z(); + float yRotation = -90.0f; + float xRotation = -90.0f; + float extraRotation = 90.0f; + Qt::AlignmentFlag alignment = Qt::AlignTop; + if (m_yFlipped) { + alignment = Qt::AlignBottom; + xRotation = -xRotation; + if (m_zFlipped) { + if (m_xFlipped) { + titleOffset = -titleOffset; + zRotation = -zRotation; + extraRotation = -extraRotation; + } else { + zRotation = -zRotation; + yRotation = -yRotation; + } + } else { + if (m_xFlipped) { + titleOffset = -titleOffset; + } else { + extraRotation = -extraRotation; + yRotation = -yRotation; + } + } + } else { + if (m_zFlipped) { + zRotation = -zRotation; + if (m_xFlipped) { + titleOffset = -titleOffset; + } else { + extraRotation = -extraRotation; + yRotation = -yRotation; + } + } else { + if (m_xFlipped) { + titleOffset = -titleOffset; + extraRotation = -extraRotation; + } else { + yRotation = -yRotation; + } + } + } + + float offsetRotation = zRotation; + if (offsetRotation == 180.0f || offsetRotation == -180.0f) + offsetRotation = 0.0f; + QQuaternion offsetRotator = QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, offsetRotation); + QVector3D titleOffsetVector = + offsetRotator.rotatedVector(QVector3D(titleOffset, 0.0f, 0.0f)); + + QQuaternion titleRotation; + if (m_axisCacheZ.isTitleFixed()) { + titleRotation = QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, zRotation) + * QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, yRotation) + * QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, xRotation); + } else { + titleRotation = totalRotation + * QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, extraRotation); + } + dummyItem.setTranslation(labelTrans + titleOffsetVector); + + m_drawer->drawLabel(dummyItem, m_axisCacheZ.titleItem(), viewMatrix, + projectionMatrix, zeroVector, titleRotation, 0, + m_cachedSelectionMode, shader, m_labelObj, activeCamera, + true, true, Drawer::LabelMid, alignment); +} + +void Abstract3DRenderer::loadGridLineMesh() +{ + ObjectHelper::resetObjectHelper(this, m_gridLineObj, + QStringLiteral(":/defaultMeshes/plane")); +} + +void Abstract3DRenderer::loadLabelMesh() +{ + ObjectHelper::resetObjectHelper(this, m_labelObj, + QStringLiteral(":/defaultMeshes/plane")); +} + + +void Abstract3DRenderer::generateBaseColorTexture(const QColor &color, GLuint *texture) +{ + m_textureHelper->deleteTexture(texture); *texture = m_textureHelper->createUniformTexture(color); } -void Abstract3DRenderer::fixGradientAndGenerateTexture(QLinearGradient *gradient, GLuint *gradientTexture) +void Abstract3DRenderer::fixGradientAndGenerateTexture(QLinearGradient *gradient, + GLuint *gradientTexture) { // Readjust start/stop to match gradient texture size gradient->setStart(qreal(gradientTextureWidth), qreal(gradientTextureHeight)); gradient->setFinalStop(0.0, 0.0); - if (*gradientTexture) { - m_textureHelper->deleteTexture(gradientTexture); - *gradientTexture = 0; - } + m_textureHelper->deleteTexture(gradientTexture); *gradientTexture = m_textureHelper->createGradientTexture(*gradient); } @@ -471,4 +832,270 @@ QString &Abstract3DRenderer::selectionLabel() return m_selectionLabel; } +QVector4D Abstract3DRenderer::indexToSelectionColor(GLint index) +{ + GLubyte idxRed = index & 0xff; + GLubyte idxGreen = (index & 0xff00) >> 8; + GLubyte idxBlue = (index & 0xff0000) >> 16; + + return QVector4D(idxRed, idxGreen, idxBlue, 0); +} + +CustomRenderItem *Abstract3DRenderer::addCustomItem(QCustom3DItem *item) +{ + CustomRenderItem *newItem = new CustomRenderItem(); + newItem->setRenderer(this); + newItem->setItemPointer(item); // Store pointer for render item updates + newItem->setMesh(item->meshFile()); + QVector3D scaling = item->scaling(); + QImage textureImage = item->d_ptr->textureImage(); + bool facingCamera = false; + if (item->d_ptr->m_isLabelItem) { + QCustom3DLabel *labelItem = static_cast<QCustom3DLabel *>(item); + float pointSize = labelItem->font().pointSizeF(); + // Check do we have custom visuals or need to use theme + if (!labelItem->dptr()->m_customVisuals) { + // Recreate texture using theme + labelItem->dptr()->createTextureImage(m_cachedTheme->labelBackgroundColor(), + m_cachedTheme->labelTextColor(), + m_cachedTheme->isLabelBackgroundEnabled(), + m_cachedTheme->isLabelBorderEnabled()); + pointSize = m_cachedTheme->font().pointSizeF(); + textureImage = item->d_ptr->textureImage(); + } + // Calculate scaling based on text (texture size), font size and asked scaling + float scaledFontSize = (0.05f + pointSize / 500.0f) / float(textureImage.height()); + scaling.setX(scaling.x() * textureImage.width() * scaledFontSize); + scaling.setY(scaling.y() * textureImage.height() * scaledFontSize); + // Check if facing camera + facingCamera = labelItem->isFacingCamera(); + } + newItem->setScaling(scaling); + 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); + 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); + m_customRenderCache.insert(item, newItem); + return newItem; +} + +void Abstract3DRenderer::updateCustomItem(CustomRenderItem *renderItem) +{ + QCustom3DItem *item = renderItem->itemPointer(); + if (item->d_ptr->m_dirtyBits.meshDirty) { + renderItem->setMesh(item->meshFile()); + item->d_ptr->m_dirtyBits.meshDirty = false; + } + if (item->d_ptr->m_dirtyBits.scalingDirty) { + QVector3D scaling = item->scaling(); + // 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); + float pointSize = labelItem->font().pointSizeF(); + // Check do we have custom visuals or need to use theme + if (labelItem->dptr()->m_customVisuals) { + // Recreate texture + labelItem->dptr()->createTextureImage(); + } else { + // Recreate texture using theme + labelItem->dptr()->createTextureImage(m_cachedTheme->labelBackgroundColor(), + m_cachedTheme->labelTextColor(), + m_cachedTheme->isLabelBackgroundEnabled(), + m_cachedTheme->isLabelBorderEnabled()); + pointSize = m_cachedTheme->font().pointSizeF(); + } + QImage textureImage = item->d_ptr->textureImage(); + // Calculate scaling based on text (texture size), font size and asked scaling + float scaledFontSize = (0.05f + pointSize / 500.0f) / float(textureImage.height()); + scaling.setX(scaling.x() * textureImage.width() * scaledFontSize); + scaling.setY(scaling.y() * textureImage.height() * scaledFontSize); + item->d_ptr->clearTextureImage(); + } + renderItem->setScaling(scaling); + item->d_ptr->m_dirtyBits.scalingDirty = false; + } + if (item->d_ptr->m_dirtyBits.rotationDirty) { + renderItem->setRotation(item->rotation()); + item->d_ptr->m_dirtyBits.rotationDirty = false; + } + if (item->d_ptr->m_dirtyBits.textureDirty) { + QImage textureImage = item->d_ptr->textureImage(); + if (item->d_ptr->m_isLabelItem) { + QCustom3DLabel *labelItem = static_cast<QCustom3DLabel *>(item); + // Check do we have custom visuals or need to use theme + if (!labelItem->dptr()->m_customVisuals) { + // Recreate texture using theme + labelItem->dptr()->createTextureImage(m_cachedTheme->labelBackgroundColor(), + m_cachedTheme->labelTextColor(), + m_cachedTheme->isLabelBackgroundEnabled(), + m_cachedTheme->isLabelBorderEnabled()); + textureImage = item->d_ptr->textureImage(); + } + } + 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; + } + if (item->d_ptr->m_dirtyBits.shadowCastingDirty) { + renderItem->setShadowCasting(item->isShadowCasting()); + item->d_ptr->m_dirtyBits.shadowCastingDirty = false; + } + if (item->d_ptr->m_isLabelItem) { + QCustom3DLabel *labelItem = static_cast<QCustom3DLabel *>(item); + if (labelItem->dptr()->m_facingCameraDirty) { + renderItem->setFacingCamera(labelItem->isFacingCamera()); + labelItem->dptr()->m_facingCameraDirty = false; + } + } +} + +void Abstract3DRenderer::updateCustomItemPositions() +{ + foreach (CustomRenderItem *renderItem, m_customRenderCache) { + QVector3D translation = convertPositionToTranslation(renderItem->position(), + renderItem->isPositionAbsolute()); + renderItem->setTranslation(translation); + } +} + +void Abstract3DRenderer::drawCustomItems(RenderingState state, + ShaderHelper *shader, + const QMatrix4x4 &viewMatrix, + const QMatrix4x4 &projectionViewMatrix, + const QMatrix4x4 &depthProjectionViewMatrix, + GLuint depthTexture, + GLfloat shadowQuality) +{ + if (m_customRenderCache.isEmpty()) + return; + + 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; + } + + 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); + } + + 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 + 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); + } else { + glDisable(GL_BLEND); + glEnable(GL_CULL_FACE); + } + +#if !defined(QT_OPENGL_ES_2) + 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()); + } + } 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); + } +} + QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/engine/abstract3drenderer_p.h b/src/datavisualization/engine/abstract3drenderer_p.h index 4eb8426f..0dfc7367 100644 --- a/src/datavisualization/engine/abstract3drenderer_p.h +++ b/src/datavisualization/engine/abstract3drenderer_p.h @@ -29,18 +29,13 @@ #ifndef ABSTRACT3DRENDERER_P_H #define ABSTRACT3DRENDERER_P_H -//#define DISPLAY_RENDER_SPEED - #include <QtGui/QOpenGLFunctions> -#ifdef DISPLAY_RENDER_SPEED -#include <QtCore/QTime> -#endif #include "datavisualizationglobal_p.h" #include "abstract3dcontroller_p.h" #include "axisrendercache_p.h" -#include "qabstractdataproxy.h" #include "seriesrendercache_p.h" +#include "customrenderitem_p.h" QT_BEGIN_NAMESPACE_DATAVISUALIZATION @@ -60,18 +55,27 @@ protected: SelectOnSlice }; - QString generateValueLabel(const QString &format, float value); + enum RenderingState { + RenderingNormal = 0, + RenderingSelection, + RenderingDepth + }; public: virtual ~Abstract3DRenderer(); virtual void updateData() = 0; - virtual void updateSeries(const QList<QAbstract3DSeries *> &seriesList, bool updateVisibility); - + virtual void updateSeries(const QList<QAbstract3DSeries *> &seriesList); + virtual void updateCustomData(const QList<QCustom3DItem *> &customItems); + virtual void updateCustomItems(); + virtual void updateCustomItemPositions(); + virtual SeriesRenderCache *createNewCache(QAbstract3DSeries *series); + virtual void cleanCache(SeriesRenderCache *cache); virtual void render(GLuint defaultFboHandle); virtual void updateTheme(Q3DTheme *theme); virtual void updateSelectionMode(QAbstract3DGraph::SelectionFlags newMode); + virtual void updateOptimizationHint(QAbstract3DGraph::OptimizationHints hint); virtual void updateScene(Q3DScene *scene); virtual void updateTextures() = 0; virtual void initSelectionBuffer() = 0; @@ -84,27 +88,64 @@ public: 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 initBackgroundShaders(const QString &vertexShader, const QString &fragmentShader) = 0; - virtual void updateAxisType(QAbstract3DAxis::AxisOrientation orientation, QAbstract3DAxis::AxisType type); - virtual void updateAxisTitle(QAbstract3DAxis::AxisOrientation orientation, const QString &title); - virtual void updateAxisLabels(QAbstract3DAxis::AxisOrientation orientation, const QStringList &labels); - virtual void updateAxisRange(QAbstract3DAxis::AxisOrientation orientation, float min, float max); + virtual void initBackgroundShaders(const QString &vertexShader, + const QString &fragmentShader) = 0; + virtual void initCustomItemShaders(const QString &vertexShader, + const QString &fragmentShader); + virtual void updateAxisType(QAbstract3DAxis::AxisOrientation orientation, + QAbstract3DAxis::AxisType type); + virtual void updateAxisTitle(QAbstract3DAxis::AxisOrientation orientation, + const QString &title); + virtual void updateAxisLabels(QAbstract3DAxis::AxisOrientation orientation, + const QStringList &labels); + virtual void updateAxisRange(QAbstract3DAxis::AxisOrientation orientation, float min, + float max); virtual void updateAxisSegmentCount(QAbstract3DAxis::AxisOrientation orientation, int count); - virtual void updateAxisSubSegmentCount(QAbstract3DAxis::AxisOrientation orientation, int count); - virtual void updateAxisLabelFormat(QAbstract3DAxis::AxisOrientation orientation, const QString &format); + virtual void updateAxisSubSegmentCount(QAbstract3DAxis::AxisOrientation orientation, + int count); + virtual void updateAxisLabelFormat(QAbstract3DAxis::AxisOrientation orientation, + const QString &format); + virtual void updateAxisReversed(QAbstract3DAxis::AxisOrientation orientation, + bool enable); + virtual void updateAxisFormatter(QAbstract3DAxis::AxisOrientation orientation, + QValue3DAxisFormatter *formatter); + virtual void updateAxisLabelAutoRotation(QAbstract3DAxis::AxisOrientation orientation, + float angle); + virtual void updateAxisTitleVisibility(QAbstract3DAxis::AxisOrientation orientation, + bool visible); + virtual void updateAxisTitleFixed(QAbstract3DAxis::AxisOrientation orientation, + bool fixed); + virtual void modifiedSeriesList(const QVector<QAbstract3DSeries *> &seriesList); virtual void fixMeshFileName(QString &fileName, QAbstract3DSeries::Mesh mesh); + + virtual CustomRenderItem *addCustomItem(QCustom3DItem *item); + virtual void updateCustomItem(CustomRenderItem *renderItem); + + virtual void updateAspectRatio(float ratio); + + virtual QVector3D convertPositionToTranslation(const QVector3D &position, + bool isAbsolute) = 0; + 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 QAbstract3DSeries *clickedSeries() const { return m_clickedSeries; } + inline QAbstract3DGraph::ElementType clickedType() { return m_clickedType; } LabelItem &selectionLabelItem(); void setSelectionLabel(const QString &label); QString &selectionLabel(); + void drawCustomItems(RenderingState state, ShaderHelper *shader, + const QMatrix4x4 &viewMatrix, + const QMatrix4x4 &projectionViewMatrix, + const QMatrix4x4 &depthProjectionViewMatrix, + GLuint depthTexture, GLfloat shadowQuality); + QVector4D indexToSelectionColor(GLint index); + signals: void needRender(); // Emit this if something in renderer causes need for another render pass. void requestShadowQuality(QAbstract3DGraph::ShadowQuality quality); // For automatic quality adjustments @@ -124,6 +165,28 @@ protected: void fixGradient(QLinearGradient *gradient, GLuint *gradientTexture); + void calculateZoomLevel(); + void drawAxisTitleY(const QVector3D &sideLabelRotation, const QVector3D &backLabelRotation, + const QVector3D &sideLabelTrans, const QVector3D &backLabelTrans, + const QQuaternion &totalSideRotation, const QQuaternion &totalBackRotation, + AbstractRenderItem &dummyItem, const Q3DCamera *activeCamera, + float labelsMaxWidth, + const QMatrix4x4 &viewMatrix, const QMatrix4x4 &projectionMatrix, + ShaderHelper *shader); + void drawAxisTitleX(const QVector3D &labelRotation, const QVector3D &labelTrans, + const QQuaternion &totalRotation, AbstractRenderItem &dummyItem, + const Q3DCamera *activeCamera, float labelsMaxWidth, + const QMatrix4x4 &viewMatrix, const QMatrix4x4 &projectionMatrix, + ShaderHelper *shader); + void drawAxisTitleZ(const QVector3D &labelRotation, const QVector3D &labelTrans, + const QQuaternion &totalRotation, AbstractRenderItem &dummyItem, + const Q3DCamera *activeCamera, float labelsMaxWidth, + const QMatrix4x4 &viewMatrix, const QMatrix4x4 &projectionMatrix, + ShaderHelper *shader); + + void loadGridLineMesh(); + void loadLabelMesh(); + bool m_hasNegativeValues; Q3DTheme *m_cachedTheme; Drawer *m_drawer; @@ -132,6 +195,7 @@ protected: GLfloat m_autoScaleAdjustment; QAbstract3DGraph::SelectionFlags m_cachedSelectionMode; + QAbstract3DGraph::OptimizationHints m_cachedOptimizationHint; AxisRenderCache m_axisCacheX; AxisRenderCache m_axisCacheY; @@ -142,22 +206,37 @@ protected: bool m_selectionDirty; SelectionState m_selectionState; QPoint m_inputPosition; - QVector<SeriesRenderCache> m_visibleSeriesList; + QHash<QAbstract3DSeries *, SeriesRenderCache *> m_renderCacheList; + CustomRenderItemArray m_customRenderCache; QRect m_primarySubViewport; QRect m_secondarySubViewport; float m_devicePixelRatio; bool m_selectionLabelDirty; bool m_clickPending; QAbstract3DSeries *m_clickedSeries; + QAbstract3DGraph::ElementType m_clickedType; + int m_selectedLabelIndex; + int m_selectedCustomItemIndex; QString m_selectionLabel; LabelItem *m_selectionLabelItem; + int m_visibleSeriesCount; -#ifdef DISPLAY_RENDER_SPEED - bool m_isFirstFrame; - QTime m_lastFrameTime; - GLint m_numFrames; -#endif + ShaderHelper *m_customItemShader; + + bool m_useOrthoProjection; + bool m_xFlipped; + bool m_yFlipped; + bool m_zFlipped; + + ObjectHelper *m_backgroundObj; // Shared reference + ObjectHelper *m_gridLineObj; // Shared reference + ObjectHelper *m_labelObj; // Shared reference + + float m_graphAspectRatio; + +private: + friend class Abstract3DController; }; QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/engine/axisrendercache.cpp b/src/datavisualization/engine/axisrendercache.cpp index a107dd23..0b7a5c7a 100644 --- a/src/datavisualization/engine/axisrendercache.cpp +++ b/src/datavisualization/engine/axisrendercache.cpp @@ -18,7 +18,6 @@ #include "axisrendercache_p.h" -#include <QtCore/qmath.h> #include <QtGui/QFontMetrics> QT_BEGIN_NAMESPACE_DATAVISUALIZATION @@ -29,10 +28,17 @@ AxisRenderCache::AxisRenderCache() m_max(10.0f), m_segmentCount(5), m_subSegmentCount(1), + m_reversed(false), m_font(QFont(QStringLiteral("Arial"))), + m_formatter(0), + m_ctrlFormatter(0), m_drawer(0), - m_segmentStep(10.0f), - m_subSegmentStep(10.0f) + m_positionsDirty(true), + m_translate(0.0f), + m_scale(1.0f), + m_labelAutoRotation(0.0f), + m_titleVisible(false), + m_titleFixed(false) { } @@ -40,6 +46,8 @@ AxisRenderCache::~AxisRenderCache() { foreach (LabelItem *label, m_labelItems) delete label; + + delete m_formatter; } void AxisRenderCache::setDrawer(Drawer *drawer) @@ -69,8 +77,6 @@ void AxisRenderCache::setType(QAbstract3DAxis::AxisType type) foreach (LabelItem *label, m_labelItems) delete label; m_labelItems.clear(); - m_segmentStep = 10.0f; - m_subSegmentStep = 10.0f; } void AxisRenderCache::setTitle(const QString &title) @@ -110,28 +116,44 @@ void AxisRenderCache::setLabels(const QStringList &labels) } } -void AxisRenderCache::setMin(float min) -{ - m_min = min; - updateSegmentStep(); -} - -void AxisRenderCache::setMax(float max) +void AxisRenderCache::updateAllPositions() { - m_max = max; - updateSegmentStep(); -} - -void AxisRenderCache::setSegmentCount(int count) -{ - m_segmentCount = count; - updateSegmentStep(); -} + // As long as grid and subgrid lines are drawn identically, we can further optimize + // by caching all grid and subgrid positions into a single vector. + // If subgrid lines are ever themed separately, this array will probably become obsolete. + if (m_formatter) { + int gridCount = m_formatter->gridPositions().size(); + int subGridCount = m_formatter->subGridPositions().size(); + int labelCount = m_formatter->labelPositions().size(); + int fullSize = gridCount + subGridCount; + + m_adjustedGridLinePositions.resize(fullSize); + m_adjustedLabelPositions.resize(labelCount); + int index = 0; + int grid = 0; + int label = 0; + float position = 0.0f; + for (; label < labelCount; label++) { + position = m_formatter->labelPositions().at(label); + if (m_reversed) + position = 1.0f - position; + m_adjustedLabelPositions[label] = position * m_scale + m_translate; + } + for (; grid < gridCount; grid++) { + position = m_formatter->gridPositions().at(grid); + if (m_reversed) + position = 1.0f - position; + m_adjustedGridLinePositions[index++] = position * m_scale + m_translate; + } + for (int subGrid = 0; subGrid < subGridCount; subGrid++) { + position = m_formatter->subGridPositions().at(subGrid); + if (m_reversed) + position = 1.0f - position; + m_adjustedGridLinePositions[index++] = position * m_scale + m_translate; + } -void AxisRenderCache::setSubSegmentCount(int count) -{ - m_subSegmentCount = count; - updateSubSegmentStep(); + m_positionsDirty = false; + } } void AxisRenderCache::updateTextures() @@ -153,23 +175,6 @@ void AxisRenderCache::updateTextures() } } -void AxisRenderCache::updateSegmentStep() -{ - if (m_segmentCount > 0) - m_segmentStep = qFabs((m_max - m_min) / m_segmentCount); - else - m_segmentStep = 0.0f; // Irrelevant - updateSubSegmentStep(); -} - -void AxisRenderCache::updateSubSegmentStep() -{ - if (m_subSegmentCount > 1) - m_subSegmentStep = m_segmentStep / m_subSegmentCount; - else - m_subSegmentStep = m_segmentStep; -} - 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 e1c51e7c..90321740 100644 --- a/src/datavisualization/engine/axisrendercache_p.h +++ b/src/datavisualization/engine/axisrendercache_p.h @@ -30,9 +30,8 @@ #define AXISRENDERCACHE_P_H #include "datavisualizationglobal_p.h" -#include "labelitem_p.h" -#include "qabstract3daxis_p.h" #include "drawer_p.h" +#include <QtCore/QPointer> QT_BEGIN_NAMESPACE_DATAVISUALIZATION @@ -48,31 +47,70 @@ public: void setType(QAbstract3DAxis::AxisType type); inline QAbstract3DAxis::AxisType type() const { return m_type; } void setTitle(const QString &title); - inline const QString &title() { return m_title; } + inline const QString &title() const { return m_title; } void setLabels(const QStringList &labels); - inline const QStringList &labels() { return m_labels; } - void setMin(float min); - inline float min() { return m_min; } - void setMax(float max); - inline float max() { return m_max; } - void setSegmentCount(int count); + inline const QStringList &labels() const { return m_labels; } + inline void setMin(float min) { m_min = min; } + inline float min() const { return m_min; } + inline void setMax(float max) { m_max = max; } + inline float max() const { return m_max; } + inline void setSegmentCount(int count) { m_segmentCount = count; m_positionsDirty = true; } inline int segmentCount() const { return m_segmentCount; } - void setSubSegmentCount(int count); + inline void setSubSegmentCount(int count) { m_subSegmentCount = count; m_positionsDirty = true; } inline int subSegmentCount() const { return m_subSegmentCount; } inline void setLabelFormat(const QString &format) { m_labelFormat = format; } - inline const QString &labelFormat() { return m_labelFormat; } + inline const QString &labelFormat() const { return m_labelFormat; } + inline void setReversed(bool enable) { m_reversed = enable; m_positionsDirty = true; } + inline bool reversed() const { return m_reversed; } + inline void setFormatter(QValue3DAxisFormatter *formatter) + { + m_formatter = formatter; m_positionsDirty = true; + } + inline QValue3DAxisFormatter *formatter() const { return m_formatter; } + inline void setCtrlFormatter(QValue3DAxisFormatter *formatter) + { + m_ctrlFormatter = formatter; + } + inline QValue3DAxisFormatter *ctrlFormatter() const { return m_ctrlFormatter; } inline LabelItem &titleItem() { return m_titleItem; } inline QList<LabelItem *> &labelItems() { return m_labelItems; } - inline GLfloat segmentStep() const { return m_segmentStep; } - inline GLfloat subSegmentStep() const { return m_subSegmentStep; } + inline float gridLinePosition(int index) { return m_adjustedGridLinePositions.at(index); } + inline int gridLineCount() { return m_adjustedGridLinePositions.size(); } + inline float labelPosition(int index) { return m_adjustedLabelPositions.at(index); } + inline int labelCount() { + // Some value axis formatters may opt to not show all labels, + // so use positions array for determining count in that case. + if (m_type == QAbstract3DAxis::AxisTypeValue) + return m_adjustedLabelPositions.size(); + else + return m_labels.size(); + } + void updateAllPositions(); + inline bool positionsDirty() const { return m_positionsDirty; } + inline void markPositionsDirty() { m_positionsDirty = true; } + inline void setTranslate(float translate) { m_translate = translate; m_positionsDirty = true; } + inline float translate() { return m_translate; } + inline void setScale(float scale) { m_scale = scale; m_positionsDirty = true; } + inline float scale() { return m_scale; } + inline float positionAt(float value) + { + if (m_reversed) + return (1.0f - m_formatter->positionAt(value)) * m_scale + m_translate; + else + return m_formatter->positionAt(value) * m_scale + m_translate; + } + inline float labelAutoRotation() const { return m_labelAutoRotation; } + inline void setLabelAutoRotation(float angle) { m_labelAutoRotation = angle; } + inline bool isTitleVisible() const { return m_titleVisible; } + inline void setTitleVisible(bool visible) { m_titleVisible = visible; } + inline bool isTitleFixed() const { return m_titleFixed; } + inline void setTitleFixed(bool fixed) { m_titleFixed = fixed; } public slots: void updateTextures(); private: - void updateSegmentStep(); - void updateSubSegmentStep(); int maxLabelWidth(const QStringList &labels) const; // Cached axis values @@ -84,14 +122,23 @@ private: int m_segmentCount; int m_subSegmentCount; QString m_labelFormat; + bool m_reversed; QFont m_font; + QValue3DAxisFormatter *m_formatter; + QPointer<QValue3DAxisFormatter> m_ctrlFormatter; // Renderer items Drawer *m_drawer; // Not owned LabelItem m_titleItem; QList<LabelItem *> m_labelItems; - GLfloat m_segmentStep; - GLfloat m_subSegmentStep; + QVector<float> m_adjustedGridLinePositions; + QVector<float> m_adjustedLabelPositions; + bool m_positionsDirty; + float m_translate; + float m_scale; + float m_labelAutoRotation; + bool m_titleVisible; + bool m_titleFixed; Q_DISABLE_COPY(AxisRenderCache) }; diff --git a/src/datavisualization/engine/bars3dcontroller.cpp b/src/datavisualization/engine/bars3dcontroller.cpp index 91240259..3a240c24 100644 --- a/src/datavisualization/engine/bars3dcontroller.cpp +++ b/src/datavisualization/engine/bars3dcontroller.cpp @@ -18,8 +18,6 @@ #include "bars3dcontroller_p.h" #include "bars3drenderer_p.h" -#include "camerahelper_p.h" -#include "qabstract3daxis_p.h" #include "qvalue3daxis_p.h" #include "qcategory3daxis_p.h" #include "qbardataproxy_p.h" @@ -27,9 +25,6 @@ #include "thememanager_p.h" #include "q3dtheme_p.h" -#include <QtGui/QMatrix4x4> -#include <QtCore/qmath.h> - QT_BEGIN_NAMESPACE_DATAVISUALIZATION Bars3DController::Bars3DController(QRect boundRect, Q3DScene *scene) @@ -81,9 +76,28 @@ void Bars3DController::synchDataToRenderer() series->d_ptr->m_changeTracker.meshChanged = true; } + // If y range or reverse changed, scene needs to be updated to update camera limits + bool needSceneUpdate = false; + if (Abstract3DController::m_changeTracker.axisYRangeChanged + || Abstract3DController::m_changeTracker.axisYReversedChanged) { + needSceneUpdate = true; + } + Abstract3DController::synchDataToRenderer(); // Notify changes to renderer + if (m_changeTracker.rowsChanged) { + m_renderer->updateRows(m_changedRows); + m_changeTracker.rowsChanged = false; + m_changedRows.clear(); + } + + if (m_changeTracker.itemChanged) { + m_renderer->updateItems(m_changedItems); + m_changeTracker.itemChanged = false; + m_changedItems.clear(); + } + if (m_changeTracker.multiSeriesScalingChanged) { m_renderer->updateMultiSeriesScaling(m_isMultiSeriesUniform); m_changeTracker.multiSeriesScalingChanged = false; @@ -99,6 +113,13 @@ void Bars3DController::synchDataToRenderer() m_renderer->updateSelectedBar(m_selectedBar, m_selectedBarSeries); 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(); + } } void Bars3DController::handleArrayReset() @@ -107,7 +128,10 @@ void Bars3DController::handleArrayReset() if (series->isVisible()) { adjustAxisRanges(); m_isDataDirty = true; + series->d_ptr->markItemLabelDirty(); } + if (!m_changedSeriesList.contains(series)) + m_changedSeriesList.append(series); // Clear selection unless still valid setSelectedBar(m_selectedBar, m_selectedBarSeries, false); emitNeedRender(); @@ -122,19 +146,45 @@ void Bars3DController::handleRowsAdded(int startIndex, int count) adjustAxisRanges(); m_isDataDirty = true; } + if (!m_changedSeriesList.contains(series)) + m_changedSeriesList.append(series); emitNeedRender(); } void Bars3DController::handleRowsChanged(int startIndex, int count) { - Q_UNUSED(startIndex) - Q_UNUSED(count) QBar3DSeries *series = static_cast<QBarDataProxy *>(sender())->series(); - if (series->isVisible()) { - adjustAxisRanges(); - m_isDataDirty = true; + int oldChangeCount = m_changedRows.size(); + if (!oldChangeCount) + m_changedRows.reserve(count); + + for (int i = 0; i < count; i++) { + bool newItem = true; + int candidate = startIndex + i; + for (int j = 0; j < oldChangeCount; j++) { + const ChangeRow &oldChangeItem = m_changedRows.at(j); + if (oldChangeItem.row == candidate && series == oldChangeItem.series) { + newItem = false; + break; + } + } + if (newItem) { + ChangeRow newChangeItem = {series, candidate}; + m_changedRows.append(newChangeItem); + if (series == m_selectedBarSeries && m_selectedBar.x() == candidate) + series->d_ptr->markItemLabelDirty(); + } + } + if (count) { + m_changeTracker.rowsChanged = true; + + if (series->isVisible()) + adjustAxisRanges(); + + // Clear selection unless still valid (row length might have changed) + setSelectedBar(m_selectedBar, m_selectedBarSeries, false); + emitNeedRender(); } - emitNeedRender(); } void Bars3DController::handleRowsRemoved(int startIndex, int count) @@ -160,6 +210,8 @@ void Bars3DController::handleRowsRemoved(int startIndex, int count) adjustAxisRanges(); m_isDataDirty = true; } + if (!m_changedSeriesList.contains(series)) + m_changedSeriesList.append(series); emitNeedRender(); } @@ -182,20 +234,36 @@ void Bars3DController::handleRowsInserted(int startIndex, int count) adjustAxisRanges(); m_isDataDirty = true; } + if (!m_changedSeriesList.contains(series)) + m_changedSeriesList.append(series); emitNeedRender(); } void Bars3DController::handleItemChanged(int rowIndex, int columnIndex) { - Q_UNUSED(rowIndex) - Q_UNUSED(columnIndex) QBar3DSeries *series = static_cast<QBarDataProxy *>(sender())->series(); - if (series->isVisible()) { - adjustAxisRanges(); - m_isDataDirty = true; + + bool newItem = true; + QPoint candidate(rowIndex, columnIndex); + foreach (ChangeItem item, m_changedItems) { + if (item.point == candidate && item.series == series) { + newItem = false; + break; + } + } + + if (newItem) { + ChangeItem newItem = {series, candidate}; + m_changedItems.append(newItem); + m_changeTracker.itemChanged = true; + + if (series == m_selectedBarSeries && m_selectedBar == candidate) + series->d_ptr->markItemLabelDirty(); + if (series->isVisible()) + adjustAxisRanges(); + emitNeedRender(); } - emitNeedRender(); } void Bars3DController::handleDataRowLabelsChanged() @@ -238,8 +306,6 @@ void Bars3DController::handleSeriesVisibilityChangedBySender(QObject *sender) { Abstract3DController::handleSeriesVisibilityChangedBySender(sender); - adjustAxisRanges(); - // Visibility changes may require disabling slicing, // so just reset selection to ensure everything is still valid. setSelectedBar(m_selectedBar, m_selectedBarSeries, false); @@ -253,6 +319,8 @@ void Bars3DController::handlePendingClick() setSelectedBar(position, series, true); + Abstract3DController::handlePendingClick(); + m_renderer->resetClickedStatus(); } @@ -337,9 +405,6 @@ void Bars3DController::insertSeries(int index, QAbstract3DSeries *series) Abstract3DController::insertSeries(index, series); if (oldSize != m_seriesList.size()) { - if (series->isVisible()) - adjustAxisRanges(); - QBar3DSeries *barSeries = static_cast<QBar3DSeries *>(series); if (!oldSize) { m_primarySeries = barSeries; @@ -460,7 +525,8 @@ void Bars3DController::setSelectedBar(const QPoint &position, QBar3DSeries *seri adjustSelectionPosition(pos, series); if (selectionMode().testFlag(QAbstract3DGraph::SelectionSlice)) { - // If the selected bar is outside data window, or there is no visible selected bar, disable slicing + // If the selected bar is outside data window, or there is no visible selected bar, + // disable slicing. if (pos.x() < m_axisZ->min() || pos.x() > m_axisZ->max() || pos.y() < m_axisX->min() || pos.y() > m_axisX->max() || !series->isVisible()) { @@ -518,7 +584,8 @@ void Bars3DController::adjustAxisRanges() int seriesCount = m_seriesList.size(); if (adjustZ || adjustX) { for (int series = 0; series < seriesCount; series++) { - const QBar3DSeries *barSeries = static_cast<QBar3DSeries *>(m_seriesList.at(series)); + const QBar3DSeries *barSeries = + static_cast<QBar3DSeries *>(m_seriesList.at(series)); if (barSeries->isVisible()) { const QBarDataProxy *proxy = barSeries->dataProxy(); @@ -546,22 +613,24 @@ void Bars3DController::adjustAxisRanges() } // Call private implementations of setRange to avoid unsetting auto adjust flag if (adjustZ) - categoryAxisZ->dptr()->setRange(0.0f, float(maxRowCount)); + categoryAxisZ->dptr()->setRange(0.0f, float(maxRowCount), true); if (adjustX) - categoryAxisX->dptr()->setRange(0.0f, float(maxColumnCount)); + categoryAxisX->dptr()->setRange(0.0f, float(maxColumnCount), true); } // Now that we know the row and column ranges, figure out the value axis range if (adjustY) { for (int series = 0; series < seriesCount; series++) { - const QBar3DSeries *barSeries = static_cast<QBar3DSeries *>(m_seriesList.at(series)); + const QBar3DSeries *barSeries = + static_cast<QBar3DSeries *>(m_seriesList.at(series)); if (barSeries->isVisible()) { const QBarDataProxy *proxy = barSeries->dataProxy(); if (adjustY && proxy) { - QPair<GLfloat, GLfloat> limits = proxy->dptrc()->limitValues(categoryAxisZ->min(), - categoryAxisZ->max(), - categoryAxisX->min(), - categoryAxisX->max()); + QPair<GLfloat, GLfloat> limits = + proxy->dptrc()->limitValues(categoryAxisZ->min(), + categoryAxisZ->max(), + categoryAxisX->min(), + categoryAxisX->max()); if (!series) { // First series initializes the values minValue = limits.first; @@ -583,7 +652,7 @@ void Bars3DController::adjustAxisRanges() minValue = 0.0f; maxValue = 1.0f; } - valueAxis->dptr()->setRange(minValue, maxValue); + valueAxis->dptr()->setRange(minValue, maxValue, true); } } } diff --git a/src/datavisualization/engine/bars3dcontroller_p.h b/src/datavisualization/engine/bars3dcontroller_p.h index 9ea59c89..4f0e1f20 100644 --- a/src/datavisualization/engine/bars3dcontroller_p.h +++ b/src/datavisualization/engine/bars3dcontroller_p.h @@ -38,16 +38,18 @@ class Bars3DRenderer; class QBar3DSeries; struct Bars3DChangeBitField { - bool slicingActiveChanged : 1; bool multiSeriesScalingChanged : 1; bool barSpecsChanged : 1; bool selectedBarChanged : 1; + bool rowsChanged : 1; + bool itemChanged : 1; Bars3DChangeBitField() : - slicingActiveChanged(true), multiSeriesScalingChanged(true), barSpecsChanged(true), - selectedBarChanged(true) + selectedBarChanged(true), + rowsChanged(false), + itemChanged(false) { } }; @@ -56,8 +58,20 @@ class QT_DATAVISUALIZATION_EXPORT Bars3DController : public Abstract3DController { Q_OBJECT +public: + struct ChangeItem { + QBar3DSeries *series; + QPoint point; + }; + struct ChangeRow { + QBar3DSeries *series; + int row; + }; + private: Bars3DChangeBitField m_changeTracker; + QVector<ChangeItem> m_changedItems; + QVector<ChangeRow> m_changedRows; // Interaction QPoint m_selectedBar; // Points to row & column in data window. @@ -118,6 +132,7 @@ public: virtual QList<QBar3DSeries *> barSeriesList(); virtual void handleAxisRangeChangedBySender(QObject *sender); + virtual void adjustAxisRanges(); public slots: void handleArrayReset(); @@ -137,11 +152,9 @@ protected: virtual QAbstract3DAxis *createDefaultAxis(QAbstract3DAxis::AxisOrientation orientation); private: - void adjustAxisRanges(); void adjustSelectionPosition(QPoint &pos, const QBar3DSeries *series); Q_DISABLE_COPY(Bars3DController) - }; QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/engine/bars3drenderer.cpp b/src/datavisualization/engine/bars3drenderer.cpp index a7f9e2b3..3805e760 100644 --- a/src/datavisualization/engine/bars3drenderer.cpp +++ b/src/datavisualization/engine/bars3drenderer.cpp @@ -17,20 +17,12 @@ ****************************************************************************/ #include "bars3drenderer_p.h" -#include "bars3dcontroller_p.h" #include "q3dcamera_p.h" #include "shaderhelper_p.h" -#include "objecthelper_p.h" #include "texturehelper_p.h" #include "utils_p.h" -#include "drawer_p.h" -#include "qbardataitem.h" -#include "q3dlight.h" -#include "qbar3dseries_p.h" - -#include <QtGui/QMatrix4x4> -#include <QtGui/QMouseEvent> -#include <QtCore/QThread> +#include "barseriesrendercache_p.h" + #include <QtCore/qmath.h> // You can verify that depth buffer drawing works correctly by uncommenting this. @@ -39,9 +31,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION -const GLfloat labelMargin = 0.05f; const GLfloat gridLineWidth = 0.005f; - const bool sliceGridLabels = true; Bars3DRenderer::Bars3DRenderer(Bars3DController *controller) @@ -52,9 +42,6 @@ Bars3DRenderer::Bars3DRenderer(Bars3DController *controller) m_selectedBar(0), m_sliceCache(0), m_sliceTitleItem(0), - m_xFlipped(false), - m_zFlipped(false), - m_yFlipped(false), m_updateLabels(false), m_barShader(0), m_barGradientShader(0), @@ -62,9 +49,6 @@ Bars3DRenderer::Bars3DRenderer(Bars3DController *controller) m_selectionShader(0), m_backgroundShader(0), m_labelShader(0), - m_backgroundObj(0), - m_gridLineObj(0), - m_labelObj(0), m_bgrTexture(0), m_depthTexture(0), m_selectionTexture(0), @@ -74,7 +58,7 @@ Bars3DRenderer::Bars3DRenderer(Bars3DController *controller) m_shadowQualityToShader(100.0f), m_shadowQualityMultiplier(3), m_heightNormalizer(1.0f), - m_negativeBackgroundAdjustment(0.0f), + m_backgroundAdjustment(0.0f), m_rowWidth(0), m_columnDepth(0), m_maxDimension(0), @@ -83,18 +67,23 @@ Bars3DRenderer::Bars3DRenderer(Bars3DController *controller) m_scaleFactor(0), m_maxSceneSize(40.0f), m_visualSelectedBarPos(Bars3DController::invalidSelectionPosition()), - m_visualSelectedBarSeriesIndex(-1), - m_hasHeightAdjustmentChanged(true), + m_resetCameraBaseOrientation(true), m_selectedBarPos(Bars3DController::invalidSelectionPosition()), - m_selectedBarSeries(0), + m_selectedSeriesCache(0), m_noZeroInRange(false), m_seriesScaleX(0.0f), m_seriesScaleZ(0.0f), m_seriesStep(0.0f), m_seriesStart(0.0f), m_clickedPosition(Bars3DController::invalidSelectionPosition()), - m_keepSeriesUniform(false) + m_keepSeriesUniform(false), + m_haveUniformColorSeries(false), + m_haveGradientSeries(false), + m_zeroPosition(0.0f) { + m_axisCacheY.setScale(2.0f); + m_axisCacheY.setTranslate(-1.0f); + initializeOpenGLFunctions(); initializeOpenGL(); } @@ -114,9 +103,6 @@ Bars3DRenderer::~Bars3DRenderer() delete m_depthShader; delete m_selectionShader; delete m_backgroundShader; - delete m_backgroundObj; - delete m_gridLineObj; - delete m_labelObj; delete m_labelShader; } @@ -148,23 +134,18 @@ void Bars3DRenderer::initializeOpenGL() void Bars3DRenderer::updateData() { - int seriesCount = m_visibleSeriesList.size(); int minRow = m_axisCacheZ.min(); int maxRow = m_axisCacheZ.max(); int minCol = m_axisCacheX.min(); int maxCol = m_axisCacheX.max(); int newRows = maxRow - minRow + 1; int newColumns = maxCol - minCol + 1; - int updateSize = 0; int dataRowCount = 0; int maxDataRowCount = 0; - if (m_renderingArrays.size() != seriesCount) { - m_renderingArrays.resize(seriesCount); - m_seriesScaleX = 1.0f / float(seriesCount); - m_seriesStep = 1.0f / float(seriesCount); - m_seriesStart = -((float(seriesCount) - 1.0f) / 2.0f) * m_seriesStep; - } + m_seriesScaleX = 1.0f / float(m_visibleSeriesCount); + m_seriesStep = 1.0f / float(m_visibleSeriesCount); + m_seriesStart = -((float(m_visibleSeriesCount) - 1.0f) / 2.0f) * m_seriesStep; if (m_keepSeriesUniform) m_seriesScaleZ = m_seriesScaleX; @@ -175,7 +156,6 @@ void Bars3DRenderer::updateData() // Force update for selection related items m_sliceCache = 0; m_sliceTitleItem = 0; - m_sliceSelection.clear(); m_cachedColumnCount = newColumns; m_cachedRowCount = newRows; @@ -187,88 +167,235 @@ void Bars3DRenderer::updateData() calculateSceneScalingFactors(); } - for (int series = 0; series < seriesCount; series++) { - BarRenderItemArray &renderArray = m_renderingArrays[series]; - if (newRows != renderArray.size() - || newColumns != renderArray.at(0).size()) { - // Destroy old render items and reallocate new array - renderArray.resize(newRows); - for (int i = 0; i < newRows; i++) - renderArray[i].resize(newColumns); - } + m_zeroPosition = m_axisCacheY.formatter()->positionAt(0.0f); + + foreach (SeriesRenderCache *baseCache, m_renderCacheList) { + BarSeriesRenderCache *cache = static_cast<BarSeriesRenderCache *>(baseCache); + if (cache->isVisible()) { + const QBar3DSeries *currentSeries = cache->series(); + BarRenderItemArray &renderArray = cache->renderArray(); + bool dimensionsChanged = false; + if (newRows != renderArray.size() + || newColumns != renderArray.at(0).size()) { + // Destroy old render items and reallocate new array + dimensionsChanged = true; + renderArray.resize(newRows); + for (int i = 0; i < newRows; i++) + renderArray[i].resize(newColumns); + cache->sliceArray().clear(); + } - // Update cached data window - QBarDataProxy *dataProxy = - static_cast<QBar3DSeries *>(m_visibleSeriesList.at(series).series())->dataProxy(); - dataRowCount = dataProxy->rowCount(); - if (maxDataRowCount < dataRowCount) - maxDataRowCount = qMin(dataRowCount, newRows); - int dataRowIndex = minRow; - GLfloat heightValue = 0.0f; - for (int i = 0; i < newRows; i++) { - int j = 0; - BarRenderItemRow &renderRow = renderArray[i]; - if (dataRowIndex < dataRowCount) { - const QBarDataRow *dataRow = dataProxy->rowAt(dataRowIndex); - updateSize = qMin((dataRow->size() - minCol), renderRow.size()); - if (dataRow) { - int dataColIndex = minCol; - for (; j < updateSize ; j++) { - float value = dataRow->at(dataColIndex).value(); - if (!m_noZeroInRange) { - heightValue = GLfloat(value); - } else { - // Adjust height to range - if (!m_hasNegativeValues) { - heightValue = value - m_axisCacheY.min(); - if (heightValue < 0.0f) - heightValue = 0.0f; - } else if (m_axisCacheY.max() < 0.0f) { - heightValue = value - m_axisCacheY.max(); - if (heightValue > 0.0f) - heightValue = 0.0f; - } - } - renderRow[j].setValue(value); - renderRow[j].setHeight(heightValue / m_heightNormalizer); - float angle = dataRow->at(dataColIndex).rotation(); - if (angle) { - renderRow[j].setRotation( - QQuaternion::fromAxisAndAngle( - upVector, angle)); - } else { - renderRow[j].setRotation(identityQuaternion); - } - dataColIndex++; - } + if (cache->dataDirty() || dimensionsChanged) { + QBarDataProxy *dataProxy = currentSeries->dataProxy(); + dataRowCount = dataProxy->rowCount(); + if (maxDataRowCount < dataRowCount) + maxDataRowCount = qMin(dataRowCount, newRows); + int dataRowIndex = minRow; + for (int i = 0; i < newRows; i++) { + BarRenderItemRow &renderRow = renderArray[i]; + const QBarDataRow *dataRow = 0; + if (dataRowIndex < dataRowCount) + dataRow = dataProxy->rowAt(dataRowIndex); + updateRenderRow(dataRow, renderRow); + dataRowIndex++; } + cache->setDataDirty(false); } - for (; j < m_renderingArrays.at(series).at(i).size(); j++) { - renderRow[j].setValue(0.0f); - renderRow[j].setHeight(0.0f); - renderRow[j].setRotation(identityQuaternion); - } - dataRowIndex++; } } // Reset selected bar to update selection - updateSelectedBar(m_selectedBarPos, m_selectedBarSeries); + updateSelectedBar(m_selectedBarPos, + m_selectedSeriesCache ? m_selectedSeriesCache->series() : 0); +} + +void Bars3DRenderer::updateRenderRow(const QBarDataRow *dataRow, BarRenderItemRow &renderRow) +{ + int j = 0; + int renderRowSize = renderRow.size(); + int startIndex = m_axisCacheX.min(); + + if (dataRow) { + int updateSize = qMin((dataRow->size() - startIndex), renderRowSize); + int dataColIndex = startIndex; + for (; j < updateSize ; j++) { + updateRenderItem(dataRow->at(dataColIndex), renderRow[j]); + dataColIndex++; + } + } + for (; j < renderRowSize; j++) { + renderRow[j].setValue(0.0f); + renderRow[j].setHeight(0.0f); + renderRow[j].setRotation(identityQuaternion); + } +} + +void Bars3DRenderer::updateRenderItem(const QBarDataItem &dataItem, BarRenderItem &renderItem) +{ + float value = dataItem.value(); + float heightValue = m_axisCacheY.formatter()->positionAt(value); + if (m_noZeroInRange) { + if (m_hasNegativeValues) { + heightValue = -1.0f + heightValue; + if (heightValue > 0.0f) + heightValue = 0.0f; + } else { + if (heightValue < 0.0f) + heightValue = 0.0f; + } + } else { + heightValue -= m_zeroPosition; + } + if (m_axisCacheY.reversed()) + heightValue = -heightValue; + + renderItem.setValue(value); + renderItem.setHeight(heightValue); + + float angle = dataItem.rotation(); + if (angle) { + renderItem.setRotation( + QQuaternion::fromAxisAndAngle( + upVector, angle)); + } else { + renderItem.setRotation(identityQuaternion); + } +} + +void Bars3DRenderer::updateSeries(const QList<QAbstract3DSeries *> &seriesList) +{ + Abstract3DRenderer::updateSeries(seriesList); + + bool noSelection = true; + int seriesCount = seriesList.size(); + int visualIndex = 0; + m_haveUniformColorSeries = false; + m_haveGradientSeries = false; + for (int i = 0; i < seriesCount; i++) { + QBar3DSeries *barSeries = static_cast<QBar3DSeries *>(seriesList[i]); + BarSeriesRenderCache *cache = + static_cast<BarSeriesRenderCache *>(m_renderCacheList.value(barSeries)); + if (barSeries->isVisible()) { + if (noSelection + && barSeries->selectedBar() != QBar3DSeries::invalidSelectionPosition()) { + if (selectionLabel() != cache->itemLabel()) + m_selectionLabelDirty = true; + noSelection = false; + } + cache->setVisualIndex(visualIndex++); + if (cache->colorStyle() == Q3DTheme::ColorStyleUniform) + m_haveUniformColorSeries = true; + else + m_haveGradientSeries = true; + } else { + cache->setVisualIndex(-1); + } + + } + if (noSelection) { + if (!selectionLabel().isEmpty()) + m_selectionLabelDirty = true; + m_selectedSeriesCache = 0; + } +} + +SeriesRenderCache *Bars3DRenderer::createNewCache(QAbstract3DSeries *series) +{ + return new BarSeriesRenderCache(series, this); +} + +void Bars3DRenderer::updateRows(const QVector<Bars3DController::ChangeRow> &rows) +{ + int minRow = m_axisCacheZ.min(); + int maxRow = m_axisCacheZ.max(); + BarSeriesRenderCache *cache = 0; + const QBar3DSeries *prevSeries = 0; + const QBarDataArray *dataArray = 0; + + foreach (Bars3DController::ChangeRow item, rows) { + const int row = item.row; + if (row < minRow || row > maxRow) + continue; + QBar3DSeries *currentSeries = item.series; + if (currentSeries != prevSeries) { + cache = static_cast<BarSeriesRenderCache *>(m_renderCacheList.value(currentSeries)); + prevSeries = currentSeries; + dataArray = item.series->dataProxy()->array(); + // Invisible series render caches are not updated, but instead just marked dirty, so that + // they can be completely recalculated when they are turned visible. + if (!cache->isVisible() && !cache->dataDirty()) + cache->setDataDirty(true); + } + if (cache->isVisible()) { + updateRenderRow(dataArray->at(row), cache->renderArray()[row - minRow]); + if (m_cachedIsSlicingActivated + && cache == m_selectedSeriesCache + && m_selectedBarPos.x() == row) { + m_selectionDirty = true; // Need to update slice view + } + } + } +} + +void Bars3DRenderer::updateItems(const QVector<Bars3DController::ChangeItem> &items) +{ + int minRow = m_axisCacheZ.min(); + int maxRow = m_axisCacheZ.max(); + int minCol = m_axisCacheX.min(); + int maxCol = m_axisCacheX.max(); + BarSeriesRenderCache *cache = 0; + const QBar3DSeries *prevSeries = 0; + const QBarDataArray *dataArray = 0; + + foreach (Bars3DController::ChangeItem item, items) { + const int row = item.point.x(); + const int col = item.point.y(); + if (row < minRow || row > maxRow || col < minCol || col > maxCol) + continue; + QBar3DSeries *currentSeries = item.series; + if (currentSeries != prevSeries) { + cache = static_cast<BarSeriesRenderCache *>(m_renderCacheList.value(currentSeries)); + prevSeries = currentSeries; + dataArray = item.series->dataProxy()->array(); + // Invisible series render caches are not updated, but instead just marked dirty, so that + // they can be completely recalculated when they are turned visible. + if (!cache->isVisible() && !cache->dataDirty()) + cache->setDataDirty(true); + } + if (cache->isVisible()) { + updateRenderItem(dataArray->at(row)->at(col), + cache->renderArray()[row - minRow][col - minCol]); + if (m_cachedIsSlicingActivated + && cache == m_selectedSeriesCache + && m_selectedBarPos == QPoint(row, col)) { + m_selectionDirty = true; // Need to update slice view + } + } + } } void Bars3DRenderer::updateScene(Q3DScene *scene) { - if (m_hasNegativeValues) + if (!m_noZeroInRange) { scene->activeCamera()->d_ptr->setMinYRotation(-90.0); - else - scene->activeCamera()->d_ptr->setMinYRotation(0.0f); + scene->activeCamera()->d_ptr->setMaxYRotation(90.0); + } else { + if ((m_hasNegativeValues && !m_axisCacheY.reversed()) + || (!m_hasNegativeValues && m_axisCacheY.reversed())) { + scene->activeCamera()->d_ptr->setMinYRotation(-90.0f); + scene->activeCamera()->d_ptr->setMaxYRotation(0.0); + } else { + scene->activeCamera()->d_ptr->setMinYRotation(0.0f); + scene->activeCamera()->d_ptr->setMaxYRotation(90.0); + } + } - if (m_hasHeightAdjustmentChanged) { + if (m_resetCameraBaseOrientation) { // Set initial camera position. Also update if height adjustment has changed. scene->activeCamera()->d_ptr->setBaseOrientation(cameraDistanceVector, zeroVector, upVector); - m_hasHeightAdjustmentChanged = false; + m_resetCameraBaseOrientation = false; } Abstract3DRenderer::updateScene(scene); @@ -281,6 +408,9 @@ void Bars3DRenderer::render(GLuint defaultFboHandle) // Handle GL state setup for FBO buffers and clearing of the render surface Abstract3DRenderer::render(defaultFboHandle); + if (m_axisCacheY.positionsDirty()) + m_axisCacheY.updateAllPositions(); + drawScene(defaultFboHandle); if (m_cachedIsSlicingActivated) drawSlicedScene(); @@ -290,7 +420,7 @@ void Bars3DRenderer::drawSlicedScene() { GLfloat barPosX = 0; QVector3D lightPos; - QVector3D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor()); + QVector4D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor()); static QQuaternion ninetyDegreeRotation = QQuaternion::fromAxisAndAngle(upVector, 90.0f); // Specify viewport @@ -301,8 +431,16 @@ void Bars3DRenderer::drawSlicedScene() // Set up projection matrix QMatrix4x4 projectionMatrix; - projectionMatrix.perspective(35.0f, (GLfloat)m_secondarySubViewport.width() - / (GLfloat)m_secondarySubViewport.height(), 0.1f, 100.0f); + GLfloat viewPortRatio = (GLfloat)m_primarySubViewport.width() + / (GLfloat)m_primarySubViewport.height(); + if (m_useOrthoProjection) { + GLfloat orthoRatio = 2.0f / m_autoScaleAdjustment; + projectionMatrix.ortho(-viewPortRatio * orthoRatio, viewPortRatio * orthoRatio, + -orthoRatio, orthoRatio, + 0.0f, 100.0f); + } else { + projectionMatrix.perspective(35.0f, viewPortRatio, 0.1f, 100.0f); + } // Set view matrix QMatrix4x4 viewMatrix; @@ -323,27 +461,37 @@ void Bars3DRenderer::drawSlicedScene() bool itemMode = m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionItem); GLfloat barPosYAdjustment = -0.8f; // Translate to -1.0 + 0.2 for row/column labels + GLfloat gridAdjustment = 1.0f + barPosYAdjustment - m_backgroundAdjustment; GLfloat scaleFactor = 0.0f; if (rowMode) scaleFactor = (1.1f * m_rowWidth) / m_scaleFactor; else scaleFactor = (1.1f * m_columnDepth) / m_scaleFactor; - GLfloat barLabelYPos = barPosYAdjustment - 0.4f - labelMargin; // 0.4 for labels + GLfloat barLabelYPos = barPosYAdjustment - labelMargin; GLfloat zeroPosAdjustment = 0.0f; - if (!m_noZeroInRange) - zeroPosAdjustment = 2.0f * m_axisCacheY.min() / m_heightNormalizer; - else if (m_hasNegativeValues) - zeroPosAdjustment = -2.0f; + GLfloat directionMultiplier = 2.0f; + GLfloat directionBase = 0.0f; + if (m_axisCacheY.reversed()) { + directionMultiplier = -2.0f; + directionBase = -2.0f; + } + zeroPosAdjustment = directionBase + + directionMultiplier * m_axisCacheY.min() / m_heightNormalizer; + zeroPosAdjustment = qBound(-2.0f, zeroPosAdjustment, 0.0f); // 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 // Bind line shader lineShader->bind(); // Set unchanging shader bindings - QVector3D lineColor = Utils::vectorFromColor(m_cachedTheme->gridLineColor()); + QVector4D lineColor = Utils::vectorFromColor(m_cachedTheme->gridLineColor()); lineShader->setUniformValue(lineShader->lightP(), lightPos); lineShader->setUniformValue(lineShader->view(), viewMatrix); lineShader->setUniformValue(lineShader->color(), lineColor); @@ -352,20 +500,18 @@ void Bars3DRenderer::drawSlicedScene() lineShader->setUniformValue(lineShader->lightS(), 0.0f); lineShader->setUniformValue(lineShader->lightColor(), lightColor); - GLfloat gridStep = (2.0f * m_axisCacheY.subSegmentStep()) / m_heightNormalizer; - GLfloat gridPos = barPosYAdjustment; - int lastSegment = m_axisCacheY.subSegmentCount() * m_axisCacheY.segmentCount(); - // Use the position of the bottom grid line as the base y position for bar labels - // Horizontal lines if (m_axisCacheY.segmentCount() > 0) { + int gridLineCount = m_axisCacheY.gridLineCount(); + QVector3D gridLineScale(scaleFactor, gridLineWidth, gridLineWidth); bool noZero = true; QMatrix4x4 MVPMatrix; QMatrix4x4 itModelMatrix; - for (int segment = 0; segment <= lastSegment; segment++) { + for (int line = 0; line < gridLineCount; line++) { QMatrix4x4 modelMatrix; + GLfloat gridPos = m_axisCacheY.gridLinePosition(line) + gridAdjustment; modelMatrix.translate(0.0f, gridPos, 0.0f); modelMatrix.scale(gridLineScale); itModelMatrix = modelMatrix; @@ -378,14 +524,17 @@ 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 // Check if we have a line at zero position already if (gridPos == (barPosYAdjustment + zeroPosAdjustment)) noZero = false; - - gridPos += gridStep; } + // Draw a line at zero, if none exists if (!m_noZeroInRange && noZero) { QMatrix4x4 modelMatrix; @@ -400,10 +549,15 @@ void Bars3DRenderer::drawSlicedScene() itModelMatrix.inverted().transposed()); lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); lineShader->setUniformValue(lineShader->color(), - Utils::vectorFromColor(m_cachedTheme->backgroundColor())); + Utils::vectorFromColor( + m_cachedTheme->labelTextColor())); // Draw the object +#if !(defined QT_OPENGL_ES_2) m_drawer->drawObject(lineShader, m_gridLineObj); +#else + m_drawer->drawLine(lineShader); +#endif } } @@ -417,24 +571,21 @@ void Bars3DRenderer::drawSlicedScene() // Draw grid labels int labelNbr = 0; - int labelCount = m_axisCacheY.labels().size(); - gridStep = (2.0f * m_axisCacheY.segmentStep()) / m_heightNormalizer; - gridPos = barPosYAdjustment; - QVector3D backLabelRotation(0.0f, 0.0f, 0.0f); + int labelCount = m_axisCacheY.labelCount(); QVector3D labelTrans = QVector3D(scaleFactor + labelMargin, 0.0f, 0.0f); for (int i = 0; i < labelCount; i++) { if (m_axisCacheY.labelItems().size() > labelNbr) { const LabelItem &axisLabelItem = *m_axisCacheY.labelItems().at(labelNbr); + GLfloat gridPos = m_axisCacheY.labelPosition(i) + gridAdjustment; labelTrans.setY(gridPos); m_dummyBarRenderItem.setTranslation(labelTrans); m_drawer->drawLabel(m_dummyBarRenderItem, axisLabelItem, viewMatrix, - projectionMatrix, zeroVector, backLabelRotation, 0, + projectionMatrix, zeroVector, identityQuaternion, 0, m_cachedSelectionMode, m_labelShader, m_labelObj, - activeCamera, true, true, Drawer::LabelMid, Qt::AlignRight); + activeCamera, true, true, Drawer::LabelMid, Qt::AlignLeft); } labelNbr++; - gridPos += gridStep; } glDisable(GL_TEXTURE_2D); glDisable(GL_BLEND); @@ -470,37 +621,40 @@ void Bars3DRenderer::drawSlicedScene() ShaderHelper *barShader = m_barShader; barShader->bind(); - int currentSeriesIndex = -1; Q3DTheme::ColorStyle previousColorStyle = Q3DTheme::ColorStyleUniform; Q3DTheme::ColorStyle colorStyle = Q3DTheme::ColorStyleUniform; ObjectHelper *barObj = 0; - QVector3D highlightColor; - QVector3D baseColor; + QVector4D highlightColor; + QVector4D baseColor; GLuint highlightGradientTexture = 0; GLuint baseGradientTexture = 0; - const SeriesRenderCache *currentSeries = 0; bool colorStyleIsUniform = true; + int firstVisualIndex = m_renderCacheList.size(); + QVector<BarRenderSliceItem> *firstVisualSliceArray = 0; + BarRenderSliceItem *selectedItem = 0; + + QQuaternion seriesRotation; + foreach (SeriesRenderCache *baseCache, m_renderCacheList) { + if (baseCache->isVisible() + && (baseCache == m_selectedSeriesCache + || m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionMultiSeries))) { + BarSeriesRenderCache *cache = static_cast<BarSeriesRenderCache *>(baseCache); + QVector<BarRenderSliceItem> &sliceArray = cache->sliceArray(); + int sliceCount = sliceArray.size(); + if (firstVisualIndex > cache->visualIndex()) { + firstVisualIndex = cache->visualIndex(); + firstVisualSliceArray = &sliceArray; + } - int sliceItemCount = m_sliceSelection.size(); - for (int bar = 0; bar < sliceItemCount; bar++) { - const BarRenderSliceItem &item = m_sliceSelection.at(bar); - if (!item.value()) - continue; - - QQuaternion seriesRotation; - - if (item.seriesIndex() != currentSeriesIndex) { - currentSeriesIndex = item.seriesIndex(); - currentSeries = &(m_visibleSeriesList.at(currentSeriesIndex)); - barObj = currentSeries->object(); - colorStyle = currentSeries->colorStyle(); + barObj = cache->object(); + colorStyle = cache->colorStyle(); colorStyleIsUniform = (colorStyle == Q3DTheme::ColorStyleUniform); if (colorStyleIsUniform) { - highlightColor = currentSeries->singleHighlightColor(); - baseColor = currentSeries->baseColor(); + highlightColor = cache->singleHighlightColor(); + baseColor = cache->baseColor(); } else { - highlightGradientTexture = currentSeries->singleHighlightGradientTexture(); - baseGradientTexture = currentSeries->baseGradientTexture(); + highlightGradientTexture = cache->singleHighlightGradientTexture(); + baseGradientTexture = cache->baseGradientTexture(); } // Rebind shader if it has changed @@ -510,7 +664,6 @@ void Bars3DRenderer::drawSlicedScene() else barShader = m_barGradientShader; barShader->bind(); - } if (!colorStyleIsUniform && (previousColorStyle != colorStyle) @@ -519,76 +672,88 @@ void Bars3DRenderer::drawSlicedScene() } previousColorStyle = colorStyle; - seriesRotation = currentSeries->meshRotation(); - } + seriesRotation = cache->meshRotation(); + bool selectedSeries = (cache == m_selectedSeriesCache); + + for (int bar = 0; bar < sliceCount; bar++) { + BarRenderSliceItem &item = cache->sliceArray()[bar]; + if (selectedSeries && itemMode && sliceGridLabels + && m_visualSelectedBarPos.x() == item.position().x() + && m_visualSelectedBarPos.y() == item.position().y()) { + selectedItem = &item; + } + if (!item.value()) + continue; - if (item.height() < 0) - glCullFace(GL_FRONT); - else - glCullFace(GL_BACK); + if (item.height() < 0) + glCullFace(GL_FRONT); + else + glCullFace(GL_BACK); - QMatrix4x4 MVPMatrix; - QMatrix4x4 modelMatrix; - QMatrix4x4 itModelMatrix; - QQuaternion barRotation = item.rotation(); - GLfloat barPosY = item.translation().y() + barPosYAdjustment - zeroPosAdjustment; + QMatrix4x4 MVPMatrix; + QMatrix4x4 modelMatrix; + QMatrix4x4 itModelMatrix; + QQuaternion barRotation = item.rotation(); + GLfloat barPosY = item.translation().y() + barPosYAdjustment - zeroPosAdjustment; + + if (rowMode) { + barPosX = item.translation().x(); + } else { + barPosX = -(item.translation().z()); // flip z; frontmost bar to the left + barRotation *= ninetyDegreeRotation; + } - if (rowMode) { - barPosX = item.translation().x(); - } else { - barPosX = -(item.translation().z()); // flip z; frontmost bar to the left - barRotation *= ninetyDegreeRotation; - } + modelMatrix.translate(barPosX, barPosY, 0.0f); + modelMatrixScaler.setY(item.height()); - modelMatrix.translate(barPosX, barPosY, 0.0f); - modelMatrixScaler.setY(item.height()); + if (!seriesRotation.isIdentity()) + barRotation *= seriesRotation; - if (!seriesRotation.isIdentity()) - barRotation *= seriesRotation; + if (!barRotation.isIdentity()) { + modelMatrix.rotate(barRotation); + itModelMatrix.rotate(barRotation); + } - if (!barRotation.isIdentity()) { - modelMatrix.rotate(barRotation); - itModelMatrix.rotate(barRotation); - } + modelMatrix.scale(modelMatrixScaler); + itModelMatrix.scale(modelMatrixScaler); - modelMatrix.scale(modelMatrixScaler); - itModelMatrix.scale(modelMatrixScaler); + MVPMatrix = projectionViewMatrix * modelMatrix; - MVPMatrix = projectionViewMatrix * modelMatrix; + QVector4D barColor; + GLuint gradientTexture = 0; - QVector3D barColor; - GLuint gradientTexture = 0; + if (itemMode && m_visualSelectedBarPos.x() == item.position().x() + && m_visualSelectedBarPos.y() == item.position().y()) { + if (colorStyleIsUniform) + barColor = highlightColor; + else + gradientTexture = highlightGradientTexture; + } else { + if (colorStyleIsUniform) + barColor = baseColor; + else + gradientTexture = baseGradientTexture; + } - if (itemMode && m_visualSelectedBarPos.x() == item.position().x() - && m_visualSelectedBarPos.y() == item.position().y()) { - if (colorStyleIsUniform) - barColor = highlightColor; - else - gradientTexture = highlightGradientTexture; - } else { - if (colorStyleIsUniform) - barColor = baseColor; - else - gradientTexture = baseGradientTexture; - } + if (item.height() != 0) { + // Set shader bindings + barShader->setUniformValue(barShader->model(), modelMatrix); + barShader->setUniformValue(barShader->nModel(), + itModelMatrix.inverted().transposed()); + barShader->setUniformValue(barShader->MVP(), MVPMatrix); + if (colorStyleIsUniform) { + barShader->setUniformValue(barShader->color(), barColor); + } else if (colorStyle == Q3DTheme::ColorStyleRangeGradient) { + barShader->setUniformValue(barShader->gradientHeight(), + (qAbs(item.height()) / m_gradientFraction)); + } - if (item.height() != 0) { - // Set shader bindings - barShader->setUniformValue(barShader->model(), modelMatrix); - barShader->setUniformValue(barShader->nModel(), - itModelMatrix.inverted().transposed()); - barShader->setUniformValue(barShader->MVP(), MVPMatrix); - if (colorStyleIsUniform) { - barShader->setUniformValue(barShader->color(), barColor); - } else if (colorStyle == Q3DTheme::ColorStyleRangeGradient) { - barShader->setUniformValue(barShader->gradientHeight(), - (qAbs(item.height()) / m_gradientFraction)); + // Draw the object + m_drawer->drawObject(barShader, + barObj, + gradientTexture); + } } - - // Draw the object - m_drawer->drawObject(barShader, - barObj, - gradientTexture); } } @@ -607,97 +772,111 @@ void Bars3DRenderer::drawSlicedScene() // Draw labels for bars QVector3D sliceValueRotation(0.0f, 0.0f, 90.0f); QVector3D sliceLabelRotation(0.0f, 0.0f, -45.0f); + QQuaternion totalSliceValueRotation = Utils::calculateRotation(sliceValueRotation); + QQuaternion totalSliceLabelRotation = Utils::calculateRotation(sliceLabelRotation); - int lastLabel = m_sliceCache->labelItems().size() - 1; + int labelCount = m_sliceCache->labelItems().size(); - for (int labelNo = 0; labelNo <= lastLabel; labelNo++) { + for (int labelNo = 0; labelNo < labelCount; labelNo++) { // Get labels from first series only - const BarRenderSliceItem &item = m_sliceSelection.at(labelNo); + const BarRenderSliceItem &item = firstVisualSliceArray->at(labelNo); m_dummyBarRenderItem.setTranslation(QVector3D(item.translation().x(), barLabelYPos, item.translation().z())); + // Draw labels m_drawer->drawLabel(m_dummyBarRenderItem, *m_sliceCache->labelItems().at(labelNo), - viewMatrix, projectionMatrix, positionComp, sliceLabelRotation, + viewMatrix, projectionMatrix, positionComp, totalSliceLabelRotation, 0, m_cachedSelectionMode, m_labelShader, m_labelObj, activeCamera, false, false, Drawer::LabelMid, - Qt::AlignRight, true); + Qt::AlignmentFlag(Qt::AlignLeft | Qt::AlignTop), true); } - for (int col = 0; col < sliceItemCount; col++) { - BarRenderSliceItem &item = m_sliceSelection[col]; - - if (!sliceGridLabels) { - // Draw values - if (item.height() != 0.0f || (!m_noZeroInRange && item.value() == 0.0f)) { - // Create label texture if we need it - if (item.sliceLabel().isNull() || m_updateLabels) { - item.setSliceLabel(generateValueLabel(m_axisCacheY.labelFormat(), - item.value())); - m_drawer->generateLabelItem(item.sliceLabelItem(), item.sliceLabel()); - m_updateLabels = false; - } - Qt::AlignmentFlag alignment = (item.height() < 0) ? Qt::AlignBottom : Qt::AlignTop; - Drawer::LabelPosition labelPos = (item.height() < 0) ? Drawer::LabelBelow : Drawer::LabelOver; - m_dummyBarRenderItem.setTranslation(QVector3D(item.translation().x(), - barPosYAdjustment - zeroPosAdjustment - + item.height(), - item.translation().z())); - - m_drawer->drawLabel(m_dummyBarRenderItem, item.sliceLabelItem(), viewMatrix, - projectionMatrix, zeroVector, sliceValueRotation, - item.height(), m_cachedSelectionMode, m_labelShader, - m_labelObj, activeCamera, false, false, labelPos, - alignment, true); - } - } else { - // Only draw value for selected item when grid labels are on - if (itemMode && m_visualSelectedBarPos.x() == item.position().x() - && m_visualSelectedBarPos.y() == item.position().y() - && item.seriesIndex() == m_visualSelectedBarSeriesIndex) { - // Create label texture if we need it - if (item.sliceLabel().isNull() || m_updateLabels) { - item.setSliceLabel(generateValueLabel(m_axisCacheY.labelFormat(), - item.value())); - m_drawer->generateLabelItem(item.sliceLabelItem(), item.sliceLabel()); - m_updateLabels = false; + if (!sliceGridLabels) { + foreach (SeriesRenderCache *baseCache, m_renderCacheList) { + if (baseCache->isVisible()) { + BarSeriesRenderCache *cache = static_cast<BarSeriesRenderCache *>(baseCache); + QVector<BarRenderSliceItem> &sliceArray = cache->sliceArray(); + int sliceCount = sliceArray.size(); + for (int col = 0; col < sliceCount; col++) { + BarRenderSliceItem &item = sliceArray[col]; + + // Draw values + if (item.height() != 0.0f || (!m_noZeroInRange && item.value() == 0.0f)) { + // Create label texture if we need it + if (item.sliceLabel().isNull() || m_updateLabels) { + QString valueLabelText = m_axisCacheY.formatter()->stringForValue( + qreal(item.value()), m_axisCacheY.labelFormat()); + item.setSliceLabel(valueLabelText); + m_drawer->generateLabelItem(item.sliceLabelItem(), item.sliceLabel()); + m_updateLabels = false; + } + Qt::AlignmentFlag alignment = + (item.height() > 0) ? Qt::AlignLeft : Qt::AlignRight; + Drawer::LabelPosition labelPos = + (item.height() < 0) ? Drawer::LabelBelow : Drawer::LabelOver; + m_dummyBarRenderItem.setTranslation(QVector3D(item.translation().x(), + barPosYAdjustment + - zeroPosAdjustment + + item.height(), + item.translation().z())); + + m_drawer->drawLabel(m_dummyBarRenderItem, item.sliceLabelItem(), viewMatrix, + projectionMatrix, zeroVector, totalSliceValueRotation, + item.height(), m_cachedSelectionMode, m_labelShader, + m_labelObj, activeCamera, false, false, labelPos, + alignment, true); + } } - Qt::AlignmentFlag alignment = (item.height() < 0) ? Qt::AlignBottom : Qt::AlignTop; - Drawer::LabelPosition labelPos = (item.height() < 0) ? Drawer::LabelBelow : Drawer::LabelOver; - m_dummyBarRenderItem.setTranslation(QVector3D(item.translation().x(), - barPosYAdjustment - zeroPosAdjustment - + item.height(), - item.translation().z())); - - m_drawer->drawLabel(m_dummyBarRenderItem, item.sliceLabelItem(), viewMatrix, - projectionMatrix, zeroVector, sliceValueRotation, - item.height(), m_cachedSelectionMode, m_labelShader, - m_labelObj, activeCamera, false, false, labelPos, - alignment, true); } } + } else if (selectedItem) { + // Only draw value for selected item when grid labels are on + // Create label texture if we need it + if (selectedItem->sliceLabel().isNull() || m_updateLabels) { + QString valueLabelText = m_axisCacheY.formatter()->stringForValue( + qreal(selectedItem->value()), m_axisCacheY.labelFormat()); + selectedItem->setSliceLabel(valueLabelText); + m_drawer->generateLabelItem(selectedItem->sliceLabelItem(), selectedItem->sliceLabel()); + m_updateLabels = false; + } + Qt::AlignmentFlag alignment = (selectedItem->height() > 0) ? Qt::AlignLeft : Qt::AlignRight; + Drawer::LabelPosition labelPos = + (selectedItem->height() < 0) ? Drawer::LabelBelow : Drawer::LabelOver; + m_dummyBarRenderItem.setTranslation(QVector3D(selectedItem->translation().x(), + barPosYAdjustment - zeroPosAdjustment + + selectedItem->height(), + selectedItem->translation().z())); + + m_drawer->drawLabel(m_dummyBarRenderItem, selectedItem->sliceLabelItem(), viewMatrix, + projectionMatrix, zeroVector, totalSliceValueRotation, + selectedItem->height(), m_cachedSelectionMode, m_labelShader, + m_labelObj, activeCamera, false, false, labelPos, + alignment, true); } // Draw labels for axes if (rowMode) { if (m_sliceTitleItem) { m_drawer->drawLabel(*dummyItem, sliceSelectionLabel, viewMatrix, projectionMatrix, - positionComp, zeroVector, 0, m_cachedSelectionMode, m_labelShader, - m_labelObj, activeCamera, false, false, Drawer::LabelTop, - Qt::AlignCenter, true); + positionComp, identityQuaternion, 0, m_cachedSelectionMode, + m_labelShader, m_labelObj, activeCamera, false, false, + Drawer::LabelTop, Qt::AlignCenter, true); } m_drawer->drawLabel(*dummyItem, m_axisCacheX.titleItem(), viewMatrix, projectionMatrix, - positionComp, zeroVector, 0, m_cachedSelectionMode, m_labelShader, - m_labelObj, activeCamera, false, false, Drawer::LabelBottom, - Qt::AlignCenter, true); + positionComp, identityQuaternion, 0, m_cachedSelectionMode, + m_labelShader, m_labelObj, activeCamera, false, false, + Drawer::LabelBottom, Qt::AlignCenter, true); } else { m_drawer->drawLabel(*dummyItem, m_axisCacheZ.titleItem(), viewMatrix, projectionMatrix, - positionComp, zeroVector, 0, m_cachedSelectionMode, m_labelShader, + positionComp, identityQuaternion, 0, m_cachedSelectionMode, + m_labelShader, m_labelObj, activeCamera, false, false, Drawer::LabelBottom, Qt::AlignCenter, true); if (m_sliceTitleItem) { m_drawer->drawLabel(*dummyItem, sliceSelectionLabel, viewMatrix, projectionMatrix, - positionComp, zeroVector, 0, m_cachedSelectionMode, m_labelShader, + positionComp, identityQuaternion, 0, m_cachedSelectionMode, + m_labelShader, m_labelObj, activeCamera, false, false, Drawer::LabelTop, Qt::AlignCenter, true); } @@ -706,9 +885,9 @@ void Bars3DRenderer::drawSlicedScene() QVector3D labelTrans = QVector3D(-scaleFactor - labelMargin, 0.2f, 0.0f); // y = 0.2 for row/column labels (see barPosYAdjustment) m_dummyBarRenderItem.setTranslation(labelTrans); m_drawer->drawLabel(m_dummyBarRenderItem, m_axisCacheY.titleItem(), viewMatrix, - projectionMatrix, zeroVector, QVector3D(0.0f, 0.0f, 90.0f), 0, + projectionMatrix, zeroVector, totalSliceValueRotation, 0, m_cachedSelectionMode, m_labelShader, m_labelObj, activeCamera, - false, false, Drawer::LabelMid, Qt::AlignHCenter); + false, false, Drawer::LabelMid, Qt::AlignBottom); glDisable(GL_TEXTURE_2D); glDisable(GL_BLEND); @@ -733,9 +912,7 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) GLfloat colPos = 0; GLfloat rowPos = 0; - QVector3D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor()); - - int seriesCount = m_visibleSeriesList.size(); + QVector4D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor()); const Q3DCamera *activeCamera = m_cachedScene->activeCamera(); @@ -748,7 +925,14 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) QMatrix4x4 projectionMatrix; GLfloat viewPortRatio = (GLfloat)m_primarySubViewport.width() / (GLfloat)m_primarySubViewport.height(); - projectionMatrix.perspective(45.0f, viewPortRatio, 0.1f, 100.0f); + if (m_useOrthoProjection) { + GLfloat orthoRatio = 2.0f; + projectionMatrix.ortho(-viewPortRatio * orthoRatio, viewPortRatio * orthoRatio, + -orthoRatio, orthoRatio, + 0.0f, 100.0f); + } else { + projectionMatrix.perspective(45.0f, viewPortRatio, 0.1f, 100.0f); + } // Get the view matrix QMatrix4x4 viewMatrix = activeCamera->d_ptr->viewMatrix(); @@ -808,6 +992,11 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) 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) { // Render scene into a depth texture for using with shadow mapping @@ -837,72 +1026,80 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) // Draw bars to depth buffer QVector3D shadowScaler(m_scaleX * m_seriesScaleX * 0.9f, 0.0f, m_scaleZ * m_seriesScaleZ * 0.9f); - float seriesPos = m_seriesStart; - for (int series = 0; series < seriesCount; series++) { - ObjectHelper *barObj = m_visibleSeriesList.at(series).object(); - QQuaternion seriesRotation(m_visibleSeriesList.at(series).meshRotation()); - for (int row = startRow; row != stopRow; row += stepRow) { - for (int bar = startBar; bar != stopBar; bar += stepBar) { - GLfloat shadowOffset = 0.0f; - const BarRenderItem &item = m_renderingArrays.at(series).at(row).at(bar); - if (!item.value()) - continue; - // Set front face culling for negative valued bars and back face culling for - // positive valued bars to remove peter-panning issues - if (item.height() > 0) { - glCullFace(GL_BACK); - if (m_yFlipped) - shadowOffset = 0.015f; - } else { - glCullFace(GL_FRONT); - if (!m_yFlipped) - shadowOffset = -0.015f; - } + foreach (SeriesRenderCache *baseCache, m_renderCacheList) { + if (baseCache->isVisible()) { + BarSeriesRenderCache *cache = static_cast<BarSeriesRenderCache *>(baseCache); + float seriesPos = m_seriesStart + m_seriesStep * cache->visualIndex() + 0.5f; + ObjectHelper *barObj = cache->object(); + QQuaternion seriesRotation(cache->meshRotation()); + const BarRenderItemArray &renderArray = cache->renderArray(); + for (int row = startRow; row != stopRow; row += stepRow) { + const BarRenderItemRow &renderRow = renderArray.at(row); + for (int bar = startBar; bar != stopBar; bar += stepBar) { + const BarRenderItem &item = renderRow.at(bar); + if (!item.value()) + continue; + GLfloat shadowOffset = 0.0f; + // Set front face culling for negative valued bars and back face culling + // for positive valued bars to remove peter-panning issues + if (item.height() > 0) { + glCullFace(GL_BACK); + if (m_yFlipped) + shadowOffset = 0.015f; + } else { + glCullFace(GL_FRONT); + if (!m_yFlipped) + shadowOffset = -0.015f; + } - QMatrix4x4 modelMatrix; - QMatrix4x4 MVPMatrix; + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; - colPos = (bar + 0.5f + seriesPos) * (m_cachedBarSpacing.width()); - rowPos = (row + 0.5f) * (m_cachedBarSpacing.height()); + colPos = (bar + seriesPos) * (m_cachedBarSpacing.width()); + rowPos = (row + 0.5f) * (m_cachedBarSpacing.height()); - // Draw shadows for bars "on the other side" a bit off ground to avoid seeing - // shadows through the ground - modelMatrix.translate((colPos - m_rowWidth) / m_scaleFactor, - item.height() + shadowOffset, - (m_columnDepth - rowPos) / m_scaleFactor); - // Scale the bars down in X and Z to reduce self-shadowing issues - shadowScaler.setY(item.height()); - if (!seriesRotation.isIdentity() || !item.rotation().isIdentity()) - modelMatrix.rotate(seriesRotation * item.rotation()); - modelMatrix.scale(shadowScaler); + // Draw shadows for bars "on the other side" a bit off ground to avoid + // seeing shadows through the ground + modelMatrix.translate((colPos - m_rowWidth) / m_scaleFactor, + item.height() + shadowOffset, + (m_columnDepth - rowPos) / m_scaleFactor); + // Scale the bars down in X and Z to reduce self-shadowing issues + shadowScaler.setY(item.height()); + if (!seriesRotation.isIdentity() || !item.rotation().isIdentity()) + modelMatrix.rotate(seriesRotation * item.rotation()); + modelMatrix.scale(shadowScaler); - MVPMatrix = depthProjectionViewMatrix * modelMatrix; + MVPMatrix = depthProjectionViewMatrix * modelMatrix; - m_depthShader->setUniformValue(m_depthShader->MVP(), MVPMatrix); + m_depthShader->setUniformValue(m_depthShader->MVP(), MVPMatrix); - // 1st attribute buffer : vertices - glEnableVertexAttribArray(m_depthShader->posAtt()); - glBindBuffer(GL_ARRAY_BUFFER, barObj->vertexBuf()); - glVertexAttribPointer(m_depthShader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0, - (void *)0); + // 1st attribute buffer : vertices + glEnableVertexAttribArray(m_depthShader->posAtt()); + glBindBuffer(GL_ARRAY_BUFFER, barObj->vertexBuf()); + glVertexAttribPointer(m_depthShader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0, + (void *)0); - // Index buffer - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, barObj->elementBuf()); + // Index buffer + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, barObj->elementBuf()); - // Draw the triangles - glDrawElements(GL_TRIANGLES, barObj->indexCount(), GL_UNSIGNED_SHORT, - (void *)0); + // Draw the triangles + glDrawElements(GL_TRIANGLES, barObj->indexCount(), GL_UNSIGNED_SHORT, + (void *)0); - // Free buffers - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - glBindBuffer(GL_ARRAY_BUFFER, 0); + // Free buffers + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); - glDisableVertexAttribArray(m_depthShader->posAtt()); + glDisableVertexAttribArray(m_depthShader->posAtt()); + } } } - seriesPos += m_seriesStep; } + Abstract3DRenderer::drawCustomItems(RenderingDepth, m_depthShader, viewMatrix, + projectionViewMatrix, depthProjectionViewMatrix, + m_depthTexture, m_shadowQualityToShader); + // Disable drawing to depth framebuffer (= enable drawing to screen) glBindFramebuffer(GL_FRAMEBUFFER, defaultFboHandle); @@ -919,7 +1116,8 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) // Skip selection mode drawing if we're slicing or have no selection mode if (!m_cachedIsSlicingActivated && m_cachedSelectionMode > QAbstract3DGraph::SelectionNone - && m_selectionState == SelectOnScene && seriesCount > 0) { + && m_selectionState == SelectOnScene + && (m_visibleSeriesCount > 0 || !m_customRenderCache.isEmpty())) { // Bind selection shader m_selectionShader->bind(); @@ -933,71 +1131,65 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) glClearColor(1.0f, 1.0f, 1.0f, 1.0f); // Set clear color to white (= selectionSkipColor) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Needed for clearing the frame buffer glDisable(GL_DITHER); // disable dithering, it may affect colors if enabled - float seriesPos = m_seriesStart; - for (int series = 0; series < seriesCount; series++) { - ObjectHelper *barObj = m_visibleSeriesList.at(series).object(); - QQuaternion seriesRotation(m_visibleSeriesList.at(series).meshRotation()); - for (int row = startRow; row != stopRow; row += stepRow) { - for (int bar = startBar; bar != stopBar; bar += stepBar) { - const BarRenderItem &item = m_renderingArrays.at(series).at(row).at(bar); - if (!item.value()) - continue; - - if (item.height() < 0) - glCullFace(GL_FRONT); - else - glCullFace(GL_BACK); - - QMatrix4x4 modelMatrix; - QMatrix4x4 MVPMatrix; - - colPos = (bar + 0.5f + seriesPos) * (m_cachedBarSpacing.width()); - rowPos = (row + 0.5f) * (m_cachedBarSpacing.height()); - - modelMatrix.translate((colPos - m_rowWidth) / m_scaleFactor, - item.height(), - (m_columnDepth - rowPos) / m_scaleFactor); - if (!seriesRotation.isIdentity() || !item.rotation().isIdentity()) - modelMatrix.rotate(seriesRotation * item.rotation()); - modelMatrix.scale(QVector3D(m_scaleX * m_seriesScaleX, - item.height(), - m_scaleZ * m_seriesScaleZ)); - - MVPMatrix = projectionViewMatrix * modelMatrix; + foreach (SeriesRenderCache *baseCache, m_renderCacheList) { + if (baseCache->isVisible()) { + BarSeriesRenderCache *cache = static_cast<BarSeriesRenderCache *>(baseCache); + float seriesPos = m_seriesStart + m_seriesStep * cache->visualIndex() + 0.5f; + ObjectHelper *barObj = cache->object(); + QQuaternion seriesRotation(cache->meshRotation()); + const BarRenderItemArray &renderArray = cache->renderArray(); + for (int row = startRow; row != stopRow; row += stepRow) { + const BarRenderItemRow &renderRow = renderArray.at(row); + for (int bar = startBar; bar != stopBar; bar += stepBar) { + const BarRenderItem &item = renderRow.at(bar); + if (!item.value()) + continue; + + if (item.height() < 0) + glCullFace(GL_FRONT); + else + glCullFace(GL_BACK); - QVector3D barColor = QVector3D(GLfloat(row) / 255.0f, - GLfloat(bar) / 255.0f, - GLfloat(series) / 255.0f); + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; - m_selectionShader->setUniformValue(m_selectionShader->MVP(), MVPMatrix); - m_selectionShader->setUniformValue(m_selectionShader->color(), barColor); + colPos = (bar + seriesPos) * (m_cachedBarSpacing.width()); + rowPos = (row + 0.5f) * (m_cachedBarSpacing.height()); - // 1st attribute buffer : vertices - glEnableVertexAttribArray(m_selectionShader->posAtt()); - glBindBuffer(GL_ARRAY_BUFFER, barObj->vertexBuf()); - glVertexAttribPointer(m_selectionShader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0, - (void *)0); + modelMatrix.translate((colPos - m_rowWidth) / m_scaleFactor, + item.height(), + (m_columnDepth - rowPos) / m_scaleFactor); + if (!seriesRotation.isIdentity() || !item.rotation().isIdentity()) + modelMatrix.rotate(seriesRotation * item.rotation()); + modelMatrix.scale(QVector3D(m_scaleX * m_seriesScaleX, + item.height(), + m_scaleZ * m_seriesScaleZ)); - // Index buffer - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, barObj->elementBuf()); + MVPMatrix = projectionViewMatrix * modelMatrix; - // Draw the triangles - glDrawElements(GL_TRIANGLES, barObj->indexCount(), GL_UNSIGNED_SHORT, - (void *)0); + QVector4D barColor = QVector4D(GLfloat(row) / 255.0f, + GLfloat(bar) / 255.0f, + GLfloat(cache->visualIndex()) / 255.0f, + itemAlpha); - // Free buffers - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - glBindBuffer(GL_ARRAY_BUFFER, 0); + m_selectionShader->setUniformValue(m_selectionShader->MVP(), MVPMatrix); + m_selectionShader->setUniformValue(m_selectionShader->color(), barColor); - glDisableVertexAttribArray(m_selectionShader->posAtt()); + m_drawer->drawSelectionObject(m_selectionShader, barObj); + } } } - seriesPos += m_seriesStep; } + glCullFace(GL_BACK); + Abstract3DRenderer::drawCustomItems(RenderingSelection, m_selectionShader, viewMatrix, + projectionViewMatrix, depthProjectionViewMatrix, + m_depthTexture, m_shadowQualityToShader); + drawLabels(true, activeCamera, viewMatrix, projectionMatrix, rowScaleFactor, + columnScaleFactor); glEnable(GL_DITHER); // Read color under cursor - QVector3D clickedColor = Utils::getSelection(m_inputPosition, + QVector4D clickedColor = Utils::getSelection(m_inputPosition, m_viewport.height()); m_clickedPosition = selectionColorToArrayPosition(clickedColor); m_clickedSeries = selectionColorToSeries(clickedColor); @@ -1018,18 +1210,9 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) ShaderHelper *barShader = 0; GLuint gradientTexture = 0; Q3DTheme::ColorStyle previousColorStyle = Q3DTheme::ColorStyleUniform; - bool haveUniformColorSeries = false; - bool haveGradientSeries = false; - - for (int i = 0; i < seriesCount; i++) { - if (m_visibleSeriesList.at(i).colorStyle() == Q3DTheme::ColorStyleUniform) - haveUniformColorSeries = true; - else - haveGradientSeries = true; - } // Set unchanging shader bindings - if (haveGradientSeries) { + if (m_haveGradientSeries) { m_barGradientShader->bind(); m_barGradientShader->setUniformValue(m_barGradientShader->lightP(), lightPos); m_barGradientShader->setUniformValue(m_barGradientShader->view(), viewMatrix); @@ -1039,7 +1222,7 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) m_barGradientShader->setUniformValue(m_barGradientShader->lightColor(), lightColor); } - if (haveUniformColorSeries) { + if (m_haveUniformColorSeries) { m_barShader->bind(); m_barShader->setUniformValue(m_barShader->lightP(), lightPos); m_barShader->setUniformValue(m_barShader->view(), viewMatrix); @@ -1052,17 +1235,13 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) previousColorStyle = Q3DTheme::ColorStyleRangeGradient; } + int sliceReserveAmount = 0; if (m_selectionDirty && m_cachedIsSlicingActivated) { // Slice doesn't own its items, no need to delete them - just clear - m_sliceSelection.clear(); - int reserveAmount; if (rowMode) - reserveAmount = m_cachedColumnCount; + sliceReserveAmount = m_cachedColumnCount; else - reserveAmount = m_cachedRowCount; - if (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionMultiSeries)) - reserveAmount *= m_visibleSeriesList.size(); - m_sliceSelection.resize(reserveAmount); + sliceReserveAmount = m_cachedRowCount; // Set slice cache, i.e. axis cache from where slice labels are taken if (rowMode) @@ -1073,259 +1252,253 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) } // Draw bars + glEnable(GL_POLYGON_OFFSET_FILL); + glPolygonOffset(0.5f, 1.0f); GLfloat adjustedLightStrength = m_cachedTheme->lightStrength() / 10.0f; GLfloat adjustedHighlightStrength = m_cachedTheme->highlightLightStrength() / 10.0f; bool barSelectionFound = false; - BarRenderItem *selectedBar(0); - QVector3D baseColor; - QVector3D barColor; + QVector4D baseColor; + QVector4D barColor; QVector3D modelScaler(m_scaleX * m_seriesScaleX, 0.0f, m_scaleZ * m_seriesScaleZ); - bool somethingSelected = (m_visualSelectedBarPos != Bars3DController::invalidSelectionPosition()); - float seriesPos = m_seriesStart; - for (int series = 0; series < seriesCount; series++) { - const SeriesRenderCache ¤tSeries = m_visibleSeriesList.at(series); - QQuaternion seriesRotation(currentSeries.meshRotation()); - ObjectHelper *barObj = currentSeries.object(); - Q3DTheme::ColorStyle colorStyle = currentSeries.colorStyle(); - bool colorStyleIsUniform = (colorStyle == Q3DTheme::ColorStyleUniform); - - // Rebind shader if it has changed - if (colorStyleIsUniform != (previousColorStyle == Q3DTheme::ColorStyleUniform)) { - if (colorStyleIsUniform) - barShader = m_barShader; - else - barShader = m_barGradientShader; - barShader->bind(); - } + bool somethingSelected = + (m_visualSelectedBarPos != Bars3DController::invalidSelectionPosition()); + foreach (SeriesRenderCache *baseCache, m_renderCacheList) { + if (baseCache->isVisible()) { + BarSeriesRenderCache *cache = static_cast<BarSeriesRenderCache *>(baseCache); + float seriesPos = m_seriesStart + m_seriesStep * cache->visualIndex() + 0.5f; + ObjectHelper *barObj = cache->object(); + QQuaternion seriesRotation(cache->meshRotation()); + Q3DTheme::ColorStyle colorStyle = cache->colorStyle(); + BarRenderItemArray &renderArray = cache->renderArray(); + bool colorStyleIsUniform = (colorStyle == Q3DTheme::ColorStyleUniform); + if (sliceReserveAmount) + cache->sliceArray().resize(sliceReserveAmount); - if (colorStyleIsUniform) { - baseColor = currentSeries.baseColor(); - } else if ((previousColorStyle != colorStyle) - && (colorStyle == Q3DTheme::ColorStyleObjectGradient)) { - m_barGradientShader->setUniformValue(m_barGradientShader->gradientHeight(), 0.5f); - } + // Rebind shader if it has changed + if (colorStyleIsUniform != (previousColorStyle == Q3DTheme::ColorStyleUniform)) { + if (colorStyleIsUniform) + barShader = m_barShader; + else + barShader = m_barGradientShader; + barShader->bind(); + } - // Always use base color when no selection mode - if (m_cachedSelectionMode == QAbstract3DGraph::SelectionNone) { - if (colorStyleIsUniform) - barColor = baseColor; - else - gradientTexture = currentSeries.baseGradientTexture(); - } + if (colorStyleIsUniform) { + baseColor = cache->baseColor(); + } else if ((previousColorStyle != colorStyle) + && (colorStyle == Q3DTheme::ColorStyleObjectGradient)) { + m_barGradientShader->setUniformValue(m_barGradientShader->gradientHeight(), 0.5f); + } - previousColorStyle = colorStyle; - int sliceSeriesAdjust = 0; - if (m_selectionDirty && m_cachedIsSlicingActivated) { - int seriesMultiplier = 0; - if (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionMultiSeries)) - seriesMultiplier = series; - if (rowMode) - sliceSeriesAdjust = seriesMultiplier * m_cachedColumnCount; - else - sliceSeriesAdjust = seriesMultiplier * m_cachedRowCount; - } + // Always use base color when no selection mode + if (m_cachedSelectionMode == QAbstract3DGraph::SelectionNone) { + if (colorStyleIsUniform) + barColor = baseColor; + else + gradientTexture = cache->baseGradientTexture(); + } - for (int row = startRow; row != stopRow; row += stepRow) { - for (int bar = startBar; bar != stopBar; bar += stepBar) { - BarRenderItem &item = m_renderingArrays[series][row][bar]; + previousColorStyle = colorStyle; - if (item.height() < 0) - glCullFace(GL_FRONT); - else - glCullFace(GL_BACK); + 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) + glCullFace(GL_FRONT); + else + glCullFace(GL_BACK); - QMatrix4x4 modelMatrix; - QMatrix4x4 itModelMatrix; - QMatrix4x4 MVPMatrix; + QMatrix4x4 modelMatrix; + QMatrix4x4 itModelMatrix; + QMatrix4x4 MVPMatrix; - colPos = (bar + 0.5f + seriesPos) * (m_cachedBarSpacing.width()); - rowPos = (row + 0.5f) * (m_cachedBarSpacing.height()); - - modelMatrix.translate((colPos - m_rowWidth) / m_scaleFactor, - item.height(), - (m_columnDepth - rowPos) / m_scaleFactor); - modelScaler.setY(item.height()); - if (!seriesRotation.isIdentity() || !item.rotation().isIdentity()) { - QQuaternion totalRotation = seriesRotation * item.rotation(); - modelMatrix.rotate(totalRotation); - itModelMatrix.rotate(totalRotation); - } - modelMatrix.scale(modelScaler); - itModelMatrix.scale(modelScaler); + colPos = (bar + seriesPos) * (m_cachedBarSpacing.width()); + rowPos = (row + 0.5f) * (m_cachedBarSpacing.height()); + + modelMatrix.translate((colPos - m_rowWidth) / m_scaleFactor, + item.height(), + (m_columnDepth - rowPos) / m_scaleFactor); + modelScaler.setY(item.height()); + if (!seriesRotation.isIdentity() || !item.rotation().isIdentity()) { + QQuaternion totalRotation = seriesRotation * item.rotation(); + modelMatrix.rotate(totalRotation); + itModelMatrix.rotate(totalRotation); + } + modelMatrix.scale(modelScaler); + itModelMatrix.scale(modelScaler); #ifdef SHOW_DEPTH_TEXTURE_SCENE - MVPMatrix = depthProjectionViewMatrix * modelMatrix; + MVPMatrix = depthProjectionViewMatrix * modelMatrix; #else - MVPMatrix = projectionViewMatrix * modelMatrix; + MVPMatrix = projectionViewMatrix * modelMatrix; #endif - GLfloat lightStrength = m_cachedTheme->lightStrength(); - GLfloat shadowLightStrength = adjustedLightStrength; - - if (m_cachedSelectionMode > QAbstract3DGraph::SelectionNone) { - Bars3DController::SelectionType selectionType = Bars3DController::SelectionNone; - if (somethingSelected) - selectionType = isSelected(row, bar, series); - - switch (selectionType) { - case Bars3DController::SelectionItem: { - if (colorStyleIsUniform) - barColor = currentSeries.singleHighlightColor(); - else - gradientTexture = currentSeries.singleHighlightGradientTexture(); - - lightStrength = m_cachedTheme->highlightLightStrength(); - shadowLightStrength = adjustedHighlightStrength; - // Insert position data into render item. We have no ownership, don't delete the previous one - if (!m_cachedIsSlicingActivated - && m_visualSelectedBarSeriesIndex == series) { - selectedBar = &item; - selectedBar->setPosition(QPoint(row, bar)); - item.setTranslation(modelMatrix.column(3).toVector3D()); - barSelectionFound = true; - } - if (m_selectionDirty && m_cachedIsSlicingActivated) { - QVector3D translation = modelMatrix.column(3).toVector3D(); - if (m_cachedSelectionMode & QAbstract3DGraph::SelectionColumn - && seriesCount > 1) { - translation.setZ((m_columnDepth - ((row + 0.5f + seriesPos) - * (m_cachedBarSpacing.height()))) - / m_scaleFactor); - } - item.setTranslation(translation); - item.setPosition(QPoint(row, bar)); - item.setSeriesIndex(series); - if (rowMode) - m_sliceSelection[sliceSeriesAdjust + bar].setItem(item); + GLfloat lightStrength = m_cachedTheme->lightStrength(); + GLfloat shadowLightStrength = adjustedLightStrength; + + if (m_cachedSelectionMode > QAbstract3DGraph::SelectionNone) { + Bars3DController::SelectionType selectionType = + Bars3DController::SelectionNone; + if (somethingSelected) + selectionType = isSelected(row, bar, cache); + + switch (selectionType) { + case Bars3DController::SelectionItem: { + if (colorStyleIsUniform) + barColor = cache->singleHighlightColor(); else - m_sliceSelection[sliceSeriesAdjust + row].setItem(item); - } - break; - } - case Bars3DController::SelectionRow: { - // Current bar is on the same row as the selected bar - if (colorStyleIsUniform) - barColor = currentSeries.multiHighlightColor(); - else - gradientTexture = currentSeries.multiHighlightGradientTexture(); - - lightStrength = m_cachedTheme->highlightLightStrength(); - shadowLightStrength = adjustedHighlightStrength; - if (m_cachedIsSlicingActivated) { - item.setTranslation(modelMatrix.column(3).toVector3D()); - item.setPosition(QPoint(row, bar)); - if (m_selectionDirty) { - item.setSeriesIndex(series); - if (!m_sliceTitleItem && m_axisCacheZ.labelItems().size() > row) - m_sliceTitleItem = m_axisCacheZ.labelItems().at(row); - m_sliceSelection[sliceSeriesAdjust + bar].setItem(item); + gradientTexture = cache->singleHighlightGradientTexture(); + + lightStrength = m_cachedTheme->highlightLightStrength(); + shadowLightStrength = adjustedHighlightStrength; + // Insert position data into render item + // We have no ownership, don't delete the previous one + if (!m_cachedIsSlicingActivated + && m_selectedSeriesCache == cache) { + selectedBar = &item; + selectedBar->setPosition(QPoint(row, bar)); + item.setTranslation(modelMatrix.column(3).toVector3D()); + barSelectionFound = true; + } + if (m_selectionDirty && m_cachedIsSlicingActivated) { + QVector3D translation = modelMatrix.column(3).toVector3D(); + if (m_cachedSelectionMode & QAbstract3DGraph::SelectionColumn + && m_visibleSeriesCount > 1) { + translation.setZ((m_columnDepth + - ((row + seriesPos) + * (m_cachedBarSpacing.height()))) + / m_scaleFactor); + } + item.setTranslation(translation); + item.setPosition(QPoint(row, bar)); + if (rowMode) + cache->sliceArray()[bar].setItem(item); + else + cache->sliceArray()[row].setItem(item); } + break; } - break; - } - case Bars3DController::SelectionColumn: { - // Current bar is on the same column as the selected bar - if (colorStyleIsUniform) - barColor = currentSeries.multiHighlightColor(); - else - gradientTexture = currentSeries.multiHighlightGradientTexture(); - - lightStrength = m_cachedTheme->highlightLightStrength(); - shadowLightStrength = adjustedHighlightStrength; - if (m_cachedIsSlicingActivated) { - QVector3D translation = modelMatrix.column(3).toVector3D(); - if (seriesCount > 1) { - translation.setZ((m_columnDepth - ((row + 0.5f + seriesPos) - * (m_cachedBarSpacing.height()))) - / m_scaleFactor); + case Bars3DController::SelectionRow: { + // Current bar is on the same row as the selected bar + if (colorStyleIsUniform) + barColor = cache->multiHighlightColor(); + else + gradientTexture = cache->multiHighlightGradientTexture(); + + lightStrength = m_cachedTheme->highlightLightStrength(); + shadowLightStrength = adjustedHighlightStrength; + if (m_cachedIsSlicingActivated) { + item.setTranslation(modelMatrix.column(3).toVector3D()); + item.setPosition(QPoint(row, bar)); + if (m_selectionDirty) { + if (!m_sliceTitleItem && m_axisCacheZ.labelItems().size() > row) + m_sliceTitleItem = m_axisCacheZ.labelItems().at(row); + cache->sliceArray()[bar].setItem(item); + } } - item.setTranslation(translation); - item.setPosition(QPoint(row, bar)); - if (m_selectionDirty) { - item.setSeriesIndex(series); - if (!m_sliceTitleItem && m_axisCacheX.labelItems().size() > bar) - m_sliceTitleItem = m_axisCacheX.labelItems().at(bar); - m_sliceSelection[sliceSeriesAdjust + row].setItem(item); + break; + } + case Bars3DController::SelectionColumn: { + // Current bar is on the same column as the selected bar + if (colorStyleIsUniform) + barColor = cache->multiHighlightColor(); + else + gradientTexture = cache->multiHighlightGradientTexture(); + + lightStrength = m_cachedTheme->highlightLightStrength(); + shadowLightStrength = adjustedHighlightStrength; + if (m_cachedIsSlicingActivated) { + QVector3D translation = modelMatrix.column(3).toVector3D(); + if (m_visibleSeriesCount > 1) { + translation.setZ((m_columnDepth + - ((row + seriesPos) + * (m_cachedBarSpacing.height()))) + / m_scaleFactor); + } + item.setTranslation(translation); + item.setPosition(QPoint(row, bar)); + if (m_selectionDirty) { + if (!m_sliceTitleItem && m_axisCacheX.labelItems().size() > bar) + m_sliceTitleItem = m_axisCacheX.labelItems().at(bar); + cache->sliceArray()[row].setItem(item); + } } + break; + } + case Bars3DController::SelectionNone: { + // Current bar is not selected, nor on a row or column + if (colorStyleIsUniform) + barColor = baseColor; + else + gradientTexture = cache->baseGradientTexture(); + break; + } } - break; - } - case Bars3DController::SelectionNone: { - // Current bar is not selected, nor on a row or column - if (colorStyleIsUniform) - barColor = baseColor; - else - gradientTexture = currentSeries.baseGradientTexture(); - break; - } } - } - // Skip drawing of 0-height bars - if (item.height() != 0) { - // Set shader bindings - barShader->setUniformValue(barShader->model(), modelMatrix); - barShader->setUniformValue(barShader->nModel(), - itModelMatrix.transposed().inverted()); - barShader->setUniformValue(barShader->MVP(), MVPMatrix); - if (colorStyleIsUniform) { - barShader->setUniformValue(barShader->color(), barColor); - } else if (colorStyle == Q3DTheme::ColorStyleRangeGradient) { - barShader->setUniformValue(barShader->gradientHeight(), - qAbs(item.height()) / m_gradientFraction); - } + // Skip drawing of 0-height bars + if (item.height() != 0) { + // Set shader bindings + barShader->setUniformValue(barShader->model(), modelMatrix); + barShader->setUniformValue(barShader->nModel(), + itModelMatrix.transposed().inverted()); + barShader->setUniformValue(barShader->MVP(), MVPMatrix); + if (colorStyleIsUniform) { + barShader->setUniformValue(barShader->color(), barColor); + } else if (colorStyle == Q3DTheme::ColorStyleRangeGradient) { + barShader->setUniformValue(barShader->gradientHeight(), + qAbs(item.height()) / m_gradientFraction); + } #if !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { - // Set shadow shader bindings - QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; - barShader->setUniformValue(barShader->shadowQ(), m_shadowQualityToShader); - barShader->setUniformValue(barShader->depth(), depthMVPMatrix); - barShader->setUniformValue(barShader->lightS(), shadowLightStrength); - barShader->setUniformValue(barShader->lightColor(), lightColor); - - // Draw the object - m_drawer->drawObject(barShader, barObj, gradientTexture, m_depthTexture); - } else + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + // Set shadow shader bindings + QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; + barShader->setUniformValue(barShader->shadowQ(), + m_shadowQualityToShader); + barShader->setUniformValue(barShader->depth(), depthMVPMatrix); + barShader->setUniformValue(barShader->lightS(), shadowLightStrength); + barShader->setUniformValue(barShader->lightColor(), lightColor); + + // Draw the object + m_drawer->drawObject(barShader, barObj, gradientTexture, + m_depthTexture); + } else #else - Q_UNUSED(shadowLightStrength); + Q_UNUSED(shadowLightStrength); #endif - { - // Set shadowless shader bindings - barShader->setUniformValue(barShader->lightS(), lightStrength); + { + // Set shadowless shader bindings + barShader->setUniformValue(barShader->lightS(), lightStrength); - // Draw the object - m_drawer->drawObject(barShader, barObj, gradientTexture); + // Draw the object + m_drawer->drawObject(barShader, barObj, gradientTexture); + } } } } } - seriesPos += m_seriesStep; } - // Bind background shader - m_backgroundShader->bind(); + glDisable(GL_POLYGON_OFFSET_FILL); // Reset culling glCullFace(GL_BACK); - // Draw background - GLfloat rowScaleFactor = m_rowWidth / m_scaleFactor; - GLfloat columnScaleFactor = m_columnDepth / m_scaleFactor; + // Bind background shader + m_backgroundShader->bind(); + // Draw background if (m_cachedTheme->isBackgroundEnabled() && m_backgroundObj) { QMatrix4x4 modelMatrix; QMatrix4x4 MVPMatrix; QMatrix4x4 itModelMatrix; QVector3D backgroundScaler(rowScaleFactor, 1.0f, columnScaleFactor); - if (m_hasNegativeValues) { - backgroundScaler.setY(0.5f); - modelMatrix.translate(0.0f, m_negativeBackgroundAdjustment, 0.0f); - } else { - modelMatrix.translate(0.0f, 1.0f, 0.0f); - } + modelMatrix.translate(0.0f, m_backgroundAdjustment, 0.0f); + modelMatrix.scale(backgroundScaler); itModelMatrix.scale(backgroundScaler); modelMatrix.rotate(backgroundRotation, 0.0f, 1.0f, 0.0f); @@ -1336,7 +1509,7 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) #else MVPMatrix = projectionViewMatrix * modelMatrix; #endif - QVector3D backgroundColor = Utils::vectorFromColor(m_cachedTheme->backgroundColor()); + QVector4D backgroundColor = Utils::vectorFromColor(m_cachedTheme->backgroundColor()); // Set shader bindings m_backgroundShader->setUniformValue(m_backgroundShader->lightP(), lightPos); @@ -1373,44 +1546,42 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) m_drawer->drawObject(m_backgroundShader, m_backgroundObj); } - // Draw floor for graph with negatives - if (m_hasNegativeValues) { - modelMatrix = QMatrix4x4(); - itModelMatrix = QMatrix4x4(); + // Draw floor + modelMatrix = QMatrix4x4(); + itModelMatrix = QMatrix4x4(); - modelMatrix.scale(backgroundScaler); + 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); + 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 = modelMatrix; #ifdef SHOW_DEPTH_TEXTURE_SCENE - MVPMatrix = depthProjectionViewMatrix * modelMatrix; + MVPMatrix = depthProjectionViewMatrix * modelMatrix; #else - MVPMatrix = projectionViewMatrix * modelMatrix; + 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); + // 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); #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 + 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(m_backgroundShader, m_gridLineObj); } } @@ -1418,15 +1589,19 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) glDisable(GL_TEXTURE_2D); // Draw grid lines - if (m_cachedTheme->isGridEnabled() && m_heightNormalizer) { + 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 QQuaternion lineRotation; // Bind bar shader lineShader->bind(); // Set unchanging shader bindings - QVector3D barColor = Utils::vectorFromColor(m_cachedTheme->gridLineColor()); + QVector4D barColor = Utils::vectorFromColor(m_cachedTheme->gridLineColor()); lineShader->setUniformValue(lineShader->lightP(), lightPos); lineShader->setUniformValue(lineShader->view(), viewMatrix); lineShader->setUniformValue(lineShader->color(), barColor); @@ -1446,11 +1621,9 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) m_cachedTheme->lightStrength() / 2.5f); } - GLfloat yFloorLinePosition = 0.0f; + GLfloat yFloorLinePosition = gridLineOffset; if (m_yFlipped) - yFloorLinePosition -= gridLineOffset; - else - yFloorLinePosition += gridLineOffset; + yFloorLinePosition = -yFloorLinePosition; QVector3D gridLineScaler(rowScaleFactor, gridLineWidth, gridLineWidth); @@ -1488,15 +1661,19 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); // Draw the object m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); - } else -#endif - { + } 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); for (GLfloat bar = 0.0f; bar <= m_cachedColumnCount; bar++) { QMatrix4x4 modelMatrix; @@ -1526,40 +1703,31 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); // Draw the object m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); - } else -#endif - { + } 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 - GLfloat heightStep = m_axisCacheY.subSegmentStep(); - GLfloat startLine = 0.0f; - int segmentCount = m_axisCacheY.segmentCount() * m_axisCacheY.subSegmentCount(); + int gridLineCount = m_axisCacheY.gridLineCount(); GLfloat zWallLinePosition = -columnScaleFactor + gridLineOffset; if (m_zFlipped) zWallLinePosition = -zWallLinePosition; - if (m_hasNegativeValues) { - if (m_noZeroInRange) - startLine = m_axisCacheY.min() - m_axisCacheY.max(); - else - startLine = m_axisCacheY.min(); - } - - GLfloat lineHeight = startLine; gridLineScaler = QVector3D(rowScaleFactor, gridLineWidth, gridLineWidth); - for (int segment = 0; segment <= segmentCount; segment++) { + for (int line = 0; line < gridLineCount; line++) { QMatrix4x4 modelMatrix; QMatrix4x4 MVPMatrix; QMatrix4x4 itModelMatrix; modelMatrix.translate(0.0f, - 2.0f * lineHeight / m_heightNormalizer, + m_axisCacheY.gridLinePosition(line), zWallLinePosition); modelMatrix.scale(gridLineScaler); itModelMatrix.scale(gridLineScaler); @@ -1583,13 +1751,13 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); // Draw the object m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); - } else -#endif - { + } else { // Draw the object m_drawer->drawObject(lineShader, m_gridLineObj); } - lineHeight += heightStep; +#else + m_drawer->drawLine(lineShader); +#endif } // Wall lines: side wall @@ -1602,15 +1770,14 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) else lineRotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, 90.0f); - lineHeight = startLine; gridLineScaler = QVector3D(gridLineWidth, gridLineWidth, columnScaleFactor); - for (int segment = 0; segment <= segmentCount; segment++) { + for (int line = 0; line < gridLineCount; line++) { QMatrix4x4 modelMatrix; QMatrix4x4 MVPMatrix; QMatrix4x4 itModelMatrix; modelMatrix.translate(xWallLinePosition, - 2.0f * lineHeight / m_heightNormalizer, + m_axisCacheY.gridLinePosition(line), 0.0f); modelMatrix.scale(gridLineScaler); itModelMatrix.scale(gridLineScaler); @@ -1632,251 +1799,457 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle) lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); // Draw the object m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); - } else -#endif - { + } else { // Draw the object m_drawer->drawObject(lineShader, m_gridLineObj); } - lineHeight += heightStep; +#else + m_drawer->drawLine(lineShader); +#endif } } } - // Bind label shader - m_labelShader->bind(); + 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) { + ShaderHelper *shader = 0; + GLfloat alphaForValueSelection = labelValueAlpha / 255.0f; + GLfloat alphaForRowSelection = labelRowAlpha / 255.0f; + GLfloat alphaForColumnSelection = labelColumnAlpha / 255.0f; + if (drawSelection) { + shader = m_selectionShader; + // m_selectionShader is already bound + } else { + shader = m_labelShader; + shader->bind(); + + glEnable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } - glEnable(GL_TEXTURE_2D); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_POLYGON_OFFSET_FILL); + float labelAutoAngle = m_axisCacheY.labelAutoRotation(); + float labelAngleFraction = labelAutoAngle / 90.0f; + float fractionCamY = activeCamera->yRotation() * labelAngleFraction; + float fractionCamX = activeCamera->xRotation() * labelAngleFraction; + float labelsMaxWidth = 0.0f; + + int startIndex; + int endIndex; + int indexStep; + // Y Labels - int labelNbr = 0; - GLfloat heightStep = m_axisCacheY.segmentStep(); - GLfloat startLine = 0.0f; - int labelCount = m_axisCacheY.labels().size(); - if (m_hasNegativeValues) { - if (m_noZeroInRange) - startLine = m_axisCacheY.min() - m_axisCacheY.max(); - else - startLine = m_axisCacheY.min(); - } - GLfloat labelPos = startLine; + int labelCount = m_axisCacheY.labelCount(); GLfloat labelMarginXTrans = labelMargin; GLfloat labelMarginZTrans = labelMargin; GLfloat labelXTrans = rowScaleFactor; GLfloat labelZTrans = columnScaleFactor; QVector3D backLabelRotation(0.0f, -90.0f, 0.0f); QVector3D sideLabelRotation(0.0f, 0.0f, 0.0f); - Qt::AlignmentFlag backAlignment = Qt::AlignLeft; - Qt::AlignmentFlag sideAlignment = Qt::AlignLeft; + Qt::AlignmentFlag backAlignment = (m_xFlipped != m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight; + Qt::AlignmentFlag sideAlignment = (m_xFlipped == m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight; + if (!m_xFlipped) { labelXTrans = -labelXTrans; labelMarginXTrans = -labelMargin; - backLabelRotation.setY(90.0f); - sideAlignment = Qt::AlignRight; } if (m_zFlipped) { labelZTrans = -labelZTrans; labelMarginZTrans = -labelMargin; - backAlignment = Qt::AlignRight; - sideLabelRotation.setY(180.f); } + + if (labelAutoAngle == 0.0f) { + if (!m_xFlipped) + backLabelRotation.setY(90.0f); + if (m_zFlipped) + sideLabelRotation.setY(180.f); + } else { + // Orient side labels somewhat towards the camera + if (m_xFlipped) { + if (m_zFlipped) + sideLabelRotation.setY(180.0f + (2.0f * labelAutoAngle) - fractionCamX); + else + sideLabelRotation.setY(-fractionCamX); + backLabelRotation.setY(-90.0f + labelAutoAngle - fractionCamX); + } else { + if (m_zFlipped) + sideLabelRotation.setY(180.0f - (2.0f * labelAutoAngle) - fractionCamX); + else + sideLabelRotation.setY(-fractionCamX); + backLabelRotation.setY(90.0f - labelAutoAngle - fractionCamX); + } + } + sideLabelRotation.setX(-fractionCamY); + backLabelRotation.setX(-fractionCamY); + + QQuaternion totalSideRotation = Utils::calculateRotation(sideLabelRotation); + QQuaternion totalBackRotation = Utils::calculateRotation(backLabelRotation); + QVector3D backLabelTrans = QVector3D(labelXTrans, 0.0f, labelZTrans + labelMarginZTrans); QVector3D sideLabelTrans = QVector3D(-labelXTrans - labelMarginXTrans, 0.0f, -labelZTrans); - for (int i = 0; i < labelCount; i++) { - if (m_axisCacheY.labelItems().size() > labelNbr) { - backLabelTrans.setY(2.0f * labelPos / m_heightNormalizer); - sideLabelTrans.setY(backLabelTrans.y()); - - glPolygonOffset(GLfloat(i) / -10.0f, 1.0f); + if (m_yFlipped) { + startIndex = labelCount - 1; + endIndex = -1; + indexStep = -1; + } else { + startIndex = 0; + endIndex = labelCount; + indexStep = 1; + } + for (int i = startIndex; i != endIndex; i = i + indexStep) { + backLabelTrans.setY(m_axisCacheY.labelPosition(i)); + sideLabelTrans.setY(backLabelTrans.y()); - const LabelItem &axisLabelItem = *m_axisCacheY.labelItems().at(labelNbr); + glPolygonOffset(GLfloat(i) / -10.0f, 1.0f); - // Back wall - m_dummyBarRenderItem.setTranslation(backLabelTrans); - m_drawer->drawLabel(m_dummyBarRenderItem, axisLabelItem, viewMatrix, projectionMatrix, - zeroVector, backLabelRotation, 0, m_cachedSelectionMode, - m_labelShader, m_labelObj, activeCamera, - true, true, Drawer::LabelMid, backAlignment); + const LabelItem &axisLabelItem = *m_axisCacheY.labelItems().at(i); - // Side wall - m_dummyBarRenderItem.setTranslation(sideLabelTrans); - m_drawer->drawLabel(m_dummyBarRenderItem, axisLabelItem, viewMatrix, projectionMatrix, - zeroVector, sideLabelRotation, 0, m_cachedSelectionMode, - m_labelShader, m_labelObj, activeCamera, - true, true, Drawer::LabelMid, sideAlignment); + if (drawSelection) { + QVector4D labelColor = QVector4D(0.0f, 0.0f, i / 255.0f, + alphaForValueSelection); + shader->setUniformValue(shader->color(), labelColor); } - labelNbr++; - labelPos += heightStep; + + // Back wall + m_dummyBarRenderItem.setTranslation(backLabelTrans); + m_drawer->drawLabel(m_dummyBarRenderItem, axisLabelItem, viewMatrix, projectionMatrix, + zeroVector, totalBackRotation, 0, m_cachedSelectionMode, + shader, m_labelObj, activeCamera, + true, true, Drawer::LabelMid, backAlignment, false, drawSelection); + + // Side wall + m_dummyBarRenderItem.setTranslation(sideLabelTrans); + m_drawer->drawLabel(m_dummyBarRenderItem, axisLabelItem, viewMatrix, projectionMatrix, + zeroVector, totalSideRotation, 0, m_cachedSelectionMode, + shader, m_labelObj, activeCamera, + true, true, Drawer::LabelMid, sideAlignment, false, drawSelection); + + labelsMaxWidth = qMax(labelsMaxWidth, float(axisLabelItem.size().width())); + } + + if (!drawSelection && m_axisCacheY.isTitleVisible()) { + sideLabelTrans.setY(m_backgroundAdjustment); + backLabelTrans.setY(m_backgroundAdjustment); + drawAxisTitleY(sideLabelRotation, backLabelRotation, sideLabelTrans, backLabelTrans, + totalSideRotation, totalBackRotation, m_dummyBarRenderItem, activeCamera, + labelsMaxWidth, viewMatrix, projectionMatrix, shader); } + // Z labels // Calculate the positions for row and column labels and store them + labelsMaxWidth = 0.0f; + labelAutoAngle = m_axisCacheZ.labelAutoRotation(); + labelAngleFraction = labelAutoAngle / 90.0f; + 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; - QVector3D labelRotation(-90.0f, 0.0f, 0.0f); - if (m_zFlipped) - labelRotation.setY(180.0f); - if (m_yFlipped) { + GLfloat rowPos = 0.0f; + GLfloat colPos = 0.0f; + Qt::AlignmentFlag alignment = (m_xFlipped == m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight; + QVector3D labelRotation; + + if (labelAutoAngle == 0.0f) { + labelRotation.setX(-90.0f); if (m_zFlipped) - labelRotation.setY(0.0f); - else labelRotation.setY(180.0f); - labelRotation.setZ(180.0f); - } - - Qt::AlignmentFlag alignment = m_xFlipped ? Qt::AlignLeft : Qt::AlignRight; - for (int row = 0; row != m_cachedRowCount; row++) { - if (m_axisCacheZ.labelItems().size() > row) { - // Go through all rows and get position of max+1 or min-1 column, depending on x flip - // We need only positions for them, labels have already been generated - rowPos = (row + 0.5f) * m_cachedBarSpacing.height(); - if (m_xFlipped) - colPos = -colPosValue; + if (m_yFlipped) { + if (m_zFlipped) + labelRotation.setY(0.0f); else - colPos = colPosValue; - - glPolygonOffset(GLfloat(row) / -10.0f, 1.0f); - - QVector3D labelPos = QVector3D(colPos, - labelYAdjustment, // raise a bit over background to avoid depth "glimmering" - (m_columnDepth - rowPos) / m_scaleFactor); - - m_dummyBarRenderItem.setTranslation(labelPos); - const LabelItem &axisLabelItem = *m_axisCacheZ.labelItems().at(row); - - m_drawer->drawLabel(m_dummyBarRenderItem, axisLabelItem, viewMatrix, projectionMatrix, - zeroVector, labelRotation, 0, m_cachedSelectionMode, - m_labelShader, m_labelObj, activeCamera, - true, true, Drawer::LabelMid, alignment, m_cachedIsSlicingActivated); + labelRotation.setY(180.0f); + labelRotation.setZ(180.0f); + } + } else { + if (m_zFlipped) + labelRotation.setY(180.0f); + if (m_yFlipped) { + if (m_zFlipped) { + if (m_xFlipped) { + labelRotation.setX(90.0f - (labelAutoAngle - fractionCamX) + * (-labelAutoAngle - fractionCamY) / labelAutoAngle); + labelRotation.setZ(labelAutoAngle + fractionCamY); + } else { + labelRotation.setX(90.0f + (labelAutoAngle + fractionCamX) + * (labelAutoAngle + fractionCamY) / labelAutoAngle); + labelRotation.setZ(-labelAutoAngle - fractionCamY); + } + } else { + if (m_xFlipped) { + labelRotation.setX(90.0f + (labelAutoAngle - fractionCamX) + * -(labelAutoAngle + fractionCamY) / labelAutoAngle); + labelRotation.setZ(-labelAutoAngle - fractionCamY); + } else { + labelRotation.setX(90.0f - (labelAutoAngle + fractionCamX) + * (labelAutoAngle + fractionCamY) / labelAutoAngle); + labelRotation.setZ(labelAutoAngle + fractionCamY); + } + } + } else { + if (m_zFlipped) { + if (m_xFlipped) { + labelRotation.setX(-90.0f + (labelAutoAngle - fractionCamX) + * (-labelAutoAngle + fractionCamY) / labelAutoAngle); + labelRotation.setZ(-labelAutoAngle + fractionCamY); + } else { + labelRotation.setX(-90.0f - (labelAutoAngle + fractionCamX) + * (labelAutoAngle - fractionCamY) / labelAutoAngle); + labelRotation.setZ(labelAutoAngle - fractionCamY); + } + } else { + if (m_xFlipped) { + labelRotation.setX(-90.0f - (labelAutoAngle - fractionCamX) + * (-labelAutoAngle + fractionCamY) / labelAutoAngle); + labelRotation.setZ(labelAutoAngle - fractionCamY); + } else { + labelRotation.setX(-90.0f + (labelAutoAngle + fractionCamX) + * (labelAutoAngle - fractionCamY) / labelAutoAngle); + labelRotation.setZ(-labelAutoAngle + fractionCamY); + } + } } } - labelRotation = QVector3D(-90.0f, 90.0f, 0.0f); - if (m_xFlipped) - labelRotation.setY(-90.0f); - if (m_yFlipped) { + + QQuaternion totalRotation = Utils::calculateRotation(labelRotation); + labelCount = qMin(m_axisCacheZ.labelCount(), m_cachedRowCount); + if (m_zFlipped) { + startIndex = 0; + endIndex = labelCount; + indexStep = 1; + } else { + startIndex = labelCount - 1; + endIndex = -1; + indexStep = -1; + } + for (int row = startIndex; row != endIndex; row = row + indexStep) { + // Go through all rows and get position of max+1 or min-1 column, depending on x flip + // We need only positions for them, labels have already been generated + rowPos = (row + 0.5f) * m_cachedBarSpacing.height(); if (m_xFlipped) - labelRotation.setY(90.0f); + colPos = -colPosValue; else - labelRotation.setY(-90.0f); - labelRotation.setZ(180.0f); - } - - alignment = m_zFlipped ? Qt::AlignRight : Qt::AlignLeft; - for (int column = 0; column != m_cachedColumnCount; column++) { - if (m_axisCacheX.labelItems().size() > column) { - // Go through all columns and get position of max+1 or min-1 row, depending on z flip - // We need only positions for them, labels have already been generated - colPos = (column + 0.5f) * m_cachedBarSpacing.width(); - if (m_zFlipped) - rowPos = -rowPosValue; - else - rowPos = rowPosValue; + colPos = colPosValue; - glPolygonOffset(GLfloat(column) / -10.0f, 1.0f); + glPolygonOffset(GLfloat(row) / -10.0f, 1.0f); - QVector3D labelPos = QVector3D((colPos - m_rowWidth) / m_scaleFactor, - labelYAdjustment, // raise a bit over background to avoid depth "glimmering" - rowPos); + QVector3D labelPos = QVector3D(colPos, + labelYAdjustment, // raise a bit over background to avoid depth "glimmering" + (m_columnDepth - rowPos) / m_scaleFactor); - m_dummyBarRenderItem.setTranslation(labelPos); - const LabelItem &axisLabelItem = *m_axisCacheX.labelItems().at(column); + m_dummyBarRenderItem.setTranslation(labelPos); + const LabelItem &axisLabelItem = *m_axisCacheZ.labelItems().at(row); - m_drawer->drawLabel(m_dummyBarRenderItem, axisLabelItem, viewMatrix, projectionMatrix, - zeroVector, labelRotation, 0, m_cachedSelectionMode, - m_labelShader, m_labelObj, activeCamera, - true, true, Drawer::LabelMid, alignment); + if (drawSelection) { + QVector4D labelColor = QVector4D(row / 255.0f, 0.0f, 0.0f, alphaForRowSelection); + shader->setUniformValue(shader->color(), labelColor); } + + m_drawer->drawLabel(m_dummyBarRenderItem, axisLabelItem, viewMatrix, projectionMatrix, + zeroVector, totalRotation, 0, m_cachedSelectionMode, + shader, m_labelObj, activeCamera, + true, true, Drawer::LabelMid, alignment, + false, drawSelection); + labelsMaxWidth = qMax(labelsMaxWidth, float(axisLabelItem.size().width())); } - glDisable(GL_POLYGON_OFFSET_FILL); + if (!drawSelection && m_axisCacheZ.isTitleVisible()) { + QVector3D titleTrans(colPos, 0.0f, 0.0f); + drawAxisTitleZ(labelRotation, titleTrans, totalRotation, m_dummyBarRenderItem, + activeCamera, labelsMaxWidth, viewMatrix, projectionMatrix, shader); + } - // 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) { - static const QString rowIndexTag(QStringLiteral("@rowIdx")); - static const QString rowLabelTag(QStringLiteral("@rowLabel")); - static const QString rowTitleTag(QStringLiteral("@rowTitle")); - static const QString colIndexTag(QStringLiteral("@colIdx")); - static const QString colLabelTag(QStringLiteral("@colLabel")); - static const QString colTitleTag(QStringLiteral("@colTitle")); - static const QString valueTitleTag(QStringLiteral("@valueTitle")); - static const QString valueLabelTag(QStringLiteral("@valueLabel")); - static const QString seriesNameTag(QStringLiteral("@seriesName")); - - // Custom format expects printf format specifier. There is no tag for it. - labelText = generateValueLabel( - m_visibleSeriesList[m_visualSelectedBarSeriesIndex].itemLabelFormat(), - selectedBar->value()); - - int selBarPosRow = selectedBar->position().x(); - int selBarPosCol = selectedBar->position().y(); - labelText.replace(rowIndexTag, QString::number(selBarPosRow)); - if (m_axisCacheZ.labels().size() > selBarPosRow) - labelText.replace(rowLabelTag, m_axisCacheZ.labels().at(selBarPosRow)); - else - labelText.replace(rowLabelTag, QString()); - labelText.replace(rowTitleTag, m_axisCacheZ.title()); - labelText.replace(colIndexTag, QString::number(selBarPosCol)); - if (m_axisCacheX.labels().size() > selBarPosCol) - labelText.replace(colLabelTag, m_axisCacheX.labels().at(selBarPosCol)); - else - labelText.replace(colLabelTag, QString()); - labelText.replace(colTitleTag, m_axisCacheX.title()); - labelText.replace(valueTitleTag, m_axisCacheY.title()); - - if (labelText.contains(valueLabelTag)) { - QString labelFormat = m_axisCacheY.labelFormat(); - if (labelFormat.isEmpty()) - labelFormat = Utils::defaultLabelFormat(); - QString valueLabelText = generateValueLabel(labelFormat, selectedBar->value()); - labelText.replace(valueLabelTag, valueLabelText); + // X labels + labelsMaxWidth = 0.0f; + labelAutoAngle = m_axisCacheX.labelAutoRotation(); + labelAngleFraction = labelAutoAngle / 90.0f; + fractionCamY = activeCamera->yRotation() * labelAngleFraction; + fractionCamX = activeCamera->xRotation() * labelAngleFraction; + alignment = (m_xFlipped != m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight; + if (labelAutoAngle == 0.0f) { + labelRotation = QVector3D(-90.0f, 90.0f, 0.0f); + if (m_xFlipped) + labelRotation.setY(-90.0f); + if (m_yFlipped) { + if (m_xFlipped) + labelRotation.setY(90.0f); + else + labelRotation.setY(-90.0f); + labelRotation.setZ(180.0f); + } + } else { + if (m_xFlipped) + labelRotation.setY(-90.0f); + else + labelRotation.setY(90.0f); + if (m_yFlipped) { + if (m_zFlipped) { + if (m_xFlipped) { + labelRotation.setX(90.0f - (2.0f * labelAutoAngle - fractionCamX) + * (labelAutoAngle + fractionCamY) / labelAutoAngle); + labelRotation.setZ(-labelAutoAngle - fractionCamY); + } else { + labelRotation.setX(90.0f - (2.0f * labelAutoAngle + fractionCamX) + * (labelAutoAngle + fractionCamY) / labelAutoAngle); + labelRotation.setZ(labelAutoAngle + fractionCamY); + } + } else { + if (m_xFlipped) { + labelRotation.setX(90.0f + fractionCamX + * -(labelAutoAngle + fractionCamY) / labelAutoAngle); + labelRotation.setZ(labelAutoAngle + fractionCamY); + } else { + labelRotation.setX(90.0f - fractionCamX + * (-labelAutoAngle - fractionCamY) / labelAutoAngle); + labelRotation.setZ(-labelAutoAngle - fractionCamY); + } + } + } else { + if (m_zFlipped) { + if (m_xFlipped) { + labelRotation.setX(-90.0f + (2.0f * labelAutoAngle - fractionCamX) + * (labelAutoAngle - fractionCamY) / labelAutoAngle); + labelRotation.setZ(labelAutoAngle - fractionCamY); + } else { + labelRotation.setX(-90.0f + (2.0f * labelAutoAngle + fractionCamX) + * (labelAutoAngle - fractionCamY) / labelAutoAngle); + labelRotation.setZ(-labelAutoAngle + fractionCamY); + } + } else { + if (m_xFlipped) { + labelRotation.setX(-90.0f - fractionCamX + * (-labelAutoAngle + fractionCamY) / labelAutoAngle); + labelRotation.setZ(-labelAutoAngle + fractionCamY); + } else { + labelRotation.setX(-90.0f + fractionCamX + * -(labelAutoAngle - fractionCamY) / labelAutoAngle); + labelRotation.setZ(labelAutoAngle - fractionCamY); } - - labelText.replace(seriesNameTag, - m_visibleSeriesList[m_visualSelectedBarSeriesIndex].name()); - - setSelectionLabel(labelText); - m_selectionLabelDirty = false; } - m_drawer->generateLabelItem(labelItem, labelText); - m_selectedBar = selectedBar; } + } - m_drawer->drawLabel(*selectedBar, labelItem, viewMatrix, projectionMatrix, - zeroVector, zeroVector, selectedBar->height(), - m_cachedSelectionMode, m_labelShader, - m_labelObj, activeCamera, true, false); - - // Reset label update flag; they should have been updated when we get here - m_updateLabels = false; - - glEnable(GL_DEPTH_TEST); + totalRotation = Utils::calculateRotation(labelRotation); + labelCount = qMin(m_axisCacheX.labelCount(), m_cachedColumnCount); + if (m_xFlipped) { + startIndex = labelCount - 1; + endIndex = -1; + indexStep = -1; } else { - m_selectedBar = 0; + startIndex = 0; + endIndex = labelCount; + indexStep = 1; } + for (int column = startIndex; column != endIndex; column = column + indexStep) { + // Go through all columns and get position of max+1 or min-1 row, depending on z flip + // We need only positions for them, labels have already been generated + colPos = (column + 0.5f) * m_cachedBarSpacing.width(); + if (m_zFlipped) + rowPos = -rowPosValue; + else + rowPos = rowPosValue; - glDisable(GL_TEXTURE_2D); - glDisable(GL_BLEND); + glPolygonOffset(GLfloat(column) / -10.0f, 1.0f); - // Release shader - glUseProgram(0); + QVector3D labelPos = QVector3D((colPos - m_rowWidth) / m_scaleFactor, + labelYAdjustment, // raise a bit over background to avoid depth "glimmering" + rowPos); - m_selectionDirty = false; + m_dummyBarRenderItem.setTranslation(labelPos); + const LabelItem &axisLabelItem = *m_axisCacheX.labelItems().at(column); + + if (drawSelection) { + QVector4D labelColor = QVector4D(0.0f, column / 255.0f, 0.0f, + alphaForColumnSelection); + shader->setUniformValue(shader->color(), labelColor); + } + + m_drawer->drawLabel(m_dummyBarRenderItem, axisLabelItem, viewMatrix, projectionMatrix, + zeroVector, totalRotation, 0, m_cachedSelectionMode, + shader, m_labelObj, activeCamera, + true, true, Drawer::LabelMid, alignment, false, drawSelection); + labelsMaxWidth = qMax(labelsMaxWidth, float(axisLabelItem.size().width())); + } + + if (!drawSelection && m_axisCacheX.isTitleVisible()) { + QVector3D titleTrans(0.0f, 0.0f, rowPos); + drawAxisTitleX(labelRotation, titleTrans, totalRotation, m_dummyBarRenderItem, + activeCamera, labelsMaxWidth, viewMatrix, projectionMatrix, shader); + } + +#if 0 // Debug label + static LabelItem debugLabelItem; + QString debugLabelString(QStringLiteral("Flips: x:%1 y:%2 z:%3 xr:%4 yr:%5")); + QString finalDebugString = debugLabelString.arg(m_xFlipped).arg(m_yFlipped).arg(m_zFlipped) + .arg(activeCamera->xRotation()).arg(activeCamera->yRotation()); + m_dummyBarRenderItem.setTranslation(QVector3D(m_xFlipped ? -1.5f : 1.5f, + m_yFlipped ? 1.5f : -1.5f, + m_zFlipped ? -1.5f : 1.5f)); + + m_drawer->generateLabelItem(debugLabelItem, finalDebugString); + m_drawer->drawLabel(m_dummyBarRenderItem, debugLabelItem, viewMatrix, projectionMatrix, + zeroVector, identityQuaternion, 0, m_cachedSelectionMode, + shader, m_labelObj, activeCamera, + true, false, Drawer::LabelMid, Qt::AlignHCenter, false, drawSelection); +#endif + glDisable(GL_POLYGON_OFFSET_FILL); } void Bars3DRenderer::updateMultiSeriesScaling(bool uniform) @@ -1884,7 +2257,7 @@ void Bars3DRenderer::updateMultiSeriesScaling(bool uniform) m_keepSeriesUniform = uniform; // Recalculate scale factors - m_seriesScaleX = 1.0f / float(m_visibleSeriesList.size()); + m_seriesScaleX = 1.0f / float(m_visibleSeriesCount); if (m_keepSeriesUniform) m_seriesScaleZ = m_seriesScaleX; else @@ -1920,46 +2293,41 @@ void Bars3DRenderer::updateAxisRange(QAbstract3DAxis::AxisOrientation orientatio Abstract3DRenderer::updateAxisRange(orientation, min, max); if (orientation == QAbstract3DAxis::AxisOrientationY) { - calculateHeightAdjustment(); // Check if we have negative values - if (min < 0 && !m_hasNegativeValues) { + if (min < 0) m_hasNegativeValues = true; - // Reload background - loadBackgroundMesh(); - emit needRender(); - } else if (min >= 0 && m_hasNegativeValues) { + else if (min >= 0) m_hasNegativeValues = false; - // Reload background - loadBackgroundMesh(); - emit needRender(); - } + calculateHeightAdjustment(); } } -void Bars3DRenderer::updateSelectedBar(const QPoint &position, const QBar3DSeries *series) +void Bars3DRenderer::updateAxisReversed(QAbstract3DAxis::AxisOrientation orientation, bool enable) +{ + Abstract3DRenderer::updateAxisReversed(orientation, enable); + if (orientation == QAbstract3DAxis::AxisOrientationY) + calculateHeightAdjustment(); +} + + +void Bars3DRenderer::updateSelectedBar(const QPoint &position, QBar3DSeries *series) { m_selectedBarPos = position; - m_selectedBarSeries = series; + m_selectedSeriesCache = static_cast<BarSeriesRenderCache *>(m_renderCacheList.value(series, 0)); m_selectionDirty = true; m_selectionLabelDirty = true; - m_visualSelectedBarSeriesIndex = -1; - if (m_renderingArrays.isEmpty()) { + if (!m_selectedSeriesCache + || !m_selectedSeriesCache->isVisible() + || m_selectedSeriesCache->renderArray().isEmpty()) { m_visualSelectedBarPos = Bars3DController::invalidSelectionPosition(); return; } int adjustedZ = m_selectedBarPos.x() - int(m_axisCacheZ.min()); int adjustedX = m_selectedBarPos.y() - int(m_axisCacheX.min()); - int maxZ = m_renderingArrays.at(0).size() - 1; - int maxX = maxZ >= 0 ? m_renderingArrays.at(0).at(0).size() - 1 : -1; - - for (int i = 0; i < m_visibleSeriesList.size(); i++) { - if (m_visibleSeriesList.at(i).series() == series) { - m_visualSelectedBarSeriesIndex = i; - break; - } - } + int maxZ = m_selectedSeriesCache->renderArray().size() - 1; + int maxX = maxZ >= 0 ? m_selectedSeriesCache->renderArray().at(0).size() - 1 : -1; if (m_selectedBarPos == Bars3DController::invalidSelectionPosition() || adjustedZ < 0 || adjustedZ > maxZ @@ -2020,29 +2388,8 @@ void Bars3DRenderer::updateShadowQuality(QAbstract3DGraph::ShadowQuality quality void Bars3DRenderer::loadBackgroundMesh() { - if (m_backgroundObj) - delete m_backgroundObj; - if (m_hasNegativeValues) - m_backgroundObj = new ObjectHelper(QStringLiteral(":/defaultMeshes/negativeBackground")); - else - m_backgroundObj = new ObjectHelper(QStringLiteral(":/defaultMeshes/background")); - m_backgroundObj->load(); -} - -void Bars3DRenderer::loadGridLineMesh() -{ - if (m_gridLineObj) - delete m_gridLineObj; - m_gridLineObj = new ObjectHelper(QStringLiteral(":/defaultMeshes/plane")); - m_gridLineObj->load(); -} - -void Bars3DRenderer::loadLabelMesh() -{ - if (m_labelObj) - delete m_labelObj; - m_labelObj = new ObjectHelper(QStringLiteral(":/defaultMeshes/plane")); - m_labelObj->load(); + ObjectHelper::resetObjectHelper(this, m_backgroundObj, + QStringLiteral(":/defaultMeshes/backgroundNoFloor")); } void Bars3DRenderer::updateTextures() @@ -2075,7 +2422,7 @@ void Bars3DRenderer::calculateSceneScalingFactors() void Bars3DRenderer::calculateHeightAdjustment() { - GLfloat newAdjustment = 0.0f; + GLfloat newAdjustment = 1.0f; GLfloat maxAbs = qFabs(m_axisCacheY.max()); if (m_axisCacheY.max() < 0.0f) { @@ -2086,7 +2433,8 @@ void Bars3DRenderer::calculateHeightAdjustment() } // Height fractions are used in gradient calculations and are therefore doubled - if (m_axisCacheY.max() < 0.0f || m_axisCacheY.min() > 0.0f) { + // 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) { m_noZeroInRange = true; m_gradientFraction = 2.0f; } else { @@ -2095,22 +2443,24 @@ void Bars3DRenderer::calculateHeightAdjustment() m_gradientFraction = qMax(minAbs, maxAbs) / m_heightNormalizer * 2.0f; } - // Calculate translation adjustment for negative background - newAdjustment = qBound(0.0f, (maxAbs / m_heightNormalizer), 1.0f) * 2.0f - 0.5f; + // Calculate translation adjustment for background floor + newAdjustment = (qBound(0.0f, (maxAbs / m_heightNormalizer), 1.0f) - 0.5f) * 2.0f; + if (m_axisCacheY.reversed()) + newAdjustment = -newAdjustment; - if (newAdjustment != m_negativeBackgroundAdjustment) { - m_hasHeightAdjustmentChanged = true; - m_negativeBackgroundAdjustment = newAdjustment; + if (newAdjustment != m_backgroundAdjustment) { + m_backgroundAdjustment = newAdjustment; + m_axisCacheY.setTranslate(m_backgroundAdjustment - 1.0f); } } -Bars3DController::SelectionType Bars3DRenderer::isSelected(int row, int bar, int seriesIndex) +Bars3DController::SelectionType Bars3DRenderer::isSelected(int row, int bar, + const BarSeriesRenderCache *cache) { Bars3DController::SelectionType isSelectedType = Bars3DController::SelectionNone; if ((m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionMultiSeries) - && m_visualSelectedBarSeriesIndex >= 0) - || seriesIndex == m_visualSelectedBarSeriesIndex) { + && m_selectedSeriesCache) || cache == m_selectedSeriesCache) { if (row == m_visualSelectedBarPos.x() && bar == m_visualSelectedBarPos.y() && (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionItem))) { isSelectedType = Bars3DController::SelectionItem; @@ -2126,24 +2476,68 @@ Bars3DController::SelectionType Bars3DRenderer::isSelected(int row, int bar, int return isSelectedType; } -QPoint Bars3DRenderer::selectionColorToArrayPosition(const QVector3D &selectionColor) +QPoint Bars3DRenderer::selectionColorToArrayPosition(const QVector4D &selectionColor) { - QPoint position; - if (selectionColor == selectionSkipColor) { - position = Bars3DController::invalidSelectionPosition(); - } else { + QPoint position = Bars3DController::invalidSelectionPosition(); + m_clickedType = QAbstract3DGraph::ElementNone; + m_selectedLabelIndex = -1; + m_selectedCustomItemIndex = -1; + if (selectionColor.w() == itemAlpha) { + // Normal selection item position = QPoint(int(selectionColor.x() + int(m_axisCacheZ.min())), int(selectionColor.y()) + int(m_axisCacheX.min())); + // Pass item clicked info to input handler + m_clickedType = QAbstract3DGraph::ElementSeries; + } else if (selectionColor.w() == labelRowAlpha) { + // Row selection + if (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionRow)) { + // Use column from previous selection in case we have row + column mode + GLint previousCol = qMax(0, m_selectedBarPos.y()); // Use 0 if previous is invalid + position = QPoint(int(selectionColor.x() + int(m_axisCacheZ.min())), previousCol); + } + m_selectedLabelIndex = selectionColor.x(); + // Pass label clicked info to input handler + m_clickedType = QAbstract3DGraph::ElementAxisZLabel; + } else if (selectionColor.w() == labelColumnAlpha) { + // Column selection + if (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionColumn)) { + // Use row from previous selection in case we have row + column mode + GLint previousRow = qMax(0, m_selectedBarPos.x()); // Use 0 if previous is invalid + position = QPoint(previousRow, int(selectionColor.y()) + int(m_axisCacheX.min())); + } + m_selectedLabelIndex = selectionColor.y(); + // Pass label clicked info to input handler + m_clickedType = QAbstract3DGraph::ElementAxisXLabel; + } else if (selectionColor.w() == labelValueAlpha) { + // Value selection + position = Bars3DController::invalidSelectionPosition(); + m_selectedLabelIndex = selectionColor.z(); + // Pass label clicked info to input handler + m_clickedType = QAbstract3DGraph::ElementAxisYLabel; + } else if (selectionColor.w() == customItemAlpha) { + // Custom item selection + position = Bars3DController::invalidSelectionPosition(); + m_selectedCustomItemIndex = int(selectionColor.x()) + + (int(selectionColor.y()) << 8) + + (int(selectionColor.z()) << 16); + m_clickedType = QAbstract3DGraph::ElementCustomItem; } return position; } -QBar3DSeries *Bars3DRenderer::selectionColorToSeries(const QVector3D &selectionColor) +QBar3DSeries *Bars3DRenderer::selectionColorToSeries(const QVector4D &selectionColor) { - if (selectionColor == selectionSkipColor) + if (selectionColor == selectionSkipColor) { return 0; - else - return static_cast<QBar3DSeries *>(m_visibleSeriesList.at(int(selectionColor.z())).series()); + } else { + int seriesIndexFromColor(selectionColor.z()); + foreach (SeriesRenderCache *baseCache, m_renderCacheList) { + BarSeriesRenderCache *cache = static_cast<BarSeriesRenderCache *>(baseCache); + if (cache->visualIndex() == seriesIndexFromColor) + return cache->series(); + } + } + return 0; } void Bars3DRenderer::updateSlicingActive(bool isSlicing) @@ -2190,10 +2584,7 @@ void Bars3DRenderer::initSelectionShader() void Bars3DRenderer::initSelectionBuffer() { - if (m_selectionTexture) { - m_textureHelper->deleteTexture(&m_selectionTexture); - m_selectionTexture = 0; - } + m_textureHelper->deleteTexture(&m_selectionTexture); if (m_cachedIsSlicingActivated || m_primarySubViewport.size().isEmpty()) return; @@ -2215,10 +2606,7 @@ void Bars3DRenderer::initDepthShader() void Bars3DRenderer::updateDepthBuffer() { - if (m_depthTexture) { - m_textureHelper->deleteTexture(&m_depthTexture); - m_depthTexture = 0; - } + m_textureHelper->deleteTexture(&m_depthTexture); if (m_primarySubViewport.size().isEmpty()) return; @@ -2250,4 +2638,29 @@ void Bars3DRenderer::initLabelShaders(const QString &vertexShader, const QString m_labelShader->initialize(); } +QVector3D Bars3DRenderer::convertPositionToTranslation(const QVector3D &position, bool isAbsolute) +{ + float xTrans = 0.0f; + float yTrans = 0.0f; + 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; + yTrans = m_axisCacheY.positionAt(position.y()); + } else { + xTrans = position.x() * m_scaleX; + yTrans = position.y() + m_backgroundAdjustment; + zTrans = position.z() * m_scaleZ; + } + return QVector3D(xTrans, yTrans, zTrans); +} + +void Bars3DRenderer::updateAspectRatio(float ratio) +{ + Q_UNUSED(ratio) +} + QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/engine/bars3drenderer_p.h b/src/datavisualization/engine/bars3drenderer_p.h index 37ac2b76..3a0ab3b8 100644 --- a/src/datavisualization/engine/bars3drenderer_p.h +++ b/src/datavisualization/engine/bars3drenderer_p.h @@ -32,19 +32,17 @@ #include "datavisualizationglobal_p.h" #include "bars3dcontroller_p.h" #include "abstract3drenderer_p.h" -#include "qbardataproxy.h" #include "barrenderitem_p.h" class QPoint; class QSizeF; -class QOpenGLShaderProgram; QT_BEGIN_NAMESPACE_DATAVISUALIZATION class ShaderHelper; -class ObjectHelper; class LabelItem; class Q3DScene; +class BarSeriesRenderCache; class QT_DATAVISUALIZATION_EXPORT Bars3DRenderer : public Abstract3DRenderer { @@ -60,12 +58,8 @@ private: // Internal state BarRenderItem *m_selectedBar; // points to renderitem array - QVector<BarRenderSliceItem> m_sliceSelection; AxisRenderCache *m_sliceCache; // not owned const LabelItem *m_sliceTitleItem; // not owned - bool m_xFlipped; - bool m_zFlipped; - bool m_yFlipped; bool m_updateLabels; ShaderHelper *m_barShader; ShaderHelper *m_barGradientShader; @@ -73,9 +67,6 @@ private: ShaderHelper *m_selectionShader; ShaderHelper *m_backgroundShader; ShaderHelper *m_labelShader; - ObjectHelper *m_backgroundObj; - ObjectHelper *m_gridLineObj; - ObjectHelper *m_labelObj; GLuint m_bgrTexture; GLuint m_depthTexture; GLuint m_selectionTexture; @@ -86,7 +77,7 @@ private: GLint m_shadowQualityMultiplier; GLfloat m_heightNormalizer; GLfloat m_gradientFraction; - GLfloat m_negativeBackgroundAdjustment; + GLfloat m_backgroundAdjustment; GLfloat m_rowWidth; GLfloat m_columnDepth; GLfloat m_maxDimension; @@ -95,12 +86,10 @@ private: GLfloat m_scaleFactor; GLfloat m_maxSceneSize; QPoint m_visualSelectedBarPos; - int m_visualSelectedBarSeriesIndex; - bool m_hasHeightAdjustmentChanged; + bool m_resetCameraBaseOrientation; QPoint m_selectedBarPos; - const QBar3DSeries *m_selectedBarSeries; + BarSeriesRenderCache *m_selectedSeriesCache; BarRenderItem m_dummyBarRenderItem; - QVector<BarRenderItemArray> m_renderingArrays; bool m_noZeroInRange; float m_seriesScaleX; float m_seriesScaleZ; @@ -108,15 +97,26 @@ private: float m_seriesStart; QPoint m_clickedPosition; bool m_keepSeriesUniform; + bool m_haveUniformColorSeries; + bool m_haveGradientSeries; + float m_zeroPosition; public: explicit Bars3DRenderer(Bars3DController *controller); ~Bars3DRenderer(); void updateData(); + void updateSeries(const QList<QAbstract3DSeries *> &seriesList); + SeriesRenderCache *createNewCache(QAbstract3DSeries *series); + void updateRows(const QVector<Bars3DController::ChangeRow> &rows); + void updateItems(const QVector<Bars3DController::ChangeItem> &items); void updateScene(Q3DScene *scene); void render(GLuint defaultFboHandle = 0); + QVector3D convertPositionToTranslation(const QVector3D &position, bool isAbsolute); + + void updateAspectRatio(float ratio); + protected: virtual void initializeOpenGL(); @@ -126,13 +126,15 @@ public slots: const QSizeF &spacing = QSizeF(1.0, 1.0), bool relative = true); void updateSlicingActive(bool isSlicing); - void updateSelectedBar(const QPoint &position, const QBar3DSeries *series); + void updateSelectedBar(const QPoint &position, QBar3DSeries *series); inline QPoint clickedPosition() const { return m_clickedPosition; } void resetClickedStatus(); // Overloaded from abstract renderer virtual void updateAxisRange(QAbstract3DAxis::AxisOrientation orientation, float min, float max); + virtual void updateAxisReversed(QAbstract3DAxis::AxisOrientation orientation, + bool enable); private: virtual void initShaders(const QString &vertexShader, const QString &fragmentShader); @@ -143,10 +145,11 @@ private: void drawSlicedScene(); void drawScene(GLuint defaultFboHandle); + void drawLabels(bool drawSelection, const Q3DCamera *activeCamera, + const QMatrix4x4 &viewMatrix, const QMatrix4x4 &projectionMatrix, + GLfloat rowScaleFactor, GLfloat columnScaleFactor); void loadBackgroundMesh(); - void loadGridLineMesh(); - void loadLabelMesh(); void initSelectionShader(); void initBackgroundShaders(const QString &vertexShader, const QString &fragmentShader); void initLabelShaders(const QString &vertexShader, const QString &fragmentShader); @@ -157,13 +160,15 @@ private: #endif void calculateSceneScalingFactors(); void calculateHeightAdjustment(); - Abstract3DController::SelectionType isSelected(int row, int bar, int seriesIndex); - QPoint selectionColorToArrayPosition(const QVector3D &selectionColor); - QBar3DSeries *selectionColorToSeries(const QVector3D &selectionColor); + Abstract3DController::SelectionType isSelected(int row, int bar, + const BarSeriesRenderCache *cache); + QPoint selectionColorToArrayPosition(const QVector4D &selectionColor); + QBar3DSeries *selectionColorToSeries(const QVector4D &selectionColor); - Q_DISABLE_COPY(Bars3DRenderer) + inline void updateRenderRow(const QBarDataRow *dataRow, BarRenderItemRow &renderRow); + inline void updateRenderItem(const QBarDataItem &dataItem, BarRenderItem &renderItem); - friend class BarRenderItem; + Q_DISABLE_COPY(Bars3DRenderer) }; QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/engine/barseriesrendercache.cpp b/src/datavisualization/engine/barseriesrendercache.cpp new file mode 100644 index 00000000..95da3680 --- /dev/null +++ b/src/datavisualization/engine/barseriesrendercache.cpp @@ -0,0 +1,42 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include "barseriesrendercache_p.h" + +QT_BEGIN_NAMESPACE_DATAVISUALIZATION + +BarSeriesRenderCache::BarSeriesRenderCache(QAbstract3DSeries *series, + Abstract3DRenderer *renderer) + : SeriesRenderCache(series, renderer), + m_visualIndex(-1) +{ +} + +BarSeriesRenderCache::~BarSeriesRenderCache() +{ +} + +void BarSeriesRenderCache::cleanup(TextureHelper *texHelper) +{ + m_renderArray.clear(); + m_sliceArray.clear(); + + SeriesRenderCache::cleanup(texHelper); +} + +QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/engine/barseriesrendercache_p.h b/src/datavisualization/engine/barseriesrendercache_p.h new file mode 100644 index 00000000..4e169300 --- /dev/null +++ b/src/datavisualization/engine/barseriesrendercache_p.h @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the QtDataVisualization API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#ifndef BARSERIESRENDERCACHE_P_H +#define BARSERIESRENDERCACHE_P_H + +#include "datavisualizationglobal_p.h" +#include "seriesrendercache_p.h" +#include "qbar3dseries_p.h" +#include "barrenderitem_p.h" + +QT_BEGIN_NAMESPACE_DATAVISUALIZATION + +class BarSeriesRenderCache : public SeriesRenderCache +{ +public: + BarSeriesRenderCache(QAbstract3DSeries *series, Abstract3DRenderer *renderer); + virtual ~BarSeriesRenderCache(); + + void cleanup(TextureHelper *texHelper); + + inline BarRenderItemArray &renderArray() { return m_renderArray; } + inline QBar3DSeries *series() const { return static_cast<QBar3DSeries *>(m_series); } + inline QVector<BarRenderSliceItem> &sliceArray() { return m_sliceArray; } + inline void setVisualIndex(int index) { m_visualIndex = index; } + inline int visualIndex() {return m_visualIndex; } + +protected: + BarRenderItemArray m_renderArray; + QVector<BarRenderSliceItem> m_sliceArray; + int m_visualIndex; // order of the series is relevant +}; + +QT_END_NAMESPACE_DATAVISUALIZATION + +#endif diff --git a/src/datavisualization/engine/drawer.cpp b/src/datavisualization/engine/drawer.cpp index 55a2c2a5..b8726840 100644 --- a/src/datavisualization/engine/drawer.cpp +++ b/src/datavisualization/engine/drawer.cpp @@ -18,13 +18,11 @@ #include "drawer_p.h" #include "shaderhelper_p.h" -#include "objecthelper_p.h" -#include "abstractobjecthelper_p.h" #include "surfaceobject_p.h" -#include "q3dcamera.h" #include "utils_p.h" #include "texturehelper_p.h" #include "abstract3drenderer_p.h" +#include "scatterpointbufferhelper_p.h" #include <QtGui/QMatrix4x4> #include <QtCore/qmath.h> @@ -45,18 +43,28 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION // Vertex array buffer for point const GLfloat point_data[] = {0.0f, 0.0f, 0.0f}; +// Vertex array buffer for line +const GLfloat line_data[] = { + -1.0f, 0.0f, 0.0f, + 1.0f, 0.0f, 0.0f, +}; + Drawer::Drawer(Q3DTheme *theme) : m_theme(theme), m_textureHelper(0), - m_pointbuffer(0) + m_pointbuffer(0), + m_linebuffer(0), + m_scaledFontSize(0.0f) { } Drawer::~Drawer() { delete m_textureHelper; - if (QOpenGLContext::currentContext()) + if (QOpenGLContext::currentContext()) { glDeleteBuffers(1, &m_pointbuffer); + glDeleteBuffers(1, &m_linebuffer); + } } void Drawer::initializeOpenGL() @@ -70,6 +78,7 @@ void Drawer::initializeOpenGL() void Drawer::setTheme(Q3DTheme *theme) { m_theme = theme; + m_scaledFontSize = 0.05f + m_theme->font().pointSizeF() / 500.0f; emit drawerChanged(); } @@ -140,6 +149,18 @@ void Drawer::drawObject(ShaderHelper *shader, AbstractObjectHelper *object, GLui } } +void Drawer::drawSelectionObject(ShaderHelper *shader, AbstractObjectHelper *object) +{ + glEnableVertexAttribArray(shader->posAtt()); + glBindBuffer(GL_ARRAY_BUFFER, object->vertexBuf()); + glVertexAttribPointer(shader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0, (void *)0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, object->elementBuf()); + glDrawElements(GL_TRIANGLES, object->indexCount(), GL_UNSIGNED_SHORT, (void *)0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glDisableVertexAttribArray(shader->posAtt()); +} + void Drawer::drawSurfaceGrid(ShaderHelper *shader, SurfaceObject *object) { // 1st attribute buffer : vertices @@ -162,6 +183,8 @@ void Drawer::drawSurfaceGrid(ShaderHelper *shader, SurfaceObject *object) void Drawer::drawPoint(ShaderHelper *shader) { + // Draw a single point + // Generate vertex buffer for point if it does not exist if (!m_pointbuffer) { glGenBuffers(1, &m_pointbuffer); @@ -183,13 +206,55 @@ void Drawer::drawPoint(ShaderHelper *shader) glDisableVertexAttribArray(shader->posAtt()); } +void Drawer::drawPoints(ShaderHelper *shader, ScatterPointBufferHelper *object) +{ + // 1st attribute buffer : vertices + glEnableVertexAttribArray(shader->posAtt()); + glBindBuffer(GL_ARRAY_BUFFER, object->pointBuf()); + glVertexAttribPointer(shader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0, (void*)0); + + // Draw the points + glDrawArrays(GL_POINTS, 0, object->indexCount()); + + // Free buffers + glBindBuffer(GL_ARRAY_BUFFER, 0); + + glDisableVertexAttribArray(shader->posAtt()); +} + +void Drawer::drawLine(ShaderHelper *shader) +{ + // Draw a single line + + // Generate vertex buffer for line if it does not exist + if (!m_linebuffer) { + glGenBuffers(1, &m_linebuffer); + glBindBuffer(GL_ARRAY_BUFFER, m_linebuffer); + glBufferData(GL_ARRAY_BUFFER, sizeof(line_data), line_data, GL_STATIC_DRAW); + } + + // 1st attribute buffer : vertices + glEnableVertexAttribArray(shader->posAtt()); + glBindBuffer(GL_ARRAY_BUFFER, m_linebuffer); + glVertexAttribPointer(shader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0, (void*)0); + + // Draw the line + glDrawArrays(GL_LINES, 0, 2); + + // Free buffers + glBindBuffer(GL_ARRAY_BUFFER, 0); + + glDisableVertexAttribArray(shader->posAtt()); +} + void Drawer::drawLabel(const AbstractRenderItem &item, const LabelItem &labelItem, const QMatrix4x4 &viewmatrix, const QMatrix4x4 &projectionmatrix, - const QVector3D &positionComp, const QVector3D &rotation, + const QVector3D &positionComp, const QQuaternion &rotation, GLfloat itemHeight, QAbstract3DGraph::SelectionFlags mode, ShaderHelper *shader, ObjectHelper *object, const Q3DCamera *camera, bool useDepth, bool rotateAlong, - LabelPosition position, Qt::AlignmentFlag alignment, bool isSlicing) + LabelPosition position, Qt::AlignmentFlag alignment, bool isSlicing, + bool isSelecting) { // Draw label if (!labelItem.textureId()) @@ -246,68 +311,20 @@ void Drawer::drawLabel(const AbstractRenderItem &item, const LabelItem &labelIte } // Calculate scale factor to get uniform font size - GLfloat scaledFontSize = 0.05f + m_theme->font().pointSizeF() / 500.0f; - GLfloat scaleFactor = scaledFontSize / (GLfloat)textureSize.height(); + GLfloat scaleFactor = m_scaledFontSize / (GLfloat)textureSize.height(); // Apply alignment - GLfloat xAlignment = 0.0f; - GLfloat yAlignment = 0.0f; - GLfloat zAlignment = 0.0f; - GLfloat sinRotY = qFabs(qSin(qDegreesToRadians(rotation.y()))); - GLfloat cosRotY = qFabs(qCos(qDegreesToRadians(rotation.y()))); - GLfloat sinRotZ = 0.0f; - GLfloat cosRotZ = 0.0f; - if (rotation.z()) { - sinRotZ = qFabs(qSin(qDegreesToRadians(rotation.z()))); - cosRotZ = qFabs(qCos(qDegreesToRadians(rotation.z()))); - } - switch (alignment) { - case Qt::AlignLeft: { - if (rotation.z() && rotation.z() != 180.0f && !rotation.y()) { - xAlignment = ((-(GLfloat)textureSize.width() * scaleFactor) * cosRotZ - - ((GLfloat)textureSize.height() * scaleFactor) * sinRotZ) / 2.0f; - yAlignment = (((GLfloat)textureSize.width() * scaleFactor) * sinRotZ - + ((GLfloat)textureSize.height() * scaleFactor) * cosRotZ) / 2.0f; - } else { - xAlignment = (-(GLfloat)textureSize.width() * scaleFactor) * cosRotY; - zAlignment = ((GLfloat)textureSize.width() * scaleFactor) * sinRotY; - } - break; - } - case Qt::AlignRight: { - if (rotation.z() && rotation.z() != 180.0f && !rotation.y()) { - xAlignment = (((GLfloat)textureSize.width() * scaleFactor) * cosRotZ - + ((GLfloat)textureSize.height() * scaleFactor) * sinRotZ) / 2.0f; - yAlignment = (((GLfloat)textureSize.width() * scaleFactor) * sinRotZ - + ((GLfloat)textureSize.height() * scaleFactor) * cosRotZ) / 2.0f; - } else { - xAlignment = ((GLfloat)textureSize.width() * scaleFactor) * cosRotY; - zAlignment = (-(GLfloat)textureSize.width() * scaleFactor) * sinRotY; - } - break; - } - case Qt::AlignTop: { - yAlignment = ((GLfloat)textureSize.width() * scaleFactor) * cosRotY; - break; - } - case Qt::AlignBottom: { - yAlignment = (-(GLfloat)textureSize.width() * scaleFactor) * cosRotY; - break; - } - case Qt::AlignHCenter: { - xAlignment = (-(GLfloat)textureSize.width() * scaleFactor) * cosRotZ - - ((GLfloat)textureSize.height() * scaleFactor) * sinRotZ; - break; - } - case Qt::AlignVCenter: { - yAlignment = ((GLfloat)textureSize.width() * scaleFactor) * cosRotZ - + ((GLfloat)textureSize.height() * scaleFactor) * sinRotZ; - break; - } - default: { - break; - } - } + QVector3D anchorPoint; + + if (alignment & Qt::AlignLeft) + anchorPoint.setX(float(textureSize.width()) * scaleFactor); + else if (alignment & Qt::AlignRight) + anchorPoint.setX(float(-textureSize.width()) * scaleFactor); + + if (alignment & Qt::AlignTop) + anchorPoint.setY(float(-textureSize.height()) * scaleFactor); + else if (alignment & Qt::AlignBottom) + anchorPoint.setY(float(textureSize.height()) * scaleFactor); if (position < LabelBottom) { xPosition = item.translation().x(); @@ -318,15 +335,9 @@ void Drawer::drawLabel(const AbstractRenderItem &item, const LabelItem &labelIte } // Position label - modelMatrix.translate(xPosition + xAlignment, yPosition + yAlignment, zPosition + zAlignment); + modelMatrix.translate(xPosition, yPosition, zPosition); // Rotate - QQuaternion rotQuatX = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, rotation.x()); - QQuaternion rotQuatY = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, rotation.y()); - QQuaternion rotQuatZ = QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, rotation.z()); - QQuaternion rotQuaternion = rotQuatY * rotQuatZ * rotQuatX; - modelMatrix.rotate(rotQuaternion); - if (useDepth && !rotateAlong) { float yComp = float(qRadiansToDegrees(qTan(positionComp.y() / cameraDistance))); // Apply negative camera rotations to keep labels facing camera @@ -334,20 +345,27 @@ void Drawer::drawLabel(const AbstractRenderItem &item, const LabelItem &labelIte float camRotationY = camera->yRotation(); modelMatrix.rotate(-camRotationX, 0.0f, 1.0f, 0.0f); modelMatrix.rotate(-camRotationY - yComp, 1.0f, 0.0f, 0.0f); + } else { + modelMatrix.rotate(rotation); } + modelMatrix.translate(anchorPoint); // Scale label based on text size modelMatrix.scale(QVector3D((GLfloat)textureSize.width() * scaleFactor, - scaledFontSize, + m_scaledFontSize, 0.0f)); MVPMatrix = projectionmatrix * viewmatrix * modelMatrix; - // Set shader bindings shader->setUniformValue(shader->MVP(), MVPMatrix); - // Draw the object - drawObject(shader, object, labelItem.textureId()); + if (isSelecting) { + // Draw the selection object + drawSelectionObject(shader, object); + } else { + // Draw the object + drawObject(shader, object, labelItem.textureId()); + } } void Drawer::generateSelectionLabelTexture(Abstract3DRenderer *renderer) diff --git a/src/datavisualization/engine/drawer_p.h b/src/datavisualization/engine/drawer_p.h index 8e98aa3a..ffcea315 100644 --- a/src/datavisualization/engine/drawer_p.h +++ b/src/datavisualization/engine/drawer_p.h @@ -44,6 +44,7 @@ class SurfaceObject; class TextureHelper; class Q3DCamera; class Abstract3DRenderer; +class ScatterPointBufferHelper; class Drawer : public QObject, public QOpenGLFunctions { @@ -71,18 +72,23 @@ public: void setTheme(Q3DTheme *theme); Q3DTheme *theme() const; QFont font() const; + inline GLfloat scaledFontSize() const { return m_scaledFontSize; } void drawObject(ShaderHelper *shader, AbstractObjectHelper *object, GLuint textureId = 0, GLuint depthTextureId = 0); + void drawSelectionObject(ShaderHelper *shader, AbstractObjectHelper *object); void drawSurfaceGrid(ShaderHelper *shader, SurfaceObject *object); void drawPoint(ShaderHelper *shader); + void drawPoints(ShaderHelper *shader, ScatterPointBufferHelper *object); + void drawLine(ShaderHelper *shader); void drawLabel(const AbstractRenderItem &item, const LabelItem &labelItem, const QMatrix4x4 &viewmatrix, const QMatrix4x4 &projectionmatrix, - const QVector3D &positionComp, const QVector3D &rotation, GLfloat itemHeight, + const QVector3D &positionComp, const QQuaternion &rotation, GLfloat itemHeight, QAbstract3DGraph::SelectionFlags mode, ShaderHelper *shader, ObjectHelper *object, const Q3DCamera *camera, bool useDepth = false, bool rotateAlong = false, LabelPosition position = LabelOver, - Qt::AlignmentFlag alignment = Qt::AlignCenter, bool isSlicing = false); + Qt::AlignmentFlag alignment = Qt::AlignCenter, bool isSlicing = false, + bool isSelecting = false); void generateSelectionLabelTexture(Abstract3DRenderer *item); void generateLabelItem(LabelItem &item, const QString &text, int widestLabel = 0); @@ -94,6 +100,8 @@ private: Q3DTheme *m_theme; TextureHelper *m_textureHelper; GLuint m_pointbuffer; + GLuint m_linebuffer; + GLfloat m_scaledFontSize; }; QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/engine/engine.pri b/src/datavisualization/engine/engine.pri index 9c2e71e4..96fa7fa9 100644 --- a/src/datavisualization/engine/engine.pri +++ b/src/datavisualization/engine/engine.pri @@ -26,7 +26,9 @@ HEADERS += $$PWD/qabstract3dgraph_p.h \ $$PWD/q3dobject.h \ $$PWD/q3dobject_p.h \ $$PWD/q3dscene_p.h \ - $$PWD/surfaceseriesrendercache_p.h + $$PWD/surfaceseriesrendercache_p.h \ + $$PWD/barseriesrendercache_p.h \ + $$PWD/scatterseriesrendercache_p.h SOURCES += $$PWD/qabstract3dgraph.cpp \ $$PWD/q3dbars.cpp \ @@ -48,7 +50,9 @@ SOURCES += $$PWD/qabstract3dgraph.cpp \ $$PWD/q3dlight.cpp \ $$PWD/q3dobject.cpp \ $$PWD/q3dscene.cpp \ - $$PWD/surfaceseriesrendercache.cpp + $$PWD/surfaceseriesrendercache.cpp \ + $$PWD/barseriesrendercache.cpp \ + $$PWD/scatterseriesrendercache.cpp RESOURCES += engine/engine.qrc diff --git a/src/datavisualization/engine/engine.qrc b/src/datavisualization/engine/engine.qrc index 18cba7fe..673b6ee0 100644 --- a/src/datavisualization/engine/engine.qrc +++ b/src/datavisualization/engine/engine.qrc @@ -24,7 +24,7 @@ <file alias="bevelbarSmoothFull">meshes/barFilledSmooth.obj</file> <file alias="barFull">meshes/cubeFilledFlat.obj</file> <file alias="barSmoothFull">meshes/cubeFilledSmooth.obj</file> - <file alias="negativeBackground">meshes/backgroundNegatives.obj</file> + <file alias="backgroundNoFloor">meshes/backgroundNoFloor.obj</file> <file alias="minimal">meshes/minimalFlat.obj</file> <file alias="minimalSmooth">meshes/minimalSmooth.obj</file> <file alias="arrow">meshes/arrowFlat.obj</file> @@ -54,5 +54,8 @@ <file alias="fragmentSurfaceShadowNoTex">shaders/surfaceShadowNoTex.frag</file> <file alias="fragmentSurfaceShadowFlat">shaders/surfaceShadowFlat.frag</file> <file alias="vertexSurfaceShadowFlat">shaders/surfaceShadowFlat.vert</file> + <file alias="fragmentTexture">shaders/texture.frag</file> + <file alias="fragmentTextureES2">shaders/texture_ES2.frag</file> + <file alias="vertexTexture">shaders/texture.vert</file> </qresource> </RCC> diff --git a/src/datavisualization/engine/meshes/backgroundNegatives.obj b/src/datavisualization/engine/meshes/backgroundNoFloor.obj index 76c7c1d8..0b94617f 100644 --- a/src/datavisualization/engine/meshes/backgroundNegatives.obj +++ b/src/datavisualization/engine/meshes/backgroundNoFloor.obj @@ -1,18 +1,18 @@ # Blender v2.66 (sub 0) OBJ File: 'backgroudNegativesWall.blend' # www.blender.org o Cube -v 0.999999 1.000000 1.000001 +v 1.000000 1.000000 1.000000 v -1.000000 1.000000 1.000000 v -1.000000 1.000000 -1.000000 -v 1.000000 -3.000000 1.000000 -v -1.000000 -3.000000 1.000000 -v -1.000000 -3.000000 -1.000000 -vt 0.000100 0.000100 -vt 0.500000 0.000100 -vt 0.500000 0.999900 -vt 0.999900 0.000100 -vt 0.999900 0.999900 -vt 0.000100 0.999900 +v 1.000000 -1.000000 1.000000 +v -1.000000 -1.000000 1.000000 +v -1.000000 -1.000000 -1.000000 +vt 0.000000 0.000000 +vt 0.500000 0.000000 +vt 0.500000 1.000000 +vt 1.000000 0.000000 +vt 1.000000 1.000000 +vt 0.000000 1.000000 vn 0.000000 -0.000000 -1.000000 vn 1.000000 0.000000 0.000000 s off diff --git a/src/datavisualization/engine/q3dbars.cpp b/src/datavisualization/engine/q3dbars.cpp index 7b37715f..a903d831 100644 --- a/src/datavisualization/engine/q3dbars.cpp +++ b/src/datavisualization/engine/q3dbars.cpp @@ -16,13 +16,7 @@ ** ****************************************************************************/ -#include "q3dbars.h" #include "q3dbars_p.h" -#include "bars3dcontroller_p.h" -#include "qvalue3daxis.h" -#include "qcategory3daxis.h" -#include "q3dcamera.h" -#include "qbar3dseries_p.h" QT_BEGIN_NAMESPACE_DATAVISUALIZATION @@ -30,7 +24,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION * \class Q3DBars * \inmodule QtDataVisualization * \brief The Q3DBars class provides methods for rendering 3D bar graphs. - * \since Qt Data Visualization 1.0 + * \since QtDataVisualization 1.0 * * This class enables developers to render bar graphs in 3D and to view them by rotating the scene * freely. Rotation is done by holding down the right mouse button and moving the mouse. Zooming diff --git a/src/datavisualization/engine/q3dcamera.cpp b/src/datavisualization/engine/q3dcamera.cpp index 50f2e319..9866a34e 100644 --- a/src/datavisualization/engine/q3dcamera.cpp +++ b/src/datavisualization/engine/q3dcamera.cpp @@ -16,10 +16,7 @@ ** ****************************************************************************/ -#include "q3dcamera.h" #include "q3dcamera_p.h" -#include "q3dscene.h" -#include "q3dobject.h" #include "utils_p.h" #include <QtCore/qmath.h> @@ -30,7 +27,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION * \class Q3DCamera * \inmodule QtDataVisualization * \brief Representation of a camera in 3D space. - * \since Qt Data Visualization 1.0 + * \since QtDataVisualization 1.0 * * Q3DCamera represents a basic orbit around centerpoint 3D camera that is used when rendering the * data visualization. The class offers simple methods for rotating the camera around the origin @@ -85,7 +82,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION * data visualization. The type offers simple methods for rotating the camera around the origin * and setting zoom level. * - * For Camera3D enums, see \l Q3DCamera::CameraPreset + * For Camera3D enums, see \l{Q3DCamera::CameraPreset}. */ /*! @@ -208,10 +205,12 @@ float Q3DCamera::xRotation() const { void Q3DCamera::setXRotation(float rotation) { - if (d_ptr->m_wrapXRotation) + if (d_ptr->m_wrapXRotation) { rotation = Utils::wrapValue(rotation, d_ptr->m_minXRotation, d_ptr->m_maxXRotation); - else - rotation = qBound(float(d_ptr->m_minXRotation), float(rotation), float(d_ptr->m_maxXRotation)); + } else { + rotation = qBound(float(d_ptr->m_minXRotation), float(rotation), + float(d_ptr->m_maxXRotation)); + } if (d_ptr->m_xRotation != rotation) { d_ptr->setXRotation(rotation); @@ -235,10 +234,12 @@ float Q3DCamera::yRotation() const { void Q3DCamera::setYRotation(float rotation) { - if (d_ptr->m_wrapYRotation) + if (d_ptr->m_wrapYRotation) { rotation = Utils::wrapValue(rotation, d_ptr->m_minYRotation, d_ptr->m_maxYRotation); - else - rotation = qBound(float(d_ptr->m_minYRotation), float(rotation), float(d_ptr->m_maxYRotation)); + } else { + rotation = qBound(float(d_ptr->m_minYRotation), float(rotation), + float(d_ptr->m_maxYRotation)); + } if (d_ptr->m_yRotation != rotation) { d_ptr->setYRotation(rotation); diff --git a/src/datavisualization/engine/q3dlight.cpp b/src/datavisualization/engine/q3dlight.cpp index 8ac9e3a0..03f094cb 100644 --- a/src/datavisualization/engine/q3dlight.cpp +++ b/src/datavisualization/engine/q3dlight.cpp @@ -16,8 +16,6 @@ ** ****************************************************************************/ -#include "q3dlight.h" -#include "q3dscene.h" #include "q3dlight_p.h" QT_BEGIN_NAMESPACE_DATAVISUALIZATION @@ -26,7 +24,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION * \class Q3DLight * \inmodule QtDataVisualization * \brief Representation of a light source in 3D space. - * \since Qt Data Visualization 1.0 + * \since QtDataVisualization 1.0 * * Q3DLight represents a monochrome non variable light source in 3D space. */ diff --git a/src/datavisualization/engine/q3dobject.cpp b/src/datavisualization/engine/q3dobject.cpp index 05edf287..56edb098 100644 --- a/src/datavisualization/engine/q3dobject.cpp +++ b/src/datavisualization/engine/q3dobject.cpp @@ -16,7 +16,6 @@ ** ****************************************************************************/ -#include "q3dobject.h" #include "q3dobject_p.h" #include "q3dscene_p.h" @@ -26,7 +25,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION \class Q3DObject \inmodule QtDataVisualization \brief Simple baseclass for all the objects in the 3D scene. - \since Qt Data Visualization 1.0 + \since QtDataVisualization 1.0 Q3DObject is a baseclass that contains only position information for an object in 3D scene. The object is considered to be a single point in the coordinate space without dimensions. diff --git a/src/datavisualization/engine/q3dscatter.cpp b/src/datavisualization/engine/q3dscatter.cpp index 7c7809f3..40bd5021 100644 --- a/src/datavisualization/engine/q3dscatter.cpp +++ b/src/datavisualization/engine/q3dscatter.cpp @@ -18,10 +18,6 @@ #include "q3dscatter.h" #include "q3dscatter_p.h" -#include "scatter3dcontroller_p.h" -#include "qvalue3daxis.h" -#include "q3dcamera.h" -#include "qscatter3dseries_p.h" QT_BEGIN_NAMESPACE_DATAVISUALIZATION @@ -29,7 +25,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION * \class Q3DScatter * \inmodule QtDataVisualization * \brief The Q3DScatter class provides methods for rendering 3D scatter graphs. - * \since Qt Data Visualization 1.0 + * \since QtDataVisualization 1.0 * * This class enables developers to render scatter graphs in 3D and to view them by rotating the scene * freely. Rotation is done by holding down the right mouse button and moving the mouse. Zooming diff --git a/src/datavisualization/engine/q3dscene.cpp b/src/datavisualization/engine/q3dscene.cpp index be64b928..9464bc8d 100644 --- a/src/datavisualization/engine/q3dscene.cpp +++ b/src/datavisualization/engine/q3dscene.cpp @@ -16,22 +16,17 @@ ** ****************************************************************************/ -#include "datavisualizationglobal_p.h" - -#include "q3dscene.h" #include "q3dscene_p.h" #include "q3dcamera_p.h" #include "q3dlight_p.h" -#include <QtCore/qmath.h> - QT_BEGIN_NAMESPACE_DATAVISUALIZATION /*! * \class Q3DScene * \inmodule QtDataVisualization * \brief Q3DScene class provides description of the 3D scene being visualized. - * \since Qt Data Visualization 1.0 + * \since QtDataVisualization 1.0 * * The 3D scene contains a single active camera and a single active light source. * Visualized data is assumed to be at a fixed location. @@ -260,8 +255,12 @@ void Q3DScene::setSecondarySubViewport(const QRect &secondarySubViewport) * * 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 - * graph tries to select a data item at the given \a point within the primary viewport. - * After the rendering pass the property is returned to its default state of invalidSelectionPoint(). + * 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 + * invalidSelectionPoint(). + * + * \sa QAbstract3DGraph::selectedElement */ void Q3DScene::setSelectionQueryPosition(const QPoint &point) { @@ -411,6 +410,7 @@ void Q3DScene::setActiveLight(Q3DLight *light) d_ptr->m_sceneDirty = true; emit activeLightChanged(light); + emit d_ptr->needRender(); } } @@ -554,7 +554,6 @@ void Q3DScenePrivate::setWindowSize(const QSize &size) m_windowSize = size; updateGLViewport(); m_changeTracker.windowSizeChanged = true; - m_sceneDirty = true; emit needRender(); } } diff --git a/src/datavisualization/engine/q3dscene.h b/src/datavisualization/engine/q3dscene.h index d663744e..1699b125 100644 --- a/src/datavisualization/engine/q3dscene.h +++ b/src/datavisualization/engine/q3dscene.h @@ -93,7 +93,9 @@ private: friend class AbstractDeclarative; friend class QAbstract3DGraph; + friend class QAbstract3DGraphPrivate; friend class Abstract3DController; + friend class Bars3DController; friend class Q3DScenePrivate; friend class Abstract3DRenderer; friend class Bars3DRenderer; diff --git a/src/datavisualization/engine/q3dscene_p.h b/src/datavisualization/engine/q3dscene_p.h index bc6a7223..2c69e5e0 100644 --- a/src/datavisualization/engine/q3dscene_p.h +++ b/src/datavisualization/engine/q3dscene_p.h @@ -36,7 +36,6 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION class Q3DCamera; class Q3DLight; -class Q3DScene; struct Q3DSceneChangeBitField { bool viewportChanged : 1; diff --git a/src/datavisualization/engine/q3dsurface.cpp b/src/datavisualization/engine/q3dsurface.cpp index 7724cb24..162b7a67 100644 --- a/src/datavisualization/engine/q3dsurface.cpp +++ b/src/datavisualization/engine/q3dsurface.cpp @@ -18,10 +18,6 @@ #include "q3dsurface.h" #include "q3dsurface_p.h" -#include "qvalue3daxis.h" -#include "qsurfacedataproxy.h" -#include "q3dcamera.h" -#include "qsurface3dseries_p.h" QT_BEGIN_NAMESPACE_DATAVISUALIZATION @@ -29,7 +25,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION * \class Q3DSurface * \inmodule QtDataVisualization * \brief The Q3DSurface class provides methods for rendering 3D surface plots. - * \since Qt Data Visualization 1.0 + * \since QtDataVisualization 1.0 * * This class enables developers to render 3D surface plots and to view them by rotating the scene * freely. The visual properties of the surface such as draw mode and shading can be controlled diff --git a/src/datavisualization/engine/qabstract3dgraph.cpp b/src/datavisualization/engine/qabstract3dgraph.cpp index bde5b585..dabec744 100644 --- a/src/datavisualization/engine/qabstract3dgraph.cpp +++ b/src/datavisualization/engine/qabstract3dgraph.cpp @@ -27,6 +27,8 @@ #include <QtGui/QOpenGLContext> #include <QtGui/QOpenGLPaintDevice> #include <QtGui/QPainter> +#include <QtGui/QOpenGLFramebufferObject> +#include <QtGui/QOffscreenSurface> QT_BEGIN_NAMESPACE_DATAVISUALIZATION @@ -34,7 +36,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION * \class QAbstract3DGraph * \inmodule QtDataVisualization * \brief The QAbstract3DGraph class provides a window and render loop for graphs. - * \since Qt Data Visualization 1.0 + * \since QtDataVisualization 1.0 * * This class subclasses a QWindow and provides render loop for graphs inheriting it. * @@ -118,6 +120,38 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION */ /*! + \enum QAbstract3DGraph::ElementType + \since QtDataVisualization 1.1 + + Type of an element in the graph. + + \value ElementNone + No defined element. + \value ElementSeries + A series (i.e. an item in a series). + \value ElementAxisXLabel + X axis label. + \value ElementAxisYLabel + Y axis label. + \value ElementAxisZLabel + Z axis label. + \value ElementCustomItem + Custom item. +*/ + +/*! + \enum QAbstract3DGraph::OptimizationHint + \since Qt Data Visualization 1.1 + + The optimization hint for rendering. + + \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. +*/ + +/*! * \internal */ QAbstract3DGraph::QAbstract3DGraph(QAbstract3DGraphPrivate *d, const QSurfaceFormat *format, @@ -126,6 +160,7 @@ QAbstract3DGraph::QAbstract3DGraph(QAbstract3DGraphPrivate *d, const QSurfaceFor d_ptr(d) { qRegisterMetaType<QAbstract3DGraph::ShadowQuality>("QAbstract3DGraph::ShadowQuality"); + qRegisterMetaType<QAbstract3DGraph::ElementType>("QAbstract3DGraph::ElementType"); // Default to frameless window, as typically graphs are not toplevel setFlags(flags() | Qt::FramelessWindowHint); @@ -164,7 +199,8 @@ QAbstract3DGraph::QAbstract3DGraph(QAbstract3DGraphPrivate *d, const QSurfaceFor #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(' ')); + 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 @@ -342,7 +378,8 @@ bool QAbstract3DGraph::shadowsSupported() const /*! * \property QAbstract3DGraph::scene * - * This property contains the read only Q3DScene that can be used to access, for example, a camera object. + * This property contains the read only Q3DScene that can be used to access, for example, a camera + * object. */ Q3DScene *QAbstract3DGraph::scene() const { @@ -358,6 +395,257 @@ void QAbstract3DGraph::clearSelection() } /*! + * Adds a QCustom3DItem \a item to the graph. Graph takes ownership of the added item. + * + * \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() + * + * \since QtDataVisualization 1.1 + */ +int QAbstract3DGraph::addCustomItem(QCustom3DItem *item) +{ + return d_ptr->m_visualController->addCustomItem(item); +} + +/*! + * Removes all custom items. Deletes the resources allocated to them. + * + * \since QtDataVisualization 1.1 + */ +void QAbstract3DGraph::removeCustomItems() +{ + d_ptr->m_visualController->deleteCustomItems(); +} + +/*! + * Removes the custom \a {item}. Deletes the resources allocated to it. + * + * \since QtDataVisualization 1.1 + */ +void QAbstract3DGraph::removeCustomItem(QCustom3DItem *item) +{ + d_ptr->m_visualController->deleteCustomItem(item); +} + +/*! + * Removes all custom items at \a {position}. Deletes the resources allocated to them. + * + * \since QtDataVisualization 1.1 + */ +void QAbstract3DGraph::removeCustomItemAt(const QVector3D &position) +{ + d_ptr->m_visualController->deleteCustomItem(position); +} + +/*! + * Gets ownership of given \a item back and removes the \a item from the graph. + * + * \since QtDataVisualization 1.1 + * + * \note If the same item is added back to the graph, the texture or the texture file needs to be + * re-set. + * + * \sa QCustom3DItem::setTextureImage(), QCustom3DItem::setTextureFile() + */ +void QAbstract3DGraph::releaseCustomItem(QCustom3DItem *item) +{ + return d_ptr->m_visualController->releaseCustomItem(item); +} + +/*! + * 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. + * + * \return index of the selected label, or -1. + * + * \since QtDataVisualization 1.1 + * + * \sa selectedElement + */ +int QAbstract3DGraph::selectedLabelIndex() const +{ + return d_ptr->m_visualController->selectedLabelIndex(); +} + +/*! + * Can be used to get the selected axis after receiving \c selectedElementChanged signal with any label + * type. Selection is valid until the next \c selectedElementChanged signal. + * + * \return pointer to the selected axis, or null. + * + * \since QtDataVisualization 1.1 + * + * \sa selectedElement + */ +QAbstract3DAxis *QAbstract3DGraph::selectedAxis() const +{ + return d_ptr->m_visualController->selectedAxis(); +} + +/*! + * Can be used to query the index of the selected custom item after receiving \c selectedElementChanged + * signal with QAbstract3DGraph::ElementCustomItem type. Selection is valid until the next + * \c selectedElementChanged signal. + * + * \return index of the selected custom item, or -1. + * + * \since QtDataVisualization 1.1 + * + * \sa selectedElement + */ +int QAbstract3DGraph::selectedCustomItemIndex() const +{ + return d_ptr->m_visualController->selectedCustomItemIndex(); +} + +/*! + * Can be used to get the selected custom item after receiving \c selectedElementChanged signal with + * QAbstract3DGraph::ElementCustomItem type. Ownership of the item remains with the graph. + * Selection is valid until the next \c selectedElementChanged signal. + * + * \return pointer to the selected custom item, or null. + * + * \since QtDataVisualization 1.1 + * + * \sa selectedElement + */ +QCustom3DItem *QAbstract3DGraph::selectedCustomItem() const +{ + return d_ptr->m_visualController->selectedCustomItem(); +} + +/*! + * \property QAbstract3DGraph::selectedElement + * + * Can be used to query the selected element type. + * Type is valid until the next \c selectedElementChanged signal. + * + * \c selectedElementChanged signal is emitted when a selection is made in the graph. + * + * Signal can be used for example for implementing custom input handlers, as demonstrated in this + * \l {Axis Range Dragging With Labels Example}{example}. + * + * \sa selectedLabelIndex(), selectedAxis(), selectedCustomItemIndex(), selectedCustomItem(), + * Q3DBars::selectedSeries(), Q3DScatter::selectedSeries(), Q3DSurface::selectedSeries(), + * Q3DScene::setSelectionQueryPosition() + * + * \since QtDataVisualization 1.1 + */ +QAbstract3DGraph::ElementType QAbstract3DGraph::selectedElement() const +{ + return d_ptr->m_visualController->selectedElement(); +} + +/*! + * Renders current frame to an image of \a imageSize. Default size is the window size. Image is + * rendered with antialiasing level given in \a msaaSamples. Default level is \c{0}. + * + * \since QtDataVisualization 1.1 + * + * \return rendered image. + */ +QImage QAbstract3DGraph::renderToImage(int msaaSamples, const QSize &imageSize) +{ + QSize renderSize = imageSize; + if (renderSize.isEmpty()) + renderSize = size(); + return d_ptr->renderToImage(msaaSamples, renderSize); +} + +/*! + * \property QAbstract3DGraph::measureFps + * \since QtDataVisualization 1.1 + * + * If \c {true}, the rendering is done continuously instead of on demand, and currentFps property + * is updated. Defaults to \c{false}. + * + * \sa currentFps + */ +void QAbstract3DGraph::setMeasureFps(bool enable) +{ + d_ptr->m_visualController->setMeasureFps(enable); +} + +bool QAbstract3DGraph::measureFps() const +{ + return d_ptr->m_visualController->measureFps(); +} + +/*! + * \property QAbstract3DGraph::currentFps + * \since QtDataVisualization 1.1 + * + * When fps measuring is enabled, the results for the last second are stored in this read-only + * property. It takes at least a second before this value updates after measurement is activated. + * + * \sa measureFps + */ +qreal QAbstract3DGraph::currentFps() const +{ + return d_ptr->m_visualController->currentFps(); +} + +/*! + * \property QAbstract3DGraph::orthoProjection + * \since QtDataVisualization 1.1 + * + * If \c {true}, orthographic projection will be used for displaying the graph. Defaults to \c{false}. + * \note Shadows will be disabled when set to \c{true}. + */ +void QAbstract3DGraph::setOrthoProjection(bool enable) +{ + d_ptr->m_visualController->setOrthoProjection(enable); +} + +bool QAbstract3DGraph::isOrthoProjection() const +{ + return d_ptr->m_visualController->isOrthoProjection(); +} + +/*! + * \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}. + * + * \note Has no effect on Q3DBars. + */ +void QAbstract3DGraph::setAspectRatio(qreal ratio) +{ + d_ptr->m_visualController->setAspectRatio(float(ratio)); +} + +qreal QAbstract3DGraph::aspectRatio() const +{ + return d_ptr->m_visualController->aspectRatio(); +} + +/*! + * \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. + * Defaults to \c{OptimizationDefault}. + */ +void QAbstract3DGraph::setOptimizationHints(OptimizationHints hints) +{ + d_ptr->m_visualController->setOptimizationHints(hints); +} + +QAbstract3DGraph::OptimizationHints QAbstract3DGraph::optimizationHints() const +{ + return d_ptr->m_visualController->optimizationHints(); +} + +/*! * \internal */ bool QAbstract3DGraph::event(QEvent *event) @@ -453,12 +741,17 @@ QAbstract3DGraphPrivate::QAbstract3DGraphPrivate(QAbstract3DGraph *q) q_ptr(q), m_updatePending(false), m_visualController(0), - m_devicePixelRatio(1.f) + m_devicePixelRatio(1.f), + m_offscreenSurface(0) { } QAbstract3DGraphPrivate::~QAbstract3DGraphPrivate() { + if (m_offscreenSurface) { + m_offscreenSurface->destroy(); + delete m_offscreenSurface; + } if (m_context) m_context->makeCurrent(q_ptr); @@ -477,6 +770,11 @@ void QAbstract3DGraphPrivate::setVisualController(Abstract3DController *controll &QAbstract3DGraph::selectionModeChanged); QObject::connect(m_visualController, &Abstract3DController::shadowQualityChanged, q_ptr, &QAbstract3DGraph::shadowQualityChanged); + QObject::connect(m_visualController, &Abstract3DController::optimizationHintsChanged, q_ptr, + &QAbstract3DGraph::optimizationHintsChanged); + QObject::connect(m_visualController, &Abstract3DController::elementSelected, q_ptr, + &QAbstract3DGraph::selectedElementChanged); + QObject::connect(m_visualController, &Abstract3DController::needRender, this, &QAbstract3DGraphPrivate::renderLater); @@ -486,6 +784,17 @@ void QAbstract3DGraphPrivate::setVisualController(Abstract3DController *controll &QAbstract3DGraphPrivate::handleAxisYChanged); QObject::connect(m_visualController, &Abstract3DController::axisZChanged, this, &QAbstract3DGraphPrivate::handleAxisZChanged); + + QObject::connect(m_visualController, &Abstract3DController::measureFpsChanged, q_ptr, + &QAbstract3DGraph::measureFpsChanged); + QObject::connect(m_visualController, &Abstract3DController::currentFpsChanged, q_ptr, + &QAbstract3DGraph::currentFpsChanged); + + QObject::connect(m_visualController, &Abstract3DController::orthoProjectionChanged, q_ptr, + &QAbstract3DGraph::orthoProjectionChanged); + + QObject::connect(m_visualController, &Abstract3DController::aspectRatioChanged, q_ptr, + &QAbstract3DGraph::aspectRatioChanged); } void QAbstract3DGraphPrivate::handleDevicePixelRatioChange() @@ -526,4 +835,42 @@ void QAbstract3DGraphPrivate::renderNow() m_context->swapBuffers(q_ptr); } +QImage QAbstract3DGraphPrivate::renderToImage(int msaaSamples, const QSize &imageSize) +{ + QImage image; + QOpenGLFramebufferObject *fbo; + QOpenGLFramebufferObjectFormat fboFormat; + if (!m_offscreenSurface) { + // Create an offscreen surface for rendering to images without rendering on screen + m_offscreenSurface = new QOffscreenSurface(q_ptr->screen()); + m_offscreenSurface->setFormat(q_ptr->requestedFormat()); + m_offscreenSurface->create(); + } + // Render the wanted frame offscreen + m_context->makeCurrent(m_offscreenSurface); + fboFormat.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); + fboFormat.setInternalTextureFormat(GL_RGB); + fboFormat.setSamples(msaaSamples); + fbo = new QOpenGLFramebufferObject(imageSize, fboFormat); + if (fbo->isValid()) { + QRect originalViewport = m_visualController->m_scene->viewport(); + m_visualController->m_scene->d_ptr->setWindowSize(imageSize); + m_visualController->m_scene->d_ptr->setViewport(QRect(0, 0, + imageSize.width(), + imageSize.height())); + m_visualController->synchDataToRenderer(); + fbo->bind(); + m_context->swapBuffers(m_offscreenSurface); + m_visualController->requestRender(fbo); + image = fbo->toImage(); + fbo->release(); + m_visualController->m_scene->d_ptr->setWindowSize(originalViewport.size()); + m_visualController->m_scene->d_ptr->setViewport(originalViewport); + } + delete fbo; + m_context->makeCurrent(q_ptr); + + return image; +} + QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/engine/qabstract3dgraph.h b/src/datavisualization/engine/qabstract3dgraph.h index 18eda7df..59f61aae 100644 --- a/src/datavisualization/engine/qabstract3dgraph.h +++ b/src/datavisualization/engine/qabstract3dgraph.h @@ -29,20 +29,31 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION class QAbstract3DGraphPrivate; +class QCustom3DItem; +class QAbstract3DAxis; class QT_DATAVISUALIZATION_EXPORT QAbstract3DGraph : public QWindow, protected QOpenGLFunctions { Q_OBJECT Q_ENUMS(ShadowQuality) + Q_ENUMS(ElementType) Q_FLAGS(SelectionFlag SelectionFlags) + Q_FLAGS(OptimizationHint OptimizationHints) Q_PROPERTY(QAbstract3DInputHandler* activeInputHandler READ activeInputHandler WRITE setActiveInputHandler NOTIFY activeInputHandlerChanged) Q_PROPERTY(Q3DTheme* activeTheme READ activeTheme WRITE setActiveTheme NOTIFY activeThemeChanged) Q_PROPERTY(SelectionFlags selectionMode READ selectionMode WRITE setSelectionMode NOTIFY selectionModeChanged) Q_PROPERTY(ShadowQuality shadowQuality READ shadowQuality WRITE setShadowQuality NOTIFY shadowQualityChanged) Q_PROPERTY(Q3DScene* scene READ scene) + Q_PROPERTY(bool measureFps READ measureFps WRITE setMeasureFps NOTIFY measureFpsChanged) + Q_PROPERTY(qreal currentFps READ currentFps NOTIFY currentFpsChanged) + Q_PROPERTY(bool orthoProjection READ isOrthoProjection WRITE setOrthoProjection NOTIFY orthoProjectionChanged) + 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) protected: - explicit QAbstract3DGraph(QAbstract3DGraphPrivate *d, const QSurfaceFormat *format, QWindow *parent = 0); + explicit QAbstract3DGraph(QAbstract3DGraphPrivate *d, const QSurfaceFormat *format, + QWindow *parent = 0); public: enum SelectionFlag { @@ -69,6 +80,21 @@ public: ShadowQualitySoftHigh }; + enum ElementType { + ElementNone = 0, + ElementSeries, + ElementAxisXLabel, + ElementAxisYLabel, + ElementAxisZLabel, + ElementCustomItem + }; + + enum OptimizationHint { + OptimizationDefault = 0, + OptimizationStatic = 1 + }; + Q_DECLARE_FLAGS(OptimizationHints, OptimizationHint) + public: virtual ~QAbstract3DGraph(); @@ -95,6 +121,35 @@ public: void clearSelection(); + int addCustomItem(QCustom3DItem *item); + void removeCustomItems(); + void removeCustomItem(QCustom3DItem *item); + void removeCustomItemAt(const QVector3D &position); + void releaseCustomItem(QCustom3DItem *item); + + int selectedLabelIndex() const; + QAbstract3DAxis *selectedAxis() const; + + int selectedCustomItemIndex() const; + QCustom3DItem *selectedCustomItem() const; + + QImage renderToImage(int msaaSamples = 0, const QSize &imageSize = QSize()); + + void setMeasureFps(bool enable); + bool measureFps() const; + qreal currentFps() const; + + void setOrthoProjection(bool enable); + bool isOrthoProjection() const; + + ElementType selectedElement() const; + + void setAspectRatio(qreal ratio); + qreal aspectRatio() const; + + void setOptimizationHints(OptimizationHints hints); + OptimizationHints optimizationHints() const; + protected: bool event(QEvent *event); void resizeEvent(QResizeEvent *event); @@ -107,12 +162,17 @@ protected: void mouseMoveEvent(QMouseEvent *event); void wheelEvent(QWheelEvent *event); - signals: void activeInputHandlerChanged(QAbstract3DInputHandler *inputHandler); void activeThemeChanged(Q3DTheme *theme); void selectionModeChanged(QAbstract3DGraph::SelectionFlags mode); void shadowQualityChanged(QAbstract3DGraph::ShadowQuality quality); + void selectedElementChanged(QAbstract3DGraph::ElementType type); + void measureFpsChanged(bool enabled); + void currentFpsChanged(qreal fps); + void orthoProjectionChanged(bool enabled); + void aspectRatioChanged(qreal ratio); + void optimizationHintsChanged(QAbstract3DGraph::OptimizationHints hints); private: Q_DISABLE_COPY(QAbstract3DGraph) @@ -123,6 +183,7 @@ private: friend class Q3DSurface; }; Q_DECLARE_OPERATORS_FOR_FLAGS(QAbstract3DGraph::SelectionFlags) +Q_DECLARE_OPERATORS_FOR_FLAGS(QAbstract3DGraph::OptimizationHints) QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/engine/qabstract3dgraph_p.h b/src/datavisualization/engine/qabstract3dgraph_p.h index d28495ab..a8b0965a 100644 --- a/src/datavisualization/engine/qabstract3dgraph_p.h +++ b/src/datavisualization/engine/qabstract3dgraph_p.h @@ -32,7 +32,7 @@ #include "datavisualizationglobal_p.h" class QOpenGLContext; -class QOpenGLPaintDevice; +class QOffscreenSurface; QT_BEGIN_NAMESPACE_DATAVISUALIZATION @@ -52,6 +52,8 @@ public: void render(); + QImage renderToImage(int msaaSamples, const QSize &imageSize); + public slots: void renderLater(); void renderNow(); @@ -67,6 +69,7 @@ public: QOpenGLContext *m_context; Abstract3DController *m_visualController; float m_devicePixelRatio; + QOffscreenSurface *m_offscreenSurface; }; QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/engine/scatter3dcontroller.cpp b/src/datavisualization/engine/scatter3dcontroller.cpp index 54292ac0..da40fc85 100644 --- a/src/datavisualization/engine/scatter3dcontroller.cpp +++ b/src/datavisualization/engine/scatter3dcontroller.cpp @@ -18,15 +18,10 @@ #include "scatter3dcontroller_p.h" #include "scatter3drenderer_p.h" -#include "camerahelper_p.h" -#include "qabstract3daxis_p.h" #include "qvalue3daxis_p.h" #include "qscatterdataproxy_p.h" #include "qscatter3dseries_p.h" -#include <QtGui/QMatrix4x4> -#include <QtCore/qmath.h> - QT_BEGIN_NAMESPACE_DATAVISUALIZATION static const int insertRemoveRecordReserveSize = 31; @@ -71,6 +66,12 @@ void Scatter3DController::synchDataToRenderer() Abstract3DController::synchDataToRenderer(); // Notify changes to renderer + if (m_changeTracker.itemChanged) { + m_renderer->updateItems(m_changedItems); + m_changeTracker.itemChanged = false; + m_changedItems.clear(); + } + if (m_changeTracker.selectedItemChanged) { m_renderer->updateSelectedItem(m_selectedItem, m_selectedItemSeries); m_changeTracker.selectedItemChanged = false; @@ -83,9 +84,6 @@ void Scatter3DController::addSeries(QAbstract3DSeries *series) Abstract3DController::addSeries(series); - if (series->isVisible()) - adjustValueAxisRange(); - QScatter3DSeries *scatterSeries = static_cast<QScatter3DSeries *>(series); if (scatterSeries->selectedItem() != invalidSelectionIndex()) setSelectedItem(scatterSeries->selectedItem(), scatterSeries); @@ -101,7 +99,7 @@ void Scatter3DController::removeSeries(QAbstract3DSeries *series) setSelectedItem(invalidSelectionIndex(), 0); if (wasVisible) - adjustValueAxisRange(); + adjustAxisRanges(); } QList<QScatter3DSeries *> Scatter3DController::scatterSeriesList() @@ -121,10 +119,13 @@ void Scatter3DController::handleArrayReset() { QScatter3DSeries *series = static_cast<QScatterDataProxy *>(sender())->series(); if (series->isVisible()) { - adjustValueAxisRange(); + adjustAxisRanges(); m_isDataDirty = true; } + if (!m_changedSeriesList.contains(series)) + m_changedSeriesList.append(series); setSelectedItem(m_selectedItem, m_selectedItemSeries); + series->d_ptr->markItemLabelDirty(); emitNeedRender(); } @@ -134,22 +135,45 @@ void Scatter3DController::handleItemsAdded(int startIndex, int count) Q_UNUSED(count) QScatter3DSeries *series = static_cast<QScatterDataProxy *>(sender())->series(); if (series->isVisible()) { - adjustValueAxisRange(); + adjustAxisRanges(); m_isDataDirty = true; } + if (!m_changedSeriesList.contains(series)) + m_changedSeriesList.append(series); emitNeedRender(); } void Scatter3DController::handleItemsChanged(int startIndex, int count) { - Q_UNUSED(startIndex) - Q_UNUSED(count) QScatter3DSeries *series = static_cast<QScatterDataProxy *>(sender())->series(); - if (series->isVisible()) { - adjustValueAxisRange(); - m_isDataDirty = true; + int oldChangeCount = m_changedItems.size(); + if (!oldChangeCount) + m_changedItems.reserve(count); + + for (int i = 0; i < count; i++) { + bool newItem = true; + int candidate = startIndex + i; + for (int j = 0; j < oldChangeCount; j++) { + const ChangeItem &oldChangeItem = m_changedItems.at(j); + if (oldChangeItem.index == candidate && series == oldChangeItem.series) { + newItem = false; + break; + } + } + if (newItem) { + ChangeItem newChangeItem = {series, candidate}; + m_changedItems.append(newChangeItem); + if (series == m_selectedItemSeries && m_selectedItem == candidate) + series->d_ptr->markItemLabelDirty(); + } + } + + if (count) { + m_changeTracker.itemChanged = true; + if (series->isVisible()) + adjustAxisRanges(); + emitNeedRender(); } - emitNeedRender(); } void Scatter3DController::handleItemsRemoved(int startIndex, int count) @@ -171,9 +195,11 @@ void Scatter3DController::handleItemsRemoved(int startIndex, int count) } if (series->isVisible()) { - adjustValueAxisRange(); + adjustAxisRanges(); m_isDataDirty = true; } + if (!m_changedSeriesList.contains(series)) + m_changedSeriesList.append(series); if (m_recordInsertsAndRemoves) { InsertRemoveRecord record(false, startIndex, count, series); @@ -198,9 +224,11 @@ void Scatter3DController::handleItemsInserted(int startIndex, int count) } if (series->isVisible()) { - adjustValueAxisRange(); + adjustAxisRanges(); m_isDataDirty = true; } + if (!m_changedSeriesList.contains(series)) + m_changedSeriesList.append(series); if (m_recordInsertsAndRemoves) { InsertRemoveRecord record(true, startIndex, count, series); @@ -229,7 +257,7 @@ void Scatter3DController::handleAxisAutoAdjustRangeChangedInOrientation( { Q_UNUSED(orientation) Q_UNUSED(autoAdjust) - adjustValueAxisRange(); + adjustAxisRanges(); } void Scatter3DController::handleAxisRangeChangedBySender(QObject *sender) @@ -240,13 +268,6 @@ void Scatter3DController::handleAxisRangeChangedBySender(QObject *sender) setSelectedItem(m_selectedItem, m_selectedItemSeries); } -void Scatter3DController::handleSeriesVisibilityChangedBySender(QObject *sender) -{ - Abstract3DController::handleSeriesVisibilityChangedBySender(sender); - - adjustValueAxisRange(); -} - void Scatter3DController::handlePendingClick() { int index = m_renderer->clickedIndex(); @@ -274,6 +295,8 @@ void Scatter3DController::handlePendingClick() setSelectedItem(index, series); + Abstract3DController::handlePendingClick(); + m_renderer->resetClickedStatus(); } @@ -329,7 +352,7 @@ void Scatter3DController::clearSelection() setSelectedItem(invalidSelectionIndex(), 0); } -void Scatter3DController::adjustValueAxisRange() +void Scatter3DController::adjustAxisRanges() { QValue3DAxis *valueAxisX = static_cast<QValue3DAxis *>(m_axisX); QValue3DAxis *valueAxisY = static_cast<QValue3DAxis *>(m_axisY); @@ -407,7 +430,7 @@ void Scatter3DController::adjustValueAxisRange() adjustment = defaultAdjustment; } } - valueAxisX->dptr()->setRange(minValueX - adjustment, maxValueX + adjustment); + valueAxisX->dptr()->setRange(minValueX - adjustment, maxValueX + adjustment, true); } if (adjustY) { // If all points at same coordinate, need to default to some valid range @@ -415,7 +438,7 @@ void Scatter3DController::adjustValueAxisRange() float adjustment = 0.0f; if (minValueY == maxValueY) adjustment = defaultAdjustment; - valueAxisY->dptr()->setRange(minValueY - adjustment, maxValueY + adjustment); + valueAxisY->dptr()->setRange(minValueY - adjustment, maxValueY + adjustment, true); } if (adjustZ) { // If all points at same coordinate, need to default to some valid range @@ -434,7 +457,7 @@ void Scatter3DController::adjustValueAxisRange() adjustment = defaultAdjustment; } } - valueAxisZ->dptr()->setRange(minValueZ - adjustment, maxValueZ + adjustment); + valueAxisZ->dptr()->setRange(minValueZ - adjustment, maxValueZ + adjustment, true); } } } diff --git a/src/datavisualization/engine/scatter3dcontroller_p.h b/src/datavisualization/engine/scatter3dcontroller_p.h index 53d24549..db5a95f5 100644 --- a/src/datavisualization/engine/scatter3dcontroller_p.h +++ b/src/datavisualization/engine/scatter3dcontroller_p.h @@ -40,9 +40,11 @@ class QScatter3DSeries; struct Scatter3DChangeBitField { bool selectedItemChanged : 1; + bool itemChanged : 1; Scatter3DChangeBitField() : - selectedItemChanged(true) + selectedItemChanged(true), + itemChanged(false) { } }; @@ -51,8 +53,14 @@ class QT_DATAVISUALIZATION_EXPORT Scatter3DController : public Abstract3DControl { Q_OBJECT +public: + struct ChangeItem { + QScatter3DSeries *series; + int index; + }; private: Scatter3DChangeBitField m_changeTracker; + QVector<ChangeItem> m_changedItems; // Rendering Scatter3DRenderer *m_renderer; @@ -108,8 +116,8 @@ public: virtual void handleAxisAutoAdjustRangeChangedInOrientation( QAbstract3DAxis::AxisOrientation orientation, bool autoAdjust); virtual void handleAxisRangeChangedBySender(QObject *sender); - virtual void handleSeriesVisibilityChangedBySender(QObject *sender); virtual void handlePendingClick(); + virtual void adjustAxisRanges(); public slots: void handleArrayReset(); @@ -125,7 +133,6 @@ protected: virtual void startRecordingRemovesAndInserts(); private: - void adjustValueAxisRange(); Q_DISABLE_COPY(Scatter3DController) }; diff --git a/src/datavisualization/engine/scatter3drenderer.cpp b/src/datavisualization/engine/scatter3drenderer.cpp index de1a769a..21d86d03 100644 --- a/src/datavisualization/engine/scatter3drenderer.cpp +++ b/src/datavisualization/engine/scatter3drenderer.cpp @@ -17,25 +17,16 @@ ****************************************************************************/ #include "scatter3drenderer_p.h" -#include "scatter3dcontroller_p.h" -#include "q3dcamera.h" #include "q3dcamera_p.h" #include "shaderhelper_p.h" -#include "objecthelper_p.h" #include "texturehelper_p.h" #include "utils_p.h" -#include "q3dlight.h" -#include "qscatter3dseries_p.h" +#include "scatterseriesrendercache_p.h" +#include "scatterobjectbufferhelper_p.h" +#include "scatterpointbufferhelper_p.h" -#include <QtGui/QMatrix4x4> -#include <QtGui/QMouseEvent> -#include <QtCore/QThread> #include <QtCore/qmath.h> -// Commenting this draws the shadow map with perspective projection. Otherwise it's drawn in -// orthographic projection. -//#define USE_WIDER_SHADOWS - // You can verify that depth buffer drawing works correctly by uncommenting this. // You should see the scene from where the light is //#define SHOW_DEPTH_TEXTURE_SCENE @@ -44,20 +35,14 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION //#define USE_UNIFORM_SCALING // Scale x and z uniformly, or based on autoscaled values -const GLfloat aspectRatio = 2.0f; // Forced ratio of x and z to y. Dynamic will make it look odd. -const GLfloat labelMargin = 0.05f; const GLfloat defaultMinSize = 0.01f; const GLfloat defaultMaxSize = 0.1f; -const GLfloat defaultMargin = 1.0f + defaultMaxSize; // Default margin for background const GLfloat itemScaler = 3.0f; const GLfloat gridLineWidth = 0.005f; Scatter3DRenderer::Scatter3DRenderer(Scatter3DController *controller) : Abstract3DRenderer(controller), m_selectedItem(0), - m_xFlipped(false), - m_zFlipped(false), - m_yFlipped(false), m_updateLabels(false), m_dotShader(0), m_dotGradientShader(0), @@ -68,9 +53,6 @@ Scatter3DRenderer::Scatter3DRenderer(Scatter3DController *controller) m_selectionShader(0), m_backgroundShader(0), m_labelShader(0), - m_backgroundObj(0), - m_gridLineObj(0), - m_labelObj(0), m_bgrTexture(0), m_depthTexture(0), m_selectionTexture(0), @@ -82,16 +64,22 @@ Scatter3DRenderer::Scatter3DRenderer(Scatter3DController *controller) m_heightNormalizer(1.0f), m_scaleFactor(0), m_selectedItemIndex(Scatter3DController::invalidSelectionIndex()), - m_selectedItemTotalIndex(Scatter3DController::invalidSelectionIndex()), - m_selectedItemSeriesIndex(Scatter3DController::invalidSelectionIndex()), - m_selectedSeries(0), + m_selectedSeriesCache(0), + m_oldSelectedSeriesCache(0), m_areaSize(QSizeF(0.0, 0.0)), m_dotSizeScale(1.0f), m_hasHeightAdjustmentChanged(true), - m_backgroundMargin(defaultMargin), + m_backgroundMargin(defaultMaxSize), m_maxItemSize(0.0f), - m_clickedIndex(Scatter3DController::invalidSelectionIndex()) + m_clickedIndex(Scatter3DController::invalidSelectionIndex()), + m_havePointSeries(false), + m_haveMeshSeries(false), + m_haveUniformColorMeshSeries(false), + m_haveGradientMeshSeries(false) { + m_axisCacheY.setScale(2.0f); + m_axisCacheY.setTranslate(-1.0f); + initializeOpenGLFunctions(); initializeOpenGL(); } @@ -111,9 +99,6 @@ Scatter3DRenderer::~Scatter3DRenderer() delete m_selectionShader; delete m_backgroundShader; delete m_labelShader; - delete m_backgroundObj; - delete m_gridLineObj; - delete m_labelObj; } void Scatter3DRenderer::initializeOpenGL() @@ -135,8 +120,10 @@ void Scatter3DRenderer::initializeOpenGL() // Init selection shader initSelectionShader(); +#if !defined(QT_OPENGL_ES_2) // Load grid line mesh loadGridLineMesh(); +#endif // Load label mesh loadLabelMesh(); @@ -151,78 +138,153 @@ void Scatter3DRenderer::initializeOpenGL() loadBackgroundMesh(); } -void Scatter3DRenderer::updateSeries(const QList<QAbstract3DSeries *> &seriesList, - bool updateVisibility) +void Scatter3DRenderer::updateData() { - Abstract3DRenderer::updateSeries(seriesList, updateVisibility); + calculateSceneScalingFactors(); + int totalDataSize = 0; - int seriesCount = m_visibleSeriesList.size(); + foreach (SeriesRenderCache *baseCache, m_renderCacheList) { + ScatterSeriesRenderCache *cache = static_cast<ScatterSeriesRenderCache *>(baseCache); + if (cache->isVisible() && cache->dataDirty()) { + 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); + + for (int i = 0; i < dataSize; i++) + updateRenderItem(dataArray.at(i), renderArray[i]); + cache->setDataDirty(false); + } + } + + if (totalDataSize) { + m_dotSizeScale = GLfloat(qBound(defaultMinSize, 2.0f / float(qSqrt(qreal(totalDataSize))), + defaultMaxSize)); + } + + if (m_cachedOptimizationHint.testFlag(QAbstract3DGraph::OptimizationStatic)) { + foreach (SeriesRenderCache *baseCache, m_renderCacheList) { + ScatterSeriesRenderCache *cache = static_cast<ScatterSeriesRenderCache *>(baseCache); + if (cache->isVisible()) { + ScatterRenderItemArray &renderArray = cache->renderArray(); + const int renderArraySize = renderArray.size(); + + if (cache->mesh() == QAbstract3DSeries::MeshPoint) { + ScatterPointBufferHelper *points = cache->bufferPoints(); + if (!points) { + points = new ScatterPointBufferHelper(); + cache->setBufferPoints(points); + } + points->load(cache); + } else { + ScatterObjectBufferHelper *object = cache->bufferObject(); + if (!object) { + object = new ScatterObjectBufferHelper(); + cache->setBufferObject(object); + } + if (renderArraySize != cache->oldArraySize() + || cache->object()->objectFile() != cache->oldMeshFileName()) { + object->fullLoad(cache, m_dotSizeScale); + cache->setOldArraySize(renderArraySize); + cache->setOldMeshFileName(cache->object()->objectFile()); + } else { + object->update(cache, m_dotSizeScale); + } + } + } + } + } + + updateSelectedItem(m_selectedItemIndex, + m_selectedSeriesCache ? m_selectedSeriesCache->series() : 0); +} + +void Scatter3DRenderer::updateSeries(const QList<QAbstract3DSeries *> &seriesList) +{ + Abstract3DRenderer::updateSeries(seriesList); + + int seriesCount = seriesList.size(); float maxItemSize = 0.0f; float itemSize = 0.0f; + bool noSelection = true; - if (m_cachedItemSize.size() != seriesCount) - m_cachedItemSize.resize(seriesCount); + m_havePointSeries = false; + m_haveMeshSeries = false; + m_haveUniformColorMeshSeries = false; + m_haveGradientMeshSeries = false; - for (int series = 0; series < seriesCount; series++) { - itemSize = static_cast<QScatter3DSeries *>(m_visibleSeriesList.at(series).series())->itemSize(); - if (maxItemSize < itemSize) - maxItemSize = itemSize; - if (m_cachedItemSize.at(series) != itemSize) - m_cachedItemSize[series] = itemSize; + for (int i = 0; i < seriesCount; i++) { + QScatter3DSeries *scatterSeries = static_cast<QScatter3DSeries *>(seriesList[i]); + if (scatterSeries->isVisible()) { + ScatterSeriesRenderCache *cache = + static_cast<ScatterSeriesRenderCache *>(m_renderCacheList.value(scatterSeries)); + itemSize = scatterSeries->itemSize(); + if (maxItemSize < itemSize) + maxItemSize = itemSize; + if (cache->itemSize() != itemSize) + cache->setItemSize(itemSize); + if (noSelection + && scatterSeries->selectedItem() != QScatter3DSeries::invalidSelectionIndex()) { + if (m_selectionLabel != cache->itemLabel()) + m_selectionLabelDirty = true; + noSelection = false; + } + + if (cache->mesh() == QAbstract3DSeries::MeshPoint) { + m_havePointSeries = true; + } else { + m_haveMeshSeries = true; + if (cache->colorStyle() == Q3DTheme::ColorStyleUniform) + m_haveUniformColorMeshSeries = true; + else + m_haveGradientMeshSeries = true; + } + } } - m_backgroundMargin = defaultMargin; m_maxItemSize = maxItemSize; if (maxItemSize > defaultMaxSize) - m_backgroundMargin += maxItemSize / itemScaler; + m_backgroundMargin = maxItemSize / itemScaler; + else + m_backgroundMargin = defaultMaxSize; + + if (noSelection) { + if (!selectionLabel().isEmpty()) + m_selectionLabelDirty = true; + m_selectedSeriesCache = 0; + } } -void Scatter3DRenderer::updateData() +SeriesRenderCache *Scatter3DRenderer::createNewCache(QAbstract3DSeries *series) { - int seriesCount = m_visibleSeriesList.size(); - calculateSceneScalingFactors(); - float minX = float(m_axisCacheX.min()); - float maxX = float(m_axisCacheX.max()); - float minY = float(m_axisCacheY.min()); - float maxY = float(m_axisCacheY.max()); - float minZ = float(m_axisCacheZ.min()); - float maxZ = float(m_axisCacheZ.max()); - int totalDataSize = 0; + return new ScatterSeriesRenderCache(series, this); +} - if (m_renderingArrays.size() != seriesCount) - m_renderingArrays.resize(seriesCount); - - for (int series = 0; series < seriesCount; series++) { - QScatterDataProxy *dataProxy = - static_cast<QScatter3DSeries *>(m_visibleSeriesList.at(series).series())->dataProxy(); - const QScatterDataArray &dataArray = *dataProxy->array(); - int dataSize = dataArray.size(); - totalDataSize += dataSize; - - if (dataSize != m_renderingArrays.at(series).size()) - m_renderingArrays[series].resize(dataSize); - - for (int i = 0; i < dataSize; i++) { - QVector3D dotPos = dataArray.at(i).position(); - ScatterRenderItem &renderItem = m_renderingArrays[series][i]; - if ((dotPos.x() >= minX && dotPos.x() <= maxX ) - && (dotPos.y() >= minY && dotPos.y() <= maxY) - && (dotPos.z() >= minZ && dotPos.z() <= maxZ)) { - renderItem.setPosition(dotPos); - renderItem.setVisible(true); - if (!dataArray.at(i).rotation().isIdentity()) - renderItem.setRotation(dataArray.at(i).rotation().normalized()); - else - renderItem.setRotation(identityQuaternion); - calculateTranslation(renderItem); - } else { - renderItem.setVisible(false); - } +void Scatter3DRenderer::updateItems(const QVector<Scatter3DController::ChangeItem> &items) +{ + ScatterSeriesRenderCache *cache = 0; + const QScatter3DSeries *prevSeries = 0; + const QScatterDataArray *dataArray = 0; + + foreach (Scatter3DController::ChangeItem item, items) { + QScatter3DSeries *currentSeries = item.series; + if (currentSeries != prevSeries) { + cache = static_cast<ScatterSeriesRenderCache *>(m_renderCacheList.value(currentSeries)); + prevSeries = currentSeries; + dataArray = item.series->dataProxy()->array(); + // Invisible series render caches are not updated, but instead just marked dirty, so that + // they can be completely recalculated when they are turned visible. + if (!cache->isVisible() && !cache->dataDirty()) + cache->setDataDirty(true); + } + if (cache->isVisible()) { + const int index = item.index; + updateRenderItem(dataArray->at(index), cache->renderArray()[index]); } } - m_dotSizeScale = GLfloat(qBound(defaultMinSize, 2.0f / float(qSqrt(qreal(totalDataSize))), - defaultMaxSize)); - - updateSelectedItem(m_selectedItemIndex, m_selectedSeries); } void Scatter3DRenderer::updateScene(Q3DScene *scene) @@ -231,7 +293,8 @@ void Scatter3DRenderer::updateScene(Q3DScene *scene) if (m_hasHeightAdjustmentChanged) { // Set initial camera position. Also update if height adjustment has changed. - scene->activeCamera()->d_ptr->setBaseOrientation(cameraDistanceVector, zeroVector, upVector); + scene->activeCamera()->d_ptr->setBaseOrientation(cameraDistanceVector, zeroVector, + upVector); m_hasHeightAdjustmentChanged = false; } @@ -249,6 +312,13 @@ void Scatter3DRenderer::render(GLuint defaultFboHandle) // Handle GL state setup for FBO buffers and clearing of the render surface Abstract3DRenderer::render(defaultFboHandle); + if (m_axisCacheX.positionsDirty()) + m_axisCacheX.updateAllPositions(); + if (m_axisCacheY.positionsDirty()) + m_axisCacheY.updateAllPositions(); + if (m_axisCacheZ.positionsDirty()) + m_axisCacheZ.updateAllPositions(); + // Draw dots scene drawScene(defaultFboHandle); } @@ -258,9 +328,13 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) GLfloat backgroundRotation = 0; GLfloat selectedItemSize = 0.0f; + // Get the optimization flag + const bool optimizationDefault = + !m_cachedOptimizationHint.testFlag(QAbstract3DGraph::OptimizationStatic); + const Q3DCamera *activeCamera = m_cachedScene->activeCamera(); - QVector3D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor()); + QVector4D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor()); // Specify viewport glViewport(m_primarySubViewport.x(), @@ -272,14 +346,19 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) QMatrix4x4 projectionMatrix; GLfloat viewPortRatio = (GLfloat)m_primarySubViewport.width() / (GLfloat)m_primarySubViewport.height(); - projectionMatrix.perspective(45.0f, viewPortRatio, 0.1f, 100.0f); + if (m_useOrthoProjection) { + GLfloat orthoRatio = 2.0f; + projectionMatrix.ortho(-viewPortRatio * orthoRatio, viewPortRatio * orthoRatio, + -orthoRatio, orthoRatio, + 0.0f, 100.0f); + } else { + projectionMatrix.perspective(45.0f, viewPortRatio, 0.1f, 100.0f); + } // Calculate view matrix QMatrix4x4 viewMatrix = activeCamera->d_ptr->viewMatrix(); QMatrix4x4 projectionViewMatrix = projectionMatrix * viewMatrix; - int seriesCount = m_visibleSeriesList.size(); - // Calculate label flipping if (viewMatrix.row(0).x() > 0) m_zFlipped = false; @@ -314,25 +393,8 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) QMatrix4x4 depthProjectionMatrix; QMatrix4x4 depthProjectionViewMatrix; - // Check if we have any series with points - bool havePointSeries = false; - bool haveMeshSeries = false; - bool haveUniformColorMeshSeries = false; - bool haveGradientMeshSeries = false; - for (int i = 0; i < seriesCount; i++) { - if (m_visibleSeriesList.at(i).mesh() == QAbstract3DSeries::MeshPoint) { - havePointSeries = true; - } else { - haveMeshSeries = true; - if (m_visibleSeriesList.at(i).colorStyle() == Q3DTheme::ColorStyleUniform) - haveUniformColorMeshSeries = true; - else - haveGradientMeshSeries = true; - } - } - #if !defined(QT_OPENGL_ES_2) - if (havePointSeries) { + if (m_havePointSeries) { glEnable(GL_POINT_SMOOTH); glEnable(GL_PROGRAM_POINT_SIZE); } @@ -360,77 +422,107 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) zeroVector, 0.0f, 2.5f / m_autoScaleAdjustment); depthViewMatrix.lookAt(depthLightPos, zeroVector, upVector); // Set the depth projection matrix -#ifndef USE_WIDER_SHADOWS - // Use this for perspective shadows depthProjectionMatrix.perspective(15.0f, viewPortRatio, 3.0f, 100.0f); -#else - // Use these for orthographic shadows - GLfloat testAspectRatio = viewPortRatio; - depthProjectionMatrix.ortho(-testAspectRatio * 2.0f, testAspectRatio * 2.0f, - -m_heightNormalizer * 2.0f, m_heightNormalizer * 2.0f, - 0.0f, 100.0f); -#endif depthProjectionViewMatrix = depthProjectionMatrix * depthViewMatrix; // Draw dots to depth buffer - for (int series = 0; series < seriesCount; series++) { - ObjectHelper *dotObj = m_visibleSeriesList.at(series).object(); - QQuaternion seriesRotation = m_visibleSeriesList.at(series).meshRotation(); - bool drawingPoints = (m_visibleSeriesList.at(series).mesh() == QAbstract3DSeries::MeshPoint); - - float itemSize = m_cachedItemSize.at(series) / 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); - - for (int dot = 0; dot < m_renderingArrays.at(series).size(); dot++) { - const ScatterRenderItem &item = m_renderingArrays.at(series).at(dot); - if (!item.isVisible()) - continue; - - QMatrix4x4 modelMatrix; - QMatrix4x4 MVPMatrix; - - modelMatrix.translate(item.translation()); - if (!drawingPoints) { - if (!seriesRotation.isIdentity() || !item.rotation().isIdentity()) - modelMatrix.rotate(seriesRotation * item.rotation()); - modelMatrix.scale(modelScaler); - } - - MVPMatrix = depthProjectionViewMatrix * modelMatrix; - - m_depthShader->setUniformValue(m_depthShader->MVP(), MVPMatrix); - + 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) { - m_drawer->drawPoint(m_depthShader); - } else { - // 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()); + // Scale points based on shadow quality for shadows, not by zoom level + 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) + continue; + + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; + + if (optimizationDefault) { + modelMatrix.translate(item.translation()); + if (!drawingPoints) { + if (!seriesRotation.isIdentity() || !item.rotation().isIdentity()) + modelMatrix.rotate(seriesRotation * item.rotation()); + modelMatrix.scale(modelScaler); + } + } + + MVPMatrix = depthProjectionViewMatrix * modelMatrix; + + m_depthShader->setUniformValue(m_depthShader->MVP(), MVPMatrix); + + if (drawingPoints) { + if (optimizationDefault) + m_drawer->drawPoint(m_depthShader); + else + m_drawer->drawPoints(m_depthShader, cache->bufferPoints()); + } else { + if (optimizationDefault) { + // 1st attribute buffer : vertices + glEnableVertexAttribArray(m_depthShader->posAtt()); + glBindBuffer(GL_ARRAY_BUFFER, dotObj->vertexBuf()); + glVertexAttribPointer(m_depthShader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0, + (void *)0); + + // Index buffer + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dotObj->elementBuf()); + + // Draw the triangles + glDrawElements(GL_TRIANGLES, dotObj->indexCount(), GL_UNSIGNED_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); + // Disable drawing to framebuffer (= enable drawing to screen) glBindFramebuffer(GL_FRAMEBUFFER, defaultFboHandle); @@ -446,14 +538,14 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) ShaderHelper *pointSelectionShader = m_selectionShader; #else - Q_UNUSED(havePointSeries); ShaderHelper *pointSelectionShader = m_pointShader; #endif ShaderHelper *selectionShader = m_selectionShader; // Skip selection mode drawing if we have no selection mode if (m_cachedSelectionMode > QAbstract3DGraph::SelectionNone - && SelectOnScene == m_selectionState && seriesCount > 0) { + && SelectOnScene == m_selectionState + && (m_visibleSeriesCount > 0 || !m_customRenderCache.isEmpty())) { // Draw dots to selection buffer glBindFramebuffer(GL_FRAMEBUFFER, m_selectionFrameBuffer); glViewport(0, 0, @@ -465,94 +557,80 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Needed for clearing the frame buffer glDisable(GL_DITHER); // disable dithering, it may affect colors if enabled - int arraySize = 0; - int totalArraySize = 0; - int dotNo = 0; - - // Init previous to opposite of first to ensure we change binding for first series - bool previousDrawingPoints = (m_visibleSeriesList.at(0).mesh() != QAbstract3DSeries::MeshPoint); - for (int series = 0; series < seriesCount; series++) { - ObjectHelper *dotObj = m_visibleSeriesList.at(series).object(); - QQuaternion seriesRotation = m_visibleSeriesList.at(series).meshRotation(); - bool drawingPoints = (m_visibleSeriesList.at(series).mesh() == QAbstract3DSeries::MeshPoint); - - float itemSize = m_cachedItemSize.at(series) / itemScaler; - if (itemSize == 0.0f) - itemSize = m_dotSizeScale; + bool previousDrawingPoints = false; + int totalIndex = 0; + foreach (SeriesRenderCache *baseCache, m_renderCacheList) { + if (baseCache->isVisible()) { + ScatterSeriesRenderCache *cache = + static_cast<ScatterSeriesRenderCache *>(baseCache); + ObjectHelper *dotObj = cache->object(); + QQuaternion seriesRotation(cache->meshRotation()); + const ScatterRenderItemArray &renderArray = cache->renderArray(); + const int renderArraySize = renderArray.size(); + bool drawingPoints = (cache->mesh() == QAbstract3DSeries::MeshPoint); + float itemSize = cache->itemSize() / itemScaler; + if (itemSize == 0.0f) + itemSize = m_dotSizeScale; #if !defined(QT_OPENGL_ES_2) - if (drawingPoints) - glPointSize(itemSize * activeCamera->zoomLevel()); // Scale points based on zoom -#endif - QVector3D modelScaler(itemSize, itemSize, itemSize); - - // Rebind selection shader if it has changed - if (drawingPoints != previousDrawingPoints) { - previousDrawingPoints = drawingPoints; if (drawingPoints) - selectionShader = pointSelectionShader; - else - selectionShader = m_selectionShader; - - selectionShader->bind(); - } - arraySize = m_renderingArrays.at(series).size(); - totalArraySize += arraySize; - - if (totalArraySize > 0xfffffe) // Max possible different selection colors, 0xffffff being skipColor - qFatal("Too many objects"); - - for (int dot = 0; dot < arraySize; dot++) { - const ScatterRenderItem &item = m_renderingArrays.at(series).at(dot); - if (!item.isVisible()) - continue; + glPointSize(itemSize * activeCamera->zoomLevel()); // Scale points based on zoom +#endif + QVector3D modelScaler(itemSize, itemSize, itemSize); - QMatrix4x4 modelMatrix; - QMatrix4x4 MVPMatrix; + // Rebind selection shader if it has changed + if (!totalIndex || drawingPoints != previousDrawingPoints) { + previousDrawingPoints = drawingPoints; + if (drawingPoints) + selectionShader = pointSelectionShader; + else + selectionShader = m_selectionShader; - modelMatrix.translate(item.translation()); - if (!drawingPoints) { - if (!seriesRotation.isIdentity() || !item.rotation().isIdentity()) - modelMatrix.rotate(seriesRotation * item.rotation()); - modelMatrix.scale(modelScaler); + selectionShader->bind(); } - - MVPMatrix = projectionViewMatrix * modelMatrix; - - QVector3D dotColor = indexToSelectionColor(dotNo); - dotColor /= 255.0f; - - selectionShader->setUniformValue(selectionShader->MVP(), MVPMatrix); - selectionShader->setUniformValue(selectionShader->color(), dotColor); - - if (drawingPoints) { - m_drawer->drawPoint(selectionShader); - } else { - // 1st attribute buffer : vertices - glEnableVertexAttribArray(selectionShader->posAtt()); - glBindBuffer(GL_ARRAY_BUFFER, dotObj->vertexBuf()); - glVertexAttribPointer(selectionShader->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(selectionShader->posAtt()); + cache->setSelectionIndexOffset(totalIndex); + for (int dot = 0; dot < renderArraySize; dot++) { + const ScatterRenderItem &item = renderArray.at(dot); + if (!item.isVisible()) { + totalIndex++; + continue; + } + + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; + + modelMatrix.translate(item.translation()); + if (!drawingPoints) { + if (!seriesRotation.isIdentity() || !item.rotation().isIdentity()) + modelMatrix.rotate(seriesRotation * item.rotation()); + modelMatrix.scale(modelScaler); + } + + MVPMatrix = projectionViewMatrix * modelMatrix; + + QVector4D dotColor = indexToSelectionColor(totalIndex++); + dotColor /= 255.0f; + + selectionShader->setUniformValue(selectionShader->MVP(), MVPMatrix); + selectionShader->setUniformValue(selectionShader->color(), dotColor); + + if (drawingPoints) + m_drawer->drawPoint(selectionShader); + else + m_drawer->drawSelectionObject(selectionShader, dotObj); } - dotNo++; } } + + Abstract3DRenderer::drawCustomItems(RenderingSelection, m_selectionShader, viewMatrix, + projectionViewMatrix, depthProjectionViewMatrix, + m_depthTexture, m_shadowQualityToShader); + + drawLabels(true, activeCamera, viewMatrix, projectionMatrix); + glEnable(GL_DITHER); // Read color under cursor - QVector3D clickedColor = Utils::getSelection(m_inputPosition, + QVector4D clickedColor = Utils::getSelection(m_inputPosition, m_viewport.height()); selectionColorToSeriesAndIndex(clickedColor, m_clickedIndex, m_clickedSeries); @@ -571,15 +649,14 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) GLuint gradientTexture = 0; bool dotSelectionFound = false; ScatterRenderItem *selectedItem(0); - int dotNo = 0; - QVector3D baseColor; - QVector3D dotColor; + QVector4D baseColor; + QVector4D dotColor; bool previousDrawingPoints = false; Q3DTheme::ColorStyle previousMeshColorStyle = Q3DTheme::ColorStyleUniform; - if (haveMeshSeries) { + if (m_haveMeshSeries) { // Set unchanging shader bindings - if (haveGradientMeshSeries) { + if (m_haveGradientMeshSeries) { m_dotGradientShader->bind(); m_dotGradientShader->setUniformValue(m_dotGradientShader->lightP(), lightPos); m_dotGradientShader->setUniformValue(m_dotGradientShader->view(), viewMatrix); @@ -587,7 +664,7 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) m_cachedTheme->ambientLightStrength()); m_dotGradientShader->setUniformValue(m_dotGradientShader->lightColor(), lightColor); } - if (haveUniformColorMeshSeries) { + if (m_haveUniformColorMeshSeries) { m_dotShader->bind(); m_dotShader->setUniformValue(m_dotShader->lightP(), lightPos); m_dotShader->setUniformValue(m_dotShader->view(), viewMatrix); @@ -607,152 +684,281 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) dotShader->bind(); } - for (int series = 0; series < seriesCount; series++) { - const SeriesRenderCache ¤tSeries = m_visibleSeriesList.at(series); - QQuaternion seriesRotation = currentSeries.meshRotation(); - ObjectHelper *dotObj = currentSeries.object(); - bool drawingPoints = (currentSeries.mesh() == QAbstract3DSeries::MeshPoint); - Q3DTheme::ColorStyle colorStyle = currentSeries.colorStyle(); - bool colorStyleIsUniform = (colorStyle == Q3DTheme::ColorStyleUniform); - bool useColor = colorStyleIsUniform || drawingPoints; - - float itemSize = m_cachedItemSize.at(series) / itemScaler; - if (itemSize == 0.0f) - itemSize = m_dotSizeScale; + foreach (SeriesRenderCache *baseCache, m_renderCacheList) { + if (baseCache->isVisible()) { + ScatterSeriesRenderCache *cache = + static_cast<ScatterSeriesRenderCache *>(baseCache); + ObjectHelper *dotObj = cache->object(); + QQuaternion seriesRotation(cache->meshRotation()); + ScatterRenderItemArray &renderArray = cache->renderArray(); + const int renderArraySize = renderArray.size(); + bool selectedSeries = m_cachedSelectionMode > QAbstract3DGraph::SelectionNone + && (m_selectedSeriesCache == cache); + bool drawingPoints = (cache->mesh() == QAbstract3DSeries::MeshPoint); + Q3DTheme::ColorStyle colorStyle = cache->colorStyle(); + bool colorStyleIsUniform = (colorStyle == Q3DTheme::ColorStyleUniform); + bool useColor = colorStyleIsUniform || drawingPoints; + bool rangeGradientPoints = drawingPoints + && (colorStyle == Q3DTheme::ColorStyleRangeGradient); + float itemSize = cache->itemSize() / itemScaler; + if (itemSize == 0.0f) + itemSize = m_dotSizeScale; #if !defined(QT_OPENGL_ES_2) - if (drawingPoints) - glPointSize(itemSize * activeCamera->zoomLevel()); // Scale points based on zoom + if (drawingPoints) + glPointSize(itemSize * activeCamera->zoomLevel()); // Scale points based on zoom #endif - QVector3D modelScaler(itemSize, itemSize, itemSize); - - // Rebind shader if it has changed - if (drawingPoints != previousDrawingPoints - || (!drawingPoints && - (colorStyleIsUniform != (previousMeshColorStyle == Q3DTheme::ColorStyleUniform)))) { - previousDrawingPoints = drawingPoints; - if (drawingPoints) { - dotShader = pointSelectionShader; - } else { - if (colorStyleIsUniform) - dotShader = m_dotShader; - else - dotShader = m_dotGradientShader; + QVector3D modelScaler(itemSize, itemSize, itemSize); + + // Rebind shader if it has changed + if (drawingPoints != previousDrawingPoints + || (!drawingPoints && + (colorStyleIsUniform != (previousMeshColorStyle + == Q3DTheme::ColorStyleUniform)))) { + previousDrawingPoints = drawingPoints; + if (drawingPoints) { + dotShader = pointSelectionShader; + } else { + if (colorStyleIsUniform) + dotShader = m_dotShader; + else + dotShader = m_dotGradientShader; + } + dotShader->bind(); } - dotShader->bind(); - } - 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); - } else { - // Each ball is of uniform color according to its Y-coordinate - m_dotGradientShader->setUniformValue(m_dotGradientShader->gradientHeight(), 0.0f); + 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); + } else { + // Each dot is of uniform color according to its Y-coordinate + m_dotGradientShader->setUniformValue(m_dotGradientShader->gradientHeight(), + 0.0f); + } } - } - if (!drawingPoints) - previousMeshColorStyle = colorStyle; + if (!drawingPoints) + previousMeshColorStyle = colorStyle; - if (useColor) { - baseColor = currentSeries.baseColor(); - dotColor = baseColor; - } + if (useColor) { + baseColor = cache->baseColor(); + dotColor = baseColor; + } + int loopCount = 1; + if (optimizationDefault) + loopCount = renderArraySize; + for (int i = 0; i < loopCount; i++) { + ScatterRenderItem &item = renderArray[i]; + if (!item.isVisible() && optimizationDefault) + continue; - int seriesSize = m_renderingArrays.at(series).size(); - for (int dot = 0; dot < seriesSize; dot++) { - ScatterRenderItem &item = m_renderingArrays[series][dot]; - if (!item.isVisible()) - continue; - - QMatrix4x4 modelMatrix; - QMatrix4x4 MVPMatrix; - 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); + QMatrix4x4 modelMatrix; + QMatrix4x4 MVPMatrix; + QMatrix4x4 itModelMatrix; + + if (optimizationDefault) { + modelMatrix.translate(item.translation()); + if (!drawingPoints) { + if (!seriesRotation.isIdentity() || !item.rotation().isIdentity()) { + QQuaternion totalRotation = seriesRotation * item.rotation(); + modelMatrix.rotate(totalRotation); + itModelMatrix.rotate(totalRotation); + } + modelMatrix.scale(modelScaler); + itModelMatrix.scale(modelScaler); + } } - modelMatrix.scale(modelScaler); - itModelMatrix.scale(modelScaler); - } #ifdef SHOW_DEPTH_TEXTURE_SCENE - MVPMatrix = depthProjectionViewMatrix * modelMatrix; + MVPMatrix = depthProjectionViewMatrix * modelMatrix; #else - MVPMatrix = projectionViewMatrix * modelMatrix; + MVPMatrix = projectionViewMatrix * modelMatrix; #endif - if (useColor) - dotColor = baseColor; - else - gradientTexture = currentSeries.baseGradientTexture(); + if (useColor) { + 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; + dotColor = Utils::vectorFromColor( + cache->gradientImage().pixel(0, position)); + } else { + dotColor = baseColor; + } + } else { + gradientTexture = cache->baseGradientTexture(); + } + + GLfloat lightStrength = m_cachedTheme->lightStrength(); + if (optimizationDefault && selectedSeries && (m_selectedItemIndex == i)) { + if (useColor) + dotColor = cache->singleHighlightColor(); + else + gradientTexture = cache->singleHighlightGradientTexture(); + lightStrength = m_cachedTheme->highlightLightStrength(); + // Insert data to ScatterRenderItem + // We don't have ownership, so don't delete the previous one + 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()); + } + + 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 !defined(QT_OPENGL_ES_2) + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + if (!drawingPoints) { + // Set shadow shader bindings + QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; + dotShader->setUniformValue(dotShader->shadowQ(), m_shadowQualityToShader); + dotShader->setUniformValue(dotShader->depth(), depthMVPMatrix); + dotShader->setUniformValue(dotShader->lightS(), lightStrength / 10.0f); + + // Draw the object + if (optimizationDefault) { + m_drawer->drawObject(dotShader, dotObj, gradientTexture, + m_depthTexture); + } else { + m_drawer->drawObject(dotShader, cache->bufferObject(), gradientTexture, + m_depthTexture); + } + } else { + // Draw the object + if (optimizationDefault) + m_drawer->drawPoint(dotShader); + else + m_drawer->drawPoints(dotShader, cache->bufferPoints()); + } + } else +#endif + { + if (!drawingPoints) { + // Set shadowless shader bindings + dotShader->setUniformValue(dotShader->lightS(), lightStrength); + // Draw the object + if (optimizationDefault) + m_drawer->drawObject(dotShader, dotObj, gradientTexture); + else + m_drawer->drawObject(dotShader, cache->bufferObject(), gradientTexture); + } else { + // Draw the object + if (optimizationDefault) + m_drawer->drawPoint(dotShader); + else + m_drawer->drawPoints(dotShader, cache->bufferPoints()); + } + } + } + + // Draw the selected item on static optimization + if (!optimizationDefault && selectedSeries + && m_selectedItemIndex != Scatter3DController::invalidSelectionIndex()) { + ScatterRenderItem &item = renderArray[m_selectedItemIndex]; + ObjectHelper *dotObj = cache->object(); + + QMatrix4x4 modelMatrix; + QMatrix4x4 itModelMatrix; + + modelMatrix.translate(item.translation()); + if (!drawingPoints) { + if (!seriesRotation.isIdentity() || !item.rotation().isIdentity()) { + QQuaternion totalRotation = seriesRotation * item.rotation(); + modelMatrix.rotate(totalRotation); + itModelMatrix.rotate(totalRotation); + } + modelMatrix.scale(modelScaler); + itModelMatrix.scale(modelScaler); + } + + QMatrix4x4 MVPMatrix; +#ifdef SHOW_DEPTH_TEXTURE_SCENE + MVPMatrix = depthProjectionViewMatrix * modelMatrix; +#else + MVPMatrix = projectionViewMatrix * modelMatrix; +#endif - GLfloat lightStrength = m_cachedTheme->lightStrength(); - if (m_cachedSelectionMode > QAbstract3DGraph::SelectionNone - && (m_selectedItemTotalIndex == dotNo)) { if (useColor) - dotColor = currentSeries.singleHighlightColor(); + dotColor = cache->singleHighlightColor(); else - gradientTexture = currentSeries.singleHighlightGradientTexture(); - lightStrength = m_cachedTheme->highlightLightStrength(); - // Insert data to ScatterRenderItem. We have no ownership, don't delete the previous one + 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 + // 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()); - } - - 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 !defined(QT_OPENGL_ES_2) - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { if (!drawingPoints) { - // Set shadow shader bindings - QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix; - dotShader->setUniformValue(dotShader->shadowQ(), m_shadowQualityToShader); - dotShader->setUniformValue(dotShader->depth(), depthMVPMatrix); - dotShader->setUniformValue(dotShader->lightS(), lightStrength / 10.0f); + // Set shader bindings + dotShader->setUniformValue(dotShader->model(), modelMatrix); + dotShader->setUniformValue(dotShader->nModel(), + itModelMatrix.inverted().transposed()); + } - // Draw the object - m_drawer->drawObject(dotShader, dotObj, gradientTexture, m_depthTexture); - } else { - // Draw the object - m_drawer->drawPoint(dotShader); + 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); } - } else -#endif - { + if (!drawingPoints) { - // Set shadowless shader bindings - dotShader->setUniformValue(dotShader->lightS(), lightStrength); - // Draw the object - m_drawer->drawObject(dotShader, dotObj, gradientTexture); - } else { - // Draw the object - m_drawer->drawPoint(dotShader); + glEnable(GL_POLYGON_OFFSET_FILL); + glPolygonOffset(-0.5f, 1.0f); + } + +#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); + } + } else +#endif + { + if (!drawingPoints) { + // Set shadowless shader bindings + dotShader->setUniformValue(dotShader->lightS(), lightStrength); + // Draw the object + m_drawer->drawObject(dotShader, dotObj, gradientTexture); + } else { + // Draw the object + m_drawer->drawPoint(dotShader); + } } + + if (!drawingPoints) + glDisable(GL_POLYGON_OFFSET_FILL); } - dotNo++; } } #if !defined(QT_OPENGL_ES_2) - if (havePointSeries) { + if (m_havePointSeries) { glDisable(GL_POINT_SMOOTH); glDisable(GL_PROGRAM_POINT_SIZE); } @@ -770,17 +976,19 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) QMatrix4x4 itModelMatrix; #ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z - GLfloat xScale = (aspectRatio * m_backgroundMargin * m_areaSize.width()) / m_scaleFactor; - GLfloat zScale = (aspectRatio * m_backgroundMargin * m_areaSize.height()) / m_scaleFactor; + 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, m_backgroundMargin, zScale); + QVector3D bgScale(xScale, 1.0f + m_backgroundMargin, zScale); #else // ..and this if we want uniform scaling based on largest dimension - QVector3D bgScale((aspectRatio * m_backgroundMargin), - m_backgroundMargin, - (aspectRatio * m_backgroundMargin)); + QVector3D bgScale((m_graphAspectRatio + m_backgroundMargin), + 1.0f + m_backgroundMargin, + (m_graphAspectRatio + m_backgroundMargin)); #endif modelMatrix.scale(bgScale); // If we're viewing from below, background object must be flipped @@ -797,7 +1005,7 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) #else MVPMatrix = projectionViewMatrix * modelMatrix; #endif - QVector3D backgroundColor = Utils::vectorFromColor(m_cachedTheme->backgroundColor()); + QVector4D backgroundColor = Utils::vectorFromColor(m_cachedTheme->backgroundColor()); // Set shader bindings m_backgroundShader->setUniformValue(m_backgroundShader->lightP(), lightPos); @@ -839,22 +1047,18 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) glDisable(GL_TEXTURE_2D); // Draw grid lines -#ifdef USE_UNIFORM_SCALING - AxisRenderCache *axisCacheMax; - if (m_axisCacheZ.max() > m_axisCacheX.max()) - axisCacheMax = &m_axisCacheZ; - else - axisCacheMax = &m_axisCacheX; -#endif - - if (m_cachedTheme->isGridEnabled() && m_heightNormalizer) { + 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 // Bind line shader lineShader->bind(); // Set unchanging shader bindings - QVector3D lineColor = Utils::vectorFromColor(m_cachedTheme->gridLineColor()); + QVector4D lineColor = Utils::vectorFromColor(m_cachedTheme->gridLineColor()); lineShader->setUniformValue(lineShader->lightP(), lightPos); lineShader->setUniformValue(lineShader->view(), viewMatrix); lineShader->setUniformValue(lineShader->color(), lineColor); @@ -887,39 +1091,33 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) else lineXRotation = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, -90.0f); - GLfloat yFloorLinePosition = -m_backgroundMargin + gridLineOffset; + GLfloat yFloorLinePosition = -1.0f - m_backgroundMargin + gridLineOffset; if (m_yFlipped) yFloorLinePosition = -yFloorLinePosition; // Rows (= Z) if (m_axisCacheZ.segmentCount() > 0) { // Floor lines -#ifndef USE_UNIFORM_SCALING - GLfloat lineStep = aspectRatio * m_axisCacheZ.subSegmentStep(); - GLfloat linePos = -aspectRatio * (m_axisCacheZ.min() - m_translationOffset.z()); // Start line - int lastSegment = m_axisCacheZ.subSegmentCount() * m_axisCacheZ.segmentCount(); -#else - GLfloat lineStep = aspectRatio * axisCacheMax->subSegmentStep(); - GLfloat linePos = -aspectRatio * m_scaleFactor; // Start line - int lastSegment = axisCacheMax->subSegmentCount() * axisCacheMax->segmentCount(); -#endif + int gridLineCount = m_axisCacheZ.gridLineCount(); #ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z - GLfloat xScale = (aspectRatio * m_backgroundMargin * m_areaSize.width()) / m_scaleFactor; + 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((aspectRatio * m_backgroundMargin), + QVector3D gridLineScaler((m_graphAspectRatio + m_backgroundMargin), gridLineWidth, gridLineWidth); #endif - for (int segment = 0; segment <= lastSegment; segment++) { + for (int line = 0; line < gridLineCount; line++) { QMatrix4x4 modelMatrix; QMatrix4x4 MVPMatrix; QMatrix4x4 itModelMatrix; - modelMatrix.translate(0.0f, yFloorLinePosition, linePos / m_scaleFactor); + modelMatrix.translate(0.0f, yFloorLinePosition, + m_axisCacheZ.gridLinePosition(line)); modelMatrix.scale(gridLineScaler); itModelMatrix.scale(gridLineScaler); @@ -942,42 +1140,45 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); // Draw the object m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); - } else -#endif - { + } else { // Draw the object m_drawer->drawObject(lineShader, m_gridLineObj); } - linePos -= lineStep; +#else + m_drawer->drawLine(lineShader); +#endif } // Side wall lines - gridLineScaler = QVector3D(gridLineWidth, m_backgroundMargin, gridLineWidth); + gridLineScaler = QVector3D(gridLineWidth, 1.0f + m_backgroundMargin, gridLineWidth); #ifndef USE_UNIFORM_SCALING - GLfloat lineXTrans = (aspectRatio * m_backgroundMargin * m_areaSize.width()) - / m_scaleFactor - gridLineOffset; + GLfloat lineXTrans = (m_graphAspectRatio * m_areaSize.width()) + / m_scaleFactor - gridLineOffset + m_backgroundMargin; if (m_maxItemSize > lineXTrans) lineXTrans = m_maxItemSize - gridLineOffset; - linePos = -aspectRatio * (m_axisCacheZ.min() - m_translationOffset.z()); // Start line #else - GLfloat lineXTrans = aspectRatio * m_backgroundMargin - gridLineOffset; - linePos = -aspectRatio * m_scaleFactor; // Start line + GLfloat lineXTrans = m_graphAspectRatio + m_backgroundMargin - gridLineOffset; #endif if (!m_xFlipped) lineXTrans = -lineXTrans; - for (int segment = 0; segment <= lastSegment; segment++) { + for (int line = 0; line < gridLineCount; line++) { QMatrix4x4 modelMatrix; QMatrix4x4 MVPMatrix; QMatrix4x4 itModelMatrix; - modelMatrix.translate(lineXTrans, 0.0f, linePos / m_scaleFactor); + modelMatrix.translate(lineXTrans, 0.0f, m_axisCacheZ.gridLinePosition(line)); modelMatrix.scale(gridLineScaler); itModelMatrix.scale(gridLineScaler); +#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; @@ -994,41 +1195,42 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); // Draw the object m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); - } else -#endif - { + } else { // Draw the object m_drawer->drawObject(lineShader, m_gridLineObj); } - linePos -= lineStep; +#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 // Floor lines + int gridLineCount = m_axisCacheX.gridLineCount(); + #ifndef USE_UNIFORM_SCALING - GLfloat lineStep = aspectRatio * m_axisCacheX.subSegmentStep(); - GLfloat linePos = aspectRatio * (m_axisCacheX.min() - m_translationOffset.x()); - int lastSegment = m_axisCacheX.subSegmentCount() * m_axisCacheX.segmentCount(); - GLfloat zScale = (aspectRatio * m_backgroundMargin * m_areaSize.height()) / m_scaleFactor; + GLfloat zScale = (m_graphAspectRatio * m_areaSize.height()) / m_scaleFactor + + m_backgroundMargin; if (m_maxItemSize > zScale) zScale = m_maxItemSize; QVector3D gridLineScaler(gridLineWidth, gridLineWidth, zScale); #else - GLfloat lineStep = aspectRatio * axisCacheMax->subSegmentStep(); - GLfloat linePos = -aspectRatio * m_scaleFactor; - int lastSegment = axisCacheMax->subSegmentCount() * axisCacheMax->segmentCount(); QVector3D gridLineScaler(gridLineWidth, gridLineWidth, - aspectRatio * m_backgroundMargin); + m_graphAspectRatio + m_backgroundMargin); #endif - for (int segment = 0; segment <= lastSegment; segment++) { + for (int line = 0; line < gridLineCount; line++) { QMatrix4x4 modelMatrix; QMatrix4x4 MVPMatrix; QMatrix4x4 itModelMatrix; - modelMatrix.translate(linePos / m_scaleFactor, yFloorLinePosition, 0.0f); + modelMatrix.translate(m_axisCacheX.gridLinePosition(line), yFloorLinePosition, + 0.0f); modelMatrix.scale(gridLineScaler); itModelMatrix.scale(gridLineScaler); @@ -1051,45 +1253,48 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); // Draw the object m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); - } else -#endif - { + } else { // Draw the object m_drawer->drawObject(lineShader, m_gridLineObj); } - linePos += lineStep; +#else + m_drawer->drawLine(lineShader); +#endif } // Back wall lines #ifndef USE_UNIFORM_SCALING - GLfloat lineZTrans = (aspectRatio * m_backgroundMargin * m_areaSize.height()) - / m_scaleFactor - gridLineOffset; + GLfloat lineZTrans = (m_graphAspectRatio * m_areaSize.height()) + / m_scaleFactor - gridLineOffset + m_backgroundMargin; if (m_maxItemSize > lineZTrans) lineZTrans = m_maxItemSize - gridLineOffset; - linePos = aspectRatio * (m_axisCacheX.min() - m_translationOffset.x()); #else - GLfloat lineZTrans = aspectRatio * m_backgroundMargin - gridLineOffset; - linePos = -aspectRatio * m_scaleFactor; + GLfloat lineZTrans = m_graphAspectRatio + m_backgroundMargin - gridLineOffset; #endif if (!m_zFlipped) lineZTrans = -lineZTrans; - gridLineScaler = QVector3D(gridLineWidth, m_backgroundMargin, gridLineWidth); + gridLineScaler = QVector3D(gridLineWidth, 1.0f + m_backgroundMargin, gridLineWidth); - for (int segment = 0; segment <= lastSegment; segment++) { + for (int line = 0; line < gridLineCount; line++) { QMatrix4x4 modelMatrix; QMatrix4x4 MVPMatrix; QMatrix4x4 itModelMatrix; - modelMatrix.translate(linePos / m_scaleFactor, 0.0f, lineZTrans); + modelMatrix.translate(m_axisCacheX.gridLinePosition(line), 0.0f, lineZTrans); modelMatrix.scale(gridLineScaler); itModelMatrix.scale(gridLineScaler); +#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 MVPMatrix = projectionViewMatrix * modelMatrix; @@ -1106,46 +1311,45 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); // Draw the object m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); - } else -#endif - { + } else { // Draw the object m_drawer->drawObject(lineShader, m_gridLineObj); } - linePos += lineStep; +#else + m_drawer->drawLine(lineShader); +#endif } } // Horizontal wall lines if (m_axisCacheY.segmentCount() > 0) { // Back wall - GLfloat lineStep = m_axisCacheY.subSegmentStep(); - GLfloat linePos = m_axisCacheY.min() - m_translationOffset.y(); - int lastSegment = m_axisCacheY.subSegmentCount() * m_axisCacheY.segmentCount(); + int gridLineCount = m_axisCacheY.gridLineCount(); #ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z - GLfloat lineZTrans = (aspectRatio * m_backgroundMargin * m_areaSize.height()) - / m_scaleFactor - gridLineOffset; + GLfloat lineZTrans = (m_graphAspectRatio * m_areaSize.height()) + / m_scaleFactor - gridLineOffset + m_backgroundMargin; if (m_maxItemSize > lineZTrans) lineZTrans = m_maxItemSize - gridLineOffset; - GLfloat xScale = (aspectRatio * m_backgroundMargin * m_areaSize.width()) / m_scaleFactor; + 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 = aspectRatio * m_backgroundMargin - gridLineOffset; - QVector3D gridLineScaler((aspectRatio * m_backgroundMargin), + GLfloat lineZTrans = m_graphAspectRatio + m_backgroundMargin - gridLineOffset; + QVector3D gridLineScaler((m_graphAspectRatio + m_backgroundMargin), gridLineWidth, gridLineWidth); #endif if (!m_zFlipped) lineZTrans = -lineZTrans; - for (int segment = 0; segment <= lastSegment; segment++) { + for (int line = 0; line < gridLineCount; line++) { QMatrix4x4 modelMatrix; QMatrix4x4 MVPMatrix; QMatrix4x4 itModelMatrix; - modelMatrix.translate(0.0f, linePos / m_heightNormalizer, lineZTrans); + modelMatrix.translate(0.0f, m_axisCacheY.gridLinePosition(line), lineZTrans); modelMatrix.scale(gridLineScaler); itModelMatrix.scale(gridLineScaler); @@ -1170,42 +1374,40 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); // Draw the object m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); - } else -#endif - { + } else { // Draw the object m_drawer->drawObject(lineShader, m_gridLineObj); } - linePos += lineStep; +#else + m_drawer->drawLine(lineShader); +#endif } // Side wall - linePos = m_axisCacheY.min() - m_translationOffset.y(); - lastSegment = m_axisCacheY.subSegmentCount() * m_axisCacheY.segmentCount(); #ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z - GLfloat lineXTrans = (aspectRatio * m_backgroundMargin * m_areaSize.width()) - / m_scaleFactor - gridLineOffset; + GLfloat lineXTrans = (m_graphAspectRatio * m_areaSize.width()) + / m_scaleFactor - gridLineOffset + m_backgroundMargin; if (m_maxItemSize > lineXTrans) lineXTrans = m_maxItemSize - gridLineOffset; - GLfloat zScale = (aspectRatio * m_backgroundMargin * m_areaSize.height()) - / m_scaleFactor; + 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 = aspectRatio * m_backgroundMargin - gridLineOffset; + GLfloat lineXTrans = m_graphAspectRatio + m_backgroundMargin - gridLineOffset; gridLineScaler = QVector3D(gridLineWidth, gridLineWidth, - aspectRatio * m_backgroundMargin); + m_graphAspectRatio + m_backgroundMargin); #endif if (!m_xFlipped) lineXTrans = -lineXTrans; - for (int segment = 0; segment <= lastSegment; segment++) { + for (int line = 0; line < gridLineCount; line++) { QMatrix4x4 modelMatrix; QMatrix4x4 MVPMatrix; QMatrix4x4 itModelMatrix; - modelMatrix.translate(lineXTrans, linePos / m_heightNormalizer, 0.0f); + modelMatrix.translate(lineXTrans, m_axisCacheY.gridLinePosition(line), 0.0f); modelMatrix.scale(gridLineScaler); itModelMatrix.scale(gridLineScaler); @@ -1228,328 +1430,478 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle) lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); // Draw the object m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); - } else -#endif - { + } else { // Draw the object m_drawer->drawObject(lineShader, m_gridLineObj); } - linePos += lineStep; +#else + m_drawer->drawLine(lineShader); +#endif + } + } + } + + Abstract3DRenderer::drawCustomItems(RenderingNormal, m_customItemShader, viewMatrix, + projectionViewMatrix, depthProjectionViewMatrix, + m_depthTexture, m_shadowQualityToShader); + + drawLabels(false, activeCamera, viewMatrix, projectionMatrix); + + // Handle selection clearing and selection label drawing + if (!dotSelectionFound) { + // We have no ownership, don't delete. Just NULL the pointer. + m_selectedItem = NULL; + } else { + glDisable(GL_DEPTH_TEST); + // Draw the selection label + LabelItem &labelItem = selectionLabelItem(); + if (m_selectedItem != selectedItem || m_updateLabels + || !labelItem.textureId() || m_selectionLabelDirty) { + QString labelText = selectionLabel(); + if (labelText.isNull() || m_selectionLabelDirty) { + labelText = m_selectedSeriesCache->itemLabel(); + setSelectionLabel(labelText); + m_selectionLabelDirty = false; } + m_drawer->generateLabelItem(labelItem, labelText); + m_selectedItem = selectedItem; } + + m_drawer->drawLabel(*selectedItem, labelItem, viewMatrix, projectionMatrix, + zeroVector, identityQuaternion, selectedItemSize, m_cachedSelectionMode, + m_labelShader, m_labelObj, activeCamera, true, false, + Drawer::LabelOver); + + // Reset label update flag; they should have been updated when we get here + m_updateLabels = false; + glEnable(GL_DEPTH_TEST); } - // Draw axis labels - // Bind label shader - m_labelShader->bind(); + glDisable(GL_TEXTURE_2D); + glDisable(GL_BLEND); + + // Release shader + glUseProgram(0); + + m_selectionDirty = false; +} + +void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCamera, + const QMatrix4x4 &viewMatrix, + const QMatrix4x4 &projectionMatrix) { + ShaderHelper *shader = 0; + GLfloat alphaForValueSelection = labelValueAlpha / 255.0f; + GLfloat alphaForRowSelection = labelRowAlpha / 255.0f; + GLfloat alphaForColumnSelection = labelColumnAlpha / 255.0f; + if (drawSelection) { + shader = m_selectionShader; + // m_selectionShader is already bound + } else { + shader = m_labelShader; + shader->bind(); + + glEnable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } - glEnable(GL_TEXTURE_2D); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_POLYGON_OFFSET_FILL); + float labelAutoAngle = m_axisCacheZ.labelAutoRotation(); + float labelAngleFraction = labelAutoAngle / 90.0f; + float fractionCamY = activeCamera->yRotation() * labelAngleFraction; + float fractionCamX = activeCamera->xRotation() * labelAngleFraction; + float labelsMaxWidth = 0.0f; + + int startIndex; + int endIndex; + int indexStep; + // Z Labels if (m_axisCacheZ.segmentCount() > 0) { + int labelCount = m_axisCacheZ.labelCount(); #ifndef USE_UNIFORM_SCALING - GLfloat posStep = aspectRatio * m_axisCacheZ.segmentStep(); - GLfloat labelPos = -aspectRatio * (m_axisCacheZ.min() - m_translationOffset.z()); - int lastSegment = m_axisCacheZ.segmentCount(); - GLfloat labelXTrans = (aspectRatio * m_backgroundMargin * m_areaSize.width()) - / m_scaleFactor + labelMargin; + GLfloat labelXTrans = (m_graphAspectRatio * m_areaSize.width()) + / m_scaleFactor + labelMargin + m_backgroundMargin; if (m_maxItemSize > labelXTrans) labelXTrans = m_maxItemSize + labelMargin; #else - GLfloat posStep = aspectRatio * axisCacheMax->segmentStep(); - GLfloat labelPos = aspectRatio * m_scaleFactor; - int lastSegment = axisCacheMax->segmentCount(); - GLfloat labelXTrans = aspectRatio * m_backgroundMargin + labelMargin; + GLfloat labelXTrans = m_graphAspectRatio + m_backgroundMargin + labelMargin; #endif - int labelNbr = 0; - GLfloat labelYTrans = -m_backgroundMargin; - GLfloat rotLabelX = -90.0f; - GLfloat rotLabelY = 0.0f; - GLfloat rotLabelZ = 0.0f; - Qt::AlignmentFlag alignment = Qt::AlignRight; - if (m_zFlipped) - rotLabelY = 180.0f; - if (m_xFlipped) { + GLfloat labelYTrans = -1.0f - m_backgroundMargin; + Qt::AlignmentFlag alignment = (m_xFlipped == m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight; + QVector3D labelRotation; + if (m_xFlipped) labelXTrans = -labelXTrans; - alignment = Qt::AlignLeft; - } - if (m_yFlipped) { - rotLabelZ += 180.0f; - rotLabelY += 180.0f; + if (m_yFlipped) labelYTrans = -labelYTrans; + if (labelAutoAngle == 0.0f) { + labelRotation.setX(-90.0f); + if (m_zFlipped) + labelRotation.setY(180.0f); + if (m_yFlipped) { + if (m_zFlipped) + labelRotation.setY(0.0f); + else + labelRotation.setY(180.0f); + labelRotation.setZ(180.0f); + } + } else { + if (m_zFlipped) + labelRotation.setY(180.0f); + if (m_yFlipped) { + if (m_zFlipped) { + if (m_xFlipped) { + labelRotation.setX(90.0f - (labelAutoAngle - fractionCamX) + * (-labelAutoAngle - fractionCamY) / labelAutoAngle); + labelRotation.setZ(labelAutoAngle + fractionCamY); + } else { + labelRotation.setX(90.0f + (labelAutoAngle + fractionCamX) + * (labelAutoAngle + fractionCamY) / labelAutoAngle); + labelRotation.setZ(-labelAutoAngle - fractionCamY); + } + } else { + if (m_xFlipped) { + labelRotation.setX(90.0f + (labelAutoAngle - fractionCamX) + * -(labelAutoAngle + fractionCamY) / labelAutoAngle); + labelRotation.setZ(-labelAutoAngle - fractionCamY); + } else { + labelRotation.setX(90.0f - (labelAutoAngle + fractionCamX) + * (labelAutoAngle + fractionCamY) / labelAutoAngle); + labelRotation.setZ(labelAutoAngle + fractionCamY); + } + } + } else { + if (m_zFlipped) { + if (m_xFlipped) { + labelRotation.setX(-90.0f + (labelAutoAngle - fractionCamX) + * (-labelAutoAngle + fractionCamY) / labelAutoAngle); + labelRotation.setZ(-labelAutoAngle + fractionCamY); + } else { + labelRotation.setX(-90.0f - (labelAutoAngle + fractionCamX) + * (labelAutoAngle - fractionCamY) / labelAutoAngle); + labelRotation.setZ(labelAutoAngle - fractionCamY); + } + } else { + if (m_xFlipped) { + labelRotation.setX(-90.0f - (labelAutoAngle - fractionCamX) + * (-labelAutoAngle + fractionCamY) / labelAutoAngle); + labelRotation.setZ(labelAutoAngle - fractionCamY); + } else { + labelRotation.setX(-90.0f + (labelAutoAngle + fractionCamX) + * (labelAutoAngle - fractionCamY) / labelAutoAngle); + labelRotation.setZ(-labelAutoAngle + fractionCamY); + } + } + } } - QVector3D labelRotateVector(rotLabelX, rotLabelY, rotLabelZ); + QQuaternion totalRotation = Utils::calculateRotation(labelRotation); QVector3D labelTrans = QVector3D(labelXTrans, labelYTrans, 0.0f); - for (int segment = 0; segment <= lastSegment; segment++) { -#ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z - if (m_axisCacheZ.labelItems().size() > labelNbr) { -#else // ..and this if we want uniform scaling based on largest dimension - if (axisCacheMax->labelItems().size() > labelNbr) { -#endif - labelTrans.setZ(labelPos / m_scaleFactor); + if (m_zFlipped) { + startIndex = 0; + endIndex = labelCount; + indexStep = 1; + } else { + startIndex = labelCount - 1; + endIndex = -1; + indexStep = -1; + } + for (int label = startIndex; label != endIndex; label = label + indexStep) { + labelTrans.setZ(m_axisCacheZ.labelPosition(label)); - glPolygonOffset(GLfloat(segment) / -10.0f, 1.0f); + glPolygonOffset(GLfloat(label) / -10.0f, 1.0f); - // Draw the label here - m_dummyRenderItem.setTranslation(labelTrans); -#ifndef USE_UNIFORM_SCALING - const LabelItem &axisLabelItem = *m_axisCacheZ.labelItems().at(labelNbr); -#else - const LabelItem &axisLabelItem = *axisCacheMax->labelItems().at(labelNbr); -#endif + // Draw the label here + m_dummyRenderItem.setTranslation(labelTrans); + const LabelItem &axisLabelItem = *m_axisCacheZ.labelItems().at(label); - m_drawer->drawLabel(m_dummyRenderItem, axisLabelItem, viewMatrix, projectionMatrix, - zeroVector, labelRotateVector, 0, m_cachedSelectionMode, - m_labelShader, m_labelObj, activeCamera, true, true, - Drawer::LabelMid, alignment); + if (drawSelection) { + QVector4D labelColor = QVector4D(label / 255.0f, 0.0f, 0.0f, + alphaForRowSelection); + shader->setUniformValue(shader->color(), labelColor); } - labelNbr++; - labelPos -= posStep; + + m_drawer->drawLabel(m_dummyRenderItem, axisLabelItem, viewMatrix, projectionMatrix, + zeroVector, totalRotation, 0, m_cachedSelectionMode, + shader, m_labelObj, activeCamera, true, true, + Drawer::LabelMid, alignment, false, drawSelection); + labelsMaxWidth = qMax(labelsMaxWidth, float(axisLabelItem.size().width())); + } + if (!drawSelection && m_axisCacheZ.isTitleVisible()) { + labelTrans.setZ(0.0f); + drawAxisTitleZ(labelRotation, labelTrans, totalRotation, m_dummyRenderItem, + activeCamera, labelsMaxWidth, viewMatrix, projectionMatrix, shader); } } + // X Labels if (m_axisCacheX.segmentCount() > 0) { + labelsMaxWidth = 0.0f; + labelAutoAngle = m_axisCacheX.labelAutoRotation(); + labelAngleFraction = labelAutoAngle / 90.0f; + fractionCamY = activeCamera->yRotation() * labelAngleFraction; + fractionCamX = activeCamera->xRotation() * labelAngleFraction; + int labelCount = m_axisCacheX.labelCount(); #ifndef USE_UNIFORM_SCALING - GLfloat posStep = aspectRatio * m_axisCacheX.segmentStep(); - GLfloat labelPos = aspectRatio * (m_axisCacheX.min() - m_translationOffset.x()); - int lastSegment = m_axisCacheX.segmentCount(); - GLfloat labelZTrans = (aspectRatio * m_backgroundMargin * m_areaSize.height()) - / m_scaleFactor + labelMargin; + GLfloat labelZTrans = (m_graphAspectRatio * m_areaSize.height()) + / m_scaleFactor + labelMargin + m_backgroundMargin; if (m_maxItemSize > labelZTrans) labelZTrans = m_maxItemSize + labelMargin; #else - GLfloat posStep = aspectRatio * axisCacheMax->segmentStep(); - GLfloat labelPos = -aspectRatio * m_scaleFactor; - int lastSegment = axisCacheMax->segmentCount(); - GLfloat labelZTrans = aspectRatio * m_backgroundMargin + labelMargin; + GLfloat labelZTrans = m_graphAspectRatio + m_backgroundMargin + labelMargin; #endif - int labelNbr = 0; - GLfloat labelYTrans = -m_backgroundMargin; - GLfloat rotLabelX = -90.0f; - GLfloat rotLabelY = 90.0f; - GLfloat rotLabelZ = 0.0f; - Qt::AlignmentFlag alignment = Qt::AlignLeft; - if (m_xFlipped) - rotLabelY = -90.0f; - if (m_zFlipped) { + GLfloat labelYTrans = -1.0f - m_backgroundMargin; + Qt::AlignmentFlag alignment = (m_xFlipped != m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight; + QVector3D labelRotation; + if (m_zFlipped) labelZTrans = -labelZTrans; - alignment = Qt::AlignRight; - } - if (m_yFlipped) { - rotLabelZ += 180.0f; - rotLabelY += 180.0f; + if (m_yFlipped) labelYTrans = -labelYTrans; + if (labelAutoAngle == 0.0f) { + labelRotation = QVector3D(-90.0f, 90.0f, 0.0f); + if (m_xFlipped) + labelRotation.setY(-90.0f); + if (m_yFlipped) { + if (m_xFlipped) + labelRotation.setY(90.0f); + else + labelRotation.setY(-90.0f); + labelRotation.setZ(180.0f); + } + } else { + if (m_xFlipped) + labelRotation.setY(-90.0f); + else + labelRotation.setY(90.0f); + if (m_yFlipped) { + if (m_zFlipped) { + if (m_xFlipped) { + labelRotation.setX(90.0f - (2.0f * labelAutoAngle - fractionCamX) + * (labelAutoAngle + fractionCamY) / labelAutoAngle); + labelRotation.setZ(-labelAutoAngle - fractionCamY); + } else { + labelRotation.setX(90.0f - (2.0f * labelAutoAngle + fractionCamX) + * (labelAutoAngle + fractionCamY) / labelAutoAngle); + labelRotation.setZ(labelAutoAngle + fractionCamY); + } + } else { + if (m_xFlipped) { + labelRotation.setX(90.0f + fractionCamX + * -(labelAutoAngle + fractionCamY) / labelAutoAngle); + labelRotation.setZ(labelAutoAngle + fractionCamY); + } else { + labelRotation.setX(90.0f - fractionCamX + * (-labelAutoAngle - fractionCamY) / labelAutoAngle); + labelRotation.setZ(-labelAutoAngle - fractionCamY); + } + } + } else { + if (m_zFlipped) { + if (m_xFlipped) { + labelRotation.setX(-90.0f + (2.0f * labelAutoAngle - fractionCamX) + * (labelAutoAngle - fractionCamY) / labelAutoAngle); + labelRotation.setZ(labelAutoAngle - fractionCamY); + } else { + labelRotation.setX(-90.0f + (2.0f * labelAutoAngle + fractionCamX) + * (labelAutoAngle - fractionCamY) / labelAutoAngle); + labelRotation.setZ(-labelAutoAngle + fractionCamY); + } + } else { + if (m_xFlipped) { + labelRotation.setX(-90.0f - fractionCamX + * (-labelAutoAngle + fractionCamY) / labelAutoAngle); + labelRotation.setZ(-labelAutoAngle + fractionCamY); + } else { + labelRotation.setX(-90.0f + fractionCamX + * -(labelAutoAngle - fractionCamY) / labelAutoAngle); + labelRotation.setZ(labelAutoAngle - fractionCamY); + } + } + } } - QVector3D labelRotateVector(rotLabelX, rotLabelY, rotLabelZ); + + QQuaternion totalRotation = Utils::calculateRotation(labelRotation); QVector3D labelTrans = QVector3D(0.0f, labelYTrans, labelZTrans); - for (int segment = 0; segment <= lastSegment; segment++) { -#ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z - if (m_axisCacheX.labelItems().size() > labelNbr) { -#else // ..and this if we want uniform scaling based on largest dimension - if (axisCacheMax->labelItems().size() > labelNbr) { -#endif - labelTrans.setX(labelPos / m_scaleFactor); + if (m_xFlipped) { + startIndex = labelCount - 1; + endIndex = -1; + indexStep = -1; + } else { + startIndex = 0; + endIndex = labelCount; + indexStep = 1; + } + for (int label = startIndex; label != endIndex; label = label + indexStep) { + labelTrans.setX(m_axisCacheX.labelPosition(label)); - glPolygonOffset(GLfloat(segment) / -10.0f, 1.0f); + glPolygonOffset(GLfloat(label) / -10.0f, 1.0f); - // Draw the label here - m_dummyRenderItem.setTranslation(labelTrans); -#ifndef USE_UNIFORM_SCALING - const LabelItem &axisLabelItem = *m_axisCacheX.labelItems().at(labelNbr); -#else - const LabelItem &axisLabelItem = *axisCacheMax->labelItems().at(labelNbr); -#endif + // Draw the label here + m_dummyRenderItem.setTranslation(labelTrans); + const LabelItem &axisLabelItem = *m_axisCacheX.labelItems().at(label); - m_drawer->drawLabel(m_dummyRenderItem, axisLabelItem, viewMatrix, projectionMatrix, - zeroVector, labelRotateVector, 0, m_cachedSelectionMode, - m_labelShader, m_labelObj, activeCamera, true, true, - Drawer::LabelMid, alignment); + if (drawSelection) { + QVector4D labelColor = QVector4D(0.0f, label / 255.0f, 0.0f, + alphaForColumnSelection); + shader->setUniformValue(shader->color(), labelColor); } - labelNbr++; - labelPos += posStep; + + m_drawer->drawLabel(m_dummyRenderItem, axisLabelItem, viewMatrix, projectionMatrix, + zeroVector, totalRotation, 0, m_cachedSelectionMode, + shader, m_labelObj, activeCamera, true, true, + Drawer::LabelMid, alignment, false, drawSelection); + labelsMaxWidth = qMax(labelsMaxWidth, float(axisLabelItem.size().width())); + } + if (!drawSelection && m_axisCacheX.isTitleVisible()) { + labelTrans.setX(0.0f); + drawAxisTitleX(labelRotation, labelTrans, totalRotation, m_dummyRenderItem, + activeCamera, labelsMaxWidth, viewMatrix, projectionMatrix, shader); } } + // Y Labels if (m_axisCacheY.segmentCount() > 0) { - GLfloat posStep = m_axisCacheY.segmentStep(); - GLfloat labelPos = m_axisCacheY.min() - m_translationOffset.y(); - int labelNbr = 0; + labelsMaxWidth = 0.0f; + labelAutoAngle = m_axisCacheY.labelAutoRotation(); + labelAngleFraction = labelAutoAngle / 90.0f; + fractionCamY = activeCamera->yRotation() * labelAngleFraction; + fractionCamX = activeCamera->xRotation() * labelAngleFraction; + int labelCount = m_axisCacheY.labelCount(); #ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z - GLfloat labelXTrans = (aspectRatio * m_backgroundMargin * m_areaSize.width()) - / m_scaleFactor; - GLfloat labelZTrans = (aspectRatio * m_backgroundMargin * m_areaSize.height()) - / m_scaleFactor; + 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 = aspectRatio * m_backgroundMargin; + GLfloat labelXTrans = m_graphAspectRatio + m_backgroundMargin; GLfloat labelZTrans = labelXTrans; #endif - // Back wall init + // Back & side wall GLfloat labelMarginXTrans = labelMargin; GLfloat labelMarginZTrans = labelMargin; - GLfloat rotLabelX = 0.0f; - GLfloat rotLabelY = -90.0f; - GLfloat rotLabelZ = 0.0f; - Qt::AlignmentFlag alignmentBack = Qt::AlignLeft; + QVector3D backLabelRotation(0.0f, -90.0f, 0.0f); + QVector3D sideLabelRotation(0.0f, 0.0f, 0.0f); + Qt::AlignmentFlag backAlignment = + (m_xFlipped != m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight; + Qt::AlignmentFlag sideAlignment = + (m_xFlipped == m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight; if (!m_xFlipped) { labelXTrans = -labelXTrans; labelMarginXTrans = -labelMargin; - rotLabelY = 90.0f; } if (m_zFlipped) { labelZTrans = -labelZTrans; labelMarginZTrans = -labelMargin; - alignmentBack = Qt::AlignRight; } - QVector3D labelRotateVectorBack(rotLabelX, rotLabelY, rotLabelZ); - QVector3D labelTransBack = QVector3D(labelXTrans, 0.0f, labelZTrans + labelMarginZTrans); + if (labelAutoAngle == 0.0f) { + if (!m_xFlipped) + backLabelRotation.setY(90.0f); + if (m_zFlipped) + sideLabelRotation.setY(180.f); + } else { + // Orient side labels somewhat towards the camera + if (m_xFlipped) { + if (m_zFlipped) + sideLabelRotation.setY(180.0f + (2.0f * labelAutoAngle) - fractionCamX); + else + sideLabelRotation.setY(-fractionCamX); + backLabelRotation.setY(-90.0f + labelAutoAngle - fractionCamX); + } else { + if (m_zFlipped) + sideLabelRotation.setY(180.0f - (2.0f * labelAutoAngle) - fractionCamX); + else + sideLabelRotation.setY(-fractionCamX); + backLabelRotation.setY(90.0f - labelAutoAngle - fractionCamX); + } + } + sideLabelRotation.setX(-fractionCamY); + backLabelRotation.setX(-fractionCamY); - // Side wall init - Qt::AlignmentFlag alignmentSide = Qt::AlignLeft; - if (m_xFlipped) - alignmentSide = Qt::AlignLeft; - else - alignmentSide = Qt::AlignRight; - if (m_zFlipped) - rotLabelY = 180.0f; - else - rotLabelY = 0.0f; + QQuaternion totalSideRotation = Utils::calculateRotation(sideLabelRotation); + QQuaternion totalBackRotation = Utils::calculateRotation(backLabelRotation); - QVector3D labelRotateVectorSide(rotLabelX, rotLabelY, rotLabelZ); + QVector3D labelTransBack = QVector3D(labelXTrans, 0.0f, labelZTrans + labelMarginZTrans); QVector3D labelTransSide(-labelXTrans - labelMarginXTrans, 0.0f, -labelZTrans); - for (int segment = 0; segment <= m_axisCacheY.segmentCount(); segment++) { - if (m_axisCacheY.labelItems().size() > labelNbr) { - const LabelItem &axisLabelItem = *m_axisCacheY.labelItems().at(labelNbr); - const GLfloat labelYTrans = labelPos / m_heightNormalizer; - - glPolygonOffset(GLfloat(segment) / -10.0f, 1.0f); - - // Back wall - labelTransBack.setY(labelYTrans); - m_dummyRenderItem.setTranslation(labelTransBack); - m_drawer->drawLabel(m_dummyRenderItem, axisLabelItem, viewMatrix, projectionMatrix, - zeroVector, labelRotateVectorBack, 0, m_cachedSelectionMode, - m_labelShader, m_labelObj, activeCamera, true, true, - Drawer::LabelMid, alignmentBack); - - // Side wall - labelTransSide.setY(labelYTrans); - m_dummyRenderItem.setTranslation(labelTransSide); - m_drawer->drawLabel(m_dummyRenderItem, axisLabelItem, viewMatrix, projectionMatrix, - zeroVector, labelRotateVectorSide, 0, m_cachedSelectionMode, - m_labelShader, m_labelObj, activeCamera, true, true, - Drawer::LabelMid, alignmentSide); - } - labelNbr++; - labelPos += posStep; + if (m_yFlipped) { + startIndex = labelCount - 1; + endIndex = -1; + indexStep = -1; + } else { + startIndex = 0; + endIndex = labelCount; + indexStep = 1; } - } - glDisable(GL_POLYGON_OFFSET_FILL); + for (int label = startIndex; label != endIndex; label = label + indexStep) { + const LabelItem &axisLabelItem = *m_axisCacheY.labelItems().at(label); + const GLfloat labelYTrans = m_axisCacheY.labelPosition(label); - // Handle selection clearing and selection label drawing - if (!dotSelectionFound) { - // We have no ownership, don't delete. Just NULL the pointer. - m_selectedItem = NULL; - } else { - glDisable(GL_DEPTH_TEST); - // Draw the selection label - LabelItem &labelItem = selectionLabelItem(); - if (m_selectedItem != selectedItem || m_updateLabels - || !labelItem.textureId() || m_selectionLabelDirty) { - QString labelText = selectionLabel(); - if (labelText.isNull() || m_selectionLabelDirty) { - static const QString xTitleTag(QStringLiteral("@xTitle")); - static const QString yTitleTag(QStringLiteral("@yTitle")); - static const QString zTitleTag(QStringLiteral("@zTitle")); - static const QString xLabelTag(QStringLiteral("@xLabel")); - static const QString yLabelTag(QStringLiteral("@yLabel")); - static const QString zLabelTag(QStringLiteral("@zLabel")); - static const QString seriesNameTag(QStringLiteral("@seriesName")); - - labelText = m_visibleSeriesList[m_selectedItemSeriesIndex].itemLabelFormat(); - - labelText.replace(xTitleTag, m_axisCacheX.title()); - labelText.replace(yTitleTag, m_axisCacheY.title()); - labelText.replace(zTitleTag, m_axisCacheZ.title()); - - if (labelText.contains(xLabelTag)) { - QString labelFormat = m_axisCacheX.labelFormat(); - if (labelFormat.isEmpty()) - labelFormat = Utils::defaultLabelFormat(); - QString valueLabelText = generateValueLabel(labelFormat, - selectedItem->position().x()); - labelText.replace(xLabelTag, valueLabelText); - } - if (labelText.contains(yLabelTag)) { - QString labelFormat = m_axisCacheY.labelFormat(); - if (labelFormat.isEmpty()) - labelFormat = Utils::defaultLabelFormat(); - QString valueLabelText = generateValueLabel(labelFormat, - selectedItem->position().y()); - labelText.replace(yLabelTag, valueLabelText); - } - if (labelText.contains(zLabelTag)) { - QString labelFormat = m_axisCacheZ.labelFormat(); - if (labelFormat.isEmpty()) - labelFormat = Utils::defaultLabelFormat(); - QString valueLabelText = generateValueLabel(labelFormat, - selectedItem->position().z()); - labelText.replace(zLabelTag, valueLabelText); - } - labelText.replace(seriesNameTag, m_visibleSeriesList[m_selectedItemSeriesIndex].name()); + glPolygonOffset(GLfloat(label) / -10.0f, 1.0f); - setSelectionLabel(labelText); - m_selectionLabelDirty = false; + if (drawSelection) { + QVector4D labelColor = QVector4D(0.0f, 0.0f, label / 255.0f, + alphaForValueSelection); + shader->setUniformValue(shader->color(), labelColor); } - m_drawer->generateLabelItem(labelItem, labelText); - m_selectedItem = selectedItem; - } - m_drawer->drawLabel(*selectedItem, labelItem, viewMatrix, projectionMatrix, - zeroVector, zeroVector, selectedItemSize, m_cachedSelectionMode, - m_labelShader, m_labelObj, activeCamera, true, false, - Drawer::LabelOver); + // Back wall + labelTransBack.setY(labelYTrans); + m_dummyRenderItem.setTranslation(labelTransBack); + m_drawer->drawLabel(m_dummyRenderItem, axisLabelItem, viewMatrix, projectionMatrix, + zeroVector, totalBackRotation, 0, m_cachedSelectionMode, + shader, m_labelObj, activeCamera, true, true, + Drawer::LabelMid, backAlignment, false, drawSelection); - // Reset label update flag; they should have been updated when we get here - m_updateLabels = false; - glEnable(GL_DEPTH_TEST); + // Side wall + labelTransSide.setY(labelYTrans); + m_dummyRenderItem.setTranslation(labelTransSide); + m_drawer->drawLabel(m_dummyRenderItem, axisLabelItem, viewMatrix, projectionMatrix, + zeroVector, totalSideRotation, 0, m_cachedSelectionMode, + shader, m_labelObj, activeCamera, true, true, + Drawer::LabelMid, sideAlignment, false, drawSelection); + labelsMaxWidth = qMax(labelsMaxWidth, float(axisLabelItem.size().width())); + } + if (!drawSelection && m_axisCacheY.isTitleVisible()) { + labelTransSide.setY(0.0f); + labelTransBack.setY(0.0f); + drawAxisTitleY(sideLabelRotation, backLabelRotation, labelTransSide, labelTransBack, + totalSideRotation, totalBackRotation, m_dummyRenderItem, activeCamera, + labelsMaxWidth, viewMatrix, projectionMatrix, + shader); + } } - - glDisable(GL_TEXTURE_2D); - glDisable(GL_BLEND); - - // Release shader - glUseProgram(0); - - m_selectionDirty = false; + glDisable(GL_POLYGON_OFFSET_FILL); } -void Scatter3DRenderer::updateSelectedItem(int index, const QScatter3DSeries *series) +void Scatter3DRenderer::updateSelectedItem(int index, QScatter3DSeries *series) { m_selectionDirty = true; m_selectionLabelDirty = true; - m_selectedSeries = series; + m_selectedSeriesCache = + static_cast<ScatterSeriesRenderCache *>(m_renderCacheList.value(series, 0)); m_selectedItemIndex = Scatter3DController::invalidSelectionIndex(); - m_selectedItemTotalIndex = Scatter3DController::invalidSelectionIndex(); - m_selectedItemSeriesIndex = Scatter3DController::invalidSelectionIndex(); - if (!m_renderingArrays.isEmpty() && index != Scatter3DController::invalidSelectionIndex()) { - int totalIndex = 0; - for (int i = 0; i < m_visibleSeriesList.size(); i++) { - if (m_visibleSeriesList.at(i).series() == series) { - m_selectedItemSeriesIndex = i; - m_selectedItemIndex = index; - m_selectedItemTotalIndex = index + totalIndex; - break; + if (m_cachedOptimizationHint.testFlag(QAbstract3DGraph::OptimizationStatic) + && m_oldSelectedSeriesCache + && m_oldSelectedSeriesCache->mesh() == QAbstract3DSeries::MeshPoint) { + m_oldSelectedSeriesCache->bufferPoints()->popPoint(); + m_oldSelectedSeriesCache = 0; + } + + if (m_selectedSeriesCache) { + const ScatterRenderItemArray &renderArray = m_selectedSeriesCache->renderArray(); + if (index < renderArray.size() && index >= 0) { + m_selectedItemIndex = index; + + if (m_cachedOptimizationHint.testFlag(QAbstract3DGraph::OptimizationStatic) + && m_selectedSeriesCache->mesh() == QAbstract3DSeries::MeshPoint) { + m_selectedSeriesCache->bufferPoints()->pushPoint(m_selectedItemIndex); + m_oldSelectedSeriesCache = m_selectedSeriesCache; } - totalIndex += m_renderingArrays.at(i).size(); } } } @@ -1598,26 +1950,8 @@ void Scatter3DRenderer::updateShadowQuality(QAbstract3DGraph::ShadowQuality qual void Scatter3DRenderer::loadBackgroundMesh() { - if (m_backgroundObj) - delete m_backgroundObj; - m_backgroundObj = new ObjectHelper(QStringLiteral(":/defaultMeshes/background")); - m_backgroundObj->load(); -} - -void Scatter3DRenderer::loadGridLineMesh() -{ - if (m_gridLineObj) - delete m_gridLineObj; - m_gridLineObj = new ObjectHelper(QStringLiteral(":/defaultMeshes/plane")); - m_gridLineObj->load(); -} - -void Scatter3DRenderer::loadLabelMesh() -{ - if (m_labelObj) - delete m_labelObj; - m_labelObj = new ObjectHelper(QStringLiteral(":/defaultMeshes/plane")); - m_labelObj->load(); + ObjectHelper::resetObjectHelper(this, m_backgroundObj, + QStringLiteral(":/defaultMeshes/background")); } void Scatter3DRenderer::updateTextures() @@ -1640,11 +1974,10 @@ void Scatter3DRenderer::fixMeshFileName(QString &fileName, QAbstract3DSeries::Me void Scatter3DRenderer::calculateTranslation(ScatterRenderItem &item) { // We need to normalize translations - GLfloat xTrans = (aspectRatio * (item.position().x() - m_translationOffset.x())) - / m_scaleFactor; - GLfloat zTrans = -(aspectRatio * (item.position().z() - m_translationOffset.z())) - / m_scaleFactor; - GLfloat yTrans = (item.position().y() - m_translationOffset.y()) / m_heightNormalizer; + const QVector3D &pos = item.position(); + float xTrans = m_axisCacheX.positionAt(pos.x()); + float yTrans = m_axisCacheY.positionAt(pos.y()); + float zTrans = m_axisCacheZ.positionAt(pos.z()); item.setTranslation(QVector3D(xTrans, yTrans, zTrans)); } @@ -1655,10 +1988,16 @@ void Scatter3DRenderer::calculateSceneScalingFactors() m_areaSize.setWidth((m_axisCacheX.max() - m_axisCacheX.min()) / 2.0f); m_scaleFactor = qMax(m_areaSize.width(), m_areaSize.height()); - // Calculate translation offsets - m_translationOffset = QVector3D((m_axisCacheX.max() + m_axisCacheX.min()) / 2.0f, - (m_axisCacheY.max() + m_axisCacheY.min()) / 2.0f, - (m_axisCacheZ.max() + m_axisCacheZ.min()) / 2.0f); +#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); } void Scatter3DRenderer::initShaders(const QString &vertexShader, const QString &fragmentShader) @@ -1689,10 +2028,7 @@ void Scatter3DRenderer::initSelectionShader() void Scatter3DRenderer::initSelectionBuffer() { - if (m_selectionTexture) { - m_textureHelper->deleteTexture(&m_selectionTexture); - m_selectionTexture = 0; - } + m_textureHelper->deleteTexture(&m_selectionTexture); if (m_primarySubViewport.size().isEmpty()) return; @@ -1714,10 +2050,7 @@ void Scatter3DRenderer::initDepthShader() void Scatter3DRenderer::updateDepthBuffer() { - if (m_depthTexture) { - m_textureHelper->deleteTexture(&m_depthTexture); - m_depthTexture = 0; - } + m_textureHelper->deleteTexture(&m_depthTexture); if (m_primarySubViewport.size().isEmpty()) return; @@ -1758,30 +2091,54 @@ void Scatter3DRenderer::initLabelShaders(const QString &vertexShader, const QStr m_labelShader->initialize(); } -QVector3D Scatter3DRenderer::indexToSelectionColor(GLint index) -{ - GLubyte dotIdxRed = index & 0xff; - GLubyte dotIdxGreen = (index & 0xff00) >> 8; - GLubyte dotIdxBlue = (index & 0xff0000) >> 16; - - return QVector3D(dotIdxRed, dotIdxGreen, dotIdxBlue); -} - -void Scatter3DRenderer::selectionColorToSeriesAndIndex(const QVector3D &color, +void Scatter3DRenderer::selectionColorToSeriesAndIndex(const QVector4D &color, int &index, QAbstract3DSeries *&series) { + m_clickedType = QAbstract3DGraph::ElementNone; + m_selectedLabelIndex = -1; + m_selectedCustomItemIndex = -1; if (color != selectionSkipColor) { - index = int(color.x()) - + (int(color.y()) << 8) - + (int(color.z()) << 16); - // Find the series and adjust the index accordingly - for (int i = 0; i < m_renderingArrays.size(); i++) { - if (index < m_renderingArrays.at(i).size()) { - series = m_visibleSeriesList.at(i).series(); - return; // Valid found and already set to return parameters, so we can return - } else { - index -= m_renderingArrays.at(i).size(); + if (color.w() == labelRowAlpha) { + // Row selection + index = Scatter3DController::invalidSelectionIndex(); + m_selectedLabelIndex = color.x(); + m_clickedType = QAbstract3DGraph::ElementAxisZLabel; + } else if (color.w() == labelColumnAlpha) { + // Column selection + index = Scatter3DController::invalidSelectionIndex(); + m_selectedLabelIndex = color.y(); + m_clickedType = QAbstract3DGraph::ElementAxisXLabel; + } else if (color.w() == labelValueAlpha) { + // Value selection + index = Scatter3DController::invalidSelectionIndex(); + m_selectedLabelIndex = color.z(); + m_clickedType = QAbstract3DGraph::ElementAxisYLabel; + } else if (color.w() == customItemAlpha) { + // Custom item selection + index = Scatter3DController::invalidSelectionIndex(); + m_selectedCustomItemIndex = int(color.x()) + + (int(color.y()) << 8) + + (int(color.z()) << 16); + m_clickedType = QAbstract3DGraph::ElementCustomItem; + } else { + int totalIndex = int(color.x()) + + (int(color.y()) << 8) + + (int(color.z()) << 16); + // Find the series and adjust the index accordingly + foreach (SeriesRenderCache *baseCache, m_renderCacheList) { + if (baseCache->isVisible()) { + ScatterSeriesRenderCache *cache = + static_cast<ScatterSeriesRenderCache *>(baseCache); + int offset = cache->selectionIndexOffset(); + if (totalIndex >= offset + && totalIndex < (offset + cache->renderArray().size())) { + index = totalIndex - offset; + series = cache->series(); + m_clickedType = QAbstract3DGraph::ElementSeries; + return; + } + } } } } @@ -1791,4 +2148,41 @@ void Scatter3DRenderer::selectionColorToSeriesAndIndex(const QVector3D &color, series = 0; } +void Scatter3DRenderer::updateRenderItem(const QScatterDataItem &dataItem, + ScatterRenderItem &renderItem) +{ + QVector3D dotPos = dataItem.position(); + if ((dotPos.x() >= m_axisCacheX.min() && dotPos.x() <= m_axisCacheX.max() ) + && (dotPos.y() >= m_axisCacheY.min() && dotPos.y() <= m_axisCacheY.max()) + && (dotPos.z() >= m_axisCacheZ.min() && dotPos.z() <= m_axisCacheZ.max())) { + renderItem.setPosition(dotPos); + renderItem.setVisible(true); + if (!dataItem.rotation().isIdentity()) + renderItem.setRotation(dataItem.rotation().normalized()); + else + renderItem.setRotation(identityQuaternion); + calculateTranslation(renderItem); + } else { + renderItem.setVisible(false); + } +} + +QVector3D Scatter3DRenderer::convertPositionToTranslation(const QVector3D &position, + bool isAbsolute) +{ + float xTrans = 0.0f; + float yTrans = 0.0f; + float zTrans = 0.0f; + if (!isAbsolute) { + xTrans = m_axisCacheX.positionAt(position.x()); + 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; + } + return QVector3D(xTrans, yTrans, zTrans); +} + QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/engine/scatter3drenderer_p.h b/src/datavisualization/engine/scatter3drenderer_p.h index 5591a362..7f213179 100644 --- a/src/datavisualization/engine/scatter3drenderer_p.h +++ b/src/datavisualization/engine/scatter3drenderer_p.h @@ -32,20 +32,16 @@ #include "datavisualizationglobal_p.h" #include "scatter3dcontroller_p.h" #include "abstract3drenderer_p.h" -#include "qscatterdataproxy.h" #include "scatterrenderitem_p.h" -class QPoint; class QSizeF; -class QOpenGLShaderProgram; QT_BEGIN_NAMESPACE_DATAVISUALIZATION class ShaderHelper; -class ObjectHelper; -class LabelItem; class Q3DScene; -class QAbstractAxisPrivate; +class ScatterSeriesRenderCache; +class QScatterDataItem; class QT_DATAVISUALIZATION_EXPORT Scatter3DRenderer : public Abstract3DRenderer { @@ -54,9 +50,6 @@ class QT_DATAVISUALIZATION_EXPORT Scatter3DRenderer : public Abstract3DRenderer private: // Internal state ScatterRenderItem *m_selectedItem; // points to renderitem array - bool m_xFlipped; - bool m_zFlipped; - bool m_yFlipped; bool m_updateLabels; ShaderHelper *m_dotShader; ShaderHelper *m_dotGradientShader; @@ -67,9 +60,6 @@ private: ShaderHelper *m_selectionShader; ShaderHelper *m_backgroundShader; ShaderHelper *m_labelShader; - ObjectHelper *m_backgroundObj; - ObjectHelper *m_gridLineObj; - ObjectHelper *m_labelObj; GLuint m_bgrTexture; GLuint m_depthTexture; GLuint m_selectionTexture; @@ -81,33 +71,40 @@ private: GLfloat m_heightNormalizer; GLfloat m_scaleFactor; int m_selectedItemIndex; - int m_selectedItemTotalIndex; - int m_selectedItemSeriesIndex; - const QScatter3DSeries *m_selectedSeries; + ScatterSeriesRenderCache *m_selectedSeriesCache; + ScatterSeriesRenderCache *m_oldSelectedSeriesCache; QSizeF m_areaSize; GLfloat m_dotSizeScale; - QVector3D m_translationOffset; bool m_hasHeightAdjustmentChanged; ScatterRenderItem m_dummyRenderItem; - QVector<ScatterRenderItemArray> m_renderingArrays; GLfloat m_backgroundMargin; GLfloat m_maxItemSize; - QVector<float> m_cachedItemSize; int m_clickedIndex; + bool m_havePointSeries; + bool m_haveMeshSeries; + bool m_haveUniformColorMeshSeries; + bool m_haveGradientMeshSeries; public: explicit Scatter3DRenderer(Scatter3DController *controller); ~Scatter3DRenderer(); - void updateSeries(const QList<QAbstract3DSeries *> &seriesList, bool updateVisibility); void updateData(); + void updateSeries(const QList<QAbstract3DSeries *> &seriesList); + SeriesRenderCache *createNewCache(QAbstract3DSeries *series); + void updateItems(const QVector<Scatter3DController::ChangeItem> &items); void updateScene(Q3DScene *scene); + QVector3D convertPositionToTranslation(const QVector3D &position, bool isAbsolute); + inline int clickedIndex() const { return m_clickedIndex; } void resetClickedStatus(); void render(GLuint defaultFboHandle); +public slots: + void updateSelectedItem(int index, QScatter3DSeries *series); + protected: virtual void initializeOpenGL(); @@ -119,10 +116,10 @@ private: virtual void fixMeshFileName(QString &fileName, QAbstract3DSeries::Mesh mesh); void drawScene(GLuint defaultFboHandle); + void drawLabels(bool drawSelection, const Q3DCamera *activeCamera, + const QMatrix4x4 &viewMatrix, const QMatrix4x4 &projectionMatrix); void loadBackgroundMesh(); - void loadGridLineMesh(); - void loadLabelMesh(); void initSelectionShader(); void initBackgroundShaders(const QString &vertexShader, const QString &fragmentShader); void initLabelShaders(const QString &vertexShader, const QString &fragmentShader); @@ -136,17 +133,11 @@ private: void calculateTranslation(ScatterRenderItem &item); void calculateSceneScalingFactors(); - Q_DISABLE_COPY(Scatter3DRenderer) - - friend class ScatterRenderItem; - -public slots: - void updateSelectedItem(int index, const QScatter3DSeries *series); - -private: - QVector3D indexToSelectionColor(GLint index); - void selectionColorToSeriesAndIndex(const QVector3D &color, int &index, + void selectionColorToSeriesAndIndex(const QVector4D &color, int &index, QAbstract3DSeries *&series); + inline void updateRenderItem(const QScatterDataItem &dataItem, ScatterRenderItem &renderItem); + + Q_DISABLE_COPY(Scatter3DRenderer) }; QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/engine/scatterseriesrendercache.cpp b/src/datavisualization/engine/scatterseriesrendercache.cpp new file mode 100644 index 00000000..e8888d19 --- /dev/null +++ b/src/datavisualization/engine/scatterseriesrendercache.cpp @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include "scatterseriesrendercache_p.h" +#include "scatterobjectbufferhelper_p.h" +#include "scatterpointbufferhelper_p.h" + +QT_BEGIN_NAMESPACE_DATAVISUALIZATION + +ScatterSeriesRenderCache::ScatterSeriesRenderCache(QAbstract3DSeries *series, + Abstract3DRenderer *renderer) + : SeriesRenderCache(series, renderer), + m_itemSize(0.0f), + m_selectionIndexOffset(0), + m_oldRenderArraySize(0), + m_oldMeshFileName(QString()), + m_scatterBufferObj(0), + m_scatterBufferPoints(0) +{ +} + +ScatterSeriesRenderCache::~ScatterSeriesRenderCache() +{ + delete m_scatterBufferObj; + delete m_scatterBufferPoints; +} + +void ScatterSeriesRenderCache::cleanup(TextureHelper *texHelper) +{ + m_renderArray.clear(); + + SeriesRenderCache::cleanup(texHelper); +} + +QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/engine/scatterseriesrendercache_p.h b/src/datavisualization/engine/scatterseriesrendercache_p.h new file mode 100644 index 00000000..490e21fb --- /dev/null +++ b/src/datavisualization/engine/scatterseriesrendercache_p.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the QtDataVisualization module. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the QtDataVisualization API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#ifndef SCATTERSERIESRENDERCACHE_P_H +#define SCATTERSERIESRENDERCACHE_P_H + +#include "datavisualizationglobal_p.h" +#include "seriesrendercache_p.h" +#include "qscatter3dseries_p.h" +#include "scatterrenderitem_p.h" + +QT_BEGIN_NAMESPACE_DATAVISUALIZATION + +class ScatterObjectBufferHelper; +class ScatterPointBufferHelper; + +class ScatterSeriesRenderCache : public SeriesRenderCache +{ +public: + ScatterSeriesRenderCache(QAbstract3DSeries *series, Abstract3DRenderer *renderer); + virtual ~ScatterSeriesRenderCache(); + + void cleanup(TextureHelper *texHelper); + + inline ScatterRenderItemArray &renderArray() { return m_renderArray; } + inline QScatter3DSeries *series() const { return static_cast<QScatter3DSeries *>(m_series); } + inline void setItemSize(float size) { m_itemSize = size; } + inline float itemSize() const { return m_itemSize; } + inline void setSelectionIndexOffset(int offset) { m_selectionIndexOffset = offset; } + inline int selectionIndexOffset() const { return m_selectionIndexOffset; } + inline int oldArraySize() const { return m_oldRenderArraySize; } + inline void setOldArraySize(int size) { m_oldRenderArraySize = size; } + inline const QString &oldMeshFileName() const { return m_oldMeshFileName; } + inline void setOldMeshFileName(const QString &meshFileName) { m_oldMeshFileName = meshFileName; } + inline void setBufferObject(ScatterObjectBufferHelper *object) { m_scatterBufferObj = object; } + inline ScatterObjectBufferHelper *bufferObject() const { return m_scatterBufferObj; } + inline void setBufferPoints(ScatterPointBufferHelper *object) { m_scatterBufferPoints = object; } + inline ScatterPointBufferHelper *bufferPoints() const { return m_scatterBufferPoints; } + +protected: + ScatterRenderItemArray m_renderArray; + float m_itemSize; + int m_selectionIndexOffset; // Temporarily cached value for selection color calculations + 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; +}; + +QT_END_NAMESPACE_DATAVISUALIZATION + +#endif diff --git a/src/datavisualization/engine/selectionpointer.cpp b/src/datavisualization/engine/selectionpointer.cpp index d4e635bc..183d3f8e 100644 --- a/src/datavisualization/engine/selectionpointer.cpp +++ b/src/datavisualization/engine/selectionpointer.cpp @@ -17,16 +17,11 @@ ****************************************************************************/ #include "selectionpointer_p.h" -#include "surface3dcontroller_p.h" #include "shaderhelper_p.h" #include "objecthelper_p.h" #include "texturehelper_p.h" #include "q3dcamera_p.h" -#include "drawer_p.h" #include "utils_p.h" -#include "q3dlight.h" - -#include <QtGui/QMatrix4x4> QT_BEGIN_NAMESPACE_DATAVISUALIZATION @@ -54,7 +49,6 @@ SelectionPointer::~SelectionPointer() { delete m_labelShader; delete m_pointShader; - delete m_labelObj; delete m_textureHelper; } @@ -66,7 +60,6 @@ void SelectionPointer::initializeOpenGL() m_drawer->initializeOpenGL(); initShaders(); - loadLabelMesh(); } void SelectionPointer::updateScene(Q3DScene *scene) @@ -74,7 +67,7 @@ void SelectionPointer::updateScene(Q3DScene *scene) m_cachedScene = scene; } -void SelectionPointer::render(GLuint defaultFboHandle) +void SelectionPointer::render(GLuint defaultFboHandle, bool useOrtho) { Q_UNUSED(defaultFboHandle) @@ -89,17 +82,22 @@ void SelectionPointer::render(GLuint defaultFboHandle) // Get view matrix QMatrix4x4 viewMatrix; QMatrix4x4 projectionMatrix; + GLfloat viewPortRatio = (GLfloat)m_mainViewPort.width() / (GLfloat)m_mainViewPort.height(); if (m_cachedIsSlicingActivated) { - GLfloat aspect = (GLfloat)m_mainViewPort.width() / (GLfloat)m_mainViewPort.height(); GLfloat sliceUnitsScaled = sliceUnits / m_autoScaleAdjustment; viewMatrix.lookAt(QVector3D(0.0f, 0.0f, 1.0f), zeroVector, upVector); - projectionMatrix.ortho(-sliceUnitsScaled * aspect, sliceUnitsScaled * aspect, + projectionMatrix.ortho(-sliceUnitsScaled * viewPortRatio, sliceUnitsScaled * viewPortRatio, -sliceUnitsScaled, sliceUnitsScaled, -1.0f, 4.0f); + } else if (useOrtho) { + viewMatrix = camera->d_ptr->viewMatrix(); + GLfloat orthoRatio = 2.0f; + projectionMatrix.ortho(-viewPortRatio * orthoRatio, viewPortRatio * orthoRatio, + -orthoRatio, orthoRatio, + 0.0f, 100.0f); } else { viewMatrix = camera->d_ptr->viewMatrix(); - projectionMatrix.perspective(45.0f, (GLfloat)m_mainViewPort.width() - / (GLfloat)m_mainViewPort.height(), 0.1f, 100.0f); + projectionMatrix.perspective(45.0f, viewPortRatio, 0.1f, 100.0f); } // Calculate scale factor to get uniform font size @@ -209,7 +207,7 @@ void SelectionPointer::updateSliceData(bool sliceActivated, GLfloat autoScaleAdj m_autoScaleAdjustment = autoScaleAdjustment; } -void SelectionPointer::setHighlightColor(const QVector3D &colorVector) +void SelectionPointer::setHighlightColor(const QVector4D &colorVector) { m_highlightColor = colorVector; } @@ -230,6 +228,11 @@ void SelectionPointer::setPointerObject(ObjectHelper *object) m_pointObj = object; } +void SelectionPointer::setLabelObject(ObjectHelper *object) +{ + m_labelObj = object; +} + void SelectionPointer::handleDrawerChange() { m_cachedTheme = m_drawer->theme(); @@ -264,12 +267,4 @@ void SelectionPointer::initShaders() } -void SelectionPointer::loadLabelMesh() -{ - if (m_labelObj) - delete m_labelObj; - m_labelObj = new ObjectHelper(QStringLiteral(":/defaultMeshes/plane")); - m_labelObj->load(); -} - QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/engine/selectionpointer_p.h b/src/datavisualization/engine/selectionpointer_p.h index 1eac22be..7dc28024 100644 --- a/src/datavisualization/engine/selectionpointer_p.h +++ b/src/datavisualization/engine/selectionpointer_p.h @@ -29,7 +29,6 @@ #ifndef SELECTIONPOINTER_P_H #define SELECTIONPOINTER_P_H -#include "q3dscene.h" #include "datavisualizationglobal_p.h" #include "surface3dcontroller_p.h" @@ -37,7 +36,6 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION class ShaderHelper; class ObjectHelper; -class SurfaceObject; class TextureHelper; class Drawer; @@ -49,26 +47,26 @@ public: explicit SelectionPointer(Drawer *drawer); ~SelectionPointer(); - void render(GLuint defaultFboHandle = 0); + void render(GLuint defaultFboHandle = 0, bool useOrtho = false); void setPosition(const QVector3D &position); void setLabel(const QString &label); void setPointerObject(ObjectHelper *object); + void setLabelObject(ObjectHelper *object); void handleDrawerChange(); void updateBoundingRect(const QRect &rect); void updateScene(Q3DScene *scene); void updateSliceData(bool sliceActivated, GLfloat autoScaleAdjustment); - void setHighlightColor(const QVector3D &colorVector); + void setHighlightColor(const QVector4D &colorVector); void setRotation(const QQuaternion &rotation); private: void initializeOpenGL(); void initShaders(); - void loadLabelMesh(); private: ShaderHelper *m_labelShader; ShaderHelper *m_pointShader; - ObjectHelper *m_labelObj; + ObjectHelper *m_labelObj; // Not owned ObjectHelper *m_pointObj; // Not owned TextureHelper *m_textureHelper; Q3DTheme *m_cachedTheme; @@ -81,7 +79,7 @@ private: QString m_label; bool m_cachedIsSlicingActivated; GLfloat m_autoScaleAdjustment; - QVector3D m_highlightColor; + QVector4D m_highlightColor; QQuaternion m_rotation; }; diff --git a/src/datavisualization/engine/seriesrendercache.cpp b/src/datavisualization/engine/seriesrendercache.cpp index 896b3b28..dc4b9db3 100644 --- a/src/datavisualization/engine/seriesrendercache.cpp +++ b/src/datavisualization/engine/seriesrendercache.cpp @@ -17,7 +17,6 @@ ****************************************************************************/ #include "seriesrendercache_p.h" -#include "objecthelper_p.h" #include "abstract3drenderer_p.h" #include "texturehelper_p.h" #include "utils_p.h" @@ -26,14 +25,19 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION const QString smoothString(QStringLiteral("Smooth")); -SeriesRenderCache::SeriesRenderCache() - : m_series(0), +SeriesRenderCache::SeriesRenderCache(QAbstract3DSeries *series, Abstract3DRenderer *renderer) + : m_series(series), m_object(0), m_mesh(QAbstract3DSeries::MeshCube), m_baseUniformTexture(0), m_baseGradientTexture(0), + m_gradientImage(0), m_singleHighlightGradientTexture(0), - m_multiHighlightGradientTexture(0) + m_multiHighlightGradientTexture(0), + m_valid(false), + m_visible(false), + m_renderer(renderer), + m_objectDirty(true) { } @@ -41,27 +45,13 @@ SeriesRenderCache::~SeriesRenderCache() { } -void SeriesRenderCache::populate(QAbstract3DSeries *series, Abstract3DRenderer *renderer) +void SeriesRenderCache::populate(bool newSeries) { - Q_ASSERT(series); + QAbstract3DSeriesChangeBitField &changeTracker = m_series->d_ptr->m_changeTracker; - bool seriesChanged = false; - - if (m_series != series) { - m_series = series; - seriesChanged = true; - } - - QAbstract3DSeriesChangeBitField &changeTracker = series->d_ptr->m_changeTracker; - - if (seriesChanged || changeTracker.itemLabelFormatChanged) { - m_itemLabelFormat = series->itemLabelFormat(); - changeTracker.itemLabelFormatChanged = false; - } - - if (seriesChanged || changeTracker.meshChanged || changeTracker.meshSmoothChanged + if (newSeries || changeTracker.meshChanged || changeTracker.meshSmoothChanged || changeTracker.userDefinedMeshChanged) { - m_mesh = series->mesh(); + m_mesh = m_series->mesh(); changeTracker.meshChanged = false; changeTracker.meshSmoothChanged = false; changeTracker.userDefinedMeshChanged = false; @@ -71,7 +61,7 @@ void SeriesRenderCache::populate(QAbstract3DSeries *series, Abstract3DRenderer * // Compose mesh filename if (m_mesh == QAbstract3DSeries::MeshUserDefined) { // Always use the supplied mesh directly - meshFileName = series->userDefinedMesh(); + meshFileName = m_series->userDefinedMesh(); } else { switch (m_mesh) { case QAbstract3DSeries::MeshBar: @@ -111,24 +101,18 @@ void SeriesRenderCache::populate(QAbstract3DSeries *series, Abstract3DRenderer * break; } - if (series->isMeshSmooth() && m_mesh != QAbstract3DSeries::MeshPoint) + if (m_series->isMeshSmooth() && m_mesh != QAbstract3DSeries::MeshPoint) meshFileName += smoothString; // Give renderer an opportunity to customize the mesh - renderer->fixMeshFileName(meshFileName, m_mesh); + m_renderer->fixMeshFileName(meshFileName, m_mesh); } - delete m_object; - if (meshFileName.isEmpty()) { - m_object = 0; - } else { - m_object = new ObjectHelper(meshFileName); - m_object->load(); - } + ObjectHelper::resetObjectHelper(m_renderer, m_object, meshFileName); } - if (seriesChanged || changeTracker.meshRotationChanged) { - m_meshRotation = series->meshRotation().normalized(); + if (newSeries || changeTracker.meshRotationChanged) { + m_meshRotation = m_series->meshRotation().normalized(); if (m_series->type() == QAbstract3DSeries::SeriesTypeBar && (m_meshRotation.x() || m_meshRotation.z())) { m_meshRotation = identityQuaternion; @@ -136,55 +120,73 @@ void SeriesRenderCache::populate(QAbstract3DSeries *series, Abstract3DRenderer * changeTracker.meshRotationChanged = false; } - if (seriesChanged || changeTracker.colorStyleChanged) { - m_colorStyle = series->colorStyle(); + if (newSeries || changeTracker.colorStyleChanged) { + m_colorStyle = m_series->colorStyle(); changeTracker.colorStyleChanged = false; } - if (seriesChanged || changeTracker.baseColorChanged) { - m_baseColor = Utils::vectorFromColor(series->baseColor()); + if (newSeries || changeTracker.baseColorChanged) { + m_baseColor = Utils::vectorFromColor(m_series->baseColor()); if (m_series->type() == QAbstract3DSeries::SeriesTypeSurface) - renderer->generateBaseColorTexture(series->baseColor(), &m_baseUniformTexture); + m_renderer->generateBaseColorTexture(m_series->baseColor(), &m_baseUniformTexture); changeTracker.baseColorChanged = false; } - if (seriesChanged || changeTracker.baseGradientChanged) { - QLinearGradient gradient = series->baseGradient(); - renderer->fixGradientAndGenerateTexture(&gradient, &m_baseGradientTexture); + if (newSeries || changeTracker.baseGradientChanged) { + QLinearGradient gradient = m_series->baseGradient(); + m_gradientImage = Utils::getGradientImage(gradient); + m_renderer->fixGradientAndGenerateTexture(&gradient, &m_baseGradientTexture); changeTracker.baseGradientChanged = false; } - if (seriesChanged || changeTracker.singleHighlightColorChanged) { - m_singleHighlightColor = Utils::vectorFromColor(series->singleHighlightColor()); + if (newSeries || changeTracker.singleHighlightColorChanged) { + m_singleHighlightColor = Utils::vectorFromColor(m_series->singleHighlightColor()); changeTracker.singleHighlightColorChanged = false; } - if (seriesChanged || changeTracker.singleHighlightGradientChanged) { - QLinearGradient gradient = series->singleHighlightGradient(); - renderer->fixGradientAndGenerateTexture(&gradient, &m_singleHighlightGradientTexture); + if (newSeries || changeTracker.singleHighlightGradientChanged) { + QLinearGradient gradient = m_series->singleHighlightGradient(); + m_renderer->fixGradientAndGenerateTexture(&gradient, &m_singleHighlightGradientTexture); changeTracker.singleHighlightGradientChanged = false; } - if (seriesChanged || changeTracker.multiHighlightColorChanged) { - m_multiHighlightColor = Utils::vectorFromColor(series->multiHighlightColor()); + if (newSeries || changeTracker.multiHighlightColorChanged) { + m_multiHighlightColor = Utils::vectorFromColor(m_series->multiHighlightColor()); changeTracker.multiHighlightColorChanged = false; } - if (seriesChanged || changeTracker.multiHighlightGradientChanged) { - QLinearGradient gradient = series->multiHighlightGradient(); - renderer->fixGradientAndGenerateTexture(&gradient, &m_multiHighlightGradientTexture); + if (newSeries || changeTracker.multiHighlightGradientChanged) { + QLinearGradient gradient = m_series->multiHighlightGradient(); + m_renderer->fixGradientAndGenerateTexture(&gradient, &m_multiHighlightGradientTexture); changeTracker.multiHighlightGradientChanged = false; } - if (seriesChanged || changeTracker.nameChanged) { - m_name = series->name(); + if (newSeries || changeTracker.nameChanged) { + m_name = m_series->name(); changeTracker.nameChanged = false; } + + if (newSeries || changeTracker.itemLabelChanged + || changeTracker.itemLabelVisibilityChanged) { + changeTracker.itemLabelChanged = false; + changeTracker.itemLabelVisibilityChanged = false; + // series->itemLabel() call resolves the item label and emits the changed signal + // if it is dirty, so we need to call it even if m_itemLabel is eventually set + // to an empty string. + m_itemLabel = m_series->itemLabel(); + if (!m_series->isItemLabelVisible()) + m_itemLabel = QString(); + } + + if (newSeries || changeTracker.visibilityChanged) { + m_visible = m_series->isVisible(); + changeTracker.visibilityChanged = false; + } } void SeriesRenderCache::cleanup(TextureHelper *texHelper) { - delete m_object; + ObjectHelper::releaseObjectHelper(m_renderer, m_object); if (QOpenGLContext::currentContext()) { texHelper->deleteTexture(&m_baseUniformTexture); texHelper->deleteTexture(&m_baseGradientTexture); diff --git a/src/datavisualization/engine/seriesrendercache_p.h b/src/datavisualization/engine/seriesrendercache_p.h index 77e050b0..96b61b87 100644 --- a/src/datavisualization/engine/seriesrendercache_p.h +++ b/src/datavisualization/engine/seriesrendercache_p.h @@ -41,48 +41,59 @@ class TextureHelper; class SeriesRenderCache { public: - SeriesRenderCache(); + SeriesRenderCache(QAbstract3DSeries *series, Abstract3DRenderer *renderer); virtual ~SeriesRenderCache(); - void populate(QAbstract3DSeries *series, Abstract3DRenderer *renderer); + virtual void populate(bool newSeries); virtual void cleanup(TextureHelper *texHelper); // NOTE: Series pointer can only be used to access the series when syncing with controller. // It is not guaranteed to be valid while rendering and should only be used as an identifier. inline QAbstract3DSeries *series() const { return m_series; } - inline const QString &itemLabelFormat() const { return m_itemLabelFormat; } inline const QAbstract3DSeries::Mesh &mesh() const { return m_mesh; } inline const QQuaternion &meshRotation() const { return m_meshRotation; } inline void setMeshRotation(const QQuaternion &rotation) { m_meshRotation = rotation; } inline ObjectHelper *object() const { return m_object; } inline const Q3DTheme::ColorStyle &colorStyle() const { return m_colorStyle; } - inline const QVector3D &baseColor() const { return m_baseColor; } + inline const QVector4D &baseColor() const { return m_baseColor; } inline const GLuint &baseUniformTexture() const { return m_baseUniformTexture; } inline const GLuint &baseGradientTexture() const { return m_baseGradientTexture; } - inline const QVector3D &singleHighlightColor() const { return m_singleHighlightColor; } + inline const QImage &gradientImage() const { return m_gradientImage; } + inline const QVector4D &singleHighlightColor() const { return m_singleHighlightColor; } inline const GLuint &singleHighlightGradientTexture() const { return m_singleHighlightGradientTexture; } - inline const QVector3D &multiHighlightColor() const { return m_multiHighlightColor; } + inline const QVector4D &multiHighlightColor() const { return m_multiHighlightColor; } inline const GLuint &multiHighlightGradientTexture() const { return m_multiHighlightGradientTexture; } inline const QString &name() const { return m_name; } + inline const QString &itemLabel() const { return m_itemLabel; } + inline void setValid(bool valid) { m_valid = valid; } + inline bool isValid() const { return m_valid; } + inline bool isVisible() const { return m_visible; } + inline void setDataDirty(bool state) { m_objectDirty = state; } + inline bool dataDirty() const { return m_objectDirty; } protected: QAbstract3DSeries *m_series; - QString m_itemLabelFormat; - ObjectHelper *m_object; + ObjectHelper *m_object; // Shared reference QAbstract3DSeries::Mesh m_mesh; QQuaternion m_meshRotation; Q3DTheme::ColorStyle m_colorStyle; - QVector3D m_baseColor; + QVector4D m_baseColor; GLuint m_baseUniformTexture; GLuint m_baseGradientTexture; - QVector3D m_singleHighlightColor; + QImage m_gradientImage; + QVector4D m_singleHighlightColor; GLuint m_singleHighlightGradientTexture; - QVector3D m_multiHighlightColor; + QVector4D m_multiHighlightColor; GLuint m_multiHighlightGradientTexture; QString m_name; + QString m_itemLabel; + bool m_valid; + bool m_visible; + Abstract3DRenderer *m_renderer; + bool m_objectDirty; }; QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/engine/shaders/colorOnY.frag b/src/datavisualization/engine/shaders/colorOnY.frag index 8c610cd7..7a5eb46b 100644 --- a/src/datavisualization/engine/shaders/colorOnY.frag +++ b/src/datavisualization/engine/shaders/colorOnY.frag @@ -6,7 +6,7 @@ uniform highp float ambientStrength; uniform sampler2D textureSampler; uniform highp float gradMin; uniform highp float gradHeight; -uniform highp vec3 lightColor; +uniform highp vec4 lightColor; varying highp vec3 position_wrld; varying highp vec3 normal_cmr; @@ -17,8 +17,8 @@ varying highp vec2 coords_mdl; void main() { highp vec2 gradientUV = vec2(0.0, gradMin + ((coords_mdl.y + 1.0) * gradHeight)); highp vec3 materialDiffuseColor = texture2D(textureSampler, gradientUV).xyz; - highp vec3 materialAmbientColor = lightColor * ambientStrength * materialDiffuseColor; - highp vec3 materialSpecularColor = lightColor; + 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); diff --git a/src/datavisualization/engine/shaders/colorOnY_ES2.frag b/src/datavisualization/engine/shaders/colorOnY_ES2.frag index 5b553562..4352de05 100644 --- a/src/datavisualization/engine/shaders/colorOnY_ES2.frag +++ b/src/datavisualization/engine/shaders/colorOnY_ES2.frag @@ -1,11 +1,11 @@ -uniform highp vec3 lightPosition_wrld; uniform highp float lightStrength; uniform highp float ambientStrength; uniform sampler2D textureSampler; uniform highp float gradMin; uniform highp float gradHeight; -uniform highp vec3 lightColor; +uniform highp vec4 lightColor; +varying highp vec3 lightPosition_wrld_frag; varying highp vec3 position_wrld; varying highp vec3 normal_cmr; varying highp vec3 eyeDirection_cmr; @@ -15,10 +15,10 @@ varying highp vec2 coords_mdl; void main() { highp vec2 gradientUV = vec2(0.0, gradMin + ((coords_mdl.y + 1.0) * gradHeight)); highp vec3 materialDiffuseColor = texture2D(textureSampler, gradientUV).xyz; - highp vec3 materialAmbientColor = lightColor * ambientStrength * materialDiffuseColor; - highp vec3 materialSpecularColor = lightColor; + highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor; + highp vec3 materialSpecularColor = lightColor.rgb; - highp float distance = length(lightPosition_wrld - position_wrld); + highp float distance = length(lightPosition_wrld_frag - position_wrld); highp vec3 n = normalize(normal_cmr); highp vec3 l = normalize(lightDirection_cmr); highp float cosTheta = dot(n, l); diff --git a/src/datavisualization/engine/shaders/default.frag b/src/datavisualization/engine/shaders/default.frag index ca6fefb9..d16055a3 100644 --- a/src/datavisualization/engine/shaders/default.frag +++ b/src/datavisualization/engine/shaders/default.frag @@ -8,15 +8,15 @@ varying highp vec3 eyeDirection_cmr; varying highp vec3 lightDirection_cmr; uniform highp vec3 lightPosition_wrld; -uniform highp vec3 color_mdl; +uniform highp vec4 color_mdl; uniform highp float lightStrength; uniform highp float ambientStrength; -uniform highp vec3 lightColor; +uniform highp vec4 lightColor; void main() { highp vec3 materialDiffuseColor = color_mdl.rgb; - highp vec3 materialAmbientColor = lightColor * ambientStrength * materialDiffuseColor; - highp vec3 materialSpecularColor = lightColor; + highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor; + highp vec3 materialSpecularColor = lightColor.rgb; highp float distance = length(lightPosition_wrld - position_wrld); diff --git a/src/datavisualization/engine/shaders/default.vert b/src/datavisualization/engine/shaders/default.vert index efb40862..b454913b 100644 --- a/src/datavisualization/engine/shaders/default.vert +++ b/src/datavisualization/engine/shaders/default.vert @@ -8,6 +8,7 @@ uniform highp mat4 M; uniform highp mat4 itM; 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; @@ -17,10 +18,11 @@ varying highp vec2 coords_mdl; void main() { gl_Position = MVP * vec4(vertexPosition_mdl, 1.0); coords_mdl = vertexPosition_mdl.xy; - position_wrld = (M * vec4(vertexPosition_mdl, 1.0)).xyz; - vec3 vertexPosition_cmr = (V * M * vec4(vertexPosition_mdl, 1.0)).xyz; + 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 = (V * vec4(lightPosition_wrld, 1.0)).xyz; + vec3 lightPosition_cmr = vec4(V * vec4(lightPosition_wrld, 1.0)).xyz; lightDirection_cmr = lightPosition_cmr + eyeDirection_cmr; - normal_cmr = (V * itM * vec4(vertexNormal_mdl, 0.0)).xyz; + normal_cmr = vec4(V * itM * 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 bc5c18b6..73d66d5b 100644 --- a/src/datavisualization/engine/shaders/default_ES2.frag +++ b/src/datavisualization/engine/shaders/default_ES2.frag @@ -4,19 +4,19 @@ varying highp vec3 position_wrld; varying highp vec3 normal_cmr; varying highp vec3 eyeDirection_cmr; varying highp vec3 lightDirection_cmr; +varying highp vec3 lightPosition_wrld_frag; -uniform highp vec3 lightPosition_wrld; -uniform highp vec3 color_mdl; +uniform highp vec4 color_mdl; uniform highp float lightStrength; uniform highp float ambientStrength; -uniform highp vec3 lightColor; +uniform highp vec4 lightColor; void main() { highp vec3 materialDiffuseColor = color_mdl.rgb; - highp vec3 materialAmbientColor = lightColor * ambientStrength * materialDiffuseColor; - highp vec3 materialSpecularColor = lightColor; + highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor; + highp vec3 materialSpecularColor = lightColor.rgb; - highp float distance = length(lightPosition_wrld - position_wrld); + highp float distance = length(lightPosition_wrld_frag - position_wrld); highp vec3 n = normalize(normal_cmr); highp vec3 l = normalize(lightDirection_cmr); diff --git a/src/datavisualization/engine/shaders/plainColor.frag b/src/datavisualization/engine/shaders/plainColor.frag index 099c87a1..da9ee060 100644 --- a/src/datavisualization/engine/shaders/plainColor.frag +++ b/src/datavisualization/engine/shaders/plainColor.frag @@ -1,7 +1,6 @@ -uniform highp vec3 color_mdl; +uniform highp vec4 color_mdl; void main() { - gl_FragColor.rgb = color_mdl; - gl_FragColor.a = 1.0; + gl_FragColor = color_mdl; } diff --git a/src/datavisualization/engine/shaders/shadow.frag b/src/datavisualization/engine/shaders/shadow.frag index e2286dc5..237e9780 100644 --- a/src/datavisualization/engine/shaders/shadow.frag +++ b/src/datavisualization/engine/shaders/shadow.frag @@ -5,7 +5,7 @@ uniform highp float ambientStrength; uniform highp float shadowQuality; uniform highp sampler2D textureSampler; uniform highp sampler2DShadow shadowMap; -uniform highp vec3 lightColor; +uniform highp vec4 lightColor; varying highp vec4 shadowCoord; varying highp vec2 UV; @@ -33,8 +33,8 @@ highp vec2 poissonDisk[16] = vec2[16](vec2(-0.94201624, -0.39906216), void main() { highp vec3 materialDiffuseColor = texture2D(textureSampler, UV).rgb; - highp vec3 materialAmbientColor = lightColor * ambientStrength * materialDiffuseColor; - highp vec3 materialSpecularColor = lightColor * 0.2; + 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); diff --git a/src/datavisualization/engine/shaders/shadow.vert b/src/datavisualization/engine/shaders/shadow.vert index e29a8a30..0adcd43c 100644 --- a/src/datavisualization/engine/shaders/shadow.vert +++ b/src/datavisualization/engine/shaders/shadow.vert @@ -28,10 +28,10 @@ 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 = (M * vec4(vertexPosition_mdl, 1.0)).xyz; - vec3 vertexPosition_cmr = (V * M * vec4(vertexPosition_mdl, 1.0)).xyz; + 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; - lightDirection_cmr = (V * vec4(lightPosition_wrld, 0.0)).xyz; - normal_cmr = (V * itM * vec4(vertexNormal_mdl, 0.0)).xyz; + 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/shadowNoTex.frag b/src/datavisualization/engine/shaders/shadowNoTex.frag index d54efea9..b2e7adfc 100644 --- a/src/datavisualization/engine/shaders/shadowNoTex.frag +++ b/src/datavisualization/engine/shaders/shadowNoTex.frag @@ -3,9 +3,9 @@ uniform highp float lightStrength; uniform highp float ambientStrength; uniform highp float shadowQuality; -uniform highp vec3 color_mdl; +uniform highp vec4 color_mdl; uniform highp sampler2DShadow shadowMap; -uniform highp vec3 lightColor; +uniform highp vec4 lightColor; varying highp vec4 shadowCoord; varying highp vec3 position_wrld; @@ -32,8 +32,8 @@ highp vec2 poissonDisk[16] = vec2[16](vec2(-0.94201624, -0.39906216), void main() { highp vec3 materialDiffuseColor = color_mdl.rgb; - highp vec3 materialAmbientColor = lightColor * ambientStrength * materialDiffuseColor; - highp vec3 materialSpecularColor = lightColor; + highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor; + highp vec3 materialSpecularColor = lightColor.rgb; highp vec3 n = normalize(normal_cmr); highp vec3 l = normalize(lightDirection_cmr); diff --git a/src/datavisualization/engine/shaders/shadowNoTexColorOnY.frag b/src/datavisualization/engine/shaders/shadowNoTexColorOnY.frag index e986a52a..73b84138 100644 --- a/src/datavisualization/engine/shaders/shadowNoTexColorOnY.frag +++ b/src/datavisualization/engine/shaders/shadowNoTexColorOnY.frag @@ -7,7 +7,7 @@ uniform highp sampler2DShadow shadowMap; uniform sampler2D textureSampler; uniform highp float gradMin; uniform highp float gradHeight; -uniform highp vec3 lightColor; +uniform highp vec4 lightColor; varying highp vec4 shadowCoord; varying highp vec3 position_wrld; @@ -36,8 +36,8 @@ highp vec2 poissonDisk[16] = vec2[16](vec2(-0.94201624, -0.39906216), void main() { highp vec2 gradientUV = vec2(0.0, gradMin + ((coords_mdl.y + 1.0) * gradHeight)); highp vec3 materialDiffuseColor = texture2D(textureSampler, gradientUV).xyz; - highp vec3 materialAmbientColor = lightColor * ambientStrength * materialDiffuseColor; - highp vec3 materialSpecularColor = lightColor; + highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor; + highp vec3 materialSpecularColor = lightColor.rgb; highp vec3 n = normalize(normal_cmr); highp vec3 l = normalize(lightDirection_cmr); diff --git a/src/datavisualization/engine/shaders/surface.frag b/src/datavisualization/engine/shaders/surface.frag index b5205d2d..f17dd73e 100644 --- a/src/datavisualization/engine/shaders/surface.frag +++ b/src/datavisualization/engine/shaders/surface.frag @@ -10,13 +10,13 @@ uniform sampler2D textureSampler; uniform highp vec3 lightPosition_wrld; uniform highp float lightStrength; uniform highp float ambientStrength; -uniform highp vec3 lightColor; +uniform highp vec4 lightColor; void main() { highp vec2 gradientUV = vec2(0.0, (coords_mdl.y + 1.0) / 2.0); highp vec3 materialDiffuseColor = texture2D(textureSampler, gradientUV).xyz; - highp vec3 materialAmbientColor = lightColor * ambientStrength * materialDiffuseColor; - highp vec3 materialSpecularColor = lightColor; + highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor; + highp vec3 materialSpecularColor = lightColor.rgb; highp float distance = length(lightPosition_wrld - position_wrld); diff --git a/src/datavisualization/engine/shaders/surfaceFlat.frag b/src/datavisualization/engine/shaders/surfaceFlat.frag index 7eaa917f..748fb3dd 100644 --- a/src/datavisualization/engine/shaders/surfaceFlat.frag +++ b/src/datavisualization/engine/shaders/surfaceFlat.frag @@ -12,13 +12,13 @@ uniform sampler2D textureSampler; uniform highp vec3 lightPosition_wrld; uniform highp float lightStrength; uniform highp float ambientStrength; -uniform highp vec3 lightColor; +uniform highp vec4 lightColor; void main() { highp vec2 gradientUV = vec2(0.0, (coords_mdl.y + 1.0) / 2.0); highp vec3 materialDiffuseColor = texture2D(textureSampler, gradientUV).xyz; - highp vec3 materialAmbientColor = lightColor * ambientStrength * materialDiffuseColor; - highp vec3 materialSpecularColor = lightColor; + highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor; + highp vec3 materialSpecularColor = lightColor.rgb; highp float distance = length(lightPosition_wrld - position_wrld); diff --git a/src/datavisualization/engine/shaders/surfaceFlat.vert b/src/datavisualization/engine/shaders/surfaceFlat.vert index 0d39f6bc..102bea78 100644 --- a/src/datavisualization/engine/shaders/surfaceFlat.vert +++ b/src/datavisualization/engine/shaders/surfaceFlat.vert @@ -20,10 +20,10 @@ varying highp vec3 coords_mdl; void main() { gl_Position = MVP * vec4(vertexPosition_mdl, 1.0); coords_mdl = vertexPosition_mdl; - position_wrld = (M * vec4(vertexPosition_mdl, 1.0)).xyz; - vec3 vertexPosition_cmr = (V * M * vec4(vertexPosition_mdl, 1.0)).xyz; + 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 = (V * vec4(lightPosition_wrld, 1.0)).xyz; + vec3 lightPosition_cmr = vec4(V * vec4(lightPosition_wrld, 1.0)).xyz; lightDirection_cmr = lightPosition_cmr + eyeDirection_cmr; - normal_cmr = (V * itM * vec4(vertexNormal_mdl, 0.0)).xyz; + normal_cmr = vec4(V * itM * vec4(vertexNormal_mdl, 0.0)).xyz; } diff --git a/src/datavisualization/engine/shaders/surfaceShadowFlat.frag b/src/datavisualization/engine/shaders/surfaceShadowFlat.frag index 9b9305ab..0613a40c 100644 --- a/src/datavisualization/engine/shaders/surfaceShadowFlat.frag +++ b/src/datavisualization/engine/shaders/surfaceShadowFlat.frag @@ -15,7 +15,7 @@ uniform highp vec3 lightPosition_wrld; uniform highp float lightStrength; uniform highp float ambientStrength; uniform highp float shadowQuality; -uniform highp vec3 lightColor; +uniform highp vec4 lightColor; highp vec2 poissonDisk[16] = vec2[16](vec2(-0.94201624, -0.39906216), vec2(0.94558609, -0.76890725), @@ -37,8 +37,8 @@ highp vec2 poissonDisk[16] = vec2[16](vec2(-0.94201624, -0.39906216), void main() { highp vec2 gradientUV = vec2(0.0, (coords_mdl.y + 1.0) / 2.0); highp vec3 materialDiffuseColor = texture2D(textureSampler, gradientUV).xyz; - highp vec3 materialAmbientColor = lightColor * ambientStrength * materialDiffuseColor; - highp vec3 materialSpecularColor = lightColor; + highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor; + highp vec3 materialSpecularColor = lightColor.rgb; highp vec3 n = normalize(normal_cmr); highp vec3 l = normalize(lightDirection_cmr); diff --git a/src/datavisualization/engine/shaders/surfaceShadowFlat.vert b/src/datavisualization/engine/shaders/surfaceShadowFlat.vert index 0a6e967f..8da7b196 100644 --- a/src/datavisualization/engine/shaders/surfaceShadowFlat.vert +++ b/src/datavisualization/engine/shaders/surfaceShadowFlat.vert @@ -28,10 +28,10 @@ void main() { gl_Position = MVP * vec4(vertexPosition_mdl, 1.0); coords_mdl = vertexPosition_mdl; shadowCoord = bias * depthMVP * vec4(vertexPosition_mdl, 1.0); - position_wrld = (M * vec4(vertexPosition_mdl, 1.0)).xyz; - vec3 vertexPosition_cmr = (V * M * vec4(vertexPosition_mdl, 1.0)).xyz; + 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 = (V * vec4(lightPosition_wrld, 1.0)).xyz; + vec3 lightPosition_cmr = vec4(V * vec4(lightPosition_wrld, 1.0)).xyz; lightDirection_cmr = lightPosition_cmr + eyeDirection_cmr; - normal_cmr = (V * itM * vec4(vertexNormal_mdl, 0.0)).xyz; + normal_cmr = vec4(V * itM * vec4(vertexNormal_mdl, 0.0)).xyz; } diff --git a/src/datavisualization/engine/shaders/surfaceShadowNoTex.frag b/src/datavisualization/engine/shaders/surfaceShadowNoTex.frag index 3427fbae..1acf8f69 100644 --- a/src/datavisualization/engine/shaders/surfaceShadowNoTex.frag +++ b/src/datavisualization/engine/shaders/surfaceShadowNoTex.frag @@ -13,7 +13,7 @@ uniform highp vec3 lightPosition_wrld; uniform highp float lightStrength; uniform highp float ambientStrength; uniform highp float shadowQuality; -uniform highp vec3 lightColor; +uniform highp vec4 lightColor; highp vec2 poissonDisk[16] = vec2[16](vec2(-0.94201624, -0.39906216), vec2(0.94558609, -0.76890725), @@ -35,8 +35,8 @@ highp vec2 poissonDisk[16] = vec2[16](vec2(-0.94201624, -0.39906216), void main() { highp vec2 gradientUV = vec2(0.0, (coords_mdl.y + 1.0) / 2.0); highp vec3 materialDiffuseColor = texture2D(textureSampler, gradientUV).xyz; - highp vec3 materialAmbientColor = lightColor * ambientStrength * materialDiffuseColor; - highp vec3 materialSpecularColor = lightColor; + highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor; + highp vec3 materialSpecularColor = lightColor.rgb; highp vec3 n = normalize(normal_cmr); highp vec3 l = normalize(lightDirection_cmr); diff --git a/src/datavisualization/engine/shaders/surface_ES2.frag b/src/datavisualization/engine/shaders/surface_ES2.frag index 0e17cacd..58d13834 100644 --- a/src/datavisualization/engine/shaders/surface_ES2.frag +++ b/src/datavisualization/engine/shaders/surface_ES2.frag @@ -4,20 +4,20 @@ varying highp vec3 position_wrld; varying highp vec3 normal_cmr; varying highp vec3 eyeDirection_cmr; varying highp vec3 lightDirection_cmr; +varying highp vec3 lightPosition_wrld_frag; uniform sampler2D textureSampler; -uniform highp vec3 lightPosition_wrld; uniform highp float lightStrength; uniform highp float ambientStrength; -uniform highp vec3 lightColor; +uniform highp vec4 lightColor; void main() { highp vec2 gradientUV = vec2(0.0, (coords_mdl.y + 1.0) / 2.0); highp vec3 materialDiffuseColor = texture2D(textureSampler, gradientUV).xyz; - highp vec3 materialAmbientColor = lightColor * ambientStrength * materialDiffuseColor; - highp vec3 materialSpecularColor = lightColor; + highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor; + highp vec3 materialSpecularColor = lightColor.rgb; - highp float distance = length(lightPosition_wrld - position_wrld); + highp float distance = length(lightPosition_wrld_frag - position_wrld); highp vec3 n = normalize(normal_cmr); highp vec3 l = normalize(lightDirection_cmr); diff --git a/src/datavisualization/engine/shaders/texture.frag b/src/datavisualization/engine/shaders/texture.frag new file mode 100644 index 00000000..41c4259b --- /dev/null +++ b/src/datavisualization/engine/shaders/texture.frag @@ -0,0 +1,37 @@ +#version 120 + +varying highp vec2 UV; +varying highp vec3 position_wrld; +varying highp vec3 normal_cmr; +varying highp vec3 eyeDirection_cmr; +varying highp vec3 lightDirection_cmr; + +uniform highp vec3 lightPosition_wrld; +uniform highp sampler2D textureSampler; +uniform highp float lightStrength; +uniform highp float ambientStrength; +uniform highp vec4 lightColor; + +void main() { + highp vec3 materialDiffuseColor = texture2D(textureSampler, UV).rgb; + 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 = texture2D(textureSampler, UV).a; + gl_FragColor.rgb = clamp(gl_FragColor.rgb, 0.0, 1.0); +} + diff --git a/src/datavisualization/engine/shaders/texture.vert b/src/datavisualization/engine/shaders/texture.vert new file mode 100644 index 00000000..90c0ac23 --- /dev/null +++ b/src/datavisualization/engine/shaders/texture.vert @@ -0,0 +1,28 @@ +uniform highp mat4 MVP; +uniform highp mat4 V; +uniform highp mat4 M; +uniform highp mat4 itM; +uniform highp vec3 lightPosition_wrld; + +attribute highp vec3 vertexPosition_mdl; +attribute highp vec2 vertexUV; +attribute highp vec3 vertexNormal_mdl; + +varying highp vec3 lightPosition_wrld_frag; +varying highp vec2 UV; +varying highp vec3 position_wrld; +varying highp vec3 normal_cmr; +varying highp vec3 eyeDirection_cmr; +varying highp vec3 lightDirection_cmr; + +void main() { + gl_Position = MVP * 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; + normal_cmr = vec4(V * itM * vec4(vertexNormal_mdl, 0.0)).xyz; + UV = vertexUV; + lightPosition_wrld_frag = lightPosition_wrld; +} diff --git a/src/datavisualization/engine/shaders/texture_ES2.frag b/src/datavisualization/engine/shaders/texture_ES2.frag new file mode 100644 index 00000000..82ad6614 --- /dev/null +++ b/src/datavisualization/engine/shaders/texture_ES2.frag @@ -0,0 +1,40 @@ +varying highp vec2 UV; +varying highp vec2 coords_mdl; +varying highp vec3 position_wrld; +varying highp vec3 normal_cmr; +varying highp vec3 eyeDirection_cmr; +varying highp vec3 lightDirection_cmr; +varying highp vec3 lightPosition_wrld_frag; + +uniform highp sampler2D textureSampler; +uniform highp float lightStrength; +uniform highp float ambientStrength; +uniform highp vec4 lightColor; + +void main() { + highp vec3 materialDiffuseColor = texture2D(textureSampler, UV).rgb; + highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor; + highp vec3 materialSpecularColor = lightColor.rgb; + + highp float distance = length(lightPosition_wrld_frag - position_wrld); + + highp vec3 n = normalize(normal_cmr); + highp vec3 l = normalize(lightDirection_cmr); + highp float cosTheta = dot(n, l); + if (cosTheta < 0.0) cosTheta = 0.0; + else if (cosTheta > 1.0) cosTheta = 1.0; + + highp vec3 E = normalize(eyeDirection_cmr); + highp vec3 R = reflect(-l, n); + highp float cosAlpha = dot(E, R); + if (cosAlpha < 0.0) cosAlpha = 0.0; + else if (cosAlpha > 1.0) cosAlpha = 1.0; + + gl_FragColor.rgb = + materialAmbientColor + + materialDiffuseColor * lightStrength * (cosTheta * cosTheta) / distance + + materialSpecularColor * lightStrength * (cosAlpha * cosAlpha * cosAlpha * cosAlpha * cosAlpha * cosAlpha * cosAlpha * cosAlpha * cosAlpha * cosAlpha) / distance; + gl_FragColor.a = texture2D(textureSampler, UV).a; + gl_FragColor.rgb = clamp(gl_FragColor.rgb, 0.0, 1.0); +} + diff --git a/src/datavisualization/engine/surface3dcontroller.cpp b/src/datavisualization/engine/surface3dcontroller.cpp index 991a1ce8..c03bafd8 100644 --- a/src/datavisualization/engine/surface3dcontroller.cpp +++ b/src/datavisualization/engine/surface3dcontroller.cpp @@ -18,15 +18,9 @@ #include "surface3dcontroller_p.h" #include "surface3drenderer_p.h" -#include "camerahelper_p.h" -#include "qabstract3daxis_p.h" #include "qvalue3daxis_p.h" -#include "qcategory3daxis.h" #include "qsurfacedataproxy_p.h" #include "qsurface3dseries_p.h" -#include "shaderhelper_p.h" - -#include <QtGui/QMatrix4x4> QT_BEGIN_NAMESPACE_DATAVISUALIZATION @@ -66,11 +60,6 @@ void Surface3DController::synchDataToRenderer() if (!isInitialized()) return; - if (m_changedSeriesList.size()) { - m_renderer->modifiedSeriesList(m_changedSeriesList); - m_changedSeriesList.clear(); - } - Abstract3DController::synchDataToRenderer(); // Notify changes to renderer @@ -81,7 +70,7 @@ void Surface3DController::synchDataToRenderer() } if (m_changeTracker.itemChanged) { - m_renderer->updateItem(m_changedItems); + m_renderer->updateItems(m_changedItems); m_changeTracker.itemChanged = false; m_changedItems.clear(); } @@ -98,7 +87,7 @@ void Surface3DController::handleAxisAutoAdjustRangeChangedInOrientation( Q_UNUSED(orientation) Q_UNUSED(autoAdjust) - adjustValueAxisRange(); + adjustAxisRanges(); } void Surface3DController::handleAxisRangeChangedBySender(QObject *sender) @@ -113,8 +102,6 @@ void Surface3DController::handleSeriesVisibilityChangedBySender(QObject *sender) { Abstract3DController::handleSeriesVisibilityChangedBySender(sender); - adjustValueAxisRange(); - // Visibility changes may require disabling slicing, // so just reset selection to ensure everything is still valid. setSelectedPoint(m_selectedPoint, m_selectedSeries, false); @@ -128,6 +115,8 @@ void Surface3DController::handlePendingClick() setSelectedPoint(position, series, true); + Abstract3DController::handlePendingClick(); + m_renderer->resetClickedStatus(); } @@ -148,9 +137,6 @@ void Surface3DController::addSeries(QAbstract3DSeries *series) Abstract3DController::addSeries(series); - if (series->isVisible()) - adjustValueAxisRange(); - QSurface3DSeries *surfaceSeries = static_cast<QSurface3DSeries *>(series); if (surfaceSeries->selectedPoint() != invalidSelectionPosition()) setSelectedPoint(surfaceSeries->selectedPoint(), surfaceSeries, false); @@ -166,7 +152,7 @@ void Surface3DController::removeSeries(QAbstract3DSeries *series) setSelectedPoint(invalidSelectionPosition(), 0, false); if (wasVisible) - adjustValueAxisRange(); + adjustAxisRanges(); } QList<QSurface3DSeries *> Surface3DController::surfaceSeriesList() @@ -214,7 +200,8 @@ void Surface3DController::setSelectionMode(QAbstract3DGraph::SelectionFlags mode } } -void Surface3DController::setSelectedPoint(const QPoint &position, QSurface3DSeries *series, bool enterSlice) +void Surface3DController::setSelectedPoint(const QPoint &position, QSurface3DSeries *series, + bool enterSlice) { // If the selection targets non-existent point, clear selection instead. QPoint pos = position; @@ -290,13 +277,15 @@ void Surface3DController::handleArrayReset() { QSurface3DSeries *series = static_cast<QSurfaceDataProxy *>(sender())->series(); if (series->isVisible()) { - adjustValueAxisRange(); - if (!m_changedSeriesList.contains(series)) - m_changedSeriesList.append(series); + adjustAxisRanges(); m_isDataDirty = true; } + if (!m_changedSeriesList.contains(series)) + m_changedSeriesList.append(series); + // Clear selection unless still valid setSelectedPoint(m_selectedPoint, m_selectedSeries, false); + series->d_ptr->markItemLabelDirty(); emitNeedRender(); } @@ -315,33 +304,34 @@ void Surface3DController::handleFlatShadingSupportedChange(bool supported) void Surface3DController::handleRowsChanged(int startIndex, int count) { - QSurfaceDataProxy *sender = static_cast<QSurfaceDataProxy *>(QObject::sender()); - if (m_changedRows.size() == 0) - m_changedRows.reserve(sender->rowCount()); - - QSurface3DSeries *series = sender->series(); + QSurface3DSeries *series = static_cast<QSurfaceDataProxy *>(QObject::sender())->series(); int oldChangeCount = m_changedRows.size(); + if (!oldChangeCount) + m_changedRows.reserve(count); + + int selectedRow = m_selectedPoint.x(); for (int i = 0; i < count; i++) { bool newItem = true; int candidate = startIndex + i; - for (int i = 0; i < oldChangeCount; i++) { - if (m_changedRows.at(i).row == candidate && - series == m_changedRows.at(i).series) { + for (int j = 0; j < oldChangeCount; j++) { + const ChangeRow &oldChangeItem = m_changedRows.at(j); + if (oldChangeItem.row == candidate && series == oldChangeItem.series) { newItem = false; break; } } if (newItem) { - ChangeRow newItem = {series, candidate}; - m_changedRows.append(newItem); + ChangeRow newChangeItem = {series, candidate}; + m_changedRows.append(newChangeItem); + if (series == m_selectedSeries && selectedRow == candidate) + series->d_ptr->markItemLabelDirty(); } } - if (m_changedRows.size()) { + if (count) { m_changeTracker.rowsChanged = true; - adjustValueAxisRange(); - // Clear selection unless still valid - setSelectedPoint(m_selectedPoint, m_selectedSeries, false); + if (series->isVisible()) + adjustAxisRanges(); emitNeedRender(); } } @@ -352,7 +342,7 @@ void Surface3DController::handleItemChanged(int rowIndex, int columnIndex) QSurface3DSeries *series = sender->series(); bool newItem = true; - QPoint candidate(columnIndex, rowIndex); + QPoint candidate(rowIndex, columnIndex); foreach (ChangeItem item, m_changedItems) { if (item.point == candidate && item.series == series) { newItem = false; @@ -364,9 +354,11 @@ void Surface3DController::handleItemChanged(int rowIndex, int columnIndex) m_changedItems.append(newItem); m_changeTracker.itemChanged = true; - adjustValueAxisRange(); - // Clear selection unless still valid - setSelectedPoint(m_selectedPoint, m_selectedSeries, false); + if (series == m_selectedSeries && m_selectedPoint == candidate) + series->d_ptr->markItemLabelDirty(); + + if (series->isVisible()) + adjustAxisRanges(); emitNeedRender(); } } @@ -377,11 +369,11 @@ void Surface3DController::handleRowsAdded(int startIndex, int count) Q_UNUSED(count) QSurface3DSeries *series = static_cast<QSurfaceDataProxy *>(sender())->series(); if (series->isVisible()) { - adjustValueAxisRange(); + adjustAxisRanges(); m_isDataDirty = true; - if (!m_changedSeriesList.contains(series)) - m_changedSeriesList.append(series); } + if (!m_changedSeriesList.contains(series)) + m_changedSeriesList.append(series); emitNeedRender(); } @@ -400,11 +392,11 @@ void Surface3DController::handleRowsInserted(int startIndex, int count) } if (series->isVisible()) { - adjustValueAxisRange(); + adjustAxisRanges(); m_isDataDirty = true; - if (!m_changedSeriesList.contains(series)) - m_changedSeriesList.append(series); } + if (!m_changedSeriesList.contains(series)) + m_changedSeriesList.append(series); emitNeedRender(); } @@ -428,16 +420,16 @@ void Surface3DController::handleRowsRemoved(int startIndex, int count) } if (series->isVisible()) { - adjustValueAxisRange(); + adjustAxisRanges(); m_isDataDirty = true; - if (!m_changedSeriesList.contains(series)) - m_changedSeriesList.append(series); } + if (!m_changedSeriesList.contains(series)) + m_changedSeriesList.append(series); emitNeedRender(); } -void Surface3DController::adjustValueAxisRange() +void Surface3DController::adjustAxisRanges() { QValue3DAxis *valueAxisX = static_cast<QValue3DAxis *>(m_axisX); QValue3DAxis *valueAxisY = static_cast<QValue3DAxis *>(m_axisY); @@ -517,7 +509,7 @@ void Surface3DController::adjustValueAxisRange() adjustment = defaultAdjustment; } } - valueAxisX->dptr()->setRange(minValueX - adjustment, maxValueX + adjustment); + valueAxisX->dptr()->setRange(minValueX - adjustment, maxValueX + adjustment, true); } if (adjustY) { // If all points at same coordinate, need to default to some valid range @@ -525,7 +517,7 @@ void Surface3DController::adjustValueAxisRange() float adjustment = 0.0f; if (minValueY == maxValueY) adjustment = defaultAdjustment; - valueAxisY->dptr()->setRange(minValueY - adjustment, maxValueY + adjustment); + valueAxisY->dptr()->setRange(minValueY - adjustment, maxValueY + adjustment, true); } if (adjustZ) { // If all points at same coordinate, need to default to some valid range @@ -544,7 +536,7 @@ void Surface3DController::adjustValueAxisRange() adjustment = defaultAdjustment; } } - valueAxisZ->dptr()->setRange(minValueZ - adjustment, maxValueZ + adjustment); + valueAxisZ->dptr()->setRange(minValueZ - adjustment, maxValueZ + adjustment, true); } } } diff --git a/src/datavisualization/engine/surface3dcontroller_p.h b/src/datavisualization/engine/surface3dcontroller_p.h index 14c0dd40..2be74f35 100644 --- a/src/datavisualization/engine/surface3dcontroller_p.h +++ b/src/datavisualization/engine/surface3dcontroller_p.h @@ -38,15 +38,11 @@ class Surface3DRenderer; class QSurface3DSeries; struct Surface3DChangeBitField { - bool smoothStatusChanged : 1; - bool surfaceGridChanged : 1; bool selectedPointChanged : 1; bool rowsChanged : 1; bool itemChanged : 1; Surface3DChangeBitField() : - smoothStatusChanged(true), - surfaceGridChanged(true), selectedPointChanged(true), rowsChanged(false), itemChanged(false) @@ -77,7 +73,6 @@ private: bool m_flatShadingSupported; QVector<ChangeItem> m_changedItems; QVector<ChangeRow> m_changedRows; - QVector<QSurface3DSeries *> m_changedSeriesList; public: explicit Surface3DController(QRect rect, Q3DScene *scene = 0); @@ -97,6 +92,7 @@ public: virtual void handleAxisRangeChangedBySender(QObject *sender); virtual void handleSeriesVisibilityChangedBySender(QObject *sender); virtual void handlePendingClick(); + virtual void adjustAxisRanges(); static QPoint invalidSelectionPosition(); bool isFlatShadingSupported(); @@ -119,8 +115,6 @@ signals: void selectedSeriesChanged(QSurface3DSeries *series); private: - void adjustValueAxisRange(); - Q_DISABLE_COPY(Surface3DController) }; diff --git a/src/datavisualization/engine/surface3drenderer.cpp b/src/datavisualization/engine/surface3drenderer.cpp index 41415393..12627966 100644 --- a/src/datavisualization/engine/surface3drenderer.cpp +++ b/src/datavisualization/engine/surface3drenderer.cpp @@ -16,22 +16,12 @@ ** ****************************************************************************/ -#include "surface3dcontroller_p.h" #include "surface3drenderer_p.h" -#include "q3dcamera.h" #include "q3dcamera_p.h" #include "shaderhelper_p.h" -#include "objecthelper_p.h" -#include "surfaceobject_p.h" #include "texturehelper_p.h" -#include "selectionpointer_p.h" #include "utils_p.h" -#include "drawer_p.h" -#include "q3dlight.h" -#include "qsurface3dseries_p.h" -#include <QtGui/QMatrix4x4> -#include <QtGui/QMouseEvent> #include <QtCore/qmath.h> static const int ID_TO_RGBA_MASK = 0xff; @@ -40,12 +30,15 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION //#define SHOW_DEPTH_TEXTURE_SCENE -const GLfloat aspectRatio = 2.0f; // Forced ratio of x and z to y. Dynamic will make it look odd. -const GLfloat backgroundMargin = 1.1f; // Margin for background (1.1f = make it 10% larger to avoid items being drawn inside background) -const GLfloat labelMargin = 0.05f; +// 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; +const uint blueMultiplier = 65536; +const uint alphaMultiplier = 16777216; Surface3DRenderer::Surface3DRenderer(Surface3DController *controller) : Abstract3DRenderer(controller), @@ -65,15 +58,6 @@ Surface3DRenderer::Surface3DRenderer(Surface3DController *controller) m_scaleZ(0.0f), m_scaleXWithBackground(0.0f), m_scaleZWithBackground(0.0f), - m_minVisibleColumnValue(0.0f), - m_maxVisibleColumnValue(0.0f), - m_minVisibleRowValue(0.0f), - m_maxVisibleRowValue(0.0f), - m_visibleColumnRange(0.0f), - m_visibleRowRange(0.0f), - m_backgroundObj(0), - m_gridLineObj(0), - m_labelObj(0), m_depthTexture(0), m_depthModelTexture(0), m_depthFrameBuffer(0), @@ -83,9 +67,6 @@ Surface3DRenderer::Surface3DRenderer(Surface3DController *controller) m_shadowQualityToShader(33.3f), m_flatSupported(true), m_selectionActive(false), - m_xFlipped(false), - m_zFlipped(false), - m_yFlipped(false), m_shadowQualityMultiplier(3), m_hasHeightAdjustmentChanged(true), m_selectedPoint(Surface3DController::invalidSelectionPosition()), @@ -94,6 +75,9 @@ Surface3DRenderer::Surface3DRenderer(Surface3DController *controller) 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")); @@ -131,16 +115,6 @@ Surface3DRenderer::~Surface3DRenderer() delete m_surfaceSliceFlatShader; delete m_surfaceSliceSmoothShader; delete m_labelShader; - - delete m_backgroundObj; - delete m_gridLineObj; - delete m_labelObj; - - foreach (SurfaceSeriesRenderCache *cache, m_renderCacheList) { - cache->cleanup(m_textureHelper); - delete cache; - } - m_renderCacheList.clear(); } void Surface3DRenderer::initializeOpenGL() @@ -160,8 +134,10 @@ void Surface3DRenderer::initializeOpenGL() // Init selection shader initSelectionShaders(); +#if !(defined QT_OPENGL_ES_2) // Load grid line mesh loadGridLineMesh(); +#endif // Load label mesh loadLabelMesh(); @@ -183,22 +159,25 @@ void Surface3DRenderer::updateData() { calculateSceneScalingFactors(); - foreach (SurfaceSeriesRenderCache *cache, m_renderCacheList) { - const QSurface3DSeries *currentSeries = cache->series(); - QSurfaceDataProxy *dataProxy = currentSeries->dataProxy(); - const QSurfaceDataArray &array = *dataProxy->array(); + foreach (SeriesRenderCache *baseCache, m_renderCacheList) { + SurfaceSeriesRenderCache *cache = static_cast<SurfaceSeriesRenderCache *>(baseCache); + if (cache->isVisible() && cache->dataDirty()) { + const QSurface3DSeries *currentSeries = cache->series(); + QSurfaceDataProxy *dataProxy = currentSeries->dataProxy(); + const QSurfaceDataArray &array = *dataProxy->array(); + QSurfaceDataArray &dataArray = cache->dataArray(); + QRect sampleSpace; - // Need minimum of 2x2 array to draw a surface - if (array.size() >= 2 && array.at(0)->size() >= 2) { - QRect sampleSpace = calculateSampleRect(cache, array); + // Need minimum of 2x2 array to draw a surface + if (array.size() >= 2 && array.at(0)->size() >= 2) + sampleSpace = calculateSampleRect(array); - QSurfaceDataArray &dataArray = cache->dataArray(); - bool dimensionChanged = false; + bool dimensionsChanged = false; if (cache->sampleSpace() != sampleSpace) { if (sampleSpace.width() >= 2) m_selectionTexturesDirty = true; - dimensionChanged = true; + dimensionsChanged = true; cache->setSampleSpace(sampleSpace); for (int i = 0; i < dataArray.size(); i++) @@ -207,7 +186,7 @@ void Surface3DRenderer::updateData() } if (sampleSpace.width() >= 2 && sampleSpace.height() >= 2) { - if (dimensionChanged) { + if (dimensionsChanged) { dataArray.reserve(sampleSpace.height()); for (int i = 0; i < sampleSpace.height(); i++) dataArray << new QSurfaceDataRow(sampleSpace.width()); @@ -219,13 +198,13 @@ void Surface3DRenderer::updateData() } } - if (dataArray.size() > 0 && (cache->objectDirty() || dimensionChanged)) { - checkFlatSupport(cache); - updateObjects(cache, dimensionChanged); - cache->setObjectDirty(false); - cache->setFlatStatusDirty(false); - } + checkFlatSupport(cache); + updateObjects(cache, dimensionsChanged); + cache->setFlatStatusDirty(false); + } else { + cache->surfaceObject()->clear(); } + cache->setDataDirty(false); } } @@ -235,31 +214,22 @@ void Surface3DRenderer::updateData() updateSelectedPoint(m_selectedPoint, m_selectedSeries); } -void Surface3DRenderer::updateSeries(const QList<QAbstract3DSeries *> &seriesList, - bool updateVisibility) +void Surface3DRenderer::updateSeries(const QList<QAbstract3DSeries *> &seriesList) { - Q_UNUSED(updateVisibility); - - foreach (SurfaceSeriesRenderCache *cache, m_renderCacheList) - cache->setValid(false); + Abstract3DRenderer::updateSeries(seriesList); + bool noSelection = true; foreach (QAbstract3DSeries *series, seriesList) { - // Item selection label may need update - if (series->d_ptr->m_changeTracker.nameChanged - || series->d_ptr->m_changeTracker.itemLabelFormatChanged) { - m_selectionLabelDirty = true; - } - QSurface3DSeries *surfaceSeries = static_cast<QSurface3DSeries *>(series); - SurfaceSeriesRenderCache *cache = m_renderCacheList.value(surfaceSeries); - if (!cache) { - cache = new SurfaceSeriesRenderCache; - m_renderCacheList[surfaceSeries] = cache; - - m_selectionTexturesDirty = true; + SurfaceSeriesRenderCache *cache = + static_cast<SurfaceSeriesRenderCache *>( m_renderCacheList.value(series)); + if (noSelection + && surfaceSeries->selectedPoint() != QSurface3DSeries::invalidSelectionPosition()) { + if (selectionLabel() != cache->itemLabel()) + m_selectionLabelDirty = true; + noSelection = false; } - cache->setValid(true); - cache->populate(surfaceSeries, this); + if (cache->isFlatStatusDirty() && cache->sampleSpace().width()) { checkFlatSupport(cache); updateObjects(cache, true); @@ -267,24 +237,16 @@ void Surface3DRenderer::updateSeries(const QList<QAbstract3DSeries *> &seriesLis } } - // Remove non-valid objects from the cache list - foreach (SurfaceSeriesRenderCache *cache, m_renderCacheList) { - if (!cache->isValid()) { - if (cache->series() == m_selectedSeries) - updateSelectedPoint(Surface3DController::invalidSelectionPosition(), 0); - - m_renderCacheList.remove(cache->series()); - cache->cleanup(m_textureHelper); - delete cache; - - m_selectionTexturesDirty = true; - } + if (noSelection && !selectionLabel().isEmpty()) { + m_selectionLabelDirty = true; + updateSelectedPoint(Surface3DController::invalidSelectionPosition(), 0); } // Selection pointer issues if (m_selectedSeries) { - foreach (SurfaceSeriesRenderCache *cache, m_renderCacheList) { - QVector3D highlightColor = + foreach (SeriesRenderCache *baseCache, m_renderCacheList) { + SurfaceSeriesRenderCache *cache = static_cast<SurfaceSeriesRenderCache *>(baseCache); + QVector4D highlightColor = Utils::vectorFromColor(cache->series()->singleHighlightColor()); SelectionPointer *slicePointer = cache->sliceSelectionPointer(); if (slicePointer) { @@ -302,19 +264,23 @@ void Surface3DRenderer::updateSeries(const QList<QAbstract3DSeries *> &seriesLis } } -void Surface3DRenderer::modifiedSeriesList(const QVector<QSurface3DSeries *> &seriesList) +SeriesRenderCache *Surface3DRenderer::createNewCache(QAbstract3DSeries *series) { - foreach (QSurface3DSeries *series, seriesList) { - SurfaceSeriesRenderCache *cache = m_renderCacheList.value(series); - if (cache) - cache->setObjectDirty(true); - } + m_selectionTexturesDirty = true; + return new SurfaceSeriesRenderCache(series, this); +} + +void Surface3DRenderer::cleanCache(SeriesRenderCache *cache) +{ + Abstract3DRenderer::cleanCache(cache); + m_selectionTexturesDirty = true; } void Surface3DRenderer::updateRows(const QVector<Surface3DController::ChangeRow> &rows) { foreach (Surface3DController::ChangeRow item, rows) { - SurfaceSeriesRenderCache *cache = m_renderCacheList.value(item.series); + SurfaceSeriesRenderCache *cache = + static_cast<SurfaceSeriesRenderCache *>(m_renderCacheList.value(item.series)); QSurfaceDataArray &dstArray = cache->dataArray(); const QRect &sampleSpace = cache->sampleSpace(); @@ -335,15 +301,10 @@ void Surface3DRenderer::updateRows(const QVector<Surface3DController::ChangeRow> srcArray->at(row)->at(j + sampleSpace.x()); } - if (cache->isFlatShadingEnabled()) { - cache->surfaceObject()->updateCoarseRow(dstArray, row - sampleSpace.y(), - m_heightNormalizer, - m_axisCacheY.min()); - } else { - cache->surfaceObject()->updateSmoothRow(dstArray, row - sampleSpace.y(), - m_heightNormalizer, - m_axisCacheY.min()); - } + if (cache->isFlatShadingEnabled()) + cache->surfaceObject()->updateCoarseRow(dstArray, row - sampleSpace.y()); + else + cache->surfaceObject()->updateSmoothRow(dstArray, row - sampleSpace.y()); } if (updateBuffers) cache->surfaceObject()->uploadBuffers(); @@ -353,10 +314,11 @@ void Surface3DRenderer::updateRows(const QVector<Surface3DController::ChangeRow> updateSelectedPoint(m_selectedPoint, m_selectedSeries); } -void Surface3DRenderer::updateItem(const QVector<Surface3DController::ChangeItem> &points) +void Surface3DRenderer::updateItems(const QVector<Surface3DController::ChangeItem> &points) { foreach (Surface3DController::ChangeItem item, points) { - SurfaceSeriesRenderCache *cache = m_renderCacheList.value(item.series); + SurfaceSeriesRenderCache *cache = + static_cast<SurfaceSeriesRenderCache *>(m_renderCacheList.value(item.series)); QSurfaceDataArray &dstArray = cache->dataArray(); const QRect &sampleSpace = cache->sampleSpace(); @@ -370,22 +332,20 @@ void Surface3DRenderer::updateItem(const QVector<Surface3DController::ChangeItem int sampleSpaceTop = sampleSpace.y() + sampleSpace.height(); int sampleSpaceRight = sampleSpace.x() + sampleSpace.width(); bool updateBuffers = false; + // Note: Point is (row, column), samplespace is (columns x rows) QPoint point = item.point; - if (point.y() <= sampleSpaceTop && point.y() >= sampleSpace.y() && - point.x() <= sampleSpaceRight && point.x() >= sampleSpace.x()) { + if (point.x() <= sampleSpaceTop && point.x() >= sampleSpace.y() && + point.y() <= sampleSpaceRight && point.y() >= sampleSpace.x()) { updateBuffers = true; - int x = point.x() - sampleSpace.x(); - int y = point.y() - sampleSpace.y(); - (*(dstArray.at(y)))[x] = srcArray->at(point.y())->at(point.x()); - - if (cache->isFlatShadingEnabled()) { - cache->surfaceObject()->updateCoarseItem(dstArray, y, x, m_heightNormalizer, - m_axisCacheY.min()); - } else { - cache->surfaceObject()->updateSmoothItem(dstArray, y, x, m_heightNormalizer, - m_axisCacheY.min()); - } + int x = point.y() - sampleSpace.x(); + int y = point.x() - sampleSpace.y(); + (*(dstArray.at(y)))[x] = srcArray->at(point.x())->at(point.y()); + + if (cache->isFlatShadingEnabled()) + cache->surfaceObject()->updateCoarseItem(dstArray, y, x); + else + cache->surfaceObject()->updateSmoothItem(dstArray, y, x); } if (updateBuffers) cache->surfaceObject()->uploadBuffers(); @@ -396,31 +356,22 @@ void Surface3DRenderer::updateItem(const QVector<Surface3DController::ChangeItem updateSelectedPoint(m_selectedPoint, m_selectedSeries); } -void Surface3DRenderer::updateAxisRange(QAbstract3DAxis::AxisOrientation orientation, float min, - float max) -{ - Abstract3DRenderer::updateAxisRange(orientation, min, max); - - if (orientation == QAbstract3DAxis::AxisOrientationY) { - foreach (SurfaceSeriesRenderCache *cache, m_renderCacheList) - cache->setObjectDirty(true); - } -} - void Surface3DRenderer::updateSliceDataModel(const QPoint &point) { - foreach (SurfaceSeriesRenderCache *cache, m_renderCacheList) - cache->sliceSurfaceObject()->clear(); + foreach (SeriesRenderCache *baseCache, m_renderCacheList) + static_cast<SurfaceSeriesRenderCache *>(baseCache)->sliceSurfaceObject()->clear(); if (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionMultiSeries)) { // Find axis coordinates for the selected point - SurfaceSeriesRenderCache *selectedCache = - m_renderCacheList.value(const_cast<QSurface3DSeries *>(m_selectedSeries)); - QSurfaceDataArray &dataArray = selectedCache->dataArray(); + SeriesRenderCache *selectedCache = + m_renderCacheList.value(const_cast<QSurface3DSeries *>(m_selectedSeries)); + QSurfaceDataArray &dataArray = + static_cast<SurfaceSeriesRenderCache *>(selectedCache)->dataArray(); QSurfaceDataItem item = dataArray.at(point.x())->at(point.y()); QPointF coords(item.x(), item.z()); - foreach (SurfaceSeriesRenderCache *cache, m_renderCacheList) { + foreach (SeriesRenderCache *baseCache, m_renderCacheList) { + SurfaceSeriesRenderCache *cache = static_cast<SurfaceSeriesRenderCache *>(baseCache); if (cache->series() != m_selectedSeries) { QPoint mappedPoint = mapCoordsToSampleSpace(cache, coords); updateSliceObject(cache, mappedPoint); @@ -431,9 +382,10 @@ void Surface3DRenderer::updateSliceDataModel(const QPoint &point) } else { if (m_selectedSeries) { SurfaceSeriesRenderCache *cache = - m_renderCacheList.value(const_cast<QSurface3DSeries *>(m_selectedSeries)); + static_cast<SurfaceSeriesRenderCache *>( + m_renderCacheList.value(m_selectedSeries)); if (cache) - updateSliceObject(cache, point); + updateSliceObject(static_cast<SurfaceSeriesRenderCache *>(cache), point); } } } @@ -536,7 +488,7 @@ void Surface3DRenderer::updateSliceObject(SurfaceSeriesRenderCache *cache, const int row = point.x(); if ((m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionRow) && row == -1) || - (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionColumn) && column == -1)) { + (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionColumn) && column == -1)) { cache->sliceSurfaceObject()->clear(); return; } @@ -550,19 +502,27 @@ void Surface3DRenderer::updateSliceObject(SurfaceSeriesRenderCache *cache, const QSurfaceDataRow *sliceRow; QSurfaceDataArray &dataArray = cache->dataArray(); float adjust = (0.025f * m_heightNormalizer) / 2.0f; - float stepDown = 2.0f * adjust; + float doubleAdjust = 2.0f * adjust; + bool flipZX = false; + float zBack; + float zFront; if (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionRow)) { QSurfaceDataRow *src = dataArray.at(row); sliceRow = new QSurfaceDataRow(src->size()); + zBack = m_axisCacheZ.min(); + zFront = m_axisCacheZ.max(); for (int i = 0; i < sliceRow->size(); i++) - (*sliceRow)[i].setPosition(QVector3D(src->at(i).x(), src->at(i).y() + adjust, -1.0f)); + (*sliceRow)[i].setPosition(QVector3D(src->at(i).x(), src->at(i).y() + adjust, zFront)); } else { + flipZX = true; const QRect &sampleSpace = cache->sampleSpace(); sliceRow = new QSurfaceDataRow(sampleSpace.height()); + zBack = m_axisCacheX.min(); + zFront = m_axisCacheX.max(); for (int i = 0; i < sampleSpace.height(); i++) { (*sliceRow)[i].setPosition(QVector3D(dataArray.at(i)->at(column).z(), dataArray.at(i)->at(column).y() + adjust, - -1.0f)); + zFront)); } } sliceDataArray << sliceRow; @@ -571,114 +531,134 @@ void Surface3DRenderer::updateSliceObject(SurfaceSeriesRenderCache *cache, const QSurfaceDataRow *duplicateRow = new QSurfaceDataRow(*sliceRow); for (int i = 0; i < sliceRow->size(); i++) { (*sliceRow)[i].setPosition(QVector3D(sliceRow->at(i).x(), - sliceRow->at(i).y() - stepDown, - 1.0f)); + sliceRow->at(i).y() - doubleAdjust, + zBack)); } sliceDataArray << duplicateRow; QRect sliceRect(0, 0, sliceRow->size(), 2); if (sliceRow->size() > 0) { - if (cache->isFlatShadingEnabled()) { - cache->sliceSurfaceObject()->setUpData(sliceDataArray, sliceRect, - m_heightNormalizer, - m_axisCacheY.min(), true); + if (cache->isFlatShadingEnabled()) + cache->sliceSurfaceObject()->setUpData(sliceDataArray, sliceRect, true, flipZX); + else + cache->sliceSurfaceObject()->setUpSmoothData(sliceDataArray, sliceRect, true, flipZX); + } +} + +inline static float getDataValue(const QSurfaceDataArray &array, bool searchRow, int index) +{ + if (searchRow) + return array.at(0)->at(index).x(); + else + return array.at(index)->at(0).z(); +} + +inline static int binarySearchArray(const QSurfaceDataArray &array, int maxIdx, float limitValue, + bool searchRow, bool lowBound, bool ascending) +{ + int min = 0; + int max = maxIdx; + int mid = 0; + int retVal; + while (max >= min) { + mid = (min + max) / 2; + float arrayValue = getDataValue(array, searchRow, mid); + if (arrayValue == limitValue) + return mid; + if (ascending) { + if (arrayValue < limitValue) + min = mid + 1; + else + max = mid - 1; } else { - cache->sliceSurfaceObject()->setUpSmoothData(sliceDataArray, sliceRect, - m_heightNormalizer, - m_axisCacheY.min(), true); + if (arrayValue > limitValue) + min = mid + 1; + else + max = mid - 1; } } + + // Exact match not found, return closest depending on bound. + // The boundary is between last mid and min/max. + if (lowBound == ascending) { + if (mid > max) + retVal = mid; + else + retVal = min; + } else { + if (mid > max) + retVal = max; + else + retVal = mid; + } + if (retVal < 0 || retVal > maxIdx) { + retVal = -1; + } else if (lowBound) { + if (getDataValue(array, searchRow, retVal) < limitValue) + retVal = -1; + } else { + if (getDataValue(array, searchRow, retVal) > limitValue) + retVal = -1; + } + return retVal; } -QRect Surface3DRenderer::calculateSampleRect(SurfaceSeriesRenderCache *cache, - const QSurfaceDataArray &array) +QRect Surface3DRenderer::calculateSampleRect(const QSurfaceDataArray &array) { QRect sampleSpace; - int rowCount = array.size(); - int columnCount = array.at(0)->size(); + const int maxRow = array.size() - 1; + const int maxColumn = array.at(0)->size() - 1; - int i; - bool found; - float axisMinX = m_axisCacheX.min(); - float axisMaxX = m_axisCacheX.max(); - float axisMinZ = m_axisCacheZ.min(); - float axisMaxZ = m_axisCacheZ.max(); + // We assume data is ordered sequentially in rows for X-value and in columns for Z-value. + // Determine if data is ascending or descending in each case. + const bool ascendingX = array.at(0)->at(0).x() < array.at(0)->at(maxColumn).x(); + const bool ascendingZ = array.at(0)->at(0).z() < array.at(maxRow)->at(0).z(); - // m_minVisibleColumnValue - for (i = 0, found = false; i < columnCount; i++) { - if (array.at(0)->at(i).x() >= axisMinX) { - found = true; - break; - } - } - if (found) { - m_minVisibleColumnValue = array.at(0)->at(i).x(); - sampleSpace.setLeft(i); + int idx = binarySearchArray(array, maxColumn, m_axisCacheX.min(), true, true, ascendingX); + if (idx != -1) { + if (ascendingX) + sampleSpace.setLeft(idx); + else + sampleSpace.setRight(idx); } else { sampleSpace.setWidth(-1); // to indicate nothing needs to be shown return sampleSpace; } - // m_maxVisibleColumnValue - for (i = columnCount - 1, found = false; i >= 0; i--) { - if (array.at(0)->at(i).x() <= axisMaxX) { - found = true; - break; - } - } - if (found) { - m_maxVisibleColumnValue = array.at(0)->at(i).x(); - sampleSpace.setRight(i); + idx = binarySearchArray(array, maxColumn, m_axisCacheX.max(), true, false, ascendingX); + if (idx != -1) { + if (ascendingX) + sampleSpace.setRight(idx); + else + sampleSpace.setLeft(idx); } else { sampleSpace.setWidth(-1); // to indicate nothing needs to be shown return sampleSpace; } - // m_minVisibleRowValue - for (i = 0, found = false; i < rowCount; i++) { - if (array.at(i)->at(0).z() >= axisMinZ) { - found = true; - break; - } - } - if (found) { - m_minVisibleRowValue = array.at(i)->at(0).z(); - sampleSpace.setTop(i); + idx = binarySearchArray(array, maxRow, m_axisCacheZ.min(), false, true, ascendingZ); + if (idx != -1) { + if (ascendingZ) + sampleSpace.setTop(idx); + else + sampleSpace.setBottom(idx); } else { sampleSpace.setWidth(-1); // to indicate nothing needs to be shown return sampleSpace; } - // m_maxVisibleRowValue - for (i = rowCount - 1, found = false; i >= 0; i--) { - if (array.at(i)->at(0).z() <= axisMaxZ) { - found = true; - break; - } - } - if (found) { - m_maxVisibleRowValue = array.at(i)->at(0).z(); - sampleSpace.setBottom(i); + idx = binarySearchArray(array, maxRow, m_axisCacheZ.max(), false, false, ascendingZ); + if (idx != -1) { + if (ascendingZ) + sampleSpace.setBottom(idx); + else + sampleSpace.setTop(idx); } else { sampleSpace.setWidth(-1); // to indicate nothing needs to be shown return sampleSpace; } - m_visibleColumnRange = m_maxVisibleColumnValue - m_minVisibleColumnValue; - m_visibleRowRange = m_maxVisibleRowValue - m_minVisibleRowValue; - GLfloat surfaceScaleX = m_scaleX * m_visibleColumnRange / m_areaSize.width(); - GLfloat surfaceScaleZ = m_scaleZ * m_visibleRowRange / m_areaSize.height(); - GLfloat axis2XCenterX = axisMinX + axisMaxX; - GLfloat axis2XCenterZ = axisMinZ + axisMaxZ; - GLfloat data2XCenterX = GLfloat(m_minVisibleColumnValue + m_maxVisibleColumnValue); - GLfloat data2XCenterZ = GLfloat(m_minVisibleRowValue + m_maxVisibleRowValue); - GLfloat surfaceOffsetX = m_scaleX * (data2XCenterX - axis2XCenterX) / m_areaSize.width(); - GLfloat surfaceOffsetZ = -m_scaleZ * (data2XCenterZ - axis2XCenterZ) / m_areaSize.height(); - - cache->setScale(QVector3D(surfaceScaleX, 1.0f, surfaceScaleZ)); - cache->setOffset(QVector3D(surfaceOffsetX, 0.0f, surfaceOffsetZ)); - return sampleSpace; } @@ -708,6 +688,13 @@ void Surface3DRenderer::render(GLuint defaultFboHandle) // Handle GL state setup for FBO buffers and clearing of the render surface Abstract3DRenderer::render(defaultFboHandle); + if (m_axisCacheX.positionsDirty()) + m_axisCacheX.updateAllPositions(); + if (m_axisCacheY.positionsDirty()) + m_axisCacheY.updateAllPositions(); + if (m_axisCacheZ.positionsDirty()) + m_axisCacheZ.updateAllPositions(); + drawScene(defaultFboHandle); if (m_cachedIsSlicingActivated) drawSlicedScene(); @@ -715,13 +702,14 @@ void Surface3DRenderer::render(GLuint defaultFboHandle) // Render selection ball if (m_selectionActive && m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionItem)) { - foreach (SurfaceSeriesRenderCache *cache, m_renderCacheList) { + foreach (SeriesRenderCache *baseCache, m_renderCacheList) { + SurfaceSeriesRenderCache *cache = static_cast<SurfaceSeriesRenderCache *>(baseCache); if (cache->slicePointerActive() && cache->renderable() && m_cachedIsSlicingActivated ) { cache->sliceSelectionPointer()->render(defaultFboHandle); } if (cache->mainPointerActive() && cache->renderable()) - cache->mainSelectionPointer()->render(defaultFboHandle); + cache->mainSelectionPointer()->render(defaultFboHandle, m_useOrthoProjection); } } } @@ -730,7 +718,7 @@ void Surface3DRenderer::drawSlicedScene() { QVector3D lightPos; - QVector3D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor()); + QVector4D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor()); // Specify viewport glViewport(m_secondarySubViewport.x(), @@ -760,13 +748,18 @@ void Surface3DRenderer::drawSlicedScene() const Q3DCamera *activeCamera = m_cachedScene->activeCamera(); bool rowMode = m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionRow); + AxisRenderCache &sliceCache = rowMode ? m_axisCacheX : m_axisCacheZ; GLfloat scaleXBackground = 0.0f; - if (m_renderCacheList.size()) { + // Disable culling to avoid ugly conditionals with reversed axes and data + glDisable(GL_CULL_FACE); + + if (!m_renderCacheList.isEmpty()) { bool drawGrid = false; - foreach (SurfaceSeriesRenderCache *cache, m_renderCacheList) { + foreach (SeriesRenderCache *baseCache, m_renderCacheList) { + SurfaceSeriesRenderCache *cache = static_cast<SurfaceSeriesRenderCache *>(baseCache); if (cache->sliceSurfaceObject()->indexCount() && cache->renderable()) { if (!drawGrid && cache->surfaceGridVisible()) { glEnable(GL_POLYGON_OFFSET_FILL); @@ -774,24 +767,16 @@ void Surface3DRenderer::drawSlicedScene() drawGrid = true; } - GLfloat scaleX = 0.0f; - GLfloat offset = 0.0f; - if (rowMode) { - scaleX = cache->scale().x(); + if (rowMode) scaleXBackground = m_scaleXWithBackground; - offset = cache->offset().x(); - } else { - scaleX = cache->scale().z(); + else scaleXBackground = m_scaleZWithBackground; - offset = -cache->offset().z(); - } QMatrix4x4 MVPMatrix; QMatrix4x4 modelMatrix; QMatrix4x4 itModelMatrix; - modelMatrix.translate(offset, 0.0f, 0.0f); - QVector3D scaling(scaleX, 1.0f, sliceZScale); + QVector3D scaling(1.0f, 1.0f, sliceZScale); modelMatrix.scale(scaling); itModelMatrix.scale(scaling); @@ -832,10 +817,13 @@ void Surface3DRenderer::drawSlicedScene() m_surfaceGridShader->bind(); m_surfaceGridShader->setUniformValue(m_surfaceGridShader->color(), Utils::vectorFromColor(m_cachedTheme->gridLineColor())); - foreach (SurfaceSeriesRenderCache *cache, m_renderCacheList) { - if (cache->sliceSurfaceObject()->indexCount() && cache->isSeriesVisible() && + foreach (SeriesRenderCache *baseCache, m_renderCacheList) { + SurfaceSeriesRenderCache *cache = + static_cast<SurfaceSeriesRenderCache *>(baseCache); + if (cache->sliceSurfaceObject()->indexCount() && cache->isVisible() && cache->surfaceGridVisible()) { - m_surfaceGridShader->setUniformValue(m_surfaceGridShader->MVP(), cache->MVPMatrix()); + m_surfaceGridShader->setUniformValue(m_surfaceGridShader->MVP(), + cache->MVPMatrix()); m_drawer->drawSurfaceGrid(m_surfaceGridShader, cache->sliceSurfaceObject()); } } @@ -845,14 +833,22 @@ 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 + // Bind line shader lineShader->bind(); // Set unchanging shader bindings - QVector3D lineColor = Utils::vectorFromColor(m_cachedTheme->gridLineColor()); + QVector4D lineColor = Utils::vectorFromColor(m_cachedTheme->gridLineColor()); lineShader->setUniformValue(lineShader->lightP(), lightPos); lineShader->setUniformValue(lineShader->view(), viewMatrix); lineShader->setUniformValue(lineShader->color(), lineColor); @@ -862,19 +858,16 @@ void Surface3DRenderer::drawSlicedScene() lineShader->setUniformValue(lineShader->lightColor(), lightColor); // Horizontal lines + int gridLineCount = m_axisCacheY.gridLineCount(); if (m_axisCacheY.segmentCount() > 0) { QVector3D gridLineScaleX(scaleXBackground, gridLineWidth, gridLineWidth); - GLfloat lineStep = 2.0f * m_axisCacheY.subSegmentStep() / m_heightNormalizer; - GLfloat linePos = -1.0f; - int lastSegment = m_axisCacheY.subSegmentCount() * m_axisCacheY.segmentCount(); - - for (int segment = 0; segment <= lastSegment; segment++) { + for (int line = 0; line < gridLineCount; line++) { QMatrix4x4 modelMatrix; QMatrix4x4 MVPMatrix; QMatrix4x4 itModelMatrix; - modelMatrix.translate(0.0f, linePos, -sliceZScale); + modelMatrix.translate(0.0f, m_axisCacheY.gridLinePosition(line), -1.0f); modelMatrix.scale(gridLineScaleX); itModelMatrix.scale(gridLineScaleX); @@ -888,36 +881,30 @@ void Surface3DRenderer::drawSlicedScene() lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); // Draw the object +#if !(defined QT_OPENGL_ES_2) m_drawer->drawObject(lineShader, m_gridLineObj); - - linePos += lineStep; +#else + m_drawer->drawLine(lineShader); +#endif } } // Vertical lines QVector3D gridLineScaleY(gridLineWidth, backgroundMargin, gridLineWidth); - int lastSegment; - GLfloat lineStep; - GLfloat linePos; - if (rowMode) { - lineStep = -2.0f * aspectRatio * m_axisCacheX.subSegmentStep() / m_scaleFactor; - lastSegment = m_axisCacheX.subSegmentCount() * m_axisCacheX.segmentCount(); - linePos = m_scaleX; - } else { - lineStep = -2.0f * aspectRatio * m_axisCacheZ.subSegmentStep() / m_scaleFactor; - lastSegment = m_axisCacheZ.subSegmentCount() * m_axisCacheZ.segmentCount(); - linePos = m_scaleZ; - } - - for (int segment = 0; segment <= lastSegment; segment++) { + gridLineCount = sliceCache.gridLineCount(); + for (int line = 0; line < gridLineCount; line++) { QMatrix4x4 modelMatrix; QMatrix4x4 MVPMatrix; QMatrix4x4 itModelMatrix; - modelMatrix.translate(linePos, 0.0f, -sliceZScale); + 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 MVPMatrix = projectionViewMatrix * modelMatrix; @@ -928,9 +915,11 @@ void Surface3DRenderer::drawSlicedScene() lineShader->setUniformValue(lineShader->MVP(), MVPMatrix); // Draw the object +#if !(defined QT_OPENGL_ES_2) m_drawer->drawObject(lineShader, m_gridLineObj); - - linePos += lineStep; +#else + m_drawer->drawLine(lineShader); +#endif } } @@ -938,96 +927,76 @@ void Surface3DRenderer::drawSlicedScene() m_labelShader->bind(); glEnable(GL_TEXTURE_2D); glDisable(GL_DEPTH_TEST); - glCullFace(GL_BACK); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Y Labels to back wall - GLfloat posStep = 2.0f * m_axisCacheY.segmentStep() / m_heightNormalizer; - GLfloat labelPos = -1.0f; int labelNbr = 0; QVector3D positionComp(0.0f, 0.0f, 0.0f); - QVector3D rotation(0.0f, 0.0f, 0.0f); - QVector3D labelTrans = QVector3D(scaleXBackground + labelMargin, labelPos, 0.0f); - for (int segment = 0; segment <= m_axisCacheY.segmentCount(); segment++) { + QVector3D labelTrans = QVector3D(scaleXBackground + labelMargin, 0.0f, 0.0f); + int labelCount = m_axisCacheY.labelCount(); + for (int label = 0; label < labelCount; label++) { if (m_axisCacheY.labelItems().size() > labelNbr) { - labelTrans.setY(labelPos); + labelTrans.setY(m_axisCacheY.labelPosition(label)); const LabelItem &axisLabelItem = *m_axisCacheY.labelItems().at(labelNbr); // Draw the label here m_dummyRenderItem.setTranslation(labelTrans); m_drawer->drawLabel(m_dummyRenderItem, axisLabelItem, viewMatrix, projectionMatrix, - positionComp, rotation, 0, m_cachedSelectionMode, m_labelShader, - m_labelObj, activeCamera, - true, true, Drawer::LabelMid, Qt::AlignRight, true); + positionComp, identityQuaternion, 0, m_cachedSelectionMode, + m_labelShader, m_labelObj, activeCamera, true, true, + Drawer::LabelMid, Qt::AlignLeft, true); } labelNbr++; - labelPos += posStep; } // X Labels to ground - int countLabelItems; - int lastSegment; - if (rowMode) { - posStep = 2.0f * aspectRatio * m_axisCacheX.segmentStep() / m_scaleFactor; - labelPos = -m_scaleX; - lastSegment = m_axisCacheX.segmentCount(); - countLabelItems = m_axisCacheX.labelItems().size(); - } else { - posStep = 2.0f * aspectRatio * m_axisCacheZ.segmentStep() / m_scaleFactor; - labelPos = -m_scaleZ; - lastSegment = m_axisCacheZ.segmentCount(); - countLabelItems = m_axisCacheZ.labelItems().size(); - } + int countLabelItems = sliceCache.labelItems().size(); + + QVector3D rotation(0.0f, 0.0f, -45.0f); + QQuaternion totalRotation = Utils::calculateRotation(rotation); labelNbr = 0; positionComp.setY(-0.1f); - rotation.setZ(-45.0f); labelTrans.setY(-backgroundMargin); - for (int segment = 0; segment <= lastSegment; segment++) { + labelCount = sliceCache.labelCount(); + for (int label = 0; label < labelCount; label++) { if (countLabelItems > labelNbr) { // Draw the label here - labelTrans.setX(labelPos); + labelTrans.setX(sliceCache.labelPosition(label)); m_dummyRenderItem.setTranslation(labelTrans); LabelItem *axisLabelItem; - if (rowMode) - axisLabelItem = m_axisCacheX.labelItems().at(labelNbr); - else - axisLabelItem = m_axisCacheZ.labelItems().at(labelNbr); + axisLabelItem = sliceCache.labelItems().at(labelNbr); m_drawer->drawLabel(m_dummyRenderItem, *axisLabelItem, viewMatrix, projectionMatrix, - positionComp, rotation, 0, QAbstract3DGraph::SelectionRow, + positionComp, totalRotation, 0, QAbstract3DGraph::SelectionRow, m_labelShader, m_labelObj, activeCamera, - false, false, Drawer::LabelBelow, Qt::AlignBottom, true); + false, false, Drawer::LabelBelow, + Qt::AlignmentFlag(Qt::AlignLeft | Qt::AlignTop), true); } labelNbr++; - labelPos += posStep; } // Draw labels for axes AbstractRenderItem *dummyItem(0); positionComp.setY(m_autoScaleAdjustment); - if (rowMode) { - m_drawer->drawLabel(*dummyItem, m_axisCacheX.titleItem(), viewMatrix, projectionMatrix, - positionComp, zeroVector, 0, m_cachedSelectionMode, m_labelShader, - m_labelObj, activeCamera, false, false, Drawer::LabelBottom, - Qt::AlignCenter, true); - } else { - m_drawer->drawLabel(*dummyItem, m_axisCacheZ.titleItem(), viewMatrix, projectionMatrix, - positionComp, zeroVector, 0, m_cachedSelectionMode, m_labelShader, - m_labelObj, activeCamera, false, false, Drawer::LabelBottom, - Qt::AlignCenter, true); - } + m_drawer->drawLabel(*dummyItem, sliceCache.titleItem(), viewMatrix, projectionMatrix, + positionComp, identityQuaternion, 0, m_cachedSelectionMode, m_labelShader, + m_labelObj, activeCamera, false, false, Drawer::LabelBottom, + Qt::AlignCenter, true); + // Y-axis label + rotation = QVector3D(0.0f, 0.0f, 90.0f); + totalRotation = Utils::calculateRotation(rotation); labelTrans = QVector3D(-scaleXBackground - labelMargin, 0.0f, 0.0f); m_dummyRenderItem.setTranslation(labelTrans); m_drawer->drawLabel(m_dummyRenderItem, m_axisCacheY.titleItem(), viewMatrix, - projectionMatrix, zeroVector, QVector3D(0.0f, 0.0f, 90.0f), 0, + projectionMatrix, zeroVector, totalRotation, 0, m_cachedSelectionMode, m_labelShader, m_labelObj, activeCamera, - false, false, Drawer::LabelMid, Qt::AlignHCenter); + false, false, Drawer::LabelMid, Qt::AlignBottom); glDisable(GL_TEXTURE_2D); glEnable(GL_DEPTH_TEST); @@ -1042,7 +1011,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) bool noShadows = true; GLfloat backgroundRotation = 0; - QVector3D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor()); + QVector4D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor()); glViewport(m_primarySubViewport.x(), m_primarySubViewport.y(), @@ -1051,8 +1020,16 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) // Set up projection matrix QMatrix4x4 projectionMatrix; - projectionMatrix.perspective(45.0f, (GLfloat)m_primarySubViewport.width() - / (GLfloat)m_primarySubViewport.height(), 0.1f, 100.0f); + GLfloat viewPortRatio = (GLfloat)m_primarySubViewport.width() + / (GLfloat)m_primarySubViewport.height(); + if (m_useOrthoProjection) { + GLfloat orthoRatio = 2.0f; + projectionMatrix.ortho(-viewPortRatio * orthoRatio, viewPortRatio * orthoRatio, + -orthoRatio, orthoRatio, + 0.0f, 100.0f); + } else { + projectionMatrix.perspective(45.0f, viewPortRatio, 0.1f, 100.0f); + } const Q3DCamera *activeCamera = m_cachedScene->activeCamera(); @@ -1089,8 +1066,9 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) // Draw depth buffer #if !defined(QT_OPENGL_ES_2) - GLfloat adjustedLightStrength = m_cachedTheme->lightStrength() / 10.0f; - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && m_renderCacheList.size()) { + GLfloat adjustedLightStrength = m_cachedTheme->lightStrength() / 10.0f; + if (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 glBindFramebuffer(GL_FRAMEBUFFER, m_depthFrameBuffer); @@ -1121,15 +1099,14 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) glDisable(GL_CULL_FACE); - foreach (SurfaceSeriesRenderCache *cache, m_renderCacheList) { + foreach (SeriesRenderCache *baseCache, m_renderCacheList) { + SurfaceSeriesRenderCache *cache = static_cast<SurfaceSeriesRenderCache *>(baseCache); SurfaceObject *object = cache->surfaceObject(); - if (object->indexCount() && cache->surfaceVisible() && cache->isSeriesVisible() + if (object->indexCount() && cache->surfaceVisible() && cache->isVisible() && cache->sampleSpace().width() >= 2 && cache->sampleSpace().height() >= 2) { QMatrix4x4 modelMatrix; QMatrix4x4 MVPMatrix; - modelMatrix.translate(cache->offset()); - modelMatrix.scale(cache->scale()); MVPMatrix = depthProjectionViewMatrix * modelMatrix; cache->setMVPMatrix(MVPMatrix); m_depthShader->setUniformValue(m_depthShader->MVP(), MVPMatrix); @@ -1149,16 +1126,18 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) } } - 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 (SurfaceSeriesRenderCache *cache, m_renderCacheList) { + foreach (SeriesRenderCache *baseCache, m_renderCacheList) { + SurfaceSeriesRenderCache *cache = static_cast<SurfaceSeriesRenderCache *>(baseCache); SurfaceObject *object = cache->surfaceObject(); - if (object->indexCount() && cache->surfaceVisible() && cache->isSeriesVisible() + if (object->indexCount() && cache->surfaceVisible() && cache->isVisible() && cache->sampleSpace().width() >= 2 && cache->sampleSpace().height() >= 2) { m_depthShader->setUniformValue(m_depthShader->MVP(), cache->MVPMatrix()); @@ -1183,6 +1162,10 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) glDisableVertexAttribArray(m_depthShader->posAtt()); + Abstract3DRenderer::drawCustomItems(RenderingDepth, m_depthShader, viewMatrix, + projectionViewMatrix, depthProjectionViewMatrix, + m_depthTexture, m_shadowQualityToShader); + // Disable drawing to depth framebuffer (= enable drawing to screen) glBindFramebuffer(GL_FRAMEBUFFER, defaultFboHandle); @@ -1201,7 +1184,9 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) glEnable(GL_TEXTURE_2D); // Draw selection buffer - if (!m_cachedIsSlicingActivated && m_renderCacheList.size() && m_selectionState == SelectOnScene + if (!m_cachedIsSlicingActivated && (!m_renderCacheList.isEmpty() + || !m_customRenderCache.isEmpty()) + && m_selectionState == SelectOnScene && m_cachedSelectionMode > QAbstract3DGraph::SelectionNone) { m_selectionShader->bind(); glBindFramebuffer(GL_FRAMEBUFFER, m_selectionFrameBuffer); @@ -1217,14 +1202,12 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) glDisable(GL_CULL_FACE); - foreach (SurfaceSeriesRenderCache *cache, m_renderCacheList) { + foreach (SeriesRenderCache *baseCache, m_renderCacheList) { + SurfaceSeriesRenderCache *cache = static_cast<SurfaceSeriesRenderCache *>(baseCache); if (cache->surfaceObject()->indexCount() && cache->renderable()) { QMatrix4x4 modelMatrix; QMatrix4x4 MVPMatrix; - modelMatrix.translate(cache->offset()); - modelMatrix.scale(cache->scale()); - MVPMatrix = projectionViewMatrix * modelMatrix; m_selectionShader->setUniformValue(m_selectionShader->MVP(), MVPMatrix); @@ -1232,21 +1215,23 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) cache->selectionTexture()); } } + m_surfaceGridShader->bind(); + Abstract3DRenderer::drawCustomItems(RenderingSelection, m_surfaceGridShader, viewMatrix, + projectionViewMatrix, depthProjectionViewMatrix, + m_depthTexture, m_shadowQualityToShader); + drawLabels(true, activeCamera, viewMatrix, projectionMatrix); glEnable(GL_DITHER); - GLubyte pixel[4] = {0, 0, 0, 0}; - glReadPixels(m_inputPosition.x(), m_viewport.height() - m_inputPosition.y(), - 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, (void *)pixel); + QVector4D clickedColor = Utils::getSelection(m_inputPosition, m_viewport.height()); glBindFramebuffer(GL_FRAMEBUFFER, defaultFboHandle); // Put the RGBA value back to uint -#if !defined(QT_OPENGL_ES_2) - uint selectionId = pixel[0] + pixel[1] * 256 + pixel[2] * 65536 + pixel[3] * 16777216; -#else - uint selectionId = pixel[0] + pixel[1] * 256 + pixel[2] * 65536; -#endif + uint selectionId = uint(clickedColor.x()) + + uint(clickedColor.y()) * greenMultiplier + + uint(clickedColor.z()) * blueMultiplier + + uint(clickedColor.w()) * alphaMultiplier; m_clickedPosition = selectionIdToSurfacePoint(selectionId); @@ -1260,21 +1245,18 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) } // Draw the surface - if (m_renderCacheList.size()) { + if (!m_renderCacheList.isEmpty()) { // For surface we can see climpses from underneath glDisable(GL_CULL_FACE); bool drawGrid = false; - foreach (SurfaceSeriesRenderCache *cache, m_renderCacheList) { + foreach (SeriesRenderCache *baseCache, m_renderCacheList) { + SurfaceSeriesRenderCache *cache = static_cast<SurfaceSeriesRenderCache *>(baseCache); QMatrix4x4 modelMatrix; QMatrix4x4 MVPMatrix; QMatrix4x4 itModelMatrix; - modelMatrix.translate(cache->offset()); - modelMatrix.scale(cache->scale()); - itModelMatrix.scale(cache->scale()); - #ifdef SHOW_DEPTH_TEXTURE_SCENE MVPMatrix = depthProjectionViewMatrix * modelMatrix; #else @@ -1283,7 +1265,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) cache->setMVPMatrix(MVPMatrix); const QRect &sampleSpace = cache->sampleSpace(); - if (cache->surfaceObject()->indexCount() && cache->isSeriesVisible() && + if (cache->surfaceObject()->indexCount() && cache->isVisible() && sampleSpace.width() >= 2 && sampleSpace.height() >= 2) { noShadows = false; if (!drawGrid && cache->surfaceGridVisible()) { @@ -1346,13 +1328,18 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) glDisable(GL_POLYGON_OFFSET_FILL); m_surfaceGridShader->bind(); m_surfaceGridShader->setUniformValue(m_surfaceGridShader->color(), - Utils::vectorFromColor(m_cachedTheme->gridLineColor())); - foreach (SurfaceSeriesRenderCache *cache, m_renderCacheList) { - m_surfaceGridShader->setUniformValue(m_surfaceGridShader->MVP(), cache->MVPMatrix()); + Utils::vectorFromColor( + m_cachedTheme->gridLineColor())); + foreach (SeriesRenderCache *baseCache, m_renderCacheList) { + SurfaceSeriesRenderCache *cache = + static_cast<SurfaceSeriesRenderCache *>(baseCache); + m_surfaceGridShader->setUniformValue(m_surfaceGridShader->MVP(), + cache->MVPMatrix()); const QRect &sampleSpace = cache->sampleSpace(); - if (cache->surfaceObject()->indexCount() && cache->surfaceGridVisible() && - cache->isSeriesVisible() && sampleSpace.width() >= 2 && sampleSpace.height() >= 2) { + if (cache->surfaceObject()->indexCount() && cache->surfaceGridVisible() + && cache->isVisible() && sampleSpace.width() >= 2 + && sampleSpace.height() >= 2) { m_drawer->drawSurfaceGrid(m_surfaceGridShader, cache->surfaceObject()); } } @@ -1388,7 +1375,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) MVPMatrix = projectionViewMatrix * modelMatrix; #endif - QVector3D backgroundColor = Utils::vectorFromColor(m_cachedTheme->backgroundColor()); + QVector4D backgroundColor = Utils::vectorFromColor(m_cachedTheme->backgroundColor()); // Set shader bindings m_backgroundShader->setUniformValue(m_backgroundShader->lightP(), lightPos); @@ -1412,7 +1399,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) m_backgroundShader->setUniformValue(m_backgroundShader->lightS(), adjustedLightStrength); // Draw the object - if (noShadows) + if (noShadows && m_customRenderCache.isEmpty()) m_drawer->drawObject(m_backgroundShader, m_backgroundObj, 0, m_noShadowTexture); else m_drawer->drawObject(m_backgroundShader, m_backgroundObj, 0, m_depthTexture); @@ -1436,13 +1423,17 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) QVector3D gridLineScaleY(gridLineWidth, backgroundMargin, 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 // Bind line shader lineShader->bind(); // Set unchanging shader bindings - QVector3D lineColor = Utils::vectorFromColor(m_cachedTheme->gridLineColor()); + QVector4D lineColor = Utils::vectorFromColor(m_cachedTheme->gridLineColor()); lineShader->setUniformValue(lineShader->lightP(), lightPos); lineShader->setUniformValue(lineShader->view(), viewMatrix); lineShader->setUniformValue(lineShader->color(), lineColor); @@ -1482,16 +1473,15 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) // Rows (= Z) if (m_axisCacheZ.segmentCount() > 0) { // Floor lines - GLfloat lineStep = 2.0f * aspectRatio * m_axisCacheZ.subSegmentStep() / m_scaleFactor; - GLfloat linePos = m_scaleZ; // Start line - int lastSegment = m_axisCacheZ.subSegmentCount() * m_axisCacheZ.segmentCount(); + int gridLineCount = m_axisCacheZ.gridLineCount(); - for (int segment = 0; segment <= lastSegment; segment++) { + for (int line = 0; line < gridLineCount; line++) { QMatrix4x4 modelMatrix; QMatrix4x4 MVPMatrix; QMatrix4x4 itModelMatrix; - modelMatrix.translate(0.0f, yFloorLinePosition, linePos); + modelMatrix.translate(0.0f, yFloorLinePosition, + m_axisCacheZ.gridLinePosition(line)); modelMatrix.scale(gridLineScaleX); itModelMatrix.scale(gridLineScaleX); @@ -1514,34 +1504,38 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); // Draw the object m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); - } else -#endif - { + } else { // Draw the object m_drawer->drawObject(lineShader, m_gridLineObj); } - linePos -= lineStep; +#else + m_drawer->drawLine(lineShader); +#endif } // Side wall lines GLfloat lineXTrans = m_scaleXWithBackground - gridLineOffset; - linePos = m_scaleZ; // Start line if (!m_xFlipped) lineXTrans = -lineXTrans; - for (int segment = 0; segment <= lastSegment; segment++) { + for (int line = 0; line < gridLineCount; line++) { QMatrix4x4 modelMatrix; QMatrix4x4 MVPMatrix; QMatrix4x4 itModelMatrix; - modelMatrix.translate(lineXTrans, 0.0f, linePos); + modelMatrix.translate(lineXTrans, 0.0f, m_axisCacheZ.gridLinePosition(line)); 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; @@ -1558,29 +1552,31 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); // Draw the object m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); - } else -#endif - { + } else { // Draw the object m_drawer->drawObject(lineShader, m_gridLineObj); } - linePos -= lineStep; +#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 // Floor lines - GLfloat lineStep = -2.0f * aspectRatio * m_axisCacheX.subSegmentStep() / m_scaleFactor; - GLfloat linePos = m_scaleX; - int lastSegment = m_axisCacheX.subSegmentCount() * m_axisCacheX.segmentCount(); + int gridLineCount = m_axisCacheX.gridLineCount(); - for (int segment = 0; segment <= lastSegment; segment++) { + for (int line = 0; line < gridLineCount; line++) { QMatrix4x4 modelMatrix; QMatrix4x4 MVPMatrix; QMatrix4x4 itModelMatrix; - modelMatrix.translate(linePos, yFloorLinePosition, 0.0f); + modelMatrix.translate(m_axisCacheX.gridLinePosition(line), yFloorLinePosition, + 0.0f); modelMatrix.scale(gridLineScaleZ); itModelMatrix.scale(gridLineScaleZ); @@ -1603,36 +1599,40 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); // Draw the object m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); - } else -#endif - { + } else { // Draw the object m_drawer->drawObject(lineShader, m_gridLineObj); } - linePos += lineStep; +#else + m_drawer->drawLine(lineShader); +#endif } // Back wall lines GLfloat lineZTrans = m_scaleZWithBackground - gridLineOffset; - linePos = m_scaleX; if (!m_zFlipped) lineZTrans = -lineZTrans; - for (int segment = 0; segment <= lastSegment; segment++) { + for (int line = 0; line < gridLineCount; line++) { QMatrix4x4 modelMatrix; QMatrix4x4 MVPMatrix; QMatrix4x4 itModelMatrix; - modelMatrix.translate(linePos, 0.0f, lineZTrans); + modelMatrix.translate(m_axisCacheX.gridLinePosition(line), 0.0f, lineZTrans); modelMatrix.scale(gridLineScaleY); itModelMatrix.scale(gridLineScaleY); +#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 MVPMatrix = projectionViewMatrix * modelMatrix; @@ -1649,34 +1649,32 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); // Draw the object m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); - } else -#endif - { + } else { // Draw the object m_drawer->drawObject(lineShader, m_gridLineObj); } - linePos += lineStep; +#else + m_drawer->drawLine(lineShader); +#endif } } // Horizontal wall lines if (m_axisCacheY.segmentCount() > 0) { // Back wall - GLfloat lineStep = 2.0f * m_axisCacheY.subSegmentStep() / m_heightNormalizer; - GLfloat linePos = -1.0f; - int lastSegment = m_axisCacheY.subSegmentCount() * m_axisCacheY.segmentCount(); + int gridLineCount = m_axisCacheY.gridLineCount(); GLfloat lineZTrans = m_scaleZWithBackground - gridLineOffset; if (!m_zFlipped) lineZTrans = -lineZTrans; - for (int segment = 0; segment <= lastSegment; segment++) { + for (int line = 0; line < gridLineCount; line++) { QMatrix4x4 modelMatrix; QMatrix4x4 MVPMatrix; QMatrix4x4 itModelMatrix; - modelMatrix.translate(0.0f, linePos, lineZTrans); + modelMatrix.translate(0.0f, m_axisCacheY.gridLinePosition(line), lineZTrans); modelMatrix.scale(gridLineScaleX); itModelMatrix.scale(gridLineScaleX); @@ -1701,29 +1699,27 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); // Draw the object m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); - } else -#endif - { + } else { // Draw the object m_drawer->drawObject(lineShader, m_gridLineObj); } - linePos += lineStep; +#else + m_drawer->drawLine(lineShader); +#endif } // Side wall - linePos = -1.0f; - lastSegment = m_axisCacheY.subSegmentCount() * m_axisCacheY.segmentCount(); GLfloat lineXTrans = m_scaleXWithBackground - gridLineOffset; if (!m_xFlipped) lineXTrans = -lineXTrans; - for (int segment = 0; segment <= lastSegment; segment++) { + for (int line = 0; line < gridLineCount; line++) { QMatrix4x4 modelMatrix; QMatrix4x4 MVPMatrix; QMatrix4x4 itModelMatrix; - modelMatrix.translate(lineXTrans, linePos, 0.0f); + modelMatrix.translate(lineXTrans, m_axisCacheY.gridLinePosition(line), 0.0f); modelMatrix.scale(gridLineScaleZ); itModelMatrix.scale(gridLineScaleZ); @@ -1746,221 +1742,434 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle) lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix); // Draw the object m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture); - } else -#endif - { + } else { // Draw the object m_drawer->drawObject(lineShader, m_gridLineObj); } - linePos += lineStep; +#else + m_drawer->drawLine(lineShader); +#endif } } } - // Draw axis labels - m_labelShader->bind(); + Abstract3DRenderer::drawCustomItems(RenderingNormal, m_customItemShader, viewMatrix, + projectionViewMatrix, depthProjectionViewMatrix, + m_depthTexture, m_shadowQualityToShader); + + drawLabels(false, activeCamera, viewMatrix, projectionMatrix); + + // Release shader + glUseProgram(0); + + // Selection handling + if (m_selectionDirty || m_selectionLabelDirty) { + QPoint visiblePoint = Surface3DController::invalidSelectionPosition(); + if (m_selectedSeries) { + SurfaceSeriesRenderCache *cache = + static_cast<SurfaceSeriesRenderCache *>( + m_renderCacheList.value(const_cast<QSurface3DSeries *>(m_selectedSeries))); + if (cache && m_selectedPoint != Surface3DController::invalidSelectionPosition()) { + const QRect &sampleSpace = cache->sampleSpace(); + int x = m_selectedPoint.x() - sampleSpace.y(); + int y = m_selectedPoint.y() - sampleSpace.x(); + if (x >= 0 && y >= 0 && x < sampleSpace.height() && y < sampleSpace.width() + && cache->dataArray().size()) { + visiblePoint = QPoint(x, y); + } + } + } + + if (m_cachedSelectionMode == QAbstract3DGraph::SelectionNone + || visiblePoint == Surface3DController::invalidSelectionPosition()) { + m_selectionActive = false; + } else { + if (m_cachedIsSlicingActivated) + updateSliceDataModel(visiblePoint); + if (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionItem)) + surfacePointSelected(visiblePoint); + m_selectionActive = true; + } + + m_selectionDirty = false; + } +} + +void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCamera, + const QMatrix4x4 &viewMatrix, + const QMatrix4x4 &projectionMatrix) +{ + ShaderHelper *shader = 0; + GLfloat alphaForValueSelection = labelValueAlpha / 255.0f; + GLfloat alphaForRowSelection = labelRowAlpha / 255.0f; + GLfloat alphaForColumnSelection = labelColumnAlpha / 255.0f; + if (drawSelection) { + shader = m_surfaceGridShader; + } else { + shader = m_labelShader; + shader->bind(); + glEnable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_POLYGON_OFFSET_FILL); + float labelAutoAngle = m_axisCacheZ.labelAutoRotation(); + float labelAngleFraction = labelAutoAngle / 90.0f; + float fractionCamY = activeCamera->yRotation() * labelAngleFraction; + float fractionCamX = activeCamera->xRotation() * labelAngleFraction; + float labelsMaxWidth = 0.0f; + + int startIndex; + int endIndex; + int indexStep; + // Z Labels QVector3D positionZComp(0.0f, 0.0f, 0.0f); if (m_axisCacheZ.segmentCount() > 0) { - GLfloat posStep = 2.0f * aspectRatio * m_axisCacheZ.segmentStep() / m_scaleFactor; - GLfloat labelPos = m_scaleZ; - int lastSegment = m_axisCacheZ.segmentCount(); - int labelNbr = 0; + int labelCount = m_axisCacheZ.labelCount(); GLfloat labelXTrans = m_scaleXWithBackground + labelMargin; GLfloat labelYTrans = -backgroundMargin; - GLfloat rotLabelX = -90.0f; - GLfloat rotLabelY = 0.0f; - GLfloat rotLabelZ = 0.0f; - Qt::AlignmentFlag alignment = Qt::AlignRight; - if (m_zFlipped) - rotLabelY = 180.0f; - if (m_xFlipped) { + Qt::AlignmentFlag alignment = (m_xFlipped == m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight; + QVector3D labelRotation; + if (m_xFlipped) labelXTrans = -labelXTrans; - alignment = Qt::AlignLeft; - } - if (m_yFlipped) { - rotLabelZ += 180.0f; - rotLabelY += 180.0f; + if (m_yFlipped) labelYTrans = -labelYTrans; + if (labelAutoAngle == 0.0f) { + labelRotation.setX(-90.0f); + if (m_zFlipped) + labelRotation.setY(180.0f); + if (m_yFlipped) { + if (m_zFlipped) + labelRotation.setY(0.0f); + else + labelRotation.setY(180.0f); + labelRotation.setZ(180.0f); + } + } else { + if (m_zFlipped) + labelRotation.setY(180.0f); + if (m_yFlipped) { + if (m_zFlipped) { + if (m_xFlipped) { + labelRotation.setX(90.0f - (labelAutoAngle - fractionCamX) + * (-labelAutoAngle - fractionCamY) / labelAutoAngle); + labelRotation.setZ(labelAutoAngle + fractionCamY); + } else { + labelRotation.setX(90.0f + (labelAutoAngle + fractionCamX) + * (labelAutoAngle + fractionCamY) / labelAutoAngle); + labelRotation.setZ(-labelAutoAngle - fractionCamY); + } + } else { + if (m_xFlipped) { + labelRotation.setX(90.0f + (labelAutoAngle - fractionCamX) + * -(labelAutoAngle + fractionCamY) / labelAutoAngle); + labelRotation.setZ(-labelAutoAngle - fractionCamY); + } else { + labelRotation.setX(90.0f - (labelAutoAngle + fractionCamX) + * (labelAutoAngle + fractionCamY) / labelAutoAngle); + labelRotation.setZ(labelAutoAngle + fractionCamY); + } + } + } else { + if (m_zFlipped) { + if (m_xFlipped) { + labelRotation.setX(-90.0f + (labelAutoAngle - fractionCamX) + * (-labelAutoAngle + fractionCamY) / labelAutoAngle); + labelRotation.setZ(-labelAutoAngle + fractionCamY); + } else { + labelRotation.setX(-90.0f - (labelAutoAngle + fractionCamX) + * (labelAutoAngle - fractionCamY) / labelAutoAngle); + labelRotation.setZ(labelAutoAngle - fractionCamY); + } + } else { + if (m_xFlipped) { + labelRotation.setX(-90.0f - (labelAutoAngle - fractionCamX) + * (-labelAutoAngle + fractionCamY) / labelAutoAngle); + labelRotation.setZ(labelAutoAngle - fractionCamY); + } else { + labelRotation.setX(-90.0f + (labelAutoAngle + fractionCamX) + * (labelAutoAngle - fractionCamY) / labelAutoAngle); + labelRotation.setZ(-labelAutoAngle + fractionCamY); + } + } + } } + + QQuaternion totalRotation = Utils::calculateRotation(labelRotation); + QVector3D labelTrans = QVector3D(labelXTrans, labelYTrans, - labelPos); - QVector3D rotation(rotLabelX, rotLabelY, rotLabelZ); - - for (int segment = 0; segment <= lastSegment; segment++) { - if (m_axisCacheZ.labelItems().size() > labelNbr) { - glPolygonOffset(GLfloat(segment) / -10.0f, 1.0f); - // Draw the label here - labelTrans.setZ(labelPos); - m_dummyRenderItem.setTranslation(labelTrans); - const LabelItem &axisLabelItem = *m_axisCacheZ.labelItems().at(labelNbr); - - m_drawer->drawLabel(m_dummyRenderItem, axisLabelItem, viewMatrix, projectionMatrix, - positionZComp, rotation, 0, m_cachedSelectionMode, - m_labelShader, m_labelObj, activeCamera, - true, true, Drawer::LabelMid, alignment); + 0.0f); + + if (m_zFlipped) { + startIndex = 0; + endIndex = labelCount; + indexStep = 1; + } else { + startIndex = labelCount - 1; + endIndex = -1; + indexStep = -1; + } + for (int label = startIndex; label != endIndex; label = label + indexStep) { + glPolygonOffset(GLfloat(label) / -10.0f, 1.0f); + // Draw the label here + labelTrans.setZ(m_axisCacheZ.labelPosition(label)); + m_dummyRenderItem.setTranslation(labelTrans); + const LabelItem &axisLabelItem = *m_axisCacheZ.labelItems().at(label); + + if (drawSelection) { + QVector4D labelColor = QVector4D(label / 255.0f, 0.0f, 0.0f, + alphaForRowSelection); + shader->setUniformValue(shader->color(), labelColor); } - labelNbr++; - labelPos -= posStep; + + m_drawer->drawLabel(m_dummyRenderItem, axisLabelItem, viewMatrix, projectionMatrix, + positionZComp, totalRotation, 0, m_cachedSelectionMode, + shader, m_labelObj, activeCamera, + true, true, Drawer::LabelMid, alignment, false, drawSelection); + labelsMaxWidth = qMax(labelsMaxWidth, float(axisLabelItem.size().width())); + } + if (!drawSelection && m_axisCacheZ.isTitleVisible()) { + labelTrans.setZ(0.0f); + drawAxisTitleZ(labelRotation, labelTrans, totalRotation, m_dummyRenderItem, + activeCamera, labelsMaxWidth, viewMatrix, projectionMatrix, shader); } } // X Labels if (m_axisCacheX.segmentCount() > 0) { - GLfloat posStep = 2.0f * aspectRatio * m_axisCacheX.segmentStep() / m_scaleFactor; - GLfloat labelPos = -m_scaleX; - int lastSegment = m_axisCacheX.segmentCount(); + labelsMaxWidth = 0.0f; + labelAutoAngle = m_axisCacheX.labelAutoRotation(); + labelAngleFraction = labelAutoAngle / 90.0f; + fractionCamY = activeCamera->yRotation() * labelAngleFraction; + fractionCamX = activeCamera->xRotation() * labelAngleFraction; + int labelCount = m_axisCacheX.labelCount(); - int labelNbr = 0; GLfloat labelZTrans = m_scaleZWithBackground + labelMargin; GLfloat labelYTrans = -backgroundMargin; - GLfloat rotLabelX = -90.0f; - GLfloat rotLabelY = 90.0f; - GLfloat rotLabelZ = 0.0f; - Qt::AlignmentFlag alignment = Qt::AlignLeft; - if (m_xFlipped) - rotLabelY = -90.0f; - if (m_zFlipped) { + Qt::AlignmentFlag alignment = (m_xFlipped != m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight; + QVector3D labelRotation; + if (m_zFlipped) labelZTrans = -labelZTrans; - alignment = Qt::AlignRight; - } - if (m_yFlipped) { - rotLabelZ += 180.0f; - rotLabelY += 180.0f; + if (m_yFlipped) labelYTrans = -labelYTrans; + if (labelAutoAngle == 0.0f) { + labelRotation = QVector3D(-90.0f, 90.0f, 0.0f); + if (m_xFlipped) + labelRotation.setY(-90.0f); + if (m_yFlipped) { + if (m_xFlipped) + labelRotation.setY(90.0f); + else + labelRotation.setY(-90.0f); + labelRotation.setZ(180.0f); + } + } else { + if (m_xFlipped) + labelRotation.setY(-90.0f); + else + labelRotation.setY(90.0f); + if (m_yFlipped) { + if (m_zFlipped) { + if (m_xFlipped) { + labelRotation.setX(90.0f - (2.0f * labelAutoAngle - fractionCamX) + * (labelAutoAngle + fractionCamY) / labelAutoAngle); + labelRotation.setZ(-labelAutoAngle - fractionCamY); + } else { + labelRotation.setX(90.0f - (2.0f * labelAutoAngle + fractionCamX) + * (labelAutoAngle + fractionCamY) / labelAutoAngle); + labelRotation.setZ(labelAutoAngle + fractionCamY); + } + } else { + if (m_xFlipped) { + labelRotation.setX(90.0f + fractionCamX + * -(labelAutoAngle + fractionCamY) / labelAutoAngle); + labelRotation.setZ(labelAutoAngle + fractionCamY); + } else { + labelRotation.setX(90.0f - fractionCamX + * (-labelAutoAngle - fractionCamY) / labelAutoAngle); + labelRotation.setZ(-labelAutoAngle - fractionCamY); + } + } + } else { + if (m_zFlipped) { + if (m_xFlipped) { + labelRotation.setX(-90.0f + (2.0f * labelAutoAngle - fractionCamX) + * (labelAutoAngle - fractionCamY) / labelAutoAngle); + labelRotation.setZ(labelAutoAngle - fractionCamY); + } else { + labelRotation.setX(-90.0f + (2.0f * labelAutoAngle + fractionCamX) + * (labelAutoAngle - fractionCamY) / labelAutoAngle); + labelRotation.setZ(-labelAutoAngle + fractionCamY); + } + } else { + if (m_xFlipped) { + labelRotation.setX(-90.0f - fractionCamX + * (-labelAutoAngle + fractionCamY) / labelAutoAngle); + labelRotation.setZ(-labelAutoAngle + fractionCamY); + } else { + labelRotation.setX(-90.0f + fractionCamX + * -(labelAutoAngle - fractionCamY) / labelAutoAngle); + labelRotation.setZ(labelAutoAngle - fractionCamY); + } + } + } } - QVector3D labelTrans = QVector3D(labelPos, + QQuaternion totalRotation = Utils::calculateRotation(labelRotation); + + QVector3D labelTrans = QVector3D(0.0f, labelYTrans, labelZTrans); - QVector3D rotation(rotLabelX, rotLabelY, rotLabelZ); - - for (int segment = 0; segment <= lastSegment; segment++) { - if (m_axisCacheX.labelItems().size() > labelNbr) { - glPolygonOffset(GLfloat(segment) / -10.0f, 1.0f); - // Draw the label here - labelTrans.setX(labelPos); - m_dummyRenderItem.setTranslation(labelTrans); - const LabelItem &axisLabelItem = *m_axisCacheX.labelItems().at(labelNbr); - - m_drawer->drawLabel(m_dummyRenderItem, axisLabelItem, viewMatrix, projectionMatrix, - positionZComp, rotation, 0, m_cachedSelectionMode, - m_labelShader, m_labelObj, activeCamera, - true, true, Drawer::LabelMid, alignment); + + if (m_xFlipped) { + startIndex = labelCount - 1; + endIndex = -1; + indexStep = -1; + } else { + startIndex = 0; + endIndex = labelCount; + indexStep = 1; + } + for (int label = startIndex; label != endIndex; label = label + indexStep) { + glPolygonOffset(GLfloat(label) / -10.0f, 1.0f); + // Draw the label here + labelTrans.setX(m_axisCacheX.labelPosition(label)); + m_dummyRenderItem.setTranslation(labelTrans); + const LabelItem &axisLabelItem = *m_axisCacheX.labelItems().at(label); + + if (drawSelection) { + QVector4D labelColor = QVector4D(0.0f, label / 255.0f, 0.0f, + alphaForColumnSelection); + shader->setUniformValue(shader->color(), labelColor); } - labelNbr++; - labelPos += posStep; + + m_drawer->drawLabel(m_dummyRenderItem, axisLabelItem, viewMatrix, projectionMatrix, + positionZComp, totalRotation, 0, m_cachedSelectionMode, + shader, m_labelObj, activeCamera, + true, true, Drawer::LabelMid, alignment, false, drawSelection); + labelsMaxWidth = qMax(labelsMaxWidth, float(axisLabelItem.size().width())); + } + if (!drawSelection && m_axisCacheX.isTitleVisible()) { + labelTrans.setX(0.0f); + drawAxisTitleX(labelRotation, labelTrans, totalRotation, m_dummyRenderItem, + activeCamera, labelsMaxWidth, viewMatrix, projectionMatrix, shader); } } // Y Labels if (m_axisCacheY.segmentCount() > 0) { - GLfloat posStep = 2.0f * m_axisCacheY.segmentStep() / m_heightNormalizer; - GLfloat labelPos = -1.0f; - int labelNbr = 0; + labelsMaxWidth = 0.0f; + labelAutoAngle = m_axisCacheY.labelAutoRotation(); + labelAngleFraction = labelAutoAngle / 90.0f; + fractionCamY = activeCamera->yRotation() * labelAngleFraction; + fractionCamX = activeCamera->xRotation() * labelAngleFraction; + int labelCount = m_axisCacheY.labelCount(); + GLfloat labelXTrans = m_scaleXWithBackground; GLfloat labelZTrans = m_scaleZWithBackground; - // Back wall init + // Back & side wall GLfloat labelMarginXTrans = labelMargin; GLfloat labelMarginZTrans = labelMargin; - GLfloat rotLabelX = 0.0f; - GLfloat rotLabelY = -90.0f; - GLfloat rotLabelZ = 0.0f; - Qt::AlignmentFlag alignmentBack = Qt::AlignLeft; + QVector3D backLabelRotation(0.0f, -90.0f, 0.0f); + QVector3D sideLabelRotation(0.0f, 0.0f, 0.0f); + Qt::AlignmentFlag backAlignment = + (m_xFlipped != m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight; + Qt::AlignmentFlag sideAlignment = + (m_xFlipped == m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight; if (!m_xFlipped) { labelXTrans = -labelXTrans; labelMarginXTrans = -labelMargin; - rotLabelY = 90.0f; } if (m_zFlipped) { labelZTrans = -labelZTrans; labelMarginZTrans = -labelMargin; - alignmentBack = Qt::AlignRight; } - QVector3D labelRotateVectorBack(rotLabelX, rotLabelY, rotLabelZ); - QVector3D labelTransBack = QVector3D(labelXTrans, 0.0f, labelZTrans + labelMarginZTrans); + if (labelAutoAngle == 0.0f) { + if (!m_xFlipped) + backLabelRotation.setY(90.0f); + if (m_zFlipped) + sideLabelRotation.setY(180.f); + } else { + // Orient side labels somewhat towards the camera + if (m_xFlipped) { + if (m_zFlipped) + sideLabelRotation.setY(180.0f + (2.0f * labelAutoAngle) - fractionCamX); + else + sideLabelRotation.setY(-fractionCamX); + backLabelRotation.setY(-90.0f + labelAutoAngle - fractionCamX); + } else { + if (m_zFlipped) + sideLabelRotation.setY(180.0f - (2.0f * labelAutoAngle) - fractionCamX); + else + sideLabelRotation.setY(-fractionCamX); + backLabelRotation.setY(90.0f - labelAutoAngle - fractionCamX); + } + } + sideLabelRotation.setX(-fractionCamY); + backLabelRotation.setX(-fractionCamY); - // Side wall init - Qt::AlignmentFlag alignmentSide = Qt::AlignLeft; - if (m_xFlipped) - alignmentSide = Qt::AlignLeft; - else - alignmentSide = Qt::AlignRight; - if (m_zFlipped) - rotLabelY = 180.0f; - else - rotLabelY = 0.0f; + QQuaternion totalSideRotation = Utils::calculateRotation(sideLabelRotation); + QQuaternion totalBackRotation = Utils::calculateRotation(backLabelRotation); - QVector3D labelRotateVectorSide(rotLabelX, rotLabelY, rotLabelZ); + QVector3D labelTransBack = QVector3D(labelXTrans, 0.0f, labelZTrans + labelMarginZTrans); QVector3D labelTransSide(-labelXTrans - labelMarginXTrans, 0.0f, -labelZTrans); - for (int segment = 0; segment <= m_axisCacheY.segmentCount(); segment++) { - if (m_axisCacheY.labelItems().size() > labelNbr) { - const LabelItem &axisLabelItem = *m_axisCacheY.labelItems().at(labelNbr); - - glPolygonOffset(GLfloat(segment) / -10.0f, 1.0f); - - // Back wall - labelTransBack.setY(labelPos); - m_dummyRenderItem.setTranslation(labelTransBack); - m_drawer->drawLabel(m_dummyRenderItem, axisLabelItem, viewMatrix, projectionMatrix, - positionZComp, labelRotateVectorBack, 0, m_cachedSelectionMode, - m_labelShader, m_labelObj, activeCamera, - true, true, Drawer::LabelMid, alignmentBack); - - // Side wall - labelTransSide.setY(labelPos); - m_dummyRenderItem.setTranslation(labelTransSide); - m_drawer->drawLabel(m_dummyRenderItem, axisLabelItem, viewMatrix, projectionMatrix, - positionZComp, labelRotateVectorSide, 0, m_cachedSelectionMode, - m_labelShader, m_labelObj, activeCamera, - true, true, Drawer::LabelMid, alignmentSide); - } - labelNbr++; - labelPos += posStep; + if (m_yFlipped) { + startIndex = labelCount - 1; + endIndex = -1; + indexStep = -1; + } else { + startIndex = 0; + endIndex = labelCount; + indexStep = 1; } - } - glDisable(GL_POLYGON_OFFSET_FILL); + for (int label = startIndex; label != endIndex; label = label + indexStep) { + const LabelItem &axisLabelItem = *m_axisCacheY.labelItems().at(label); + const GLfloat labelYTrans = m_axisCacheY.labelPosition(label); - glDisable(GL_TEXTURE_2D); - glDisable(GL_BLEND); + glPolygonOffset(GLfloat(label) / -10.0f, 1.0f); - // Release shader - glUseProgram(0); - - // Selection handling - if (m_selectionDirty || m_selectionLabelDirty) { - QPoint visiblePoint = Surface3DController::invalidSelectionPosition(); - if (m_selectedSeries) { - SurfaceSeriesRenderCache *cache = - m_renderCacheList.value(const_cast<QSurface3DSeries *>(m_selectedSeries)); - if (cache && m_selectedPoint != Surface3DController::invalidSelectionPosition()) { - const QRect &sampleSpace = cache->sampleSpace(); - int x = m_selectedPoint.x() - sampleSpace.y(); - int y = m_selectedPoint.y() - sampleSpace.x(); - if (x >= 0 && y >= 0 && x < sampleSpace.height() && y < sampleSpace.width() - && cache->dataArray().size()) { - visiblePoint = QPoint(x, y); - } + if (drawSelection) { + QVector4D labelColor = QVector4D(0.0f, 0.0f, label / 255.0f, + alphaForValueSelection); + shader->setUniformValue(shader->color(), labelColor); } - } - if (m_cachedSelectionMode == QAbstract3DGraph::SelectionNone - || visiblePoint == Surface3DController::invalidSelectionPosition()) { - m_selectionActive = false; - } else { - if (m_cachedIsSlicingActivated) - updateSliceDataModel(visiblePoint); - if (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionItem)) - surfacePointSelected(visiblePoint); - m_selectionActive = true; + // Back wall + labelTransBack.setY(labelYTrans); + m_dummyRenderItem.setTranslation(labelTransBack); + m_drawer->drawLabel(m_dummyRenderItem, axisLabelItem, viewMatrix, projectionMatrix, + positionZComp, totalBackRotation, 0, m_cachedSelectionMode, + shader, m_labelObj, activeCamera, + true, true, Drawer::LabelMid, backAlignment, false, + drawSelection); + + // Side wall + labelTransSide.setY(labelYTrans); + m_dummyRenderItem.setTranslation(labelTransSide); + m_drawer->drawLabel(m_dummyRenderItem, axisLabelItem, viewMatrix, projectionMatrix, + positionZComp, totalSideRotation, 0, m_cachedSelectionMode, + shader, m_labelObj, activeCamera, + true, true, Drawer::LabelMid, sideAlignment, false, + drawSelection); + labelsMaxWidth = qMax(labelsMaxWidth, float(axisLabelItem.size().width())); + } + if (!drawSelection && m_axisCacheY.isTitleVisible()) { + labelTransSide.setY(0.0f); + labelTransBack.setY(0.0f); + drawAxisTitleY(sideLabelRotation, backLabelRotation, labelTransSide, labelTransBack, + totalSideRotation, totalBackRotation, m_dummyRenderItem, activeCamera, + labelsMaxWidth, viewMatrix, projectionMatrix, + shader); } + } + glDisable(GL_POLYGON_OFFSET_FILL); - m_selectionDirty = false; + if (!drawSelection) { + glDisable(GL_TEXTURE_2D); + glDisable(GL_BLEND); } } @@ -1976,7 +2185,9 @@ void Surface3DRenderer::updateSelectionTextures() { uint lastSelectionId = 1; - foreach (SurfaceSeriesRenderCache *cache, m_renderCacheList) { + foreach (SeriesRenderCache *baseCache, m_renderCacheList) { + SurfaceSeriesRenderCache *cache = + static_cast<SurfaceSeriesRenderCache *>(baseCache); GLuint texture = cache->selectionTexture(); m_textureHelper->deleteTexture(&texture); createSelectionTexture(cache, lastSelectionId); @@ -1992,6 +2203,13 @@ void Surface3DRenderer::createSelectionTexture(SurfaceSeriesRenderCache *cache, const QRect &sampleSpace = cache->sampleSpace(); int idImageWidth = (sampleSpace.width() - 1) * 4; int idImageHeight = (sampleSpace.height() - 1) * 4; + + if (idImageHeight <= 0 || idImageWidth <= 0) { + cache->setSelectionIdRange(-1, -1); + cache->setSelectionTexture(0); + return; + } + int stride = idImageWidth * 4 * sizeof(uchar); // 4 = number of color components (rgba) uint idStart = lastSelectionId; @@ -2031,10 +2249,7 @@ void Surface3DRenderer::createSelectionTexture(SurfaceSeriesRenderCache *cache, void Surface3DRenderer::initSelectionBuffer() { // Create the result selection texture and buffers - if (m_selectionResultTexture) { - m_textureHelper->deleteTexture(&m_selectionResultTexture); - m_selectionResultTexture = 0; - } + m_textureHelper->deleteTexture(&m_selectionResultTexture); m_selectionResultTexture = m_textureHelper->createSelectionTexture(m_primarySubViewport.size(), m_selectionFrameBuffer, @@ -2076,10 +2291,16 @@ void Surface3DRenderer::calculateSceneScalingFactors() 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 = aspectRatio * m_areaSize.width() / m_scaleFactor; - m_scaleZ = aspectRatio * m_areaSize.height() / m_scaleFactor; - m_scaleXWithBackground = m_scaleX * backgroundMargin; - m_scaleZWithBackground = m_scaleZ * backgroundMargin; + 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); } void Surface3DRenderer::checkFlatSupport(SurfaceSeriesRenderCache *cache) @@ -2098,17 +2319,13 @@ void Surface3DRenderer::updateObjects(SurfaceSeriesRenderCache *cache, bool dime QSurfaceDataArray &dataArray = cache->dataArray(); const QRect &sampleSpace = cache->sampleSpace(); - - if (cache->isFlatShadingEnabled()) { - cache->surfaceObject()->setUpData(dataArray, sampleSpace, m_heightNormalizer, - m_axisCacheY.min(), dimensionChanged); - } else { - cache->surfaceObject()->setUpSmoothData(dataArray, sampleSpace, m_heightNormalizer, - m_axisCacheY.min(), dimensionChanged); - } + if (cache->isFlatShadingEnabled()) + cache->surfaceObject()->setUpData(dataArray, sampleSpace, dimensionChanged); + else + cache->surfaceObject()->setUpSmoothData(dataArray, sampleSpace, dimensionChanged); } -void Surface3DRenderer::updateSelectedPoint(const QPoint &position, const QSurface3DSeries *series) +void Surface3DRenderer::updateSelectedPoint(const QPoint &position, QSurface3DSeries *series) { m_selectedPoint = position; m_selectedSeries = series; @@ -2123,23 +2340,15 @@ void Surface3DRenderer::resetClickedStatus() void Surface3DRenderer::loadBackgroundMesh() { - if (m_backgroundObj) - delete m_backgroundObj; - m_backgroundObj = new ObjectHelper(QStringLiteral(":/defaultMeshes/background")); - m_backgroundObj->load(); -} - -void Surface3DRenderer::loadGridLineMesh() -{ - if (m_gridLineObj) - delete m_gridLineObj; - m_gridLineObj = new ObjectHelper(QStringLiteral(":/defaultMeshes/plane")); - m_gridLineObj->load(); + ObjectHelper::resetObjectHelper(this, m_backgroundObj, + QStringLiteral(":/defaultMeshes/background")); } void Surface3DRenderer::surfacePointSelected(const QPoint &point) { - foreach (SurfaceSeriesRenderCache *cache, m_renderCacheList) { + foreach (SeriesRenderCache *baseCache, m_renderCacheList) { + SurfaceSeriesRenderCache *cache = + static_cast<SurfaceSeriesRenderCache *>(baseCache); cache->setSlicePointerActivity(false); cache->setMainPointerActivity(false); } @@ -2147,12 +2356,15 @@ void Surface3DRenderer::surfacePointSelected(const QPoint &point) if (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionMultiSeries)) { // Find axis coordinates for the selected point SurfaceSeriesRenderCache *selectedCache = - m_renderCacheList.value(const_cast<QSurface3DSeries *>(m_selectedSeries)); + static_cast<SurfaceSeriesRenderCache *>( + m_renderCacheList.value(const_cast<QSurface3DSeries *>(m_selectedSeries))); QSurfaceDataArray &dataArray = selectedCache->dataArray(); QSurfaceDataItem item = dataArray.at(point.x())->at(point.y()); QPointF coords(item.x(), item.z()); - foreach (SurfaceSeriesRenderCache *cache, m_renderCacheList) { + foreach (SeriesRenderCache *baseCache, m_renderCacheList) { + SurfaceSeriesRenderCache *cache = + static_cast<SurfaceSeriesRenderCache *>(baseCache); if (cache->series() != m_selectedSeries) { QPoint mappedPoint = mapCoordsToSampleSpace(cache, coords); updateSelectionPoint(cache, mappedPoint, false); @@ -2163,7 +2375,8 @@ void Surface3DRenderer::surfacePointSelected(const QPoint &point) } else { if (m_selectedSeries) { SurfaceSeriesRenderCache *cache = - m_renderCacheList.value(const_cast<QSurface3DSeries *>(m_selectedSeries)); + static_cast<SurfaceSeriesRenderCache *>( + m_renderCacheList.value(const_cast<QSurface3DSeries *>(m_selectedSeries))); if (cache) updateSelectionPoint(cache, point, true); } @@ -2179,9 +2392,6 @@ void Surface3DRenderer::updateSelectionPoint(SurfaceSeriesRenderCache *cache, co if (column < 0 || row < 0) return; - QSurfaceDataArray &dataArray = cache->dataArray(); - float value = dataArray.at(row)->at(column).y(); - SelectionPointer *slicePointer = cache->sliceSelectionPointer(); if (!slicePointer && m_cachedIsSlicingActivated) { slicePointer = new SelectionPointer(m_drawer); @@ -2193,28 +2403,28 @@ void Surface3DRenderer::updateSelectionPoint(SurfaceSeriesRenderCache *cache, co cache->setMainSelectionPointer(mainPointer); } - const QVector3D &scale = cache->scale(); - const QVector3D &offset = cache->offset(); QString selectionLabel; - if (label) - selectionLabel = createSelectionLabel(cache, value, column, row); + if (label) { + m_selectionLabelDirty = false; + selectionLabel = cache->itemLabel(); + } if (m_cachedIsSlicingActivated) { - QVector3D subPos; + QVector3D subPosFront; + QVector3D subPosBack; if (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionRow)) { - subPos = cache->sliceSurfaceObject()->vertexAt(column, 0); - subPos *= QVector3D(scale.x(), 1.0f, 0.0f); - subPos += QVector3D(offset.x(), 0.0f, 0.0f); + subPosFront = cache->sliceSurfaceObject()->vertexAt(column, 0); + subPosBack = cache->sliceSurfaceObject()->vertexAt(column, 1); } else if (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionColumn)) { - subPos = cache->sliceSurfaceObject()->vertexAt(row, 0); - subPos *= QVector3D(scale.z(), 1.0f, 0.0f); - subPos += QVector3D(-offset.z(), 0.0f, 0.0f); + subPosFront = cache->sliceSurfaceObject()->vertexAt(row, 0); + subPosBack = cache->sliceSurfaceObject()->vertexAt(row, 1); } slicePointer->updateBoundingRect(m_secondarySubViewport); slicePointer->updateSliceData(true, m_autoScaleAdjustment); - slicePointer->setPosition(subPos); + slicePointer->setPosition((subPosFront + subPosBack) / 2.0f); slicePointer->setLabel(selectionLabel); slicePointer->setPointerObject(cache->object()); + slicePointer->setLabelObject(m_labelObj); slicePointer->setHighlightColor(cache->singleHighlightColor()); slicePointer->updateScene(m_cachedScene); slicePointer->setRotation(cache->meshRotation()); @@ -2223,13 +2433,12 @@ void Surface3DRenderer::updateSelectionPoint(SurfaceSeriesRenderCache *cache, co QVector3D mainPos; mainPos = cache->surfaceObject()->vertexAt(column, row); - mainPos *= scale; - mainPos += offset; mainPointer->updateBoundingRect(m_primarySubViewport); mainPointer->updateSliceData(false, m_autoScaleAdjustment); mainPointer->setPosition(mainPos); mainPointer->setLabel(selectionLabel); mainPointer->setPointerObject(cache->object()); + mainPointer->setLabelObject(m_labelObj); mainPointer->setHighlightColor(cache->singleHighlightColor()); mainPointer->updateScene(m_cachedScene); mainPointer->setRotation(cache->meshRotation()); @@ -2239,8 +2448,33 @@ void Surface3DRenderer::updateSelectionPoint(SurfaceSeriesRenderCache *cache, co // Maps selection Id to surface point in data array QPoint Surface3DRenderer::selectionIdToSurfacePoint(uint id) { + m_clickedType = QAbstract3DGraph::ElementNone; + m_selectedLabelIndex = -1; + m_selectedCustomItemIndex = -1; + // Check for label and custom item selection + if (id / alphaMultiplier == labelRowAlpha) { + m_selectedLabelIndex = id - (alphaMultiplier * uint(labelRowAlpha)); + m_clickedType = QAbstract3DGraph::ElementAxisZLabel; + return Surface3DController::invalidSelectionPosition(); + } else if (id / alphaMultiplier == labelColumnAlpha) { + m_selectedLabelIndex = (id - (alphaMultiplier * uint(labelColumnAlpha))) / greenMultiplier; + m_clickedType = QAbstract3DGraph::ElementAxisXLabel; + return Surface3DController::invalidSelectionPosition(); + } else if (id / alphaMultiplier == labelValueAlpha) { + m_selectedLabelIndex = (id - (alphaMultiplier * uint(labelValueAlpha))) / blueMultiplier; + m_clickedType = QAbstract3DGraph::ElementAxisYLabel; + return Surface3DController::invalidSelectionPosition(); + } else if (id / alphaMultiplier == customItemAlpha) { + // Custom item selection + m_clickedType = QAbstract3DGraph::ElementCustomItem; + m_selectedCustomItemIndex = id - (alphaMultiplier * uint(customItemAlpha)); + return Surface3DController::invalidSelectionPosition(); + } + + // Not a label selection SurfaceSeriesRenderCache *selectedCache = 0; - foreach (SurfaceSeriesRenderCache *cache, m_renderCacheList) { + foreach (SeriesRenderCache *baseCache, m_renderCacheList) { + SurfaceSeriesRenderCache *cache = static_cast<SurfaceSeriesRenderCache *>(baseCache); if (cache->isWithinIdRange(id)) { selectedCache = cache; break; @@ -2257,57 +2491,10 @@ QPoint Surface3DRenderer::selectionIdToSurfacePoint(uint id) int row = ((idInSeries - 1) / sampleSpace.width()) + sampleSpace.y(); m_clickedSeries = selectedCache->series(); + m_clickedType = QAbstract3DGraph::ElementSeries; return QPoint(row, column); } -QString Surface3DRenderer::createSelectionLabel(SurfaceSeriesRenderCache *cache, float value, - int column, int row) -{ - QSurfaceDataArray &dataArray = cache->dataArray(); - QString labelText = cache->itemLabelFormat(); - static const QString xTitleTag(QStringLiteral("@xTitle")); - static const QString yTitleTag(QStringLiteral("@yTitle")); - static const QString zTitleTag(QStringLiteral("@zTitle")); - static const QString xLabelTag(QStringLiteral("@xLabel")); - static const QString yLabelTag(QStringLiteral("@yLabel")); - static const QString zLabelTag(QStringLiteral("@zLabel")); - static const QString seriesNameTag(QStringLiteral("@seriesName")); - - labelText.replace(xTitleTag, m_axisCacheX.title()); - labelText.replace(yTitleTag, m_axisCacheY.title()); - labelText.replace(zTitleTag, m_axisCacheZ.title()); - - if (labelText.contains(xLabelTag)) { - QString labelFormat = m_axisCacheX.labelFormat(); - if (labelFormat.isEmpty()) - labelFormat = Utils::defaultLabelFormat(); - QString valueLabelText = generateValueLabel(labelFormat, - dataArray.at(row)->at(column).x()); - labelText.replace(xLabelTag, valueLabelText); - } - if (labelText.contains(yLabelTag)) { - QString labelFormat = m_axisCacheY.labelFormat(); - if (labelFormat.isEmpty()) - labelFormat = Utils::defaultLabelFormat(); - QString valueLabelText = generateValueLabel(labelFormat, value); - labelText.replace(yLabelTag, valueLabelText); - } - if (labelText.contains(zLabelTag)) { - QString labelFormat = m_axisCacheZ.labelFormat(); - if (labelFormat.isEmpty()) - labelFormat = Utils::defaultLabelFormat(); - QString valueLabelText = generateValueLabel(labelFormat, - dataArray.at(row)->at(column).z()); - labelText.replace(zLabelTag, valueLabelText); - } - - labelText.replace(seriesNameTag, cache->name()); - - m_selectionLabelDirty = false; - - return labelText; -} - void Surface3DRenderer::updateShadowQuality(QAbstract3DGraph::ShadowQuality quality) { m_cachedShadowQuality = quality; @@ -2371,20 +2558,13 @@ void Surface3DRenderer::updateSlicingActive(bool isSlicing) m_selectionDirty = true; - foreach (SurfaceSeriesRenderCache *cache, m_renderCacheList) { + foreach (SeriesRenderCache *baseCache, m_renderCacheList) { + SurfaceSeriesRenderCache *cache = static_cast<SurfaceSeriesRenderCache *>(baseCache); if (cache->mainSelectionPointer()) cache->mainSelectionPointer()->updateBoundingRect(m_primarySubViewport); } } -void Surface3DRenderer::loadLabelMesh() -{ - if (m_labelObj) - delete m_labelObj; - m_labelObj = new ObjectHelper(QStringLiteral(":/defaultMeshes/plane")); - m_labelObj->load(); -} - void Surface3DRenderer::initShaders(const QString &vertexShader, const QString &fragmentShader) { Q_UNUSED(vertexShader); @@ -2408,17 +2588,22 @@ void Surface3DRenderer::initShaders(const QString &vertexShader, const QString & m_surfaceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"), QStringLiteral(":/shaders/fragmentSurface")); } - if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { - m_surfaceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceShadowFlat"), - QStringLiteral(":/shaders/fragmentSurfaceShadowFlat")); - } else { - m_surfaceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceFlat"), - QStringLiteral(":/shaders/fragmentSurfaceFlat")); - } m_surfaceSliceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"), QStringLiteral(":/shaders/fragmentSurface")); - m_surfaceSliceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceFlat"), - QStringLiteral(":/shaders/fragmentSurfaceFlat")); + if (m_flatSupported) { + if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) { + m_surfaceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceShadowFlat"), + QStringLiteral(":/shaders/fragmentSurfaceShadowFlat")); + } else { + m_surfaceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceFlat"), + QStringLiteral(":/shaders/fragmentSurfaceFlat")); + } + m_surfaceSliceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceFlat"), + QStringLiteral(":/shaders/fragmentSurfaceFlat")); + } else { + m_surfaceFlatShader = 0; + m_surfaceSliceFlatShader = 0; + } #else m_surfaceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"), QStringLiteral(":/shaders/fragmentSurfaceES2")); @@ -2430,9 +2615,11 @@ void Surface3DRenderer::initShaders(const QString &vertexShader, const QString & QStringLiteral(":/shaders/fragmentSurfaceES2")); #endif m_surfaceSmoothShader->initialize(); - m_surfaceFlatShader->initialize(); m_surfaceSliceSmoothShader->initialize(); - m_surfaceSliceFlatShader->initialize(); + if (m_flatSupported) { + m_surfaceFlatShader->initialize(); + m_surfaceSliceFlatShader->initialize(); + } } void Surface3DRenderer::initBackgroundShaders(const QString &vertexShader, @@ -2486,14 +2673,8 @@ void Surface3DRenderer::initDepthShader() void Surface3DRenderer::updateDepthBuffer() { - if (m_depthTexture) { - m_textureHelper->deleteTexture(&m_depthTexture); - m_depthTexture = 0; - } - if (m_depthModelTexture) { - m_textureHelper->deleteTexture(&m_depthModelTexture); - m_depthModelTexture = 0; - } + m_textureHelper->deleteTexture(&m_depthTexture); + m_textureHelper->deleteTexture(&m_depthModelTexture); if (m_primarySubViewport.size().isEmpty()) return; @@ -2512,4 +2693,22 @@ void Surface3DRenderer::updateDepthBuffer() } #endif +QVector3D Surface3DRenderer::convertPositionToTranslation(const QVector3D &position, + bool isAbsolute) +{ + float xTrans = 0.0f; + float yTrans = 0.0f; + float zTrans = 0.0f; + if (!isAbsolute) { + xTrans = m_axisCacheX.positionAt(position.x()); + 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; + } + return QVector3D(xTrans, yTrans, zTrans); +} + QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/engine/surface3drenderer_p.h b/src/datavisualization/engine/surface3drenderer_p.h index 2c55d902..efa8ff7e 100644 --- a/src/datavisualization/engine/surface3drenderer_p.h +++ b/src/datavisualization/engine/surface3drenderer_p.h @@ -32,17 +32,12 @@ #include "datavisualizationglobal_p.h" #include "surface3dcontroller_p.h" #include "abstract3drenderer_p.h" -#include "scatterrenderitem_p.h" -#include "qsurfacedataproxy.h" #include "surfaceseriesrendercache_p.h" QT_BEGIN_NAMESPACE_DATAVISUALIZATION class ShaderHelper; -class ObjectHelper; -class SurfaceObject; class Q3DScene; -class SelectionPointer; class QT_DATAVISUALIZATION_EXPORT Surface3DRenderer : public Abstract3DRenderer { @@ -67,15 +62,6 @@ private: GLfloat m_scaleZ; GLfloat m_scaleXWithBackground; GLfloat m_scaleZWithBackground; - GLfloat m_minVisibleColumnValue; - GLfloat m_maxVisibleColumnValue; - GLfloat m_minVisibleRowValue; - GLfloat m_maxVisibleRowValue; - GLfloat m_visibleColumnRange; - GLfloat m_visibleRowRange; - ObjectHelper *m_backgroundObj; - ObjectHelper *m_gridLineObj; - ObjectHelper *m_labelObj; GLuint m_depthTexture; GLuint m_depthModelTexture; GLuint m_depthFrameBuffer; @@ -85,17 +71,13 @@ private: GLfloat m_shadowQualityToShader; bool m_flatSupported; bool m_selectionActive; - bool m_xFlipped; - bool m_zFlipped; - bool m_yFlipped; AbstractRenderItem m_dummyRenderItem; GLint m_shadowQualityMultiplier; QSizeF m_areaSize; bool m_hasHeightAdjustmentChanged; QPoint m_selectedPoint; - const QSurface3DSeries *m_selectedSeries; + QSurface3DSeries *m_selectedSeries; QPoint m_clickedPosition; - QHash<QSurface3DSeries *, SurfaceSeriesRenderCache *> m_renderCacheList; bool m_selectionTexturesDirty; GLuint m_noShadowTexture; @@ -104,19 +86,19 @@ public: ~Surface3DRenderer(); void updateData(); - void updateSeries(const QList<QAbstract3DSeries *> &seriesList, bool updateVisibility); + void updateSeries(const QList<QAbstract3DSeries *> &seriesList); + SeriesRenderCache *createNewCache(QAbstract3DSeries *series); + void cleanCache(SeriesRenderCache *cache); void updateSelectionMode(QAbstract3DGraph::SelectionFlags mode); - void modifiedSeriesList(const QVector<QSurface3DSeries *> &seriesList); void updateRows(const QVector<Surface3DController::ChangeRow> &rows); - void updateItem(const QVector<Surface3DController::ChangeItem> &points); - void updateAxisRange(QAbstract3DAxis::AxisOrientation orientation, float min, float max); + void updateItems(const QVector<Surface3DController::ChangeItem> &points); void updateScene(Q3DScene *scene); void updateSlicingActive(bool isSlicing); - void updateSelectedPoint(const QPoint &position, const QSurface3DSeries *series); + void updateSelectedPoint(const QPoint &position, QSurface3DSeries *series); inline QPoint clickedPosition() const { return m_clickedPosition; } void resetClickedStatus(); + QVector3D convertPositionToTranslation(const QVector3D &position, bool isAbsolute); - void drawSlicedScene(); void render(GLuint defaultFboHandle = 0); protected: @@ -136,11 +118,14 @@ private: void updateShadowQuality(QAbstract3DGraph::ShadowQuality quality); void updateTextures(); void initShaders(const QString &vertexShader, const QString &fragmentShader); - QRect calculateSampleRect(SurfaceSeriesRenderCache *cache, const QSurfaceDataArray &array); + QRect calculateSampleRect(const QSurfaceDataArray &array); void loadBackgroundMesh(); - void loadGridLineMesh(); - void loadLabelMesh(); + + void drawSlicedScene(); void drawScene(GLuint defaultFboHandle); + void drawLabels(bool drawSelection, const Q3DCamera *activeCamera, + const QMatrix4x4 &viewMatrix, const QMatrix4x4 &projectionMatrix); + void calculateSceneScalingFactors(); void initBackgroundShaders(const QString &vertexShader, const QString &fragmentShader); void initLabelShaders(const QString &vertexShader, const QString &fragmentShader); @@ -155,13 +140,14 @@ private: void surfacePointSelected(const QPoint &point); void updateSelectionPoint(SurfaceSeriesRenderCache *cache, const QPoint &point, bool label); QPoint selectionIdToSurfacePoint(uint id); - QString createSelectionLabel(SurfaceSeriesRenderCache *cache, float value, int column, int row); #if !defined(QT_OPENGL_ES_2) void updateDepthBuffer(); #endif void emitSelectedPointChanged(QPoint position); Q_DISABLE_COPY(Surface3DRenderer) + + friend class SurfaceObject; }; QT_END_NAMESPACE_DATAVISUALIZATION diff --git a/src/datavisualization/engine/surfaceseriesrendercache.cpp b/src/datavisualization/engine/surfaceseriesrendercache.cpp index ba25d71d..1cce5288 100644 --- a/src/datavisualization/engine/surfaceseriesrendercache.cpp +++ b/src/datavisualization/engine/surfaceseriesrendercache.cpp @@ -16,35 +16,30 @@ ** ****************************************************************************/ -#include "seriesrendercache_p.h" #include "surfaceseriesrendercache_p.h" -#include "objecthelper_p.h" -#include "abstract3drenderer_p.h" +#include "surface3drenderer_p.h" #include "texturehelper_p.h" -#include "utils_p.h" QT_BEGIN_NAMESPACE_DATAVISUALIZATION -SurfaceSeriesRenderCache::SurfaceSeriesRenderCache() - : m_surfaceVisible(false), +SurfaceSeriesRenderCache::SurfaceSeriesRenderCache(QAbstract3DSeries *series, + Surface3DRenderer *renderer) + : SeriesRenderCache(series, renderer), + m_surfaceVisible(false), m_surfaceGridVisible(false), - m_surfaceFlatShading(true), - m_surfaceObj(new SurfaceObject), - m_sliceSurfaceObj(new SurfaceObject), - m_sampleSpace(QRect(0, 0, 0 , 0)), + m_surfaceFlatShading(false), + m_surfaceObj(new SurfaceObject(renderer)), + m_sliceSurfaceObj(new SurfaceObject(renderer)), + m_sampleSpace(QRect(0, 0, 0, 0)), m_selectionTexture(0), m_selectionIdStart(0), m_selectionIdEnd(0), m_flatChangeAllowed(true), - m_flatStatusDirty(false), - m_scale(QVector3D(1.0f, 1.0f, 1.0f)), - m_offset(QVector3D(0.0f, 0.0f, 0.0f)), + m_flatStatusDirty(true), m_sliceSelectionPointer(0), m_mainSelectionPointer(0), m_slicePointerActive(false), - m_mainPointerActive(false), - m_valid(false), - m_objectDirty(true) + m_mainPointerActive(false) { } @@ -52,17 +47,15 @@ SurfaceSeriesRenderCache::~SurfaceSeriesRenderCache() { } -void SurfaceSeriesRenderCache::populate(QSurface3DSeries *series, Abstract3DRenderer *renderer) +void SurfaceSeriesRenderCache::populate(bool newSeries) { - Q_ASSERT(series); + SeriesRenderCache::populate(newSeries); - SeriesRenderCache::populate(series, renderer); - - QSurface3DSeries::DrawFlags drawMode = series->drawMode(); + QSurface3DSeries::DrawFlags drawMode = series()->drawMode(); m_surfaceVisible = drawMode.testFlag(QSurface3DSeries::DrawSurface); m_surfaceGridVisible = drawMode.testFlag(QSurface3DSeries::DrawWireframe); - if (m_flatChangeAllowed && m_surfaceFlatShading != series->isFlatShadingEnabled()) { - m_surfaceFlatShading = series->isFlatShadingEnabled(); + if (m_flatChangeAllowed && m_surfaceFlatShading != series()->isFlatShadingEnabled()) { + m_surfaceFlatShading = series()->isFlatShadingEnabled(); m_flatStatusDirty = true; } } diff --git a/src/datavisualization/engine/surfaceseriesrendercache_p.h b/src/datavisualization/engine/surfaceseriesrendercache_p.h index 2dda0670..b6254a75 100644 --- a/src/datavisualization/engine/surfaceseriesrendercache_p.h +++ b/src/datavisualization/engine/surfaceseriesrendercache_p.h @@ -31,7 +31,6 @@ #include "datavisualizationglobal_p.h" #include "seriesrendercache_p.h" -#include "qabstract3dseries_p.h" #include "qsurface3dseries_p.h" #include "surfaceobject_p.h" #include "selectionpointer_p.h" @@ -40,17 +39,15 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION -class Abstract3DRenderer; -class ObjectHelper; -class TextureHelper; +class Surface3DRenderer; class SurfaceSeriesRenderCache : public SeriesRenderCache { public: - SurfaceSeriesRenderCache(); + SurfaceSeriesRenderCache(QAbstract3DSeries *series, Surface3DRenderer *renderer); virtual ~SurfaceSeriesRenderCache(); - void populate(QSurface3DSeries *series, Abstract3DRenderer *renderer); + virtual void populate(bool newSeries); virtual void cleanup(TextureHelper *texHelper); inline bool surfaceVisible() const { return m_surfaceVisible; } @@ -58,8 +55,6 @@ public: inline bool isFlatShadingEnabled() const { return m_surfaceFlatShading; } inline void setFlatShadingEnabled(bool enabled) { m_surfaceFlatShading = enabled; } inline void setFlatChangeAllowed(bool allowed) { m_flatChangeAllowed = allowed; } - inline void setValid(bool valid) { m_valid = valid; } - inline bool isValid() const { return m_valid; } inline SurfaceObject *surfaceObject() { return m_surfaceObj; } inline SurfaceObject *sliceSurfaceObject() { return m_sliceSurfaceObj; } inline const QRect &sampleSpace() const { return m_sampleSpace; } @@ -67,11 +62,8 @@ public: inline QSurface3DSeries *series() const { return static_cast<QSurface3DSeries *>(m_series); } inline QSurfaceDataArray &dataArray() { return m_dataArray; } inline QSurfaceDataArray &sliceDataArray() { return m_sliceDataArray; } - inline bool isSeriesVisible() const { return m_series->isVisible(); } - inline bool renderable() const { return m_series->isVisible() && (m_surfaceVisible || - m_surfaceGridVisible); } - inline void setObjectDirty(bool state) { m_objectDirty = state; } - inline bool objectDirty() const { return m_objectDirty; } + inline bool renderable() const { return m_visible && (m_surfaceVisible || + m_surfaceGridVisible); } inline void setSelectionTexture(GLuint texture) { m_selectionTexture = texture; } inline GLuint selectionTexture() const { return m_selectionTexture; } inline void setSelectionIdRange(uint start, uint end) { m_selectionIdStart = start; @@ -81,11 +73,6 @@ public: selection <= m_selectionIdEnd; } inline bool isFlatStatusDirty() const { return m_flatStatusDirty; } inline void setFlatStatusDirty(bool status) { m_flatStatusDirty = status; } - inline void setScale(const QVector3D &scale) { m_scale = scale; } - inline const QVector3D &scale() const { return m_scale; } - inline void setOffset(const QVector3D &offset) { m_offset = offset; } - inline const QVector3D &offset() const { return m_offset; } - // m_MVPMatrix is volatile, used only for optimizing rendering a bit inline void setMVPMatrix(const QMatrix4x4 &matrix) { m_MVPMatrix = matrix; } inline const QMatrix4x4 &MVPMatrix() { return m_MVPMatrix; } @@ -113,16 +100,11 @@ protected: uint m_selectionIdEnd; bool m_flatChangeAllowed; bool m_flatStatusDirty; - QVector3D m_scale; - QVector3D m_offset; QMatrix4x4 m_MVPMatrix; SelectionPointer *m_sliceSelectionPointer; SelectionPointer *m_mainSelectionPointer; bool m_slicePointerActive; bool m_mainPointerActive; - - bool m_valid; - bool m_objectDirty; }; QT_END_NAMESPACE_DATAVISUALIZATION |