summaryrefslogtreecommitdiffstats
path: root/src/datavisualization
diff options
context:
space:
mode:
Diffstat (limited to 'src/datavisualization')
-rw-r--r--src/datavisualization/axis/axis.pri2
-rw-r--r--src/datavisualization/axis/qlogvalue3daxisformatter.cpp1
-rw-r--r--src/datavisualization/axis/qvalue3daxis.cpp24
-rw-r--r--src/datavisualization/axis/qvalue3daxisformatter.cpp40
-rw-r--r--src/datavisualization/axis/qvalue3daxisformatter.h4
-rw-r--r--src/datavisualization/axis/qvalue3daxisformatter_p.h8
-rw-r--r--src/datavisualization/common.pri7
-rw-r--r--src/datavisualization/data/abstractrenderitem_p.h6
-rw-r--r--src/datavisualization/data/customrenderitem.cpp68
-rw-r--r--src/datavisualization/data/customrenderitem_p.h109
-rw-r--r--src/datavisualization/data/data.pri9
-rw-r--r--src/datavisualization/data/labelitem.cpp6
-rw-r--r--src/datavisualization/data/qabstract3dseries.cpp3
-rw-r--r--src/datavisualization/data/qabstract3dseries.h1
-rw-r--r--src/datavisualization/data/qbar3dseries.cpp21
-rw-r--r--src/datavisualization/data/qcustom3ditem.cpp101
-rw-r--r--src/datavisualization/data/qcustom3ditem.h5
-rw-r--r--src/datavisualization/data/qcustom3ditem_p.h5
-rw-r--r--src/datavisualization/data/qcustom3dlabel.cpp1
-rw-r--r--src/datavisualization/data/qcustom3dvolume.cpp1278
-rw-r--r--src/datavisualization/data/qcustom3dvolume.h147
-rw-r--r--src/datavisualization/data/qcustom3dvolume_p.h108
-rw-r--r--src/datavisualization/data/qheightmapsurfacedataproxy.cpp16
-rw-r--r--src/datavisualization/data/qsurface3dseries.cpp66
-rw-r--r--src/datavisualization/data/qsurface3dseries.h9
-rw-r--r--src/datavisualization/data/qsurface3dseries_p.h3
-rw-r--r--src/datavisualization/data/qsurfacedataproxy.cpp4
-rw-r--r--src/datavisualization/datavisualization.pro8
-rw-r--r--src/datavisualization/doc/qtdatavisualization.qdocconf12
-rw-r--r--src/datavisualization/doc/snippets/doc_src_qmldatavisualization.cpp8
-rw-r--r--src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc151
-rw-r--r--src/datavisualization/doc/src/qtdatavisualization-qml-bars3d.qdoc8
-rw-r--r--src/datavisualization/doc/src/qtdatavisualization-qml-surface3d.qdoc21
-rw-r--r--src/datavisualization/doc/src/qtdatavisualization.qdoc23
-rw-r--r--src/datavisualization/engine/abstract3dcontroller.cpp270
-rw-r--r--src/datavisualization/engine/abstract3dcontroller_p.h85
-rw-r--r--src/datavisualization/engine/abstract3drenderer.cpp1242
-rw-r--r--src/datavisualization/engine/abstract3drenderer_p.h119
-rw-r--r--src/datavisualization/engine/axisrendercache.cpp18
-rw-r--r--src/datavisualization/engine/axisrendercache_p.h2
-rw-r--r--src/datavisualization/engine/bars3dcontroller.cpp30
-rw-r--r--src/datavisualization/engine/bars3dcontroller_p.h7
-rw-r--r--src/datavisualization/engine/bars3drenderer.cpp869
-rw-r--r--src/datavisualization/engine/bars3drenderer_p.h28
-rw-r--r--src/datavisualization/engine/drawer.cpp68
-rw-r--r--src/datavisualization/engine/drawer_p.h4
-rw-r--r--src/datavisualization/engine/engine.pri2
-rw-r--r--src/datavisualization/engine/engine.qrc13
-rw-r--r--src/datavisualization/engine/q3dbars.cpp20
-rw-r--r--src/datavisualization/engine/q3dbars.h4
-rw-r--r--src/datavisualization/engine/q3dcamera.cpp233
-rw-r--r--src/datavisualization/engine/q3dcamera.h13
-rw-r--r--src/datavisualization/engine/q3dcamera_p.h19
-rw-r--r--src/datavisualization/engine/q3dlight.cpp9
-rw-r--r--src/datavisualization/engine/q3dobject.cpp9
-rw-r--r--src/datavisualization/engine/q3dscene.cpp123
-rw-r--r--src/datavisualization/engine/q3dscene.h5
-rw-r--r--src/datavisualization/engine/q3dscene_p.h7
-rw-r--r--src/datavisualization/engine/q3dsurface.cpp27
-rw-r--r--src/datavisualization/engine/q3dsurface.h4
-rw-r--r--src/datavisualization/engine/qabstract3dgraph.cpp300
-rw-r--r--src/datavisualization/engine/qabstract3dgraph.h41
-rw-r--r--src/datavisualization/engine/scatter3drenderer.cpp1661
-rw-r--r--src/datavisualization/engine/scatter3drenderer_p.h32
-rw-r--r--src/datavisualization/engine/scatterseriesrendercache.cpp4
-rw-r--r--src/datavisualization/engine/scatterseriesrendercache_p.h10
-rw-r--r--src/datavisualization/engine/selectionpointer.cpp33
-rw-r--r--src/datavisualization/engine/selectionpointer_p.h2
-rw-r--r--src/datavisualization/engine/seriesrendercache.cpp8
-rw-r--r--src/datavisualization/engine/seriesrendercache_p.h3
-rw-r--r--src/datavisualization/engine/shaders/3dsliceframes.frag15
-rw-r--r--src/datavisualization/engine/shaders/default.frag3
-rw-r--r--src/datavisualization/engine/shaders/defaultNoMatrices.vert26
-rw-r--r--src/datavisualization/engine/shaders/default_ES2.frag2
-rw-r--r--src/datavisualization/engine/shaders/point_ES2_UV.vert12
-rw-r--r--src/datavisualization/engine/shaders/position.vert10
-rw-r--r--src/datavisualization/engine/shaders/positionmap.frag11
-rw-r--r--src/datavisualization/engine/shaders/shadowNoMatrices.vert36
-rw-r--r--src/datavisualization/engine/shaders/shadowNoTex.frag2
-rw-r--r--src/datavisualization/engine/shaders/surface.frag4
-rw-r--r--src/datavisualization/engine/shaders/surfaceFlat.frag4
-rw-r--r--src/datavisualization/engine/shaders/surfaceFlat.vert3
-rw-r--r--src/datavisualization/engine/shaders/surfaceShadowFlat.frag10
-rw-r--r--src/datavisualization/engine/shaders/surfaceShadowFlat.vert16
-rw-r--r--src/datavisualization/engine/shaders/surfaceShadowNoTex.frag8
-rw-r--r--src/datavisualization/engine/shaders/surfaceTexturedFlat.frag39
-rw-r--r--src/datavisualization/engine/shaders/surfaceTexturedShadow.frag67
-rw-r--r--src/datavisualization/engine/shaders/surfaceTexturedShadowFlat.frag72
-rw-r--r--src/datavisualization/engine/shaders/surface_ES2.frag4
-rw-r--r--src/datavisualization/engine/shaders/texture3d.frag155
-rw-r--r--src/datavisualization/engine/shaders/texture3d.vert36
-rw-r--r--src/datavisualization/engine/shaders/texture3dlowdef.frag109
-rw-r--r--src/datavisualization/engine/shaders/texture3dslice.frag155
-rw-r--r--src/datavisualization/engine/surface3dcontroller.cpp42
-rw-r--r--src/datavisualization/engine/surface3dcontroller_p.h20
-rw-r--r--src/datavisualization/engine/surface3drenderer.cpp1326
-rw-r--r--src/datavisualization/engine/surface3drenderer_p.h32
-rw-r--r--src/datavisualization/engine/surfaceseriesrendercache.cpp7
-rw-r--r--src/datavisualization/engine/surfaceseriesrendercache_p.h3
-rw-r--r--src/datavisualization/global/datavisualizationglobal_p.h2
-rw-r--r--src/datavisualization/global/global.pri2
-rw-r--r--src/datavisualization/global/qdatavisualizationglobal.h4
-rw-r--r--src/datavisualization/input/input.pri2
-rw-r--r--src/datavisualization/input/q3dinputhandler.cpp306
-rw-r--r--src/datavisualization/input/q3dinputhandler.h19
-rw-r--r--src/datavisualization/input/q3dinputhandler_p.h25
-rw-r--r--src/datavisualization/input/qtouch3dinputhandler.cpp158
-rw-r--r--src/datavisualization/input/qtouch3dinputhandler_p.h9
-rw-r--r--src/datavisualization/theme/q3dtheme.cpp6
-rw-r--r--src/datavisualization/theme/theme.pri2
-rw-r--r--src/datavisualization/utils/abstractobjecthelper.cpp1
-rw-r--r--src/datavisualization/utils/abstractobjecthelper_p.h4
-rw-r--r--src/datavisualization/utils/objecthelper.cpp5
-rw-r--r--src/datavisualization/utils/objecthelper_p.h2
-rw-r--r--src/datavisualization/utils/qutils.h12
-rw-r--r--src/datavisualization/utils/scatterobjectbufferhelper.cpp237
-rw-r--r--src/datavisualization/utils/scatterobjectbufferhelper_p.h13
-rw-r--r--src/datavisualization/utils/scatterpointbufferhelper.cpp109
-rw-r--r--src/datavisualization/utils/scatterpointbufferhelper_p.h13
-rw-r--r--src/datavisualization/utils/shaderhelper.cpp177
-rw-r--r--src/datavisualization/utils/shaderhelper_p.h112
-rw-r--r--src/datavisualization/utils/surfaceobject.cpp745
-rw-r--r--src/datavisualization/utils/surfaceobject_p.h52
-rw-r--r--src/datavisualization/utils/texturehelper.cpp244
-rw-r--r--src/datavisualization/utils/texturehelper_p.h14
-rw-r--r--src/datavisualization/utils/utils.cpp252
-rw-r--r--src/datavisualization/utils/utils.pri2
-rw-r--r--src/datavisualization/utils/utils_p.h15
128 files changed, 9643 insertions, 2737 deletions
diff --git a/src/datavisualization/axis/axis.pri b/src/datavisualization/axis/axis.pri
index 0173b597..4c142c91 100644
--- a/src/datavisualization/axis/axis.pri
+++ b/src/datavisualization/axis/axis.pri
@@ -16,3 +16,5 @@ SOURCES += \
$$PWD/qcategory3daxis.cpp \
$$PWD/qvalue3daxisformatter.cpp \
$$PWD/qlogvalue3daxisformatter.cpp
+
+INCLUDEPATH += $$PWD
diff --git a/src/datavisualization/axis/qlogvalue3daxisformatter.cpp b/src/datavisualization/axis/qlogvalue3daxisformatter.cpp
index 7367e7c5..85fd5c4f 100644
--- a/src/datavisualization/axis/qlogvalue3daxisformatter.cpp
+++ b/src/datavisualization/axis/qlogvalue3daxisformatter.cpp
@@ -42,6 +42,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
* \since QtDataVisualization 1.1
* \ingroup datavisualization_qml
* \instantiates QLogValue3DAxisFormatter
+ * \inherits ValueAxis3DFormatter
* \brief LogValueAxis3DFormatter implements logarithmic value axis formatter.
*
* This type provides formatting rules for a logarithmic ValueAxis3D.
diff --git a/src/datavisualization/axis/qvalue3daxis.cpp b/src/datavisualization/axis/qvalue3daxis.cpp
index 8207174f..3b9c9e3d 100644
--- a/src/datavisualization/axis/qvalue3daxis.cpp
+++ b/src/datavisualization/axis/qvalue3daxis.cpp
@@ -18,6 +18,7 @@
#include "qvalue3daxis_p.h"
#include "qvalue3daxisformatter_p.h"
+#include "abstract3dcontroller_p.h"
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
@@ -68,8 +69,16 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
/*!
* \qmlproperty string ValueAxis3D::labelFormat
*
- * Defines the label format to be used for the labels on this axis. Supported specifiers are:
+ * Defines the label format to be used for the labels on this axis. How the format is interpreted
+ * depends on the axis formatter and the locale in use. Using the default formatter and default
+ * locale (\c{"C"}), the formatting uses QString::sprintf(). Supported specifiers are:
* \c {d, i, o, x, X, f, F, e, E, g, G, c}. See QString::sprintf() for additional details.
+ * For other locales, the default formatter uses reduced set of printf format specifiers:
+ * \c {d, i, f, F, e, E, g, G}. In these cases, the only supported modifier is the precision
+ * modifier for the floating point and exponential formats. The decimal point and other locale
+ * dependent formatting is done according to the graph locale.
+ *
+ * \sa AbstractGraph3D::locale
*/
/*!
@@ -164,12 +173,20 @@ int QValue3DAxis::subSegmentCount() const
/*!
* \property QValue3DAxis::labelFormat
*
- * Defines the label \a format to be used for the labels on this axis. Supported specifiers are:
+ * Defines the label format to be used for the labels on this axis. How the format is interpreted
+ * depends on the axis formatter and the locale in use. Using the default formatter and default
+ * locale (\c{"C"}), the formatting uses QString::sprintf(). Supported specifiers are:
* \c {d, i, o, x, X, f, F, e, E, g, G, c}. See QString::sprintf() for additional details.
+ * For other locales, the default formatter uses reduced set of printf format specifiers:
+ * \c {d, i, f, F, e, E, g, G}. In these cases, the only supported modifier is the precision
+ * modifier for the floating point and exponential formats. The decimal point and other locale
+ * dependent formatting is done according to the graph locale.
*
* Usage example:
*
* \c {axis->setLabelFormat("%.2f mm");}
+ *
+ * \sa formatter, QAbstract3DGraph::locale
*/
void QValue3DAxis::setLabelFormat(const QString &format)
{
@@ -201,6 +218,9 @@ void QValue3DAxis::setFormatter(QValue3DAxisFormatter *formatter)
dptr()->m_formatter = formatter;
formatter->setParent(this);
formatter->d_ptr->setAxis(this);
+ Abstract3DController *controller = qobject_cast<Abstract3DController *>(parent());
+ if (controller)
+ formatter->setLocale(controller->locale());
emit formatterChanged(formatter);
emit dptr()->formatterDirty();
}
diff --git a/src/datavisualization/axis/qvalue3daxisformatter.cpp b/src/datavisualization/axis/qvalue3daxisformatter.cpp
index 56ca3b0f..f6b705a9 100644
--- a/src/datavisualization/axis/qvalue3daxisformatter.cpp
+++ b/src/datavisualization/axis/qvalue3daxisformatter.cpp
@@ -270,6 +270,28 @@ QStringList &QValue3DAxisFormatter::labelStrings() const
return d_ptr->m_labelStrings;
}
+/*!
+ * Sets the \a locale that this formatter uses.
+ * The graph automatically sets the formatter's locale to a graph's locale whenever the parent axis
+ * is set as an active axis of the graph, the axis formatter is set to an axis attached to
+ * the graph, or the graph's locale changes.
+ *
+ * \sa locale(), QAbstract3DGraph::locale
+ */
+void QValue3DAxisFormatter::setLocale(const QLocale &locale)
+{
+ d_ptr->m_cLocaleInUse = (locale == QLocale::c());
+ d_ptr->m_locale = locale;
+ markDirty(true);
+}
+/*!
+ * \return the current locale this formatter is using.
+ */
+QLocale QValue3DAxisFormatter::locale() const
+{
+ return d_ptr->m_locale;
+}
+
// QValue3DAxisFormatterPrivate
QValue3DAxisFormatterPrivate::QValue3DAxisFormatterPrivate(QValue3DAxisFormatter *q)
: QObject(0),
@@ -281,7 +303,10 @@ QValue3DAxisFormatterPrivate::QValue3DAxisFormatterPrivate(QValue3DAxisFormatter
m_axis(0),
m_preparsedParamType(Utils::ParamTypeUnknown),
m_allowNegatives(true),
- m_allowZero(true)
+ m_allowZero(true),
+ m_formatPrecision(6), // 6 and 'g' are defaults in Qt API for format precision and spec
+ m_formatSpec('g'),
+ m_cLocaleInUse(true)
{
}
@@ -363,12 +388,19 @@ QString QValue3DAxisFormatterPrivate::stringForValue(qreal value, const QString
{
if (m_previousLabelFormat.compare(format)) {
// Format string different than the previous one used, reparse it
- m_previousLabelFormat = format;
- m_preparsedParamType = Utils::findFormatParamType(format);
m_labelFormatArray = format.toUtf8();
+ m_previousLabelFormat = format;
+ m_preparsedParamType = Utils::preParseFormat(format, m_formatPreStr, m_formatPostStr,
+ m_formatPrecision, m_formatSpec);
}
- return Utils::formatLabel(m_labelFormatArray, m_preparsedParamType, value);
+ if (m_cLocaleInUse) {
+ return Utils::formatLabelSprintf(m_labelFormatArray, m_preparsedParamType, value);
+ } else {
+ return Utils::formatLabelLocalized(m_preparsedParamType, value, m_locale, m_formatPreStr,
+ m_formatPostStr, m_formatPrecision, m_formatSpec,
+ m_labelFormatArray);
+ }
}
float QValue3DAxisFormatterPrivate::positionAt(float value) const
diff --git a/src/datavisualization/axis/qvalue3daxisformatter.h b/src/datavisualization/axis/qvalue3daxisformatter.h
index c7b0bac5..82e49f21 100644
--- a/src/datavisualization/axis/qvalue3daxisformatter.h
+++ b/src/datavisualization/axis/qvalue3daxisformatter.h
@@ -24,6 +24,7 @@
#include <QtCore/QScopedPointer>
#include <QtCore/QVector>
#include <QtCore/QStringList>
+#include <QtCore/QLocale>
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
@@ -60,6 +61,9 @@ protected:
QVector<float> &labelPositions() const;
QStringList &labelStrings() const;
+ void setLocale(const QLocale &locale);
+ QLocale locale() const;
+
QScopedPointer<QValue3DAxisFormatterPrivate> d_ptr;
private:
diff --git a/src/datavisualization/axis/qvalue3daxisformatter_p.h b/src/datavisualization/axis/qvalue3daxisformatter_p.h
index 2d1dc920..9571d001 100644
--- a/src/datavisualization/axis/qvalue3daxisformatter_p.h
+++ b/src/datavisualization/axis/qvalue3daxisformatter_p.h
@@ -32,6 +32,7 @@
#include "datavisualizationglobal_p.h"
#include "qvalue3daxisformatter.h"
#include "utils_p.h"
+#include <QtCore/QLocale>
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
@@ -83,6 +84,13 @@ protected:
bool m_allowNegatives;
bool m_allowZero;
+ QLocale m_locale;
+ QString m_formatPreStr;
+ QString m_formatPostStr;
+ int m_formatPrecision;
+ char m_formatSpec;
+ bool m_cLocaleInUse;
+
friend class QValue3DAxisFormatter;
};
diff --git a/src/datavisualization/common.pri b/src/datavisualization/common.pri
deleted file mode 100644
index 9a497c2c..00000000
--- a/src/datavisualization/common.pri
+++ /dev/null
@@ -1,7 +0,0 @@
-INCLUDEPATH += $$PWD/engine \
- $$PWD/global \
- $$PWD/utils \
- $$PWD/axis \
- $$PWD/data \
- $$PWD/input \
- $$PWD/theme
diff --git a/src/datavisualization/data/abstractrenderitem_p.h b/src/datavisualization/data/abstractrenderitem_p.h
index 57977a3c..ccee2b00 100644
--- a/src/datavisualization/data/abstractrenderitem_p.h
+++ b/src/datavisualization/data/abstractrenderitem_p.h
@@ -48,10 +48,12 @@ public:
inline void setTranslation(const QVector3D &translation) { m_translation = translation; }
inline const QVector3D &translation() const {return m_translation; }
- inline QQuaternion rotation() const { return m_rotation; }
+ inline const QQuaternion &rotation() const { return m_rotation; }
inline void setRotation(const QQuaternion &rotation)
{
- if (m_rotation != rotation)
+ if (rotation.isNull())
+ m_rotation = identityQuaternion;
+ else
m_rotation = rotation;
}
diff --git a/src/datavisualization/data/customrenderitem.cpp b/src/datavisualization/data/customrenderitem.cpp
index a1c70057..f56ca86e 100644
--- a/src/datavisualization/data/customrenderitem.cpp
+++ b/src/datavisualization/data/customrenderitem.cpp
@@ -23,9 +23,32 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
CustomRenderItem::CustomRenderItem()
: AbstractRenderItem(),
m_texture(0),
+ m_positionAbsolute(false),
+ m_scalingAbsolute(true),
m_object(0),
+ m_needBlend(true),
m_visible(true),
- m_renderer(0)
+ m_valid(true),
+ m_index(0),
+ m_shadowCasting(false),
+ m_isFacingCamera(false),
+ m_item(0),
+ m_renderer(0),
+ m_labelItem(false),
+ m_textureWidth(0),
+ m_textureHeight(0),
+ m_textureDepth(0),
+ m_isVolume(false),
+ m_textureFormat(QImage::Format_ARGB32),
+ m_sliceIndexX(-1),
+ m_sliceIndexY(-1),
+ m_sliceIndexZ(-1),
+ m_alphaMultiplier(1.0f),
+ m_preserveOpacity(true),
+ m_useHighDefShader(true),
+ m_drawSlices(false),
+ m_drawSliceFrames(false)
+
{
}
@@ -39,4 +62,47 @@ void CustomRenderItem::setMesh(const QString &meshFile)
ObjectHelper::resetObjectHelper(m_renderer, m_object, meshFile);
}
+void CustomRenderItem::setColorTable(const QVector<QRgb> &colors)
+{
+ m_colorTable.resize(256);
+ for (int i = 0; i < 256; i++) {
+ if (i < colors.size()) {
+ const QRgb &rgb = colors.at(i);
+ m_colorTable[i] = QVector4D(float(qRed(rgb)) / 255.0f,
+ float(qGreen(rgb)) / 255.0f,
+ float(qBlue(rgb)) / 255.0f,
+ float(qAlpha(rgb)) / 255.0f);
+ } else {
+ m_colorTable[i] = QVector4D(0.0f, 0.0f, 0.0f, 0.0f);
+ }
+ }
+}
+
+void CustomRenderItem::setMinBounds(const QVector3D &bounds)
+{
+ m_minBounds = bounds;
+ m_minBoundsNormal = m_minBounds;
+ m_minBoundsNormal.setY(-m_minBoundsNormal.y());
+ m_minBoundsNormal.setZ(-m_minBoundsNormal.z());
+ m_minBoundsNormal = 0.5f * (m_minBoundsNormal + oneVector);
+}
+
+void CustomRenderItem::setMaxBounds(const QVector3D &bounds)
+{
+ m_maxBounds = bounds;
+ m_maxBoundsNormal = m_maxBounds;
+ m_maxBoundsNormal.setY(-m_maxBoundsNormal.y());
+ m_maxBoundsNormal.setZ(-m_maxBoundsNormal.z());
+ m_maxBoundsNormal = 0.5f * (m_maxBoundsNormal + oneVector);
+}
+
+void CustomRenderItem::setSliceFrameColor(const QColor &color)
+{
+ const QRgb &rgb = color.rgba();
+ m_sliceFrameColor = QVector4D(float(qRed(rgb)) / 255.0f,
+ float(qGreen(rgb)) / 255.0f,
+ float(qBlue(rgb)) / 255.0f,
+ float(1.0f)); // Alpha not supported for frames
+}
+
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/data/customrenderitem_p.h b/src/datavisualization/data/customrenderitem_p.h
index 4fb94276..ad868fac 100644
--- a/src/datavisualization/data/customrenderitem_p.h
+++ b/src/datavisualization/data/customrenderitem_p.h
@@ -31,6 +31,9 @@
#include "abstractrenderitem_p.h"
#include "objecthelper_p.h"
+#include <QtGui/QRgb>
+#include <QtGui/QImage>
+#include <QtGui/QColor>
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
@@ -48,11 +51,17 @@ public:
void setMesh(const QString &meshFile);
inline ObjectHelper *mesh() const { return m_object; }
inline void setScaling(const QVector3D &scaling) { m_scaling = scaling; }
- inline QVector3D scaling() const { return m_scaling; }
+ inline const QVector3D &scaling() const { return m_scaling; }
+ inline void setOrigScaling(const QVector3D &scaling) { m_origScaling = scaling; }
+ inline const QVector3D &origScaling() const { return m_origScaling; }
inline void setPosition(const QVector3D &position) { m_position = position; }
- inline QVector3D position() const { return m_position; }
- inline void setPositionAbsolute(bool absolute) { m_absolute = absolute; }
- inline bool isPositionAbsolute() const { return m_absolute; }
+ inline const QVector3D &position() const { return m_position; }
+ inline void setOrigPosition(const QVector3D &position) { m_origPosition = position; }
+ inline const QVector3D &origPosition() const { return m_origPosition; }
+ inline void setPositionAbsolute(bool absolute) { m_positionAbsolute = absolute; }
+ inline bool isPositionAbsolute() const { return m_positionAbsolute; }
+ inline void setScalingAbsolute(bool absolute) { m_scalingAbsolute = absolute; }
+ inline bool isScalingAbsolute() const { return m_scalingAbsolute; }
inline void setBlendNeeded(bool blend) { m_needBlend = blend; }
inline bool isBlendNeeded() const { return m_needBlend; }
inline void setVisible(bool visible) { m_visible = visible; }
@@ -68,14 +77,78 @@ public:
inline void setFacingCamera(bool facing) { m_isFacingCamera = facing; }
inline bool isFacingCamera() const { return m_isFacingCamera; }
inline void setRenderer(Abstract3DRenderer *renderer) { m_renderer = renderer; }
+ inline void setLabelItem(bool isLabel) { m_labelItem = isLabel; }
+ inline bool isLabel() const { return m_labelItem; }
+
+ // Volume specific
+ inline void setTextureWidth(int width) { m_textureWidth = width; setSliceIndexX(m_sliceIndexX); }
+ inline int textureWidth() const { return m_textureWidth; }
+ inline void setTextureHeight(int height) { m_textureHeight = height; setSliceIndexY(m_sliceIndexY); }
+ inline int textureHeight() const { return m_textureHeight; }
+ inline void setTextureDepth(int depth) { m_textureDepth = depth; setSliceIndexZ(m_sliceIndexZ); }
+ inline int textureDepth() const { return m_textureDepth; }
+ inline int textureSize() const { return m_textureWidth * m_textureHeight * m_textureDepth; }
+ inline void setColorTable(const QVector<QVector4D> &colors) { m_colorTable = colors; }
+ void setColorTable(const QVector<QRgb> &colors);
+ inline const QVector<QVector4D> &colorTable() const { return m_colorTable; }
+ inline void setVolume(bool volume) { m_isVolume = volume; }
+ inline bool isVolume() const { return m_isVolume; }
+ inline void setTextureFormat(QImage::Format format) { m_textureFormat = format; }
+ inline QImage::Format textureFormat() const { return m_textureFormat; }
+ inline void setSliceIndexX(int index)
+ {
+ m_sliceIndexX = index;
+ m_sliceFractions.setX((float(index) + 0.5f) / float(m_textureWidth) * 2.0 - 1.0);
+ }
+ inline void setSliceIndexY(int index)
+ {
+ m_sliceIndexY = index;
+ m_sliceFractions.setY((float(index) + 0.5f) / float(m_textureHeight) * 2.0 - 1.0);
+ }
+ inline void setSliceIndexZ(int index)
+ {
+ m_sliceIndexZ = index;
+ m_sliceFractions.setZ((float(index) + 0.5f) / float(m_textureDepth) * 2.0 - 1.0);
+ }
+ inline const QVector3D &sliceFractions() const { return m_sliceFractions; }
+ inline int sliceIndexX() const { return m_sliceIndexX; }
+ inline int sliceIndexY() const { return m_sliceIndexY; }
+ inline int sliceIndexZ() const { return m_sliceIndexZ; }
+ inline void setAlphaMultiplier(float mult) { m_alphaMultiplier = mult; }
+ inline float alphaMultiplier() const { return m_alphaMultiplier; }
+ inline void setPreserveOpacity(bool enable) { m_preserveOpacity = enable; }
+ inline bool preserveOpacity() const { return m_preserveOpacity; }
+ inline void setUseHighDefShader(bool enable) { m_useHighDefShader = enable; }
+ inline bool useHighDefShader() const {return m_useHighDefShader; }
+ void setMinBounds(const QVector3D &bounds);
+ inline const QVector3D &minBounds() const { return m_minBounds; }
+ void setMaxBounds(const QVector3D &bounds);
+ inline const QVector3D &maxBounds() const { return m_maxBounds; }
+ inline const QVector3D &minBoundsNormal() const { return m_minBoundsNormal; }
+ inline const QVector3D &maxBoundsNormal() const { return m_maxBoundsNormal; }
+ inline void setDrawSlices(bool enable) { m_drawSlices = enable; }
+ inline bool drawSlices() const {return m_drawSlices; }
+ inline void setDrawSliceFrames(bool enable) { m_drawSliceFrames = enable; }
+ inline bool drawSliceFrames() const {return m_drawSliceFrames; }
+ void setSliceFrameColor(const QColor &color);
+ inline const QVector4D &sliceFrameColor() const { return m_sliceFrameColor; }
+ inline void setSliceFrameWidths(const QVector3D &widths) { m_sliceFrameWidths = widths * 2.0f; }
+ inline const QVector3D &sliceFrameWidths() const { return m_sliceFrameWidths; }
+ inline void setSliceFrameGaps(const QVector3D &gaps) { m_sliceFrameGaps = gaps * 2.0f; }
+ inline const QVector3D &sliceFrameGaps() const { return m_sliceFrameGaps; }
+ inline void setSliceFrameThicknesses(const QVector3D &thicknesses) { m_sliceFrameThicknesses = thicknesses; }
+ inline const QVector3D &sliceFrameThicknesses() const { return m_sliceFrameThicknesses; }
private:
Q_DISABLE_COPY(CustomRenderItem)
GLuint m_texture;
QVector3D m_scaling;
+ QVector3D m_origScaling;
QVector3D m_position;
- bool m_absolute;
+ QVector3D m_origPosition;
+ bool m_positionAbsolute;
+ bool m_scalingAbsolute;
ObjectHelper *m_object; // shared reference
bool m_needBlend;
bool m_visible;
@@ -85,6 +158,32 @@ private:
bool m_isFacingCamera;
QCustom3DItem *m_item;
Abstract3DRenderer *m_renderer;
+ bool m_labelItem;
+
+ // Volume specific
+ int m_textureWidth;
+ int m_textureHeight;
+ int m_textureDepth;
+ QVector<QVector4D> m_colorTable;
+ bool m_isVolume;
+ QImage::Format m_textureFormat;
+ int m_sliceIndexX;
+ int m_sliceIndexY;
+ int m_sliceIndexZ;
+ QVector3D m_sliceFractions;
+ float m_alphaMultiplier;
+ bool m_preserveOpacity;
+ bool m_useHighDefShader;
+ QVector3D m_minBounds;
+ QVector3D m_maxBounds;
+ QVector3D m_minBoundsNormal;
+ QVector3D m_maxBoundsNormal;
+ bool m_drawSlices;
+ bool m_drawSliceFrames;
+ QVector4D m_sliceFrameColor;
+ QVector3D m_sliceFrameWidths;
+ QVector3D m_sliceFrameGaps;
+ QVector3D m_sliceFrameThicknesses;
};
typedef QHash<QCustom3DItem *, CustomRenderItem *> CustomRenderItemArray;
diff --git a/src/datavisualization/data/data.pri b/src/datavisualization/data/data.pri
index 73f398bf..e2bbd6eb 100644
--- a/src/datavisualization/data/data.pri
+++ b/src/datavisualization/data/data.pri
@@ -41,7 +41,9 @@ HEADERS += \
$$PWD/qcustom3ditem.h \
$$PWD/qcustom3ditem_p.h \
$$PWD/qcustom3dlabel.h \
- $$PWD/qcustom3dlabel_p.h
+ $$PWD/qcustom3dlabel_p.h \
+ $$PWD/qcustom3dvolume.h \
+ $$PWD/qcustom3dvolume_p.h
SOURCES += \
$$PWD/labelitem.cpp \
@@ -69,4 +71,7 @@ SOURCES += \
$$PWD/qsurface3dseries.cpp \
$$PWD/customrenderitem.cpp \
$$PWD/qcustom3ditem.cpp \
- $$PWD/qcustom3dlabel.cpp
+ $$PWD/qcustom3dlabel.cpp \
+ $$PWD/qcustom3dvolume.cpp
+
+INCLUDEPATH += $$PWD
diff --git a/src/datavisualization/data/labelitem.cpp b/src/datavisualization/data/labelitem.cpp
index 859b0550..ec8ba3fd 100644
--- a/src/datavisualization/data/labelitem.cpp
+++ b/src/datavisualization/data/labelitem.cpp
@@ -28,7 +28,7 @@ LabelItem::LabelItem()
LabelItem::~LabelItem()
{
- glDeleteTextures(1, &m_textureId);
+ QOpenGLContext::currentContext()->functions()->glDeleteTextures(1, &m_textureId);
}
void LabelItem::setSize(const QSize &size)
@@ -43,7 +43,7 @@ QSize LabelItem::size() const
void LabelItem::setTextureId(GLuint textureId)
{
- glDeleteTextures(1, &m_textureId);
+ QOpenGLContext::currentContext()->functions()->glDeleteTextures(1, &m_textureId);
m_textureId = textureId;
}
@@ -55,7 +55,7 @@ GLuint LabelItem::textureId() const
void LabelItem::clear()
{
if (m_textureId) {
- glDeleteTextures(1, &m_textureId);
+ QOpenGLContext::currentContext()->functions()->glDeleteTextures(1, &m_textureId);
m_textureId = 0;
}
m_size = QSize(0, 0);
diff --git a/src/datavisualization/data/qabstract3dseries.cpp b/src/datavisualization/data/qabstract3dseries.cpp
index f8fe6b4f..caa95062 100644
--- a/src/datavisualization/data/qabstract3dseries.cpp
+++ b/src/datavisualization/data/qabstract3dseries.cpp
@@ -657,6 +657,9 @@ QAbstract3DSeriesPrivate::QAbstract3DSeriesPrivate(QAbstract3DSeries *q,
m_mesh(QAbstract3DSeries::MeshCube),
m_meshSmooth(false),
m_colorStyle(Q3DTheme::ColorStyleUniform),
+ m_baseColor(Qt::black),
+ m_singleHighlightColor(Qt::black),
+ m_multiHighlightColor(Qt::black),
m_itemLabelDirty(true),
m_itemLabelVisible(true)
{
diff --git a/src/datavisualization/data/qabstract3dseries.h b/src/datavisualization/data/qabstract3dseries.h
index 87c4f3c1..c26c40d1 100644
--- a/src/datavisualization/data/qabstract3dseries.h
+++ b/src/datavisualization/data/qabstract3dseries.h
@@ -153,6 +153,7 @@ private:
friend class Bars3DController;
friend class Surface3DController;
friend class Surface3DRenderer;
+ friend class Scatter3DRenderer;
friend class Scatter3DController;
friend class QBar3DSeries;
friend class SeriesRenderCache;
diff --git a/src/datavisualization/data/qbar3dseries.cpp b/src/datavisualization/data/qbar3dseries.cpp
index 19c3bf79..0aa60b66 100644
--- a/src/datavisualization/data/qbar3dseries.cpp
+++ b/src/datavisualization/data/qbar3dseries.cpp
@@ -43,26 +43,27 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
* \row
* \li @valueTitle \li Title from value axis
* \row
- * \li @rowIdx \li Visible row index
+ * \li @rowIdx \li Visible row index. Localized using graph locale.
* \row
- * \li @colIdx \li Visible Column index
+ * \li @colIdx \li Visible Column index. Localized using graph locale.
* \row
* \li @rowLabel \li Label from row axis
* \row
* \li @colLabel \li Label from column axis
* \row
- * \li @valueLabel \li Item value formatted using the same format the value axis attached to the graph uses,
- * see \l{QValue3DAxis::setLabelFormat()} for more information.
+ * \li @valueLabel \li Item value formatted using the same format the value axis attached to
+ * the graph uses. See \l{QValue3DAxis::labelFormat} for more information.
* \row
* \li @seriesName \li Name of the series
* \row
- * \li %<format spec> \li Item value in specified format.
+ * \li %<format spec> \li Item value in specified format. Formatted using the same rules as
+ * \l{QValue3DAxis::labelFormat}.
* \endtable
*
* For example:
* \snippet doc_src_qtdatavisualization.cpp 1
*
- * \sa {Qt Data Visualization Data Handling}
+ * \sa {Qt Data Visualization Data Handling}, QAbstract3DGraph::locale
*/
/*!
@@ -334,6 +335,10 @@ void QBar3DSeriesPrivate::createItemLabel()
return;
}
+ QLocale locale(QLocale::c());
+ if (m_controller)
+ locale = m_controller->locale();
+
QCategory3DAxis *categoryAxisZ = static_cast<QCategory3DAxis *>(m_controller->axisZ());
QCategory3DAxis *categoryAxisX = static_cast<QCategory3DAxis *>(m_controller->axisX());
QValue3DAxis *valueAxis = static_cast<QValue3DAxis *>(m_controller->axisY());
@@ -344,13 +349,13 @@ void QBar3DSeriesPrivate::createItemLabel()
int selBarPosRow = m_selectedBar.x();
int selBarPosCol = m_selectedBar.y();
- m_itemLabel.replace(rowIndexTag, QString::number(selBarPosRow));
+ m_itemLabel.replace(rowIndexTag, locale.toString(selBarPosRow));
if (categoryAxisZ->labels().size() > selBarPosRow)
m_itemLabel.replace(rowLabelTag, categoryAxisZ->labels().at(selBarPosRow));
else
m_itemLabel.replace(rowLabelTag, QString());
m_itemLabel.replace(rowTitleTag, categoryAxisZ->title());
- m_itemLabel.replace(colIndexTag, QString::number(selBarPosCol));
+ m_itemLabel.replace(colIndexTag, locale.toString(selBarPosCol));
if (categoryAxisX->labels().size() > selBarPosCol)
m_itemLabel.replace(colLabelTag, categoryAxisX->labels().at(selBarPosCol));
else
diff --git a/src/datavisualization/data/qcustom3ditem.cpp b/src/datavisualization/data/qcustom3ditem.cpp
index efee3b15..b9825732 100644
--- a/src/datavisualization/data/qcustom3ditem.cpp
+++ b/src/datavisualization/data/qcustom3ditem.cpp
@@ -62,13 +62,15 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
*
* Holds the item \a position as a vector3d. Defaults to \c {vector3d(0.0, 0.0, 0.0)}.
*
- * Item position is either in data coordinates or in absolute coordinates, depending on
+ * Item position is either in data coordinates or in absolute coordinates, depending on the
* positionAbsolute property. When using absolute coordinates, values between \c{-1.0...1.0} are
* within axis ranges.
*
- * \note Items positioned outside any axis range are not rendered if positionAbsolute is \c{false}.
+ * \note Items positioned outside any axis range are not rendered if positionAbsolute is \c{false},
+ * unless the item is a Custom3DVolume that would be partially visible and scalingAbsolute is also
+ * \c{false}. In that case, the visible portion of the volume will be rendered.
*
- * \sa positionAbsolute
+ * \sa positionAbsolute, scalingAbsolute
*/
/*! \qmlproperty bool Custom3DItem::positionAbsolute
@@ -83,8 +85,33 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
/*! \qmlproperty vector3d Custom3DItem::scaling
*
* Holds the item \a scaling as a vector3d. Defaults to \c {vector3d(0.1, 0.1, 0.1)}.
- * The default value sets the item to 10% of the height of the graph, provided the item size is
- * normalized.
+ *
+ * Item scaling is either in data values or in absolute values, depending on the
+ * scalingAbsolute property. The default vector interpreted as absolute values sets the item to
+ * 10% of the height of the graph, provided the item mesh is normalized and the graph aspect ratios
+ * haven't been changed from the defaults.
+ *
+ * \sa scalingAbsolute
+ */
+
+/*! \qmlproperty bool Custom3DItem::scalingAbsolute
+ * \since QtDataVisualization 1.2
+ *
+ * This property dictates if item scaling is to be handled in data values or in absolute
+ * values. Defaults to \c{true}. Items with absolute scaling will be rendered at the same
+ * size, regardless of axis ranges. Items with data scaling will change their apparent size
+ * according to the axis ranges. If positionAbsolute value is \c{true}, this property is ignored
+ * and scaling is interpreted as an absolute value. If the item has rotation, the data scaling
+ * is calculated on the unrotated item. Similarly, for Custom3DVolume items, the range clipping
+ * is calculated on the unrotated item.
+ *
+ * \note: Only absolute scaling is supported for Custom3DLabel items or for custom items used in
+ * \l{AbstractGraph3D::polar}{polar} graphs.
+ *
+ * \note: The custom item's mesh must be normalized to range \c{[-1 ,1]}, or the data
+ * scaling will not be accurate.
+ *
+ * \sa scaling, positionAbsolute
*/
/*! \qmlproperty quaternion Custom3DItem::rotation
@@ -180,7 +207,9 @@ QString QCustom3DItem::meshFile() const
* positionAbsolute property. When using absolute coordinates, values between \c{-1.0...1.0} are
* within axis ranges.
*
- * \note Items positioned outside any axis range are not rendered if positionAbsolute is \c{false}.
+ * \note Items positioned outside any axis range are not rendered if positionAbsolute is \c{false},
+ * unless the item is a QCustom3DVolume that would be partially visible and scalingAbsolute is also
+ * \c{false}. In that case, the visible portion of the volume will be rendered.
*
* \sa positionAbsolute
*/
@@ -211,7 +240,7 @@ void QCustom3DItem::setPositionAbsolute(bool positionAbsolute)
{
if (d_ptr->m_positionAbsolute != positionAbsolute) {
d_ptr->m_positionAbsolute = positionAbsolute;
- d_ptr->m_dirtyBits.positionAbsoluteDirty = true;
+ d_ptr->m_dirtyBits.positionDirty = true;
emit positionAbsoluteChanged(positionAbsolute);
emit d_ptr->needUpdate();
}
@@ -225,8 +254,13 @@ bool QCustom3DItem::isPositionAbsolute() const
/*! \property QCustom3DItem::scaling
*
* Holds the item \a scaling as a QVector3D. Defaults to \c {QVector3D(0.1, 0.1, 0.1)}.
- * The default value sets the item to 10% of the height of the graph, provided the item size is
- * normalized.
+ *
+ * Item scaling is either in data values or in absolute values, depending on the
+ * scalingAbsolute property. The default vector interpreted as absolute values sets the item to
+ * 10% of the height of the graph, provided the item mesh is normalized and the graph aspect ratios
+ * haven't been changed from the defaults.
+ *
+ * \sa scalingAbsolute
*/
void QCustom3DItem::setScaling(const QVector3D &scaling)
{
@@ -243,6 +277,42 @@ QVector3D QCustom3DItem::scaling() const
return d_ptr->m_scaling;
}
+/*! \property QCustom3DItem::scalingAbsolute
+ * \since QtDataVisualization 1.2
+ *
+ * This property dictates if item scaling is to be handled in data values or in absolute
+ * values. Defaults to \c{true}. Items with absolute scaling will be rendered at the same
+ * size, regardless of axis ranges. Items with data scaling will change their apparent size
+ * according to the axis ranges. If positionAbsolute value is \c{true}, this property is ignored
+ * and scaling is interpreted as an absolute value. If the item has rotation, the data scaling
+ * is calculated on the unrotated item. Similarly, for QCustom3DVolume items, the range clipping
+ * is calculated on the unrotated item.
+ *
+ * \note: Only absolute scaling is supported for QCustom3DLabel items or for custom items used in
+ * \l{QAbstract3DGraph::polar}{polar} graphs.
+ *
+ * \note: The custom item's mesh must be normalized to range \c{[-1 ,1]}, or the data
+ * scaling will not be accurate.
+ *
+ * \sa scaling, positionAbsolute
+ */
+void QCustom3DItem::setScalingAbsolute(bool scalingAbsolute)
+{
+ if (d_ptr->m_isLabelItem && !scalingAbsolute) {
+ qWarning() << __FUNCTION__ << "Data bounds are not supported for label items.";
+ } else if (d_ptr->m_scalingAbsolute != scalingAbsolute) {
+ d_ptr->m_scalingAbsolute = scalingAbsolute;
+ d_ptr->m_dirtyBits.scalingDirty = true;
+ emit scalingAbsoluteChanged(scalingAbsolute);
+ emit d_ptr->needUpdate();
+ }
+}
+
+bool QCustom3DItem::isScalingAbsolute() const
+{
+ return d_ptr->m_scalingAbsolute;
+}
+
/*! \property QCustom3DItem::rotation
*
* Holds the item \a rotation as a QQuaternion. Defaults to \c {QQuaternion(0.0, 0.0, 0.0, 0.0)}.
@@ -367,13 +437,16 @@ QString QCustom3DItem::textureFile() const
QCustom3DItemPrivate::QCustom3DItemPrivate(QCustom3DItem *q) :
q_ptr(q),
+ m_textureImage(QImage(1, 1, QImage::Format_ARGB32)),
m_position(QVector3D(0.0f, 0.0f, 0.0f)),
m_positionAbsolute(false),
m_scaling(QVector3D(0.1f, 0.1f, 0.1f)),
- m_rotation(QQuaternion(0.0f, 0.0f, 0.0f, 0.0f)),
+ m_scalingAbsolute(true),
+ m_rotation(identityQuaternion),
m_visible(true),
m_shadowCasting(true),
- m_isLabelItem(false)
+ m_isLabelItem(false),
+ m_isVolumeItem(false)
{
}
@@ -381,14 +454,17 @@ QCustom3DItemPrivate::QCustom3DItemPrivate(QCustom3DItem *q, const QString &mesh
const QVector3D &position, const QVector3D &scaling,
const QQuaternion &rotation) :
q_ptr(q),
+ m_textureImage(QImage(1, 1, QImage::Format_ARGB32)),
m_meshFile(meshFile),
m_position(position),
m_positionAbsolute(false),
m_scaling(scaling),
+ m_scalingAbsolute(true),
m_rotation(rotation),
m_visible(true),
m_shadowCasting(true),
- m_isLabelItem(false)
+ m_isLabelItem(false),
+ m_isVolumeItem(false)
{
}
@@ -412,7 +488,6 @@ void QCustom3DItemPrivate::resetDirtyBits()
m_dirtyBits.textureDirty = false;
m_dirtyBits.meshDirty = false;
m_dirtyBits.positionDirty = false;
- m_dirtyBits.positionAbsoluteDirty = false;
m_dirtyBits.scalingDirty = false;
m_dirtyBits.rotationDirty = false;
m_dirtyBits.visibleDirty = false;
diff --git a/src/datavisualization/data/qcustom3ditem.h b/src/datavisualization/data/qcustom3ditem.h
index 2f7f37cf..5c880213 100644
--- a/src/datavisualization/data/qcustom3ditem.h
+++ b/src/datavisualization/data/qcustom3ditem.h
@@ -39,6 +39,7 @@ class QT_DATAVISUALIZATION_EXPORT QCustom3DItem : public QObject
Q_PROPERTY(QQuaternion rotation READ rotation WRITE setRotation NOTIFY rotationChanged)
Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged)
Q_PROPERTY(bool shadowCasting READ isShadowCasting WRITE setShadowCasting NOTIFY shadowCastingChanged)
+ Q_PROPERTY(bool scalingAbsolute READ isScalingAbsolute WRITE setScalingAbsolute NOTIFY scalingAbsoluteChanged REVISION 1)
public:
explicit QCustom3DItem(QObject *parent = 0);
@@ -62,6 +63,9 @@ public:
void setScaling(const QVector3D &scaling);
QVector3D scaling() const;
+ void setScalingAbsolute(bool scalingAbsolute);
+ bool isScalingAbsolute() const;
+
void setRotation(const QQuaternion &rotation);
QQuaternion rotation();
@@ -84,6 +88,7 @@ signals:
void rotationChanged(const QQuaternion &rotation);
void visibleChanged(bool visible);
void shadowCastingChanged(bool shadowCasting);
+ Q_REVISION(1) void scalingAbsoluteChanged(bool scalingAbsolute);
protected:
QCustom3DItem(QCustom3DItemPrivate *d, QObject *parent = 0);
diff --git a/src/datavisualization/data/qcustom3ditem_p.h b/src/datavisualization/data/qcustom3ditem_p.h
index c1ce5996..627bf53f 100644
--- a/src/datavisualization/data/qcustom3ditem_p.h
+++ b/src/datavisualization/data/qcustom3ditem_p.h
@@ -29,6 +29,7 @@
#ifndef QCUSTOM3DITEM_P_H
#define QCUSTOM3DITEM_P_H
+#include "datavisualizationglobal_p.h"
#include "qcustom3ditem.h"
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
@@ -37,7 +38,6 @@ struct QCustomItemDirtyBitField {
bool textureDirty : 1;
bool meshDirty : 1;
bool positionDirty : 1;
- bool positionAbsoluteDirty : 1;
bool scalingDirty : 1;
bool rotationDirty : 1;
bool visibleDirty : 1;
@@ -47,7 +47,6 @@ struct QCustomItemDirtyBitField {
: textureDirty(false),
meshDirty(false),
positionDirty(false),
- positionAbsoluteDirty(false),
scalingDirty(false),
rotationDirty(false),
visibleDirty(false),
@@ -77,11 +76,13 @@ public:
QVector3D m_position;
bool m_positionAbsolute;
QVector3D m_scaling;
+ bool m_scalingAbsolute;
QQuaternion m_rotation;
bool m_visible;
bool m_shadowCasting;
bool m_isLabelItem;
+ bool m_isVolumeItem;
QCustomItemDirtyBitField m_dirtyBits;
diff --git a/src/datavisualization/data/qcustom3dlabel.cpp b/src/datavisualization/data/qcustom3dlabel.cpp
index 85a37e2d..bb5d71d0 100644
--- a/src/datavisualization/data/qcustom3dlabel.cpp
+++ b/src/datavisualization/data/qcustom3dlabel.cpp
@@ -44,6 +44,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
* \since QtDataVisualization 1.1
* \ingroup datavisualization_qml
* \instantiates QCustom3DLabel
+ * \inherits Custom3DItem
* \brief The Custom3DLabel type is for creating custom labels to be added to a graph.
*
* This type is for creating custom labels to be added to a graph. You can set text, font,
diff --git a/src/datavisualization/data/qcustom3dvolume.cpp b/src/datavisualization/data/qcustom3dvolume.cpp
new file mode 100644
index 00000000..f8287a81
--- /dev/null
+++ b/src/datavisualization/data/qcustom3dvolume.cpp
@@ -0,0 +1,1278 @@
+/****************************************************************************
+**
+** 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 "qcustom3dvolume_p.h"
+#include "utils_p.h"
+
+QT_BEGIN_NAMESPACE_DATAVISUALIZATION
+
+/*!
+ * \class QCustom3DVolume
+ * \inmodule QtDataVisualization
+ * \brief The QCustom3DVolume class is for creating volume rendered objects to be added to a graph.
+ * \since QtDataVisualization 1.2
+ *
+ * This class is for creating volume rendered objects to be added to a graph. A volume rendered
+ * object is a box with a 3D texture. Three slice planes are supported for the volume, one along
+ * each main axis of the volume.
+ *
+ * Rendering volume objects is very performance intensive, especially when the volume is largely
+ * transparent, as the contents of the volume are ray-traced. The performance scales nearly linearly
+ * with the amount of pixels that the volume occupies on the screen, so showing the volume in a
+ * smaller view or limiting the zoom level of the graph are easy ways to improve performance.
+ * Similarly, the volume texture dimensions have a large impact on performance.
+ * If the frame rate is more important than pixel-perfect rendering of the volume contents, consider
+ * turning the high definition shader off by setting useHighDefShader property to \c{false}.
+ *
+ * \note Volumetric objects are only supported with orthographic projection.
+ *
+ * \note Volumetric objects utilize 3D textures, which are not supported in OpenGL ES2 environments.
+ *
+ * \sa QAbstract3DGraph::addCustomItem(), QAbstract3DGraph::orthoProjection, useHighDefShader
+ */
+
+/*!
+ * \qmltype Custom3DVolume
+ * \inqmlmodule QtDataVisualization
+ * \since QtDataVisualization 1.2
+ * \ingroup datavisualization_qml
+ * \instantiates QCustom3DVolume
+ * \inherits Custom3DItem
+ * \brief The Custom3DVolume type is for creating volume rendered objects to be added to a graph.
+ *
+ * This class is for creating volume rendered objects to be added to a graph. A volume rendered
+ * object is a box with a 3D texture. Three slice planes are supported for the volume, one along
+ * each main axis of the volume.
+ *
+ * Rendering volume objects is very performance intensive, especially when the volume is largely
+ * transparent, as the contents of the volume are ray-traced. The performance scales nearly linearly
+ * with the amount of pixels that the volume occupies on the screen, so showing the volume in a
+ * smaller view or limiting the zoom level of the graph are easy ways to improve performance.
+ * Similarly, the volume texture dimensions have a large impact on performance.
+ * If the frame rate is more important than pixel-perfect rendering of the volume contents, consider
+ * turning the high definition shader off by setting useHighDefShader property to \c{false}.
+ *
+ * \note: Filling in the volume data would not typically be efficient or practical from pure QML,
+ * so properties directly related to that are not fully supported from QML.
+ * Make a hybrid QML/C++ application if you want to use volume objects with a QML UI.
+ *
+ * \note Volumetric objects are only supported with orthographic projection.
+ *
+ * \note Volumetric objects utilize 3D textures, which are not supported in OpenGL ES2 environments.
+ *
+ * \sa AbstractGraph3D::orthoProjection, useHighDefShader
+ */
+
+/*! \qmlproperty int Custom3DVolume::textureWidth
+ *
+ * The width of the 3D texture defining the volume content in pixels. Defaults to \c{0}.
+ *
+ * \note: Changing this property from QML is not supported, as the texture data cannot be resized
+ * to match.
+ */
+
+/*! \qmlproperty int Custom3DVolume::textureHeight
+ *
+ * The height of the 3D texture defining the volume content in pixels. Defaults to \c{0}.
+ *
+ * \note: Changing this property from QML is not supported, as the texture data cannot be resized
+ * to match.
+ */
+
+/*! \qmlproperty int Custom3DVolume::textureDepth
+ *
+ * The depth of the 3D texture defining the volume content in pixels. Defaults to \c{0}.
+ *
+ * \note: Changing this property from QML is not supported, as the texture data cannot be resized
+ * to match.
+ */
+
+/*! \qmlproperty int Custom3DVolume::sliceIndexX
+ *
+ * The X dimension index into the texture data indicating which vertical slice to show.
+ * Setting any dimension to negative indicates no slice or slice frame for that dimension is drawn.
+ * If all dimensions are negative, no slices or slice frames are drawn and the volume is drawn
+ * normally.
+ * Defaults to \c{-1}.
+ *
+ * \sa QCustom3DVolume::textureData, drawSlices, drawSliceFrames
+ */
+
+/*! \qmlproperty int Custom3DVolume::sliceIndexY
+ *
+ * The Y dimension index into the texture data indicating which horizontal slice to show.
+ * Setting any dimension to negative indicates no slice or slice frame for that dimension is drawn.
+ * If all dimensions are negative, no slices or slice frames are drawn and the volume is drawn
+ * normally.
+ * Defaults to \c{-1}.
+ *
+ * \sa QCustom3DVolume::textureData, drawSlices, drawSliceFrames
+ */
+
+/*! \qmlproperty int Custom3DVolume::sliceIndexZ
+ *
+ * The Z dimension index into the texture data indicating which vertical slice to show.
+ * Setting any dimension to negative indicates no slice or slice frame for that dimension is drawn.
+ * If all dimensions are negative, no slices or slice frames are drawn and the volume is drawn
+ * normally.
+ * Defaults to \c{-1}.
+ *
+ * \sa QCustom3DVolume::textureData, drawSlices, drawSliceFrames
+ */
+
+/*!
+ * \qmlproperty real Custom3DVolume::alphaMultiplier
+ *
+ * The alpha value of every texel of the volume texture is multiplied with this value at
+ * the render time. This can be used to introduce uniform transparency to the volume.
+ * If preserveOpacity is \c{true}, only texels with at least some transparency to begin with are
+ * affected, and fully opaque texels are not affected.
+ * The value must not be negative.
+ * Defaults to \c{1.0}.
+ *
+ * \sa preserveOpacity
+ */
+
+/*!
+ * \qmlproperty bool Custom3DVolume::preserveOpacity
+ *
+ * If this property value is \c{true}, alphaMultiplier is only applied to texels that already have
+ * some transparency. If it is \c{false}, the multiplier is applied to the alpha value of all
+ * texels.
+ * Defaults to \c{true}.
+ *
+ * \sa alphaMultiplier
+ */
+
+/*!
+ * \qmlproperty bool Custom3DVolume::useHighDefShader
+ *
+ * If this property value is \c{true}, a high definition shader is used to render the volume.
+ * If it is \c{false}, a low definition shader is used.
+ *
+ * The high definition shader guarantees that every visible texel of the volume texture is sampled
+ * when the volume is rendered.
+ * The low definition shader renders only a rough approximation of the volume contents,
+ * but at much higher frame rate. The low definition shader doesn't guarantee every texel of the
+ * volume texture is sampled, so there may be flickering if the volume contains distinct thin
+ * features.
+ *
+ * \note This value doesn't affect the level of detail when rendering the slices of the volume.
+ *
+ * Defaults to \c{true}.
+ */
+
+/*!
+ * \qmlproperty bool Custom3DVolume::drawSlices
+ *
+ * If this property value is \c{true}, the slices indicated by slice index properties
+ * will be drawn instead of the full volume.
+ * If it is \c{false}, the full volume will always be drawn.
+ * Defaults to \c{false}.
+ *
+ * \note The slices are always drawn along the item axes, so if the item is rotated, the slices are
+ * rotated as well.
+ *
+ * \sa sliceIndexX, sliceIndexY, sliceIndexZ
+ */
+
+/*!
+ * \qmlproperty bool Custom3DVolume::drawSliceFrames
+ *
+ * If this property value is \c{true}, the frames of slices indicated by slice index properties
+ * will be drawn around the volume.
+ * If it is \c{false}, no slice frames will be drawn.
+ * Drawing slice frames is independent of drawing slices, so you can show the full volume and
+ * still draw the slice frames around it.
+ * Defaults to \c{false}.
+ *
+ * \sa sliceIndexX, sliceIndexY, sliceIndexZ, drawSlices
+ */
+
+/*!
+ * \qmlproperty color Custom3DVolume::sliceFrameColor
+ *
+ * Indicates the color of the slice frame. Transparent slice frame color is not supported.
+ *
+ * Defaults to black.
+ *
+ * \sa drawSliceFrames
+ */
+
+/*!
+ * \qmlproperty vector3d Custom3DVolume::sliceFrameWidths
+ *
+ * Indicates the widths of the slice frame. The width can be different on different dimensions,
+ * so you can for example omit drawing the frames on certain sides of the volume by setting the
+ * value for that dimension to zero. The values are fractions of the volume thickness in the same
+ * dimension. The values cannot be negative.
+ *
+ * Defaults to \c{vector3d(0.01, 0.01, 0.01)}.
+ *
+ * \sa drawSliceFrames
+ */
+
+/*!
+ * \qmlproperty vector3d Custom3DVolume::sliceFrameGaps
+ *
+ * Indicates the amount of air gap left between the volume itself and the frame in each dimension.
+ * The gap can be different on different dimensions. The values are fractions of the volume
+ * thickness in the same dimension. The values cannot be negative.
+ *
+ * Defaults to \c{vector3d(0.01, 0.01, 0.01)}.
+ *
+ * \sa drawSliceFrames
+ */
+
+/*!
+ * \qmlproperty vector3d Custom3DVolume::sliceFrameThicknesses
+ *
+ * Indicates the thickness of the slice frames for each dimension. The values are fractions of
+ * the volume thickness in the same dimension. The values cannot be negative.
+ *
+ * Defaults to \c{vector3d(0.01, 0.01, 0.01)}.
+ *
+ * \sa drawSliceFrames
+ */
+
+/*!
+ * Constructs QCustom3DVolume with given \a parent.
+ */
+QCustom3DVolume::QCustom3DVolume(QObject *parent) :
+ QCustom3DItem(new QCustom3DVolumePrivate(this), parent)
+{
+}
+
+/*!
+ * Constructs QCustom3DVolume with given \a position, \a scaling, \a rotation,
+ * \a textureWidth, \a textureHeight, \a textureDepth, \a textureData, \a textureFormat,
+ * \a colorTable, and optional \a parent.
+ *
+ * \sa textureData, setTextureFormat(), colorTable
+ */
+QCustom3DVolume::QCustom3DVolume(const QVector3D &position, const QVector3D &scaling,
+ const QQuaternion &rotation, int textureWidth,
+ int textureHeight, int textureDepth,
+ QVector<uchar> *textureData, QImage::Format textureFormat,
+ const QVector<QRgb> &colorTable, QObject *parent) :
+ QCustom3DItem(new QCustom3DVolumePrivate(this, position, scaling, rotation, textureWidth,
+ textureHeight, textureDepth,
+ textureData, textureFormat, colorTable), parent)
+{
+}
+
+
+/*!
+ * Destroys QCustom3DVolume.
+ */
+QCustom3DVolume::~QCustom3DVolume()
+{
+}
+
+/*! \property QCustom3DVolume::textureWidth
+ *
+ * The width of the 3D texture defining the volume content in pixels. Defaults to \c{0}.
+ *
+ * \note The textureData may need to be resized or recreated if this value is changed.
+ * Defaults to \c{0}.
+ *
+ * \sa textureData, textureHeight, textureDepth, setTextureFormat(), textureDataWidth()
+ */
+void QCustom3DVolume::setTextureWidth(int value)
+{
+ if (value >= 0) {
+ if (dptr()->m_textureWidth != value) {
+ dptr()->m_textureWidth = value;
+ dptr()->m_dirtyBitsVolume.textureDimensionsDirty = true;
+ emit textureWidthChanged(value);
+ emit dptr()->needUpdate();
+ }
+ } else {
+ qWarning() << __FUNCTION__ << "Cannot set negative value.";
+ }
+}
+
+int QCustom3DVolume::textureWidth() const
+{
+ return dptrc()->m_textureWidth;
+}
+
+/*! \property QCustom3DVolume::textureHeight
+ *
+ * The height of the 3D texture defining the volume content in pixels. Defaults to \c{0}.
+ *
+ * \note The textureData may need to be resized or recreated if this value is changed.
+ * Defaults to \c{0}.
+ *
+ * \sa textureData, textureWidth, textureDepth, setTextureFormat()
+ */
+void QCustom3DVolume::setTextureHeight(int value)
+{
+ if (value >= 0) {
+ if (dptr()->m_textureHeight != value) {
+ dptr()->m_textureHeight = value;
+ dptr()->m_dirtyBitsVolume.textureDimensionsDirty = true;
+ emit textureHeightChanged(value);
+ emit dptr()->needUpdate();
+ }
+ } else {
+ qWarning() << __FUNCTION__ << "Cannot set negative value.";
+ }
+
+}
+
+int QCustom3DVolume::textureHeight() const
+{
+ return dptrc()->m_textureHeight;
+}
+
+/*! \property QCustom3DVolume::textureDepth
+ *
+ * The depth of the 3D texture defining the volume content in pixels. Defaults to \c{0}.
+ *
+ * \note The textureData may need to be resized or recreated if this value is changed.
+ * Defaults to \c{0}.
+ *
+ * \sa textureData, textureWidth, textureHeight, setTextureFormat()
+ */
+void QCustom3DVolume::setTextureDepth(int value)
+{
+ if (value >= 0) {
+ if (dptr()->m_textureDepth != value) {
+ dptr()->m_textureDepth = value;
+ dptr()->m_dirtyBitsVolume.textureDimensionsDirty = true;
+ emit textureDepthChanged(value);
+ emit dptr()->needUpdate();
+ }
+ } else {
+ qWarning() << __FUNCTION__ << "Cannot set negative value.";
+ }
+}
+
+int QCustom3DVolume::textureDepth() const
+{
+ return dptrc()->m_textureDepth;
+}
+
+/*!
+ * A convenience function for setting all three texture dimensions
+ * (\a width, \a height, and \a depth) at once.
+ *
+ * \sa textureData
+ */
+void QCustom3DVolume::setTextureDimensions(int width, int height, int depth)
+{
+ setTextureWidth(width);
+ setTextureHeight(height);
+ setTextureDepth(depth);
+}
+
+/*!
+ * \return the actual texture data width. When the texture format is QImage::Format_Indexed8,
+ * this is textureWidth aligned to 32bit boundary. Otherwise this is four times textureWidth.
+ */
+int QCustom3DVolume::textureDataWidth() const
+{
+ int dataWidth = dptrc()->m_textureWidth;
+
+ if (dptrc()->m_textureFormat == QImage::Format_Indexed8)
+ dataWidth += dataWidth % 4;
+ else
+ dataWidth *= 4;
+
+ return dataWidth;
+}
+
+/*! \property QCustom3DVolume::sliceIndexX
+ *
+ * The X dimension index into the texture data indicating which vertical slice to show.
+ * Setting any dimension to negative indicates no slice or slice frame for that dimension is drawn.
+ * If all dimensions are negative, no slices or slice frames are drawn and the volume is drawn
+ * normally.
+ * Defaults to \c{-1}.
+ *
+ * \sa textureData, drawSlices, drawSliceFrames
+ */
+void QCustom3DVolume::setSliceIndexX(int value)
+{
+ if (dptr()->m_sliceIndexX != value) {
+ dptr()->m_sliceIndexX = value;
+ dptr()->m_dirtyBitsVolume.slicesDirty = true;
+ emit sliceIndexXChanged(value);
+ emit dptr()->needUpdate();
+ }
+}
+
+int QCustom3DVolume::sliceIndexX() const
+{
+ return dptrc()->m_sliceIndexX;
+}
+
+/*! \property QCustom3DVolume::sliceIndexY
+ *
+ * The Y dimension index into the texture data indicating which horizontal slice to show.
+ * Setting any dimension to negative indicates no slice or slice frame for that dimension is drawn.
+ * If all dimensions are negative, no slices or slice frames are drawn and the volume is drawn
+ * normally.
+ * Defaults to \c{-1}.
+ *
+ * \sa textureData, drawSlices, drawSliceFrames
+ */
+void QCustom3DVolume::setSliceIndexY(int value)
+{
+ if (dptr()->m_sliceIndexY != value) {
+ dptr()->m_sliceIndexY = value;
+ dptr()->m_dirtyBitsVolume.slicesDirty = true;
+ emit sliceIndexYChanged(value);
+ emit dptr()->needUpdate();
+ }
+}
+
+int QCustom3DVolume::sliceIndexY() const
+{
+ return dptrc()->m_sliceIndexY;
+}
+
+/*! \property QCustom3DVolume::sliceIndexZ
+ *
+ * The Z dimension index into the texture data indicating which vertical slice to show.
+ * Setting any dimension to negative indicates no slice or slice frame for that dimension is drawn.
+ * If all dimensions are negative, no slices or slice frames are drawn and the volume is drawn
+ * normally.
+ * Defaults to \c{-1}.
+ *
+ * \sa textureData, drawSlices, drawSliceFrames
+ */
+void QCustom3DVolume::setSliceIndexZ(int value)
+{
+ if (dptr()->m_sliceIndexZ != value) {
+ dptr()->m_sliceIndexZ = value;
+ dptr()->m_dirtyBitsVolume.slicesDirty = true;
+ emit sliceIndexZChanged(value);
+ emit dptr()->needUpdate();
+ }
+}
+
+int QCustom3DVolume::sliceIndexZ() const
+{
+ return dptrc()->m_sliceIndexZ;
+}
+
+/*!
+ * A convenience function for setting all three slice indices (\a x, \a y, and \a z) at once.
+ *
+ * \sa textureData
+ */
+void QCustom3DVolume::setSliceIndices(int x, int y, int z)
+{
+ setSliceIndexX(x);
+ setSliceIndexY(y);
+ setSliceIndexZ(z);
+}
+
+/*! \property QCustom3DVolume::colorTable
+ *
+ * The array containing the colors for indexed texture formats. If the texture format is not
+ * indexed, this array is not used and can be empty.
+ *
+ * Defaults to \c{0}.
+ *
+ * \sa textureData, setTextureFormat(), QImage::colorTable()
+ */
+void QCustom3DVolume::setColorTable(const QVector<QRgb> &colors)
+{
+ if (dptr()->m_colorTable != colors) {
+ dptr()->m_colorTable = colors;
+ dptr()->m_dirtyBitsVolume.colorTableDirty = true;
+ emit colorTableChanged();
+ emit dptr()->needUpdate();
+ }
+}
+
+QVector<QRgb> QCustom3DVolume::colorTable() const
+{
+ return dptrc()->m_colorTable;
+}
+
+/*! \property QCustom3DVolume::textureData
+ *
+ * The array containing the texture data in the format specified by textureFormat.
+ * The size of this array must be at least
+ * (\c{textureDataWidth * textureHeight * textureDepth * texture format color depth in bytes}).
+ *
+ * A 3D texture is defined by a stack of 2D subtextures. Each subtexture must be of identical size
+ * (\c{textureDataWidth * textureHeight}), and the depth of the stack is defined by textureDepth
+ * property. Each 2D texture data is identical to a QImage data with the same format, so
+ * QImage::bits() can be used to supply the data for each subtexture.
+ *
+ * Ownership of the new array transfers to QCustom3DVolume instance.
+ * If another array is set, the previous array is deleted.
+ * If the same array is set again, it is assumed that the array contents have been changed and the
+ * graph rendering is triggered.
+ *
+ * \note Each X-line of the data needs to be 32bit aligned. If the textureFormat is
+ * QImage::Format_Indexed8 and textureWidth is not divisible by four, padding bytes need
+ * to be added to each X-line of the \a data. You can get the padded byte count with
+ * textureDataWidth() function. The padding bytes should indicate an fully transparent color
+ * to avoid rendering artifacts.
+ *
+ * Defaults to \c{0}.
+ *
+ * \sa colorTable, setTextureFormat(), setSubTextureData(), textureDataWidth()
+ */
+void QCustom3DVolume::setTextureData(QVector<uchar> *data)
+{
+ if (dptr()->m_textureData != data)
+ delete dptr()->m_textureData;
+
+ // Even if the pointer is same as previously, consider this property changed, as the values
+ // can be changed unbeknownst to us via the array pointer.
+ dptr()->m_textureData = data;
+ dptr()->m_dirtyBitsVolume.textureDataDirty = true;
+ emit textureDataChanged(data);
+ emit dptr()->needUpdate();
+}
+
+/*!
+ * This function creates a new texture data array from an array of \a images and sets it as
+ * textureData for this volume object. The texture dimensions are also set according to image
+ * and array dimensions. All of the images in the array must be the same size. If the images are not
+ * all in QImage::Format_Indexed8 format, all texture data will be converted into
+ * QImage::Format_ARGB32 format. If the images are in QImage::Format_Indexed8 format, the colorTable
+ * for the entire volume will be taken from the first image.
+ *
+ * \return pointer to the newly created array.
+ *
+ * \sa textureData, textureWidth, textureHeight, textureDepth, setTextureFormat()
+ */
+QVector<uchar> *QCustom3DVolume::createTextureData(const QVector<QImage *> &images)
+{
+ int imageCount = images.size();
+ if (imageCount) {
+ QImage *currentImage = images.at(0);
+ int imageWidth = currentImage->width();
+ int imageHeight = currentImage->height();
+ QImage::Format imageFormat = currentImage->format();
+ bool convert = false;
+ if (imageFormat != QImage::Format_Indexed8 && imageFormat != QImage::Format_ARGB32) {
+ convert = true;
+ imageFormat = QImage::Format_ARGB32;
+ } else {
+ for (int i = 0; i < imageCount; i++) {
+ currentImage = images.at(i);
+ if (imageWidth != currentImage->width() || imageHeight != currentImage->height()) {
+ qWarning() << __FUNCTION__ << "Not all images were of the same size.";
+ setTextureData(0);
+ setTextureWidth(0);
+ setTextureHeight(0);
+ setTextureDepth(0);
+ return 0;
+
+ }
+ if (currentImage->format() != imageFormat) {
+ convert = true;
+ imageFormat = QImage::Format_ARGB32;
+ break;
+ }
+ }
+ }
+ int colorBytes = (imageFormat == QImage::Format_Indexed8) ? 1 : 4;
+ int imageByteWidth = (imageFormat == QImage::Format_Indexed8)
+ ? currentImage->bytesPerLine() : imageWidth;
+ int frameSize = imageByteWidth * imageHeight * colorBytes;
+ QVector<uchar> *newTextureData = new QVector<uchar>;
+ newTextureData->resize(frameSize * imageCount);
+ uchar *texturePtr = newTextureData->data();
+ QImage convertedImage;
+
+ for (int i = 0; i < imageCount; i++) {
+ currentImage = images.at(i);
+ if (convert) {
+ convertedImage = currentImage->convertToFormat(imageFormat);
+ currentImage = &convertedImage;
+ }
+ memcpy(texturePtr, static_cast<void *>(currentImage->bits()), frameSize);
+ texturePtr += frameSize;
+ }
+
+ if (imageFormat == QImage::Format_Indexed8)
+ setColorTable(images.at(0)->colorTable());
+ setTextureData(newTextureData);
+ setTextureFormat(imageFormat);
+ setTextureWidth(imageWidth);
+ setTextureHeight(imageHeight);
+ setTextureDepth(imageCount);
+ } else {
+ setTextureData(0);
+ setTextureWidth(0);
+ setTextureHeight(0);
+ setTextureDepth(0);
+ }
+ return dptr()->m_textureData;
+}
+
+QVector<uchar> *QCustom3DVolume::textureData() const
+{
+ return dptrc()->m_textureData;
+}
+
+/*!
+ * This function allows setting a single 2D subtexture of the 3D texture along the specified
+ * \a axis of the volume.
+ * The \a index parameter specifies the subtexture to set.
+ * The texture \a data must be in the format specified by textureFormat property and have size of
+ * the cross-section of the volume texture along the specified axis multiplied by
+ * the texture format color depth in bytes.
+ * The \a data is expected to be ordered similarly to the data in images produced by renderSlice()
+ * method along the same axis.
+ *
+ * \note Each X-line of the data needs to be 32bit aligned when targeting Y-axis or Z-axis.
+ * If the textureFormat is QImage::Format_Indexed8 and textureWidth is not divisible by four,
+ * padding bytes need to be added to each X-line of the \a data in cases it is not already
+ * properly aligned. The padding bytes should indicate an fully transparent color to avoid
+ * rendering artifacts.
+ *
+ * \sa textureData, renderSlice()
+ */
+void QCustom3DVolume::setSubTextureData(Qt::Axis axis, int index, const uchar *data)
+{
+ if (data) {
+ int lineSize = textureDataWidth();
+ int frameSize = lineSize * dptr()->m_textureHeight;
+ int dataSize = dptr()->m_textureData->size();
+ int pixelWidth = (dptr()->m_textureFormat == QImage::Format_Indexed8) ? 1 : 4;
+ int targetIndex;
+ uchar *dataPtr = dptr()->m_textureData->data();
+ bool invalid = (index < 0);
+ if (axis == Qt::XAxis) {
+ targetIndex = index * pixelWidth;
+ if (index >= dptr()->m_textureWidth
+ || (frameSize * (dptr()->m_textureDepth - 1) + targetIndex) > dataSize) {
+ invalid = true;
+ }
+ } else if (axis == Qt::YAxis) {
+ targetIndex = (index * lineSize) + (frameSize * (dptr()->m_textureDepth - 1));
+ if (index >= dptr()->m_textureHeight || (targetIndex + lineSize > dataSize))
+ invalid = true;
+ } else {
+ targetIndex = index * frameSize;
+ if (index >= dptr()->m_textureDepth || ((targetIndex + frameSize) > dataSize))
+ invalid = true;
+ }
+
+ if (invalid) {
+ qWarning() << __FUNCTION__ << "Attempted to set invalid subtexture.";
+ } else {
+ const uchar *sourcePtr = data;
+ uchar *targetPtr = dataPtr + targetIndex;
+ if (axis == Qt::XAxis) {
+ int targetWidth = dptr()->m_textureDepth;
+ int targetHeight = dptr()->m_textureHeight;
+ for (int i = 0; i < targetHeight; i++) {
+ targetPtr = dataPtr + targetIndex + (lineSize * i);
+ for (int j = 0; j < targetWidth; j++) {
+ for (int k = 0; k < pixelWidth; k++)
+ *targetPtr++ = *sourcePtr++;
+ targetPtr += (frameSize - pixelWidth);
+ }
+ }
+ } else if (axis == Qt::YAxis) {
+ int targetHeight = dptr()->m_textureDepth;
+ for (int i = 0; i < targetHeight; i++){
+ for (int j = 0; j < lineSize; j++)
+ *targetPtr++ = *sourcePtr++;
+ targetPtr -= (frameSize + lineSize);
+ }
+ } else {
+ void *subTexPtr = dataPtr + targetIndex;
+ memcpy(subTexPtr, static_cast<const void *>(data), frameSize);
+ }
+ dptr()->m_dirtyBitsVolume.textureDataDirty = true;
+ emit textureDataChanged(dptr()->m_textureData);
+ emit dptr()->needUpdate();
+ }
+ } else {
+ qWarning() << __FUNCTION__ << "Tried to set null data.";
+ }
+}
+
+/*!
+ * This function allows setting a single 2D subtexture of the 3D texture along the specified
+ * \a axis of the volume.
+ * The \a index parameter specifies the subtexture to set.
+ * The source \a image must be in the format specified by the textureFormat property if the
+ * textureFormat is indexed. If the textureFormat is QImage::Format_ARGB32, the image is converted
+ * to that format. The image must have the size of the cross-section of the volume texture along
+ * the specified axis. The orientation of the image should correspond to the orientation of
+ * the slice image produced by renderSlice() method along the same axis.
+ *
+ * \note Each X-line of the data needs to be 32bit aligned when targeting Y-axis or Z-axis.
+ * If the textureFormat is QImage::Format_Indexed8 and textureWidth is not divisible by four,
+ * padding bytes need to be added to each X-line of the \a image in cases it is not already
+ * properly aligned. The padding bytes should indicate an fully transparent color to avoid
+ * rendering artifacts. It is not guaranteed QImage will do this automatically.
+ *
+ * \sa textureData, renderSlice()
+ */
+void QCustom3DVolume::setSubTextureData(Qt::Axis axis, int index, const QImage &image)
+{
+ int sourceWidth = image.width();
+ int sourceHeight = image.height();
+ int targetWidth;
+ int targetHeight;
+ if (axis == Qt::XAxis) {
+ targetWidth = dptr()->m_textureDepth;
+ targetHeight = dptr()->m_textureHeight;
+ } else if (axis == Qt::YAxis) {
+ targetWidth = dptr()->m_textureWidth;
+ targetHeight = dptr()->m_textureDepth;
+ } else {
+ targetWidth = dptr()->m_textureWidth;
+ targetHeight = dptr()->m_textureHeight;
+ }
+
+ if (sourceWidth == targetWidth
+ && sourceHeight == targetHeight
+ && (image.format() == dptr()->m_textureFormat
+ || dptr()->m_textureFormat == QImage::Format_ARGB32)) {
+ QImage convertedImage;
+ if (dptr()->m_textureFormat == QImage::Format_ARGB32
+ && image.format() != QImage::Format_ARGB32) {
+ convertedImage = image.convertToFormat(QImage::Format_ARGB32);
+ } else {
+ convertedImage = image;
+ }
+ setSubTextureData(axis, index, convertedImage.bits());
+ } else {
+ qWarning() << __FUNCTION__ << "Invalid image size or format.";
+ }
+}
+
+// Note: textureFormat is not a Q_PROPERTY to work around an issue in meta object system that
+// doesn't allow QImage::format to be a property type. Qt 5.2.1 at least has this problem.
+
+/*!
+ * Sets the format of the textureData to \a format. Only two formats are supported currently:
+ * QImage::Format_Indexed8 and QImage::Format_ARGB32. If an indexed format is specified, colorTable
+ * must also be set.
+ * Defaults to QImage::Format_ARGB32.
+ *
+ * \sa colorTable, textureData
+ */
+void QCustom3DVolume::setTextureFormat(QImage::Format format)
+{
+ if (format == QImage::Format_ARGB32 || format == QImage::Format_Indexed8) {
+ if (dptr()->m_textureFormat != format) {
+ dptr()->m_textureFormat = format;
+ dptr()->m_dirtyBitsVolume.textureFormatDirty = true;
+ emit textureFormatChanged(format);
+ emit dptr()->needUpdate();
+ }
+ } else {
+ qWarning() << __FUNCTION__ << "Attempted to set invalid texture format.";
+ }
+}
+
+/*!
+ * \return the format of the textureData.
+ *
+ * \sa setTextureFormat()
+ */
+QImage::Format QCustom3DVolume::textureFormat() const
+{
+ return dptrc()->m_textureFormat;
+}
+
+/*!
+ * \fn void QCustom3DVolume::textureFormatChanged(QImage::Format format)
+ *
+ * This signal is emitted when the textureData \a format changes.
+ *
+ * \sa setTextureFormat()
+ */
+
+/*!
+ * \property QCustom3DVolume::alphaMultiplier
+ *
+ * The alpha value of every texel of the volume texture is multiplied with this value at
+ * the render time. This can be used to introduce uniform transparency to the volume.
+ * If preserveOpacity is \c{true}, only texels with at least some transparency to begin with are
+ * affected, and fully opaque texels are not affected.
+ * The value must not be negative.
+ * Defaults to \c{1.0f}.
+ *
+ * \sa preserveOpacity, textureData
+ */
+void QCustom3DVolume::setAlphaMultiplier(float mult)
+{
+ if (mult >= 0.0f) {
+ if (dptr()->m_alphaMultiplier != mult) {
+ dptr()->m_alphaMultiplier = mult;
+ dptr()->m_dirtyBitsVolume.alphaDirty = true;
+ emit alphaMultiplierChanged(mult);
+ emit dptr()->needUpdate();
+ }
+ } else {
+ qWarning() << __FUNCTION__ << "Attempted to set negative multiplier.";
+ }
+}
+
+float QCustom3DVolume::alphaMultiplier() const
+{
+ return dptrc()->m_alphaMultiplier;
+}
+
+/*!
+ * \property QCustom3DVolume::preserveOpacity
+ *
+ * If this property value is \c{true}, alphaMultiplier is only applied to texels that already have
+ * some transparency. If it is \c{false}, the multiplier is applied to the alpha value of all
+ * texels.
+ * Defaults to \c{true}.
+ *
+ * \sa alphaMultiplier
+ */
+void QCustom3DVolume::setPreserveOpacity(bool enable)
+{
+ if (dptr()->m_preserveOpacity != enable) {
+ dptr()->m_preserveOpacity = enable;
+ dptr()->m_dirtyBitsVolume.alphaDirty = true;
+ emit preserveOpacityChanged(enable);
+ emit dptr()->needUpdate();
+ }
+}
+
+bool QCustom3DVolume::preserveOpacity() const
+{
+ return dptrc()->m_preserveOpacity;
+}
+
+/*!
+ * \property QCustom3DVolume::useHighDefShader
+ *
+ * If this property value is \c{true}, a high definition shader is used to render the volume.
+ * If it is \c{false}, a low definition shader is used.
+ *
+ * The high definition shader guarantees that every visible texel of the volume texture is sampled
+ * when the volume is rendered.
+ * The low definition shader renders only a rough approximation of the volume contents,
+ * but at much higher frame rate. The low definition shader doesn't guarantee every texel of the
+ * volume texture is sampled, so there may be flickering if the volume contains distinct thin
+ * features.
+ *
+ * \note This value doesn't affect the level of detail when rendering the slices of the volume.
+ *
+ * Defaults to \c{true}.
+ *
+ * \sa renderSlice()
+ */
+void QCustom3DVolume::setUseHighDefShader(bool enable)
+{
+ if (dptr()->m_useHighDefShader != enable) {
+ dptr()->m_useHighDefShader = enable;
+ dptr()->m_dirtyBitsVolume.shaderDirty = true;
+ emit useHighDefShaderChanged(enable);
+ emit dptr()->needUpdate();
+ }
+}
+
+bool QCustom3DVolume::useHighDefShader() const
+{
+ return dptrc()->m_useHighDefShader;
+}
+
+/*!
+ * \property QCustom3DVolume::drawSlices
+ *
+ * If this property value is \c{true}, the slices indicated by slice index properties
+ * will be drawn instead of the full volume.
+ * If it is \c{false}, the full volume will always be drawn.
+ * Defaults to \c{false}.
+ *
+ * \note The slices are always drawn along the item axes, so if the item is rotated, the slices are
+ * rotated as well.
+ *
+ * \sa sliceIndexX, sliceIndexY, sliceIndexZ
+ */
+void QCustom3DVolume::setDrawSlices(bool enable)
+{
+ if (dptr()->m_drawSlices != enable) {
+ dptr()->m_drawSlices = enable;
+ dptr()->m_dirtyBitsVolume.slicesDirty = true;
+ emit drawSlicesChanged(enable);
+ emit dptr()->needUpdate();
+ }
+}
+
+bool QCustom3DVolume::drawSlices() const
+{
+ return dptrc()->m_drawSlices;
+}
+
+/*!
+ * \property QCustom3DVolume::drawSliceFrames
+ *
+ * If this property value is \c{true}, the frames of slices indicated by slice index properties
+ * will be drawn around the volume.
+ * If it is \c{false}, no slice frames will be drawn.
+ * Drawing slice frames is independent of drawing slices, so you can show the full volume and
+ * still draw the slice frames around it. This is useful when using renderSlice() to display the
+ * slices outside the graph itself.
+ * Defaults to \c{false}.
+ *
+ * \sa sliceIndexX, sliceIndexY, sliceIndexZ, drawSlices, renderSlice()
+ */
+void QCustom3DVolume::setDrawSliceFrames(bool enable)
+{
+ if (dptr()->m_drawSliceFrames != enable) {
+ dptr()->m_drawSliceFrames = enable;
+ dptr()->m_dirtyBitsVolume.slicesDirty = true;
+ emit drawSliceFramesChanged(enable);
+ emit dptr()->needUpdate();
+ }
+}
+
+bool QCustom3DVolume::drawSliceFrames() const
+{
+ return dptrc()->m_drawSliceFrames;
+}
+
+/*!
+ * \property QCustom3DVolume::sliceFrameColor
+ *
+ * Indicates the color of the slice frame. Transparent slice frame color is not supported.
+ *
+ * Defaults to black.
+ *
+ * \sa drawSliceFrames
+ */
+void QCustom3DVolume::setSliceFrameColor(const QColor &color)
+{
+ if (dptr()->m_sliceFrameColor != color) {
+ dptr()->m_sliceFrameColor = color;
+ dptr()->m_dirtyBitsVolume.slicesDirty = true;
+ emit sliceFrameColorChanged(color);
+ emit dptr()->needUpdate();
+ }
+}
+
+QColor QCustom3DVolume::sliceFrameColor() const
+{
+ return dptrc()->m_sliceFrameColor;
+}
+
+/*!
+ * \property QCustom3DVolume::sliceFrameWidths
+ *
+ * Indicates the widths of the slice frame. The width can be different on different dimensions,
+ * so you can for example omit drawing the frames on certain sides of the volume by setting the
+ * value for that dimension to zero. The values are fractions of the volume thickness in the same
+ * dimension. The values cannot be negative.
+ *
+ * Defaults to \c{QVector3D(0.01, 0.01, 0.01)}.
+ *
+ * \sa drawSliceFrames
+ */
+void QCustom3DVolume::setSliceFrameWidths(const QVector3D &values)
+{
+ if (values.x() < 0.0f || values.y() < 0.0f || values.z() < 0.0f) {
+ qWarning() << __FUNCTION__ << "Attempted to set negative values.";
+ } else if (dptr()->m_sliceFrameWidths != values) {
+ dptr()->m_sliceFrameWidths = values;
+ dptr()->m_dirtyBitsVolume.slicesDirty = true;
+ emit sliceFrameWidthsChanged(values);
+ emit dptr()->needUpdate();
+ }
+}
+
+QVector3D QCustom3DVolume::sliceFrameWidths() const
+{
+ return dptrc()->m_sliceFrameWidths;
+}
+
+/*!
+ * \property QCustom3DVolume::sliceFrameGaps
+ *
+ * Indicates the amount of air gap left between the volume itself and the frame in each dimension.
+ * The gap can be different on different dimensions. The values are fractions of the volume
+ * thickness in the same dimension. The values cannot be negative.
+ *
+ * Defaults to \c{QVector3D(0.01, 0.01, 0.01)}.
+ *
+ * \sa drawSliceFrames
+ */
+void QCustom3DVolume::setSliceFrameGaps(const QVector3D &values)
+{
+ if (values.x() < 0.0f || values.y() < 0.0f || values.z() < 0.0f) {
+ qWarning() << __FUNCTION__ << "Attempted to set negative values.";
+ } else if (dptr()->m_sliceFrameGaps != values) {
+ dptr()->m_sliceFrameGaps = values;
+ dptr()->m_dirtyBitsVolume.slicesDirty = true;
+ emit sliceFrameGapsChanged(values);
+ emit dptr()->needUpdate();
+ }
+}
+
+QVector3D QCustom3DVolume::sliceFrameGaps() const
+{
+ return dptrc()->m_sliceFrameGaps;
+}
+
+/*!
+ * \property QCustom3DVolume::sliceFrameThicknesses
+ *
+ * Indicates the thickness of the slice frames for each dimension. The values are fractions of
+ * the volume thickness in the same dimension. The values cannot be negative.
+ *
+ * Defaults to \c{QVector3D(0.01, 0.01, 0.01)}.
+ *
+ * \sa drawSliceFrames
+ */
+void QCustom3DVolume::setSliceFrameThicknesses(const QVector3D &values)
+{
+ if (values.x() < 0.0f || values.y() < 0.0f || values.z() < 0.0f) {
+ qWarning() << __FUNCTION__ << "Attempted to set negative values.";
+ } else if (dptr()->m_sliceFrameThicknesses != values) {
+ dptr()->m_sliceFrameThicknesses = values;
+ dptr()->m_dirtyBitsVolume.slicesDirty = true;
+ emit sliceFrameThicknessesChanged(values);
+ emit dptr()->needUpdate();
+ }
+}
+
+QVector3D QCustom3DVolume::sliceFrameThicknesses() const
+{
+ return dptrc()->m_sliceFrameThicknesses;
+}
+
+/*!
+ * Renders the slice specified by \a index along \a axis into an image.
+ * The texture format of this object is used.
+ *
+ * \return the rendered image of the slice, or a null image if invalid index is specified.
+ *
+ * \sa setTextureFormat()
+ */
+QImage QCustom3DVolume::renderSlice(Qt::Axis axis, int index)
+{
+ return dptr()->renderSlice(axis, index);
+}
+
+/*!
+ * \internal
+ */
+QCustom3DVolumePrivate *QCustom3DVolume::dptr()
+{
+ return static_cast<QCustom3DVolumePrivate *>(d_ptr.data());
+}
+
+/*!
+ * \internal
+ */
+const QCustom3DVolumePrivate *QCustom3DVolume::dptrc() const
+{
+ return static_cast<const QCustom3DVolumePrivate *>(d_ptr.data());
+}
+
+QCustom3DVolumePrivate::QCustom3DVolumePrivate(QCustom3DVolume *q) :
+ QCustom3DItemPrivate(q),
+ m_textureWidth(0),
+ m_textureHeight(0),
+ m_textureDepth(0),
+ m_sliceIndexX(-1),
+ m_sliceIndexY(-1),
+ m_sliceIndexZ(-1),
+ m_textureFormat(QImage::Format_ARGB32),
+ m_textureData(0),
+ m_alphaMultiplier(1.0f),
+ m_preserveOpacity(true),
+ m_useHighDefShader(true),
+ m_drawSlices(false),
+ m_drawSliceFrames(false),
+ m_sliceFrameColor(Qt::black),
+ m_sliceFrameWidths(QVector3D(0.01f, 0.01f, 0.01f)),
+ m_sliceFrameGaps(QVector3D(0.01f, 0.01f, 0.01f)),
+ m_sliceFrameThicknesses(QVector3D(0.01f, 0.01f, 0.01f))
+{
+ m_isVolumeItem = true;
+ m_meshFile = QStringLiteral(":/defaultMeshes/barFull");
+}
+
+QCustom3DVolumePrivate::QCustom3DVolumePrivate(QCustom3DVolume *q, const QVector3D &position,
+ const QVector3D &scaling,
+ const QQuaternion &rotation,
+ int textureWidth, int textureHeight,
+ int textureDepth, QVector<uchar> *textureData,
+ QImage::Format textureFormat,
+ const QVector<QRgb> &colorTable) :
+ QCustom3DItemPrivate(q, QStringLiteral(":/defaultMeshes/barFull"), position, scaling, rotation),
+ m_textureWidth(textureWidth),
+ m_textureHeight(textureHeight),
+ m_textureDepth(textureDepth),
+ m_sliceIndexX(-1),
+ m_sliceIndexY(-1),
+ m_sliceIndexZ(-1),
+ m_textureFormat(textureFormat),
+ m_colorTable(colorTable),
+ m_textureData(textureData),
+ m_alphaMultiplier(1.0f),
+ m_preserveOpacity(true),
+ m_useHighDefShader(true),
+ m_drawSlices(false),
+ m_drawSliceFrames(false),
+ m_sliceFrameColor(Qt::black),
+ m_sliceFrameWidths(QVector3D(0.01f, 0.01f, 0.01f)),
+ m_sliceFrameGaps(QVector3D(0.01f, 0.01f, 0.01f)),
+ m_sliceFrameThicknesses(QVector3D(0.01f, 0.01f, 0.01f))
+{
+ m_isVolumeItem = true;
+ m_shadowCasting = false;
+
+ if (m_textureWidth < 0)
+ m_textureWidth = 0;
+ if (m_textureHeight < 0)
+ m_textureHeight = 0;
+ if (m_textureDepth < 0)
+ m_textureDepth = 0;
+
+ if (m_textureFormat != QImage::Format_Indexed8)
+ m_textureFormat = QImage::Format_ARGB32;
+
+}
+
+QCustom3DVolumePrivate::~QCustom3DVolumePrivate()
+{
+ delete m_textureData;
+}
+
+void QCustom3DVolumePrivate::resetDirtyBits()
+{
+ QCustom3DItemPrivate::resetDirtyBits();
+
+ m_dirtyBitsVolume.textureDimensionsDirty = false;
+ m_dirtyBitsVolume.slicesDirty = false;
+ m_dirtyBitsVolume.colorTableDirty = false;
+ m_dirtyBitsVolume.textureDataDirty = false;
+ m_dirtyBitsVolume.textureFormatDirty = false;
+ m_dirtyBitsVolume.alphaDirty = false;
+ m_dirtyBitsVolume.shaderDirty = false;
+}
+
+QImage QCustom3DVolumePrivate::renderSlice(Qt::Axis axis, int index)
+{
+ if (index < 0)
+ return QImage();
+
+ int x;
+ int y;
+ if (axis == Qt::XAxis) {
+ if (index >= m_textureWidth)
+ return QImage();
+ x = m_textureDepth;
+ y = m_textureHeight;
+ } else if (axis == Qt::YAxis) {
+ if (index >= m_textureHeight)
+ return QImage();
+ x = m_textureWidth;
+ y = m_textureDepth;
+ } else {
+ if (index >= m_textureDepth)
+ return QImage();
+ x = m_textureWidth;
+ y = m_textureHeight;
+ }
+
+ int padding = 0;
+ int pixelWidth = 4;
+ int dataWidth = qptr()->textureDataWidth();
+ if (m_textureFormat == QImage::Format_Indexed8) {
+ padding = x % 4;
+ pixelWidth = 1;
+ }
+ QVector<uchar> data((x + padding) * y * pixelWidth);
+ int frameSize = qptr()->textureDataWidth() * m_textureHeight;
+
+ int dataIndex = 0;
+ if (axis == Qt::XAxis) {
+ for (int i = 0; i < y; i++) {
+ const uchar *p = m_textureData->constData()
+ + (index * pixelWidth) + (dataWidth * i);
+ for (int j = 0; j < x; j++) {
+ for (int k = 0; k < pixelWidth; k++)
+ data[dataIndex++] = *(p + k);
+ p += frameSize;
+ }
+ }
+ } else if (axis == Qt::YAxis) {
+ for (int i = y - 1; i >= 0; i--) {
+ const uchar *p = m_textureData->constData() + (index * dataWidth)
+ + (frameSize * i);
+ for (int j = 0; j < (x * pixelWidth); j++) {
+ data[dataIndex++] = *p;
+ p++;
+ }
+ }
+ } else {
+ for (int i = 0; i < y; i++) {
+ const uchar *p = m_textureData->constData() + (index * frameSize) + (dataWidth * i);
+ for (int j = 0; j < (x * pixelWidth); j++) {
+ data[dataIndex++] = *p;
+ p++;
+ }
+ }
+ }
+
+ if (m_textureFormat != QImage::Format_Indexed8 && m_alphaMultiplier != 1.0f) {
+ for (int i = pixelWidth - 1; i < data.size(); i += pixelWidth)
+ data[i] = static_cast<uchar>(multipliedAlphaValue(data.at(i)));
+ }
+
+ QImage image(data.constData(), x, y, x * pixelWidth, m_textureFormat);
+ image.bits(); // Call bits() to detach the new image from local data
+ if (m_textureFormat == QImage::Format_Indexed8) {
+ QVector<QRgb> colorTable = m_colorTable;
+ if (m_alphaMultiplier != 1.0f) {
+ for (int i = 0; i < colorTable.size(); i++) {
+ QRgb curCol = colorTable.at(i);
+ int alpha = multipliedAlphaValue(qAlpha(curCol));
+ if (alpha != qAlpha(curCol))
+ colorTable[i] = qRgba(qRed(curCol), qGreen(curCol), qBlue(curCol), alpha);
+ }
+ }
+ image.setColorTable(colorTable);
+ }
+
+ return image;
+}
+
+int QCustom3DVolumePrivate::multipliedAlphaValue(int alpha)
+{
+ int modifiedAlpha = alpha;
+ if (!m_preserveOpacity || alpha != 255) {
+ modifiedAlpha = int(m_alphaMultiplier * float(alpha));
+ modifiedAlpha = qMin(modifiedAlpha, 255);
+ }
+ return modifiedAlpha;
+}
+
+QCustom3DVolume *QCustom3DVolumePrivate::qptr()
+{
+ return static_cast<QCustom3DVolume *>(q_ptr);
+}
+
+QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/data/qcustom3dvolume.h b/src/datavisualization/data/qcustom3dvolume.h
new file mode 100644
index 00000000..a9dfda86
--- /dev/null
+++ b/src/datavisualization/data/qcustom3dvolume.h
@@ -0,0 +1,147 @@
+/****************************************************************************
+**
+** 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
+**
+****************************************************************************/
+
+#ifndef QCUSTOM3DVOLUME_H
+#define QCUSTOM3DVOLUME_H
+
+#include <QtDataVisualization/qdatavisualizationglobal.h>
+#include <QtDataVisualization/QCustom3DItem>
+#include <QtGui/QColor>
+#include <QtGui/QImage>
+
+QT_BEGIN_NAMESPACE_DATAVISUALIZATION
+
+class QCustom3DVolumePrivate;
+
+class QT_DATAVISUALIZATION_EXPORT QCustom3DVolume : public QCustom3DItem
+{
+ Q_OBJECT
+ Q_PROPERTY(int textureWidth READ textureWidth WRITE setTextureWidth NOTIFY textureWidthChanged)
+ Q_PROPERTY(int textureHeight READ textureHeight WRITE setTextureHeight NOTIFY textureHeightChanged)
+ Q_PROPERTY(int textureDepth READ textureDepth WRITE setTextureDepth NOTIFY textureDepthChanged)
+ Q_PROPERTY(int sliceIndexX READ sliceIndexX WRITE setSliceIndexX NOTIFY sliceIndexXChanged)
+ Q_PROPERTY(int sliceIndexY READ sliceIndexY WRITE setSliceIndexY NOTIFY sliceIndexYChanged)
+ Q_PROPERTY(int sliceIndexZ READ sliceIndexZ WRITE setSliceIndexZ NOTIFY sliceIndexZChanged)
+ Q_PROPERTY(QVector<QRgb> colorTable READ colorTable WRITE setColorTable NOTIFY colorTableChanged)
+ Q_PROPERTY(QVector<uchar> *textureData READ textureData WRITE setTextureData NOTIFY textureDataChanged)
+ Q_PROPERTY(float alphaMultiplier READ alphaMultiplier WRITE setAlphaMultiplier NOTIFY alphaMultiplierChanged)
+ Q_PROPERTY(bool preserveOpacity READ preserveOpacity WRITE setPreserveOpacity NOTIFY preserveOpacityChanged)
+ Q_PROPERTY(bool useHighDefShader READ useHighDefShader WRITE setUseHighDefShader NOTIFY useHighDefShaderChanged)
+ Q_PROPERTY(bool drawSlices READ drawSlices WRITE setDrawSlices NOTIFY drawSlicesChanged)
+ Q_PROPERTY(bool drawSliceFrames READ drawSliceFrames WRITE setDrawSliceFrames NOTIFY drawSliceFramesChanged)
+ Q_PROPERTY(QColor sliceFrameColor READ sliceFrameColor WRITE setSliceFrameColor NOTIFY sliceFrameColorChanged)
+ Q_PROPERTY(QVector3D sliceFrameWidths READ sliceFrameWidths WRITE setSliceFrameWidths NOTIFY sliceFrameWidthsChanged)
+ Q_PROPERTY(QVector3D sliceFrameGaps READ sliceFrameGaps WRITE setSliceFrameGaps NOTIFY sliceFrameGapsChanged)
+ Q_PROPERTY(QVector3D sliceFrameThicknesses READ sliceFrameThicknesses WRITE setSliceFrameThicknesses NOTIFY sliceFrameThicknessesChanged)
+
+public:
+
+ explicit QCustom3DVolume(QObject *parent = 0);
+ explicit QCustom3DVolume(const QVector3D &position, const QVector3D &scaling,
+ const QQuaternion &rotation, int textureWidth,
+ int textureHeight, int textureDepth,
+ QVector<uchar> *textureData, QImage::Format textureFormat,
+ const QVector<QRgb> &colorTable, QObject *parent = 0);
+ virtual ~QCustom3DVolume();
+
+ void setTextureWidth(int value);
+ int textureWidth() const;
+ void setTextureHeight(int value);
+ int textureHeight() const;
+ void setTextureDepth(int value);
+ int textureDepth() const;
+ void setTextureDimensions(int width, int height, int depth);
+ int textureDataWidth() const;
+
+ void setSliceIndexX(int value);
+ int sliceIndexX() const;
+ void setSliceIndexY(int value);
+ int sliceIndexY() const;
+ void setSliceIndexZ(int value);
+ int sliceIndexZ() const;
+ void setSliceIndices(int x, int y, int z);
+
+ void setColorTable(const QVector<QRgb> &colors);
+ QVector<QRgb> colorTable() const;
+
+ void setTextureData(QVector<uchar> *data);
+ QVector<uchar> *createTextureData(const QVector<QImage *> &images);
+ QVector<uchar> *textureData() const;
+ void setSubTextureData(Qt::Axis axis, int index, const uchar *data);
+ void setSubTextureData(Qt::Axis axis, int index, const QImage &image);
+
+ void setTextureFormat(QImage::Format format);
+ QImage::Format textureFormat() const;
+
+ void setAlphaMultiplier(float mult);
+ float alphaMultiplier() const;
+ void setPreserveOpacity(bool enable);
+ bool preserveOpacity() const;
+
+ void setUseHighDefShader(bool enable);
+ bool useHighDefShader() const;
+
+ void setDrawSlices(bool enable);
+ bool drawSlices() const;
+ void setDrawSliceFrames(bool enable);
+ bool drawSliceFrames() const;
+
+ void setSliceFrameColor(const QColor &color);
+ QColor sliceFrameColor() const;
+ void setSliceFrameWidths(const QVector3D &values);
+ QVector3D sliceFrameWidths() const;
+ void setSliceFrameGaps(const QVector3D &values);
+ QVector3D sliceFrameGaps() const;
+ void setSliceFrameThicknesses(const QVector3D &values);
+ QVector3D sliceFrameThicknesses() const;
+
+ QImage renderSlice(Qt::Axis axis, int index);
+
+signals:
+ void textureWidthChanged(int value);
+ void textureHeightChanged(int value);
+ void textureDepthChanged(int value);
+ void sliceIndexXChanged(int value);
+ void sliceIndexYChanged(int value);
+ void sliceIndexZChanged(int value);
+ void colorTableChanged();
+ void textureDataChanged(QVector<uchar> *data);
+ void textureFormatChanged(QImage::Format format);
+ void alphaMultiplierChanged(float mult);
+ void preserveOpacityChanged(bool enabled);
+ void useHighDefShaderChanged(bool enabled);
+ void drawSlicesChanged(bool enabled);
+ void drawSliceFramesChanged(bool enabled);
+ void sliceFrameColorChanged(const QColor &color);
+ void sliceFrameWidthsChanged(const QVector3D &values);
+ void sliceFrameGapsChanged(const QVector3D &values);
+ void sliceFrameThicknessesChanged(const QVector3D &values);
+
+protected:
+ QCustom3DVolumePrivate *dptr();
+ const QCustom3DVolumePrivate *dptrc() const;
+
+private:
+ Q_DISABLE_COPY(QCustom3DVolume)
+
+ friend class Abstract3DRenderer;
+};
+
+QT_END_NAMESPACE_DATAVISUALIZATION
+
+#endif
diff --git a/src/datavisualization/data/qcustom3dvolume_p.h b/src/datavisualization/data/qcustom3dvolume_p.h
new file mode 100644
index 00000000..642d2af5
--- /dev/null
+++ b/src/datavisualization/data/qcustom3dvolume_p.h
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** 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 QCUSTOM3DVOLUME_P_H
+#define QCUSTOM3DVOLUME_P_H
+
+#include "qcustom3dvolume.h"
+#include "qcustom3ditem_p.h"
+
+QT_BEGIN_NAMESPACE_DATAVISUALIZATION
+
+struct QCustomVolumeDirtyBitField {
+ bool textureDimensionsDirty : 1;
+ bool slicesDirty : 1;
+ bool colorTableDirty : 1;
+ bool textureDataDirty : 1;
+ bool textureFormatDirty : 1;
+ bool alphaDirty : 1;
+ bool shaderDirty : 1;
+
+ QCustomVolumeDirtyBitField()
+ : textureDimensionsDirty(false),
+ slicesDirty(false),
+ colorTableDirty(false),
+ textureDataDirty(false),
+ textureFormatDirty(false),
+ alphaDirty(false),
+ shaderDirty(false)
+ {
+ }
+};
+
+class QCustom3DVolumePrivate : public QCustom3DItemPrivate
+{
+ Q_OBJECT
+
+public:
+ QCustom3DVolumePrivate(QCustom3DVolume *q);
+ QCustom3DVolumePrivate(QCustom3DVolume *q, const QVector3D &position, const QVector3D &scaling,
+ const QQuaternion &rotation, int textureWidth,
+ int textureHeight, int textureDepth, QVector<uchar> *textureData,
+ QImage::Format textureFormat, const QVector<QRgb> &colorTable);
+ virtual ~QCustom3DVolumePrivate();
+
+ void resetDirtyBits();
+ QImage renderSlice(Qt::Axis axis, int index);
+
+ QCustom3DVolume *qptr();
+
+public:
+ int m_textureWidth;
+ int m_textureHeight;
+ int m_textureDepth;
+ int m_sliceIndexX;
+ int m_sliceIndexY;
+ int m_sliceIndexZ;
+
+ QImage::Format m_textureFormat;
+ QVector<QRgb> m_colorTable;
+ QVector<uchar> *m_textureData;
+
+ float m_alphaMultiplier;
+ bool m_preserveOpacity;
+ bool m_useHighDefShader;
+
+ bool m_drawSlices;
+ bool m_drawSliceFrames;
+ QColor m_sliceFrameColor;
+ QVector3D m_sliceFrameWidths;
+ QVector3D m_sliceFrameGaps;
+ QVector3D m_sliceFrameThicknesses;
+
+ QCustomVolumeDirtyBitField m_dirtyBitsVolume;
+
+private:
+ int multipliedAlphaValue(int alpha);
+
+ friend class QCustom3DVolume;
+};
+
+QT_END_NAMESPACE_DATAVISUALIZATION
+
+#endif
diff --git a/src/datavisualization/data/qheightmapsurfacedataproxy.cpp b/src/datavisualization/data/qheightmapsurfacedataproxy.cpp
index d64046be..85aed432 100644
--- a/src/datavisualization/data/qheightmapsurfacedataproxy.cpp
+++ b/src/datavisualization/data/qheightmapsurfacedataproxy.cpp
@@ -84,7 +84,7 @@ const float defaultMaxValue = 10.0f;
/*!
* \qmlproperty real HeightMapSurfaceDataProxy::minXValue
*
- * The minimum X value for the generated surface points.
+ * The minimum X value for the generated surface points. Defaults to \c{0.0}.
* When setting this property the corresponding maximum value is adjusted if necessary,
* to ensure that the range remains valid.
*/
@@ -92,7 +92,7 @@ const float defaultMaxValue = 10.0f;
/*!
* \qmlproperty real HeightMapSurfaceDataProxy::maxXValue
*
- * The maximum X value for the generated surface points.
+ * The maximum X value for the generated surface points. Defaults to \c{10.0}.
* When setting this property the corresponding minimum value is adjusted if necessary,
* to ensure that the range remains valid.
*/
@@ -100,7 +100,7 @@ const float defaultMaxValue = 10.0f;
/*!
* \qmlproperty real HeightMapSurfaceDataProxy::minZValue
*
- * The minimum Z value for the generated surface points.
+ * The minimum Z value for the generated surface points. Defaults to \c{0.0}.
* When setting this property the corresponding maximum value is adjusted if necessary,
* to ensure that the range remains valid.
*/
@@ -108,7 +108,7 @@ const float defaultMaxValue = 10.0f;
/*!
* \qmlproperty real HeightMapSurfaceDataProxy::maxZValue
*
- * The maximum Z value for the generated surface points.
+ * The maximum Z value for the generated surface points. Defaults to \c{10.0}.
* When setting this property the corresponding minimum value is adjusted if necessary,
* to ensure that the range remains valid.
*/
@@ -228,7 +228,7 @@ void QHeightMapSurfaceDataProxy::setValueRanges(float minX, float maxX, float mi
/*!
* \property QHeightMapSurfaceDataProxy::minXValue
*
- * The minimum X value for the generated surface points.
+ * The minimum X value for the generated surface points. Defaults to \c{0.0}.
* When setting this property the corresponding maximum value is adjusted if necessary,
* to ensure that the range remains valid.
*/
@@ -245,7 +245,7 @@ float QHeightMapSurfaceDataProxy::minXValue() const
/*!
* \property QHeightMapSurfaceDataProxy::maxXValue
*
- * The maximum X value for the generated surface points.
+ * The maximum X value for the generated surface points. Defaults to \c{10.0}.
* When setting this property the corresponding minimum value is adjusted if necessary,
* to ensure that the range remains valid.
*/
@@ -262,7 +262,7 @@ float QHeightMapSurfaceDataProxy::maxXValue() const
/*!
* \property QHeightMapSurfaceDataProxy::minZValue
*
- * The minimum Z value for the generated surface points.
+ * The minimum Z value for the generated surface points. Defaults to \c{0.0}.
* When setting this property the corresponding maximum value is adjusted if necessary,
* to ensure that the range remains valid.
*/
@@ -279,7 +279,7 @@ float QHeightMapSurfaceDataProxy::minZValue() const
/*!
* \property QHeightMapSurfaceDataProxy::maxZValue
*
- * The maximum Z value for the generated surface points.
+ * The maximum Z value for the generated surface points. Defaults to \c{10.0}.
* When setting this property the corresponding minimum value is adjusted if necessary,
* to ensure that the range remains valid.
*/
diff --git a/src/datavisualization/data/qsurface3dseries.cpp b/src/datavisualization/data/qsurface3dseries.cpp
index 1107d721..7d4dacfe 100644
--- a/src/datavisualization/data/qsurface3dseries.cpp
+++ b/src/datavisualization/data/qsurface3dseries.cpp
@@ -141,6 +141,14 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
*/
/*!
+ * \qmlproperty string Surface3DSeries::textureFile
+ *
+ * Holds the texture file name for the surface texture. To clear the texture, set empty
+ * file name.
+ */
+
+
+/*!
* \enum QSurface3DSeries::DrawFlag
*
* Drawing mode of the surface. Values of this enumeration can be combined with OR operator.
@@ -300,6 +308,57 @@ QSurface3DSeries::DrawFlags QSurface3DSeries::drawMode() const
}
/*!
+ * \property QSurface3DSeries::texture
+ *
+ * Set the \a texture as a QImage for the surface. To clear the texture, set empty
+ * QImage as texture.
+ */
+void QSurface3DSeries::setTexture(const QImage &texture)
+{
+ if (dptr()->m_texture != texture) {
+ dptr()->setTexture(texture);
+
+ emit textureChanged(texture);
+ dptr()->m_textureFile.clear();
+ }
+}
+
+QImage QSurface3DSeries::texture() const
+{
+ return dptrc()->m_texture;
+}
+
+/*!
+ * \property QSurface3DSeries::textureFile
+ *
+ * Holds the texture file name for the surface texture. To clear the texture, set empty
+ * file name.
+ */
+void QSurface3DSeries::setTextureFile(const QString &filename)
+{
+ if (dptr()->m_textureFile != filename) {
+ if (filename.isEmpty()) {
+ setTexture(QImage());
+ } else {
+ QImage image(filename);
+ if (image.isNull()) {
+ qWarning() << "Warning: Tried to set invalid image file as surface texture.";
+ return;
+ }
+ setTexture(image);
+ }
+
+ dptr()->m_textureFile = filename;
+ emit textureFileChanged(filename);
+ }
+}
+
+QString QSurface3DSeries::textureFile() const
+{
+ return dptrc()->m_textureFile;
+}
+
+/*!
* \internal
*/
QSurface3DSeriesPrivate *QSurface3DSeries::dptr()
@@ -445,4 +504,11 @@ void QSurface3DSeriesPrivate::setDrawMode(QSurface3DSeries::DrawFlags mode)
}
}
+void QSurface3DSeriesPrivate::setTexture(const QImage &texture)
+{
+ m_texture = texture;
+ if (static_cast<Surface3DController *>(m_controller))
+ static_cast<Surface3DController *>(m_controller)->updateSurfaceTexture(qptr());
+}
+
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/data/qsurface3dseries.h b/src/datavisualization/data/qsurface3dseries.h
index 64df7202..7051e583 100644
--- a/src/datavisualization/data/qsurface3dseries.h
+++ b/src/datavisualization/data/qsurface3dseries.h
@@ -35,6 +35,8 @@ class QT_DATAVISUALIZATION_EXPORT QSurface3DSeries : public QAbstract3DSeries
Q_PROPERTY(bool flatShadingEnabled READ isFlatShadingEnabled WRITE setFlatShadingEnabled NOTIFY flatShadingEnabledChanged)
Q_PROPERTY(bool flatShadingSupported READ isFlatShadingSupported NOTIFY flatShadingSupportedChanged)
Q_PROPERTY(DrawFlags drawMode READ drawMode WRITE setDrawMode NOTIFY drawModeChanged)
+ Q_PROPERTY(QImage texture READ texture WRITE setTexture NOTIFY textureChanged)
+ Q_PROPERTY(QString textureFile READ textureFile WRITE setTextureFile NOTIFY textureFileChanged)
public:
enum DrawFlag {
@@ -63,12 +65,19 @@ public:
bool isFlatShadingSupported() const;
+ void setTexture(const QImage &texture);
+ QImage texture() const;
+ void setTextureFile(const QString &filename);
+ QString textureFile() const;
+
signals:
void dataProxyChanged(QSurfaceDataProxy *proxy);
void selectedPointChanged(const QPoint &position);
void flatShadingEnabledChanged(bool enable);
void flatShadingSupportedChanged(bool enable);
void drawModeChanged(QSurface3DSeries::DrawFlags mode);
+ void textureChanged(const QImage &image);
+ void textureFileChanged(const QString &filename);
protected:
explicit QSurface3DSeries(QSurface3DSeriesPrivate *d, QObject *parent = 0);
diff --git a/src/datavisualization/data/qsurface3dseries_p.h b/src/datavisualization/data/qsurface3dseries_p.h
index d4cc2820..270a3ad1 100644
--- a/src/datavisualization/data/qsurface3dseries_p.h
+++ b/src/datavisualization/data/qsurface3dseries_p.h
@@ -48,6 +48,7 @@ public:
void setSelectedPoint(const QPoint &position);
void setFlatShadingEnabled(bool enabled);
void setDrawMode(QSurface3DSeries::DrawFlags mode);
+ void setTexture(const QImage &texture);
private:
QSurface3DSeries *qptr();
@@ -55,6 +56,8 @@ private:
QPoint m_selectedPoint;
bool m_flatShadingEnabled;
QSurface3DSeries::DrawFlags m_drawMode;
+ QImage m_texture;
+ QString m_textureFile;
private:
friend class QSurface3DSeries;
diff --git a/src/datavisualization/data/qsurfacedataproxy.cpp b/src/datavisualization/data/qsurfacedataproxy.cpp
index dbe7cc49..12b24465 100644
--- a/src/datavisualization/data/qsurfacedataproxy.cpp
+++ b/src/datavisualization/data/qsurfacedataproxy.cpp
@@ -53,6 +53,10 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
* \note Surfaces with less than two rows or columns are not considered valid surfaces and will
* not be rendered.
*
+ * \note On some environments, surfaces with a lot of visible vertices may not render, because
+ * they exceed the per-draw vertex count supported by the graphics driver.
+ * This is mostly an issue on 32bit and/or OpenGL ES2 platforms.
+ *
* \sa {Qt Data Visualization Data Handling}
*/
diff --git a/src/datavisualization/datavisualization.pro b/src/datavisualization/datavisualization.pro
index 44f0f60c..645a1733 100644
--- a/src/datavisualization/datavisualization.pro
+++ b/src/datavisualization/datavisualization.pro
@@ -1,5 +1,5 @@
# Target can't start with 'Qt' as it gets major version number inserted into it in that case,
-# which we don't want. Exception is mac bundles, where the target name must match the module name
+# which we don't want. Exception is OS X bundles, where the target name must match the module name
mac:CONFIG(shared, static|shared):contains(QT_CONFIG, qt_framework) {
TARGET = QtDataVisualization
} else {
@@ -7,7 +7,8 @@ mac:CONFIG(shared, static|shared):contains(QT_CONFIG, qt_framework) {
}
message($$QT_CONFIG)
-QT = core gui
+QT += core gui
+osx: QT += gui-private
DEFINES += QT_DATAVISUALIZATION_LIBRARY
# Fix exports in static builds for applications linking datavisualization module
@@ -19,9 +20,8 @@ QMAKE_DOCS = $$PWD/doc/qtdatavisualization.qdocconf
load(qt_module)
-include($$PWD/common.pri)
-include($$PWD/engine/engine.pri)
include($$PWD/global/global.pri)
+include($$PWD/engine/engine.pri)
include($$PWD/utils/utils.pri)
include($$PWD/theme/theme.pri)
include($$PWD/axis/axis.pri)
diff --git a/src/datavisualization/doc/qtdatavisualization.qdocconf b/src/datavisualization/doc/qtdatavisualization.qdocconf
index 6e9f09b4..4d63b526 100644
--- a/src/datavisualization/doc/qtdatavisualization.qdocconf
+++ b/src/datavisualization/doc/qtdatavisualization.qdocconf
@@ -6,7 +6,7 @@ include($QT_INSTALL_DOCS/global/qt-html-templates-offline.qdocconf)
project = QtDataVisualization
description = Qt Data Visualization Reference Documentation
-version = 1.1.0
+version = 1.2.0
exampledirs += ../../../examples/datavisualization \
snippets
@@ -27,14 +27,14 @@ indexes += $QT_INSTALL_DOCS/qtcore/qtcore.index \
qhp.projects = QtDataVisualization
qhp.QtDataVisualization.file = qtdatavisualization.qhp
-qhp.QtDataVisualization.namespace = com.digia.qtdatavisualization.110
+qhp.QtDataVisualization.namespace = com.digia.qtdatavisualization.120
qhp.QtDataVisualization.virtualFolder = qtdatavisualization
qhp.QtDataVisualization.indexTitle = Qt Data Visualization
qhp.QtDataVisualization.indexRoot =
-qhp.QtDataVisualization.filterAttributes = qtdatavisualization 1.1.0 qtrefdoc
-qhp.QtDataVisualization.customFilters.Qt.name = QtDataVisualization 1.1.0
-qhp.QtDataVisualization.customFilters.Qt.filterAttributes = qtdatavisualization 1.1.0
+qhp.QtDataVisualization.filterAttributes = qtdatavisualization 1.2.0 qtrefdoc
+qhp.QtDataVisualization.customFilters.Qt.name = QtDataVisualization 1.2.0
+qhp.QtDataVisualization.customFilters.Qt.filterAttributes = qtdatavisualization 1.2.0
qhp.QtDataVisualization.subprojects = gettingstarted examples classes types
qhp.QtDataVisualization.subprojects.gettingstarted.title = Getting Started
qhp.QtDataVisualization.subprojects.gettingstarted.indexTitle = Qt Data Visualization Getting Started
@@ -50,7 +50,7 @@ qhp.QtDataVisualization.subprojects.classes.selectors = class
qhp.QtDataVisualization.subprojects.classes.sortPages = true
qhp.QtDataVisualization.subprojects.types.title = QML Types
qhp.QtDataVisualization.subprojects.types.indexTitle = Qt Data Visualization QML Types
-qhp.QtDataVisualization.subprojects.types.selectors = fake:qmlclass
+qhp.QtDataVisualization.subprojects.types.selectors = qmlclass
qhp.QtDataVisualization.subprojects.types.sortPages = true
navigation.landingpage = Qt Data Visualization
diff --git a/src/datavisualization/doc/snippets/doc_src_qmldatavisualization.cpp b/src/datavisualization/doc/snippets/doc_src_qmldatavisualization.cpp
index 56bfc5ee..1ca3f597 100644
--- a/src/datavisualization/doc/snippets/doc_src_qmldatavisualization.cpp
+++ b/src/datavisualization/doc/snippets/doc_src_qmldatavisualization.cpp
@@ -17,12 +17,12 @@
****************************************************************************/
//! [0]
-import QtDataVisualization 1.1
+import QtDataVisualization 1.2
//! [0]
//! [1]
import QtQuick 2.0
-import QtDataVisualization 1.1
+import QtDataVisualization 1.2
Item {
width: 640
@@ -61,7 +61,7 @@ Item {
//! [2]
import QtQuick 2.0
-import QtDataVisualization 1.1
+import QtDataVisualization 1.2
Item {
width: 640
@@ -94,7 +94,7 @@ Item {
//! [3]
import QtQuick 2.0
-import QtDataVisualization 1.1
+import QtDataVisualization 1.2
Item {
width: 640
diff --git a/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc b/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc
index 2cc3eece..b5a678e5 100644
--- a/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc
+++ b/src/datavisualization/doc/src/qtdatavisualization-qml-abstractdeclarative.qdoc
@@ -153,6 +153,32 @@
*/
/*!
+ * \qmlproperty bool AbstractGraph3D::polar
+ * \since QtDataVisualization 1.2
+ *
+ * If \c {true}, the horizontal axes are changed into polar axes. The X axis becomes the
+ * angular axis and the Z axis becomes the radial axis.
+ * Polar mode is not available for bar graphs.
+ *
+ * Defaults to \c{false}.
+ *
+ * \sa orthoProjection, radialLabelOffset
+ */
+
+/*!
+ * \qmlproperty real AbstractGraph3D::radialLabelOffset
+ * \since QtDataVisualization 1.2
+ *
+ * This property specifies the normalized horizontal offset for the axis labels of the radial
+ * polar axis. The value 0.0 indicates the labels should be drawn next to the 0-angle angular
+ * axis grid line. The value 1.0 indicates the labels are drawn on their normal place at the edge
+ * of the graph background.
+ * This property is ignored if polar property value is \c{false}. Defaults to 1.0.
+ *
+ * \sa polar
+ */
+
+/*!
* \qmlmethod void AbstractGraph3D::clearSelection()
* Clears selection from all attached series.
*/
@@ -289,22 +315,123 @@
* \qmlproperty real AbstractGraph3D::aspectRatio
* \since QtDataVisualization 1.1
*
- * Aspect ratio of the graph data. This is the ratio of data scaling between horizontal and
- * vertical axes. Defaults to \c{2.0}.
+ * The aspect ratio is the ratio of the graph scaling between the longest axis on the horizontal
+ * plane and the Y-axis. Defaults to \c{2.0}.
*
* \note Has no effect on Bars3D.
+ *
+ * \sa horizontalAspectRatio
+ */
+
+/*!
+ * \qmlproperty real AbstractGraph3D::horizontalAspectRatio
+ * \since QtDataVisualization 1.2
+ *
+ * The horizontal aspect ratio is the ratio of the graph scaling between the X and Z axes.
+ * Value of 0.0 indicates automatic scaling according to axis ranges.
+ * Defaults to \c{0.0}.
+ *
+ * \note Has no effect on Bars3D, which handles scaling on the horizontal plane via
+ * \l{Bars3D::barThickness}{barThickness} and \l{Bars3D::barSpacing}{barSpacing} properties.
+ * Polar graphs also ignore this property.
+ *
+ * \sa aspectRatio, polar, Bars3D::barThickness, Bars3D::barSpacing
*/
/*!
* \qmlproperty AbstractGraph3D.OptimizationHints AbstractGraph3D::optimizationHints
- * \since Qt Data Visualization 1.1
- *
- * 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}
+ * \since QtDataVisualization 1.1
+ *
+ * Defines if the rendering optimization is default or static. Default mode provides the full
+ * feature set at reasonable performance. Static mode optimizes graph rendering and is ideal for
+ * large non-changing data sets. It is slower with dynamic data changes and item rotations.
+ * Selection is not optimized, so using it with massive data sets is not advisable.
+ * Static works only on the Scatter graph.
+ * Defaults to \c{OptimizationDefault}.
+ *
+ * \note On some environments, large graphs using static optimization may not render, because
+ * all of the items are rendered using a single draw call, and different graphics drivers have
+ * different maximum vertice counts per call that they support.
+ * This is mostly an issue on 32bit and/or OpenGL ES2 platforms.
+ * To work around this issue, choose an item mesh with low vertex count or use the point mesh.
+ *
+ * \sa Abstract3DSeries::mesh
+ */
+
+/*!
+ * \qmlproperty bool AbstractGraph3D::reflection
+ * \since QtDataVisualization 1.2
+ *
+ * Sets floor reflections on or off. Defaults to \c{false}.
+ *
+ * \note Affects only Bars3D.
+ *
+ * \note In Bars3D graphs holding both positive and negative values, reflections are not supported
+ * for custom items that intersect the floor plane. In that case, reflections should be turned off
+ * to avoid incorrect rendering.
+ *
+ * \sa reflectivity
+ */
+
+/*!
+ * \qmlproperty real AbstractGraph3D::reflectivity
+ * \since QtDataVisualization 1.2
+ *
+ * Adjusts floor reflectivity, larger number being more reflective. Valid range is \c{[0...1]}.
+ * Defaults to \c{0.5}.
+ *
+ * \note Affects only Bars3D.
+ *
+ * \sa reflection
+ */
+
+/*!
+ * \qmlproperty locale AbstractGraph3D::locale
+ * \since QtDataVisualization 1.2
+ *
+ * Sets the locale used for formatting various numeric labels.
+ * Defaults to \c{"C"} locale.
+ *
+ * \sa ValueAxis3D::labelFormat
+ */
+
+/*!
+ * \qmlproperty vector3d AbstractGraph3D::queriedGraphPosition
+ * \since QtDataVisualization 1.2
+ *
+ * This read-only property contains the latest graph position values along each axis queried using
+ * Scene3D::graphPositionQuery. The values are normalized to range \c{[-1, 1]}.
+ * If the queried position was outside the graph bounds, the values
+ * will not reflect the real position, but will instead be some undefined position outside
+ * the range \c{[-1, 1]}. The value will be undefined before any queries are made.
+ *
+ * There isn't a single correct 3D coordinate to match to each specific screen position, so to be
+ * consistent, the queries are always done against the inner sides of an invisible box surrounding
+ * the graph.
+ *
+ * \note Bar graphs only allow querying graph position at the graph floor level,
+ * so the Y-value is always zero for bar graphs and the valid queries can be only made at
+ * screen positions that contain the floor of the graph.
+ *
+ * \sa Scene3D::graphPositionQuery
+ */
+
+/*!
+ * \qmlproperty real AbstractGraph3D::margin
+ * \since QtDataVisualization 1.2
+ *
+ * This property contains the absolute value used for graph margin. The graph margin is the space
+ * left between the edge of the plottable graph area and the edge of the graph background.
+ * If the margin value is negative, the margins are determined automatically and can vary according
+ * to size of the items in the series and the type of the graph.
+ * The value is interpreted as a fraction of Y-axis range, provided the graph aspect ratios have
+ * not beed changed from the defaults.
+ * Defaults to \c{-1.0}.
+ *
+ * \note Having smaller than the automatically determined margin on scatter graph can cause
+ * the scatter items at the edges of the graph to overlap with the graph background.
+ *
+ * \note On scatter and surface graphs, if the margin is comparatively small to the axis label
+ * size, the positions of the edge labels of the axes are adjusted to avoid overlap with
+ * the edge labels of the neighboring axes.
*/
diff --git a/src/datavisualization/doc/src/qtdatavisualization-qml-bars3d.qdoc b/src/datavisualization/doc/src/qtdatavisualization-qml-bars3d.qdoc
index 6ee51742..0348652a 100644
--- a/src/datavisualization/doc/src/qtdatavisualization-qml-bars3d.qdoc
+++ b/src/datavisualization/doc/src/qtdatavisualization-qml-bars3d.qdoc
@@ -115,6 +115,14 @@
*/
/*!
+ * \qmlproperty real Bars3D::floorLevel
+ *
+ * The desired floor level for the bar graph in Y-axis data coordinates.
+ * The actual floor level cannot go below Y-axis minimum or above Y-axis maximum.
+ * Defaults to zero.
+ */
+
+/*!
* \qmlmethod void Bars3D::addSeries(Bar3DSeries series)
* Adds the \a series to the graph. A graph can contain multiple series, but only one set of axes,
* so the rows and columns of all series must match for the visualized data to be meaningful.
diff --git a/src/datavisualization/doc/src/qtdatavisualization-qml-surface3d.qdoc b/src/datavisualization/doc/src/qtdatavisualization-qml-surface3d.qdoc
index 23a9a004..2b83b807 100644
--- a/src/datavisualization/doc/src/qtdatavisualization-qml-surface3d.qdoc
+++ b/src/datavisualization/doc/src/qtdatavisualization-qml-surface3d.qdoc
@@ -73,11 +73,6 @@
*/
/*!
- \qmlproperty ColorGradient Surface3D::gradient
- The current surface gradient. Setting this property replaces the previous gradient.
- */
-
-/*!
* \qmlproperty list<Surface3DSeries> Surface3D::seriesList
* \default
* This property holds the series of the graph.
@@ -86,6 +81,22 @@
*/
/*!
+ * \qmlproperty bool Surface3D::flipHorizontalGrid
+ * \since QtDataVisualization 1.2
+ *
+ * In some use cases the horizontal axis grid is mostly covered by the surface, so it can be more
+ * useful to display the horizontal axis grid on top of the graph rather than on the bottom.
+ * A typical use case for this is showing 2D spectrograms using orthoGraphic projection with
+ * a top-down viewpoint.
+ *
+ * If \c{false}, the horizontal axis grid and labels are drawn on the horizontal background
+ * of the graph.
+ * If \c{true}, the horizontal axis grid and labels are drawn on the opposite side of the graph
+ * from the horizontal background.
+ * Defaults to \c{false}.
+ */
+
+/*!
* \qmlmethod void Surface3D::addSeries(Surface3DSeries series)
* Adds the \a series to the graph.
*/
diff --git a/src/datavisualization/doc/src/qtdatavisualization.qdoc b/src/datavisualization/doc/src/qtdatavisualization.qdoc
index af419814..b07074b1 100644
--- a/src/datavisualization/doc/src/qtdatavisualization.qdoc
+++ b/src/datavisualization/doc/src/qtdatavisualization.qdoc
@@ -37,7 +37,7 @@
*/
/*!
- \qmlmodule QtDataVisualization 1.1
+ \qmlmodule QtDataVisualization 1.2
\title Qt Data Visualization QML Types
\ingroup qmlmodules
@@ -98,7 +98,7 @@
\row
\li Windows (MSVC) \li nmake
\row
- \li OSX \li make
+ \li OS X \li make
\endtable
The above generates the default makefiles for your configuration, which is typically
@@ -127,7 +127,7 @@
make release
\endcode
- For both builds (Windows/Mac only):
+ For both builds (Windows/OS X only):
\code
qmake CONFIG+="debug_and_release build_all"
make
@@ -323,20 +323,21 @@
\title Qt Data Visualization Known Issues
\list
- \li Android doesn't support both widgets and OpenGL simultaneously, so only
- the Qt Quick 2 API is usable in practice in Android.
+ \li Some platforms like Android and WinRT cannot handle multiple native windows properly,
+ so only the Qt Quick 2 graphs are available in practice for those platforms.
\li Shadows are not supported with OpenGL ES2 (including Angle builds in Windows).
\li Anti-aliasing doesn't work with OpenGL ES2 (including Angle builds in Windows).
+ \li QCustom3DVolume items are not supported with OpenGL ES2 (including Angle builds in
+ Windows).
\li Surfaces with non-straight rows and columns do not always render properly.
\li Q3DLight class (and Light3D QML item) are currently not usable for anything.
- \li Changing any of Q3DScene properties affecting subviewports currently has no effect.
- \li The color style Q3DTheme::ColorStyleObjectGradient doesn't work for surface graphs.
+ \li Changing most of Q3DScene properties affecting subviewports currently has no effect.
\li Widget based examples layout incorrectly in iOS.
\li Reparenting a graph to an item in another QQuickWindow is not supported.
- \li There is a low-impact binary break between 1.0 and 1.1. The break is due to a QML type
- registration conflict with QAbstractItemModel between QtDataVisualization and
- QtCommercial.Charts. Introducing the binary break makes it possible to use both
- Charts and Data Visualization in the same QML application.
+ \li Android builds of QML applications importing QtDataVisualization also require
+ "QT += datavisualization" in the pro file. This is because Qt Data Visualization
+ QML plugin has a dependency to Qt Data Visualization C++ library, which Qt Creator
+ doesn't automatically add to the deployment package.
\endlist
*/
diff --git a/src/datavisualization/engine/abstract3dcontroller.cpp b/src/datavisualization/engine/abstract3dcontroller.cpp
index 30434dca..275d0fe2 100644
--- a/src/datavisualization/engine/abstract3dcontroller.cpp
+++ b/src/datavisualization/engine/abstract3dcontroller.cpp
@@ -25,6 +25,7 @@
#include "thememanager_p.h"
#include "q3dtheme_p.h"
#include "qcustom3ditem_p.h"
+#include "utils_p.h"
#include <QtCore/QThread>
#include <QtGui/QOpenGLFramebufferObject>
@@ -37,8 +38,12 @@ Abstract3DController::Abstract3DController(QRect initialViewport, Q3DScene *scen
m_selectionMode(QAbstract3DGraph::SelectionItem),
m_shadowQuality(QAbstract3DGraph::ShadowQualityMedium),
m_useOrthoProjection(false),
- m_aspectRatio(2.0f),
+ m_aspectRatio(2.0),
+ m_horizontalAspectRatio(0.0),
m_optimizationHints(QAbstract3DGraph::OptimizationDefault),
+ m_reflectionEnabled(false),
+ m_reflectivity(0.5),
+ m_locale(QLocale::c()),
m_scene(scene),
m_activeInputHandler(0),
m_axisX(0),
@@ -50,12 +55,19 @@ Abstract3DController::Abstract3DController(QRect initialViewport, Q3DScene *scen
m_isCustomItemDirty(true),
m_isSeriesVisualsDirty(true),
m_renderPending(false),
+ m_isPolar(false),
+ m_radialLabelOffset(1.0f),
m_measureFps(false),
m_numFrames(0),
- m_currentFps(0.0)
+ m_currentFps(0.0),
+ m_clickedType(QAbstract3DGraph::ElementNone),
+ m_selectedLabelIndex(-1),
+ m_selectedCustomItemIndex(-1),
+ m_margin(-1.0)
{
if (!m_scene)
m_scene = new Q3DScene;
+ m_scene->setParent(this);
// Set initial theme
Q3DTheme *defaultTheme = new Q3DTheme(Q3DTheme::ThemeQt);
@@ -72,10 +84,6 @@ Abstract3DController::Abstract3DController(QRect initialViewport, Q3DScene *scen
inputHandler = new QTouch3DInputHandler();
inputHandler->d_ptr->m_isDefaultHandler = true;
setActiveInputHandler(inputHandler);
- connect(inputHandler, &QAbstract3DInputHandler::inputViewChanged, this,
- &Abstract3DController::handleInputViewChanged);
- connect(inputHandler, &QAbstract3DInputHandler::positionChanged, this,
- &Abstract3DController::handleInputPositionChanged);
connect(m_scene->d_ptr.data(), &Q3DScenePrivate::needRender, this,
&Abstract3DController::emitNeedRender);
}
@@ -93,7 +101,7 @@ Abstract3DController::~Abstract3DController()
void Abstract3DController::destroyRenderer()
{
// Renderer can be in another thread, don't delete it directly in that case
- if (m_renderer && m_renderer->thread() != QThread::currentThread())
+ if (m_renderer && m_renderer->thread() && m_renderer->thread() != this->thread())
m_renderer->deleteLater();
else
delete m_renderer;
@@ -107,6 +115,13 @@ void Abstract3DController::destroyRenderer()
void Abstract3DController::setRenderer(Abstract3DRenderer *renderer)
{
m_renderer = renderer;
+
+ // If renderer is created in different thread than controller, make sure renderer gets
+ // destroyed before the render thread finishes.
+ if (renderer->thread() != this->thread()) {
+ QObject::connect(renderer->thread(), &QThread::finished, this,
+ &Abstract3DController::destroyRenderer, Qt::DirectConnection);
+ }
}
void Abstract3DController::addSeries(QAbstract3DSeries *series)
@@ -163,11 +178,14 @@ void Abstract3DController::synchDataToRenderer()
{
// Subclass implementations check for renderer validity already, so no need to check here.
- // If there is a pending click from renderer, handle that first.
- if (m_renderer->isClickPending()) {
+ m_renderPending = false;
+
+ // If there are pending queries, handle those first
+ if (m_renderer->isGraphPositionQueryResolved())
+ handlePendingGraphPositionQuery();
+
+ if (m_renderer->isClickQueryResolved())
handlePendingClick();
- m_renderer->clearClickPending();
- }
startRecordingRemovesAndInserts();
@@ -176,6 +194,16 @@ void Abstract3DController::synchDataToRenderer()
m_renderer->updateTheme(m_themeManager->activeTheme());
+ if (m_changeTracker.polarChanged) {
+ m_renderer->updatePolar(m_isPolar);
+ m_changeTracker.polarChanged = false;
+ }
+
+ if (m_changeTracker.radialLabelOffsetChanged) {
+ m_renderer->updateRadialLabelOffset(m_radialLabelOffset);
+ m_changeTracker.radialLabelOffsetChanged = false;
+ }
+
if (m_changeTracker.shadowQualityChanged) {
m_renderer->updateShadowQuality(m_shadowQuality);
m_changeTracker.shadowQualityChanged = false;
@@ -192,15 +220,31 @@ void Abstract3DController::synchDataToRenderer()
}
if (m_changeTracker.aspectRatioChanged) {
- m_renderer->updateAspectRatio(m_aspectRatio);
+ m_renderer->updateAspectRatio(float(m_aspectRatio));
m_changeTracker.aspectRatioChanged = false;
}
+ if (m_changeTracker.horizontalAspectRatioChanged) {
+ m_renderer->updateHorizontalAspectRatio(float(m_horizontalAspectRatio));
+ m_changeTracker.horizontalAspectRatioChanged = false;
+ }
+
if (m_changeTracker.optimizationHintChanged) {
m_renderer->updateOptimizationHint(m_optimizationHints);
m_changeTracker.optimizationHintChanged = false;
}
+ if (m_changeTracker.reflectionChanged) {
+ m_renderer->m_reflectionEnabled = m_reflectionEnabled;
+ m_changeTracker.reflectionChanged = false;
+ }
+
+ if (m_changeTracker.reflectivityChanged) {
+ // Invert value to match functionality to the property description
+ m_renderer->m_reflectivity = -(m_reflectivity - 1.0);
+ m_changeTracker.reflectivityChanged = false;
+ }
+
if (m_changeTracker.axisXFormatterChanged) {
m_changeTracker.axisXFormatterChanged = false;
if (m_axisX->type() & QAbstract3DAxis::AxisTypeValue) {
@@ -445,6 +489,11 @@ void Abstract3DController::synchDataToRenderer()
m_changeTracker.axisZTitleFixedChanged = false;
}
+ if (m_changeTracker.marginChanged) {
+ m_renderer->updateMargin(float(m_margin));
+ m_changeTracker.marginChanged = false;
+ }
+
if (m_changedSeriesList.size()) {
m_renderer->modifiedSeriesList(m_changedSeriesList);
m_changedSeriesList.clear();
@@ -475,8 +524,6 @@ void Abstract3DController::synchDataToRenderer()
void Abstract3DController::render(const GLuint defaultFboHandle)
{
- m_renderPending = false;
-
// If not initialized, do nothing.
if (!m_renderer)
return;
@@ -765,8 +812,9 @@ void Abstract3DController::setActiveInputHandler(QAbstract3DInputHandler *inputH
m_inputHandlers.removeAll(m_activeInputHandler);
delete m_activeInputHandler;
} else {
- // Disconnect the old input handler from the scene
+ // Disconnect the old input handler
m_activeInputHandler->setScene(0);
+ QObject::disconnect(m_activeInputHandler, 0, this, 0);
}
}
@@ -775,9 +823,16 @@ void Abstract3DController::setActiveInputHandler(QAbstract3DInputHandler *inputH
addInputHandler(inputHandler);
m_activeInputHandler = inputHandler;
- if (m_activeInputHandler)
+ if (m_activeInputHandler) {
m_activeInputHandler->setScene(m_scene);
+ // Connect the input handler
+ QObject::connect(m_activeInputHandler, &QAbstract3DInputHandler::inputViewChanged, this,
+ &Abstract3DController::handleInputViewChanged);
+ QObject::connect(m_activeInputHandler, &QAbstract3DInputHandler::positionChanged, this,
+ &Abstract3DController::handleInputPositionChanged);
+ }
+
// Notify change of input handler
emit activeInputHandlerChanged(m_activeInputHandler);
}
@@ -886,11 +941,7 @@ QAbstract3DGraph::OptimizationHints Abstract3DController::optimizationHints() co
bool Abstract3DController::shadowsSupported() const
{
-#if defined(QT_OPENGL_ES_2)
- return false;
-#else
- return true;
-#endif
+ return !isOpenGLES();
}
bool Abstract3DController::isSlicingActive() const
@@ -989,6 +1040,11 @@ void Abstract3DController::releaseCustomItem(QCustom3DItem *item)
}
}
+QList<QCustom3DItem *> Abstract3DController::customItems() const
+{
+ return m_customItems;
+}
+
void Abstract3DController::updateCustomItem()
{
m_isCustomItemDirty = true;
@@ -1303,6 +1359,11 @@ void Abstract3DController::markSeriesItemLabelsDirty()
m_seriesList.at(i)->d_ptr->markItemLabelDirty();
}
+bool Abstract3DController::isOpenGLES() const
+{
+ return Utils::isOpenGLES();
+}
+
void Abstract3DController::setAxisHelper(QAbstract3DAxis::AxisOrientation orientation,
QAbstract3DAxis *axis, QAbstract3DAxis **axisPtr)
{
@@ -1381,6 +1442,8 @@ void Abstract3DController::setAxisHelper(QAbstract3DAxis::AxisOrientation orient
handleAxisLabelFormatChangedBySender(valueAxis);
handleAxisReversedChangedBySender(valueAxis);
handleAxisFormatterDirtyBySender(valueAxis->dptr());
+
+ valueAxis->formatter()->setLocale(m_locale);
}
}
@@ -1427,13 +1490,37 @@ void Abstract3DController::emitNeedRender()
void Abstract3DController::handlePendingClick()
{
- QAbstract3DGraph::ElementType type = m_renderer->clickedType();
- emit elementSelected(type);
+ m_clickedType = m_renderer->clickedType();
+ m_selectedLabelIndex = m_renderer->m_selectedLabelIndex;
+ m_selectedCustomItemIndex = m_renderer->m_selectedCustomItemIndex;
+
+ // Invalidate query position to indicate the query has been handled, unless another
+ // point has been queried.
+ if (m_renderer->cachedClickQuery() == m_scene->selectionQueryPosition())
+ m_scene->setSelectionQueryPosition(Q3DScene::invalidSelectionPoint());
+
+ m_renderer->clearClickQueryResolved();
+
+ emit elementSelected(m_clickedType);
+}
+
+void Abstract3DController::handlePendingGraphPositionQuery()
+{
+ m_queriedGraphPosition = m_renderer->queriedGraphPosition();
+
+ // Invalidate query position to indicate the query has been handled, unless another
+ // point has been queried.
+ if (m_renderer->cachedGraphPositionQuery() == m_scene->graphPositionQuery())
+ m_scene->setGraphPositionQuery(Q3DScene::invalidSelectionPoint());
+
+ m_renderer->clearGraphPositionQueryResolved();
+
+ emit queriedGraphPositionChanged(m_queriedGraphPosition);
}
int Abstract3DController::selectedLabelIndex() const
{
- int index = m_renderer->m_selectedLabelIndex;
+ int index = m_selectedLabelIndex;
QAbstract3DAxis *axis = selectedAxis();
if (axis && axis->labels().count() <= index)
index = -1;
@@ -1443,7 +1530,7 @@ int Abstract3DController::selectedLabelIndex() const
QAbstract3DAxis *Abstract3DController::selectedAxis() const
{
QAbstract3DAxis *axis = 0;
- QAbstract3DGraph::ElementType type = m_renderer->clickedType();
+ QAbstract3DGraph::ElementType type = m_clickedType;
switch (type) {
case QAbstract3DGraph::ElementAxisXLabel:
axis = axisX();
@@ -1464,7 +1551,7 @@ QAbstract3DAxis *Abstract3DController::selectedAxis() const
int Abstract3DController::selectedCustomItemIndex() const
{
- int index = m_renderer->m_selectedCustomItemIndex;
+ int index = m_selectedCustomItemIndex;
if (m_customItems.count() <= index)
index = -1;
return index;
@@ -1481,10 +1568,7 @@ QCustom3DItem *Abstract3DController::selectedCustomItem() const
QAbstract3DGraph::ElementType Abstract3DController::selectedElement() const
{
- if (m_renderer)
- return m_renderer->clickedType();
- else
- return QAbstract3DGraph::ElementNone;
+ return m_clickedType;
}
void Abstract3DController::setOrthoProjection(bool enable)
@@ -1505,7 +1589,7 @@ bool Abstract3DController::isOrthoProjection() const
return m_useOrthoProjection;
}
-void Abstract3DController::setAspectRatio(float ratio)
+void Abstract3DController::setAspectRatio(qreal ratio)
{
if (m_aspectRatio != ratio) {
m_aspectRatio = ratio;
@@ -1516,9 +1600,131 @@ void Abstract3DController::setAspectRatio(float ratio)
}
}
-float Abstract3DController::aspectRatio()
+qreal Abstract3DController::aspectRatio()
{
return m_aspectRatio;
}
+void Abstract3DController::setHorizontalAspectRatio(qreal ratio)
+{
+ if (m_horizontalAspectRatio != ratio) {
+ m_horizontalAspectRatio = ratio;
+ m_changeTracker.horizontalAspectRatioChanged = true;
+ emit horizontalAspectRatioChanged(m_horizontalAspectRatio);
+ m_isDataDirty = true;
+ emitNeedRender();
+ }
+}
+
+qreal Abstract3DController::horizontalAspectRatio() const
+{
+ return m_horizontalAspectRatio;
+}
+
+void Abstract3DController::setReflection(bool enable)
+{
+ if (m_reflectionEnabled != enable) {
+ m_reflectionEnabled = enable;
+ m_changeTracker.reflectionChanged = true;
+ emit reflectionChanged(m_reflectionEnabled);
+ emitNeedRender();
+ }
+}
+
+bool Abstract3DController::reflection() const
+{
+ return m_reflectionEnabled;
+}
+
+void Abstract3DController::setReflectivity(qreal reflectivity)
+{
+ if (m_reflectivity != reflectivity) {
+ m_reflectivity = reflectivity;
+ m_changeTracker.reflectivityChanged = true;
+ emit reflectivityChanged(m_reflectivity);
+ emitNeedRender();
+ }
+}
+
+qreal Abstract3DController::reflectivity() const
+{
+ return m_reflectivity;
+}
+
+void Abstract3DController::setPolar(bool enable)
+{
+ if (enable != m_isPolar) {
+ m_isPolar = enable;
+ m_changeTracker.polarChanged = true;
+ m_isDataDirty = true;
+ emit polarChanged(m_isPolar);
+ emitNeedRender();
+ }
+}
+
+bool Abstract3DController::isPolar() const
+{
+ return m_isPolar;
+}
+
+void Abstract3DController::setRadialLabelOffset(float offset)
+{
+ if (m_radialLabelOffset != offset) {
+ m_radialLabelOffset = offset;
+ m_changeTracker.radialLabelOffsetChanged = true;
+ emit radialLabelOffsetChanged(m_radialLabelOffset);
+ emitNeedRender();
+ }
+}
+
+float Abstract3DController::radialLabelOffset() const
+{
+ return m_radialLabelOffset;
+}
+
+void Abstract3DController::setLocale(const QLocale &locale)
+{
+ if (m_locale != locale) {
+ m_locale = locale;
+
+ // Value axis formatters need to be updated
+ QValue3DAxis *axis = qobject_cast<QValue3DAxis *>(m_axisX);
+ if (axis)
+ axis->formatter()->setLocale(m_locale);
+ axis = qobject_cast<QValue3DAxis *>(m_axisY);
+ if (axis)
+ axis->formatter()->setLocale(m_locale);
+ axis = qobject_cast<QValue3DAxis *>(m_axisZ);
+ if (axis)
+ axis->formatter()->setLocale(m_locale);
+ emit localeChanged(m_locale);
+ }
+}
+
+QLocale Abstract3DController::locale() const
+{
+ return m_locale;
+}
+
+QVector3D Abstract3DController::queriedGraphPosition() const
+{
+ return m_queriedGraphPosition;
+}
+
+void Abstract3DController::setMargin(qreal margin)
+{
+ if (m_margin != margin) {
+ m_margin = margin;
+ m_changeTracker.marginChanged = true;
+ emit marginChanged(margin);
+ emitNeedRender();
+ }
+}
+
+qreal Abstract3DController::margin() const
+{
+ return m_margin;
+}
+
+
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/engine/abstract3dcontroller_p.h b/src/datavisualization/engine/abstract3dcontroller_p.h
index 0e4d1add..d5a1ac8f 100644
--- a/src/datavisualization/engine/abstract3dcontroller_p.h
+++ b/src/datavisualization/engine/abstract3dcontroller_p.h
@@ -38,6 +38,7 @@
#include "qcustom3ditem.h"
#include <QtGui/QLinearGradient>
#include <QtCore/QTime>
+#include <QtCore/QLocale>
class QOpenGLFramebufferObject;
@@ -84,12 +85,18 @@ struct Abstract3DChangeBitField {
bool axisYLabelAutoRotationChanged : 1;
bool axisZLabelAutoRotationChanged : 1;
bool aspectRatioChanged : 1;
+ bool horizontalAspectRatioChanged : 1;
bool axisXTitleVisibilityChanged : 1;
bool axisYTitleVisibilityChanged : 1;
bool axisZTitleVisibilityChanged : 1;
bool axisXTitleFixedChanged : 1;
bool axisYTitleFixedChanged : 1;
bool axisZTitleFixedChanged : 1;
+ bool polarChanged : 1;
+ bool radialLabelOffsetChanged : 1;
+ bool reflectionChanged : 1;
+ bool reflectivityChanged : 1;
+ bool marginChanged : 1;
Abstract3DChangeBitField() :
themeChanged(true),
@@ -128,12 +135,18 @@ struct Abstract3DChangeBitField {
axisYLabelAutoRotationChanged(true),
axisZLabelAutoRotationChanged(true),
aspectRatioChanged(true),
+ horizontalAspectRatioChanged(true),
axisXTitleVisibilityChanged(true),
axisYTitleVisibilityChanged(true),
axisZTitleVisibilityChanged(true),
axisXTitleFixedChanged(true),
axisYTitleFixedChanged(true),
- axisZTitleFixedChanged(true)
+ axisZTitleFixedChanged(true),
+ polarChanged(true),
+ radialLabelOffsetChanged(true),
+ reflectionChanged(true),
+ reflectivityChanged(true),
+ marginChanged(true)
{
}
};
@@ -156,8 +169,13 @@ private:
QAbstract3DGraph::SelectionFlags m_selectionMode;
QAbstract3DGraph::ShadowQuality m_shadowQuality;
bool m_useOrthoProjection;
- float m_aspectRatio;
+ qreal m_aspectRatio;
+ qreal m_horizontalAspectRatio;
QAbstract3DGraph::OptimizationHints m_optimizationHints;
+ bool m_reflectionEnabled;
+ qreal m_reflectivity;
+ QLocale m_locale;
+ QVector3D m_queriedGraphPosition;
protected:
Q3DScene *m_scene;
@@ -175,6 +193,8 @@ protected:
bool m_isCustomItemDirty;
bool m_isSeriesVisualsDirty;
bool m_renderPending;
+ bool m_isPolar;
+ float m_radialLabelOffset;
QList<QAbstract3DSeries *> m_seriesList;
@@ -187,13 +207,17 @@ protected:
QList<QCustom3DItem *> m_customItems;
+ QAbstract3DGraph::ElementType m_clickedType;
+ int m_selectedLabelIndex;
+ int m_selectedCustomItemIndex;
+ qreal m_margin;
+
explicit Abstract3DController(QRect initialViewport, Q3DScene *scene, QObject *parent = 0);
public:
virtual ~Abstract3DController();
inline bool isInitialized() { return (m_renderer != 0); }
- virtual void destroyRenderer();
virtual void synchDataToRenderer();
virtual void render(const GLuint defaultFboHandle = 0);
virtual void initializeOpenGL() = 0;
@@ -252,6 +276,7 @@ public:
void deleteCustomItem(QCustom3DItem *item);
void deleteCustomItem(const QVector3D &position);
void releaseCustomItem(QCustom3DItem *item);
+ QList<QCustom3DItem *> customItems() const;
int selectedLabelIndex() const;
QAbstract3DAxis *selectedAxis() const;
@@ -261,6 +286,35 @@ public:
void setOrthoProjection(bool enable);
bool isOrthoProjection() const;
+ void setMeasureFps(bool enable);
+ inline bool measureFps() const { return m_measureFps; }
+ inline qreal currentFps() const { return m_currentFps; }
+
+ QAbstract3DGraph::ElementType selectedElement() const;
+
+ void setAspectRatio(qreal ratio);
+ qreal aspectRatio();
+ void setHorizontalAspectRatio(qreal ratio);
+ qreal horizontalAspectRatio() const;
+
+ void setReflection(bool enable);
+ bool reflection() const;
+ void setReflectivity(qreal reflectivity);
+ qreal reflectivity() const;
+
+ void setPolar(bool enable);
+ bool isPolar() const;
+ void setRadialLabelOffset(float offset);
+ float radialLabelOffset() const;
+
+ void setLocale(const QLocale &locale);
+ QLocale locale() const;
+
+ QVector3D queriedGraphPosition() const;
+
+ void setMargin(qreal margin);
+ qreal margin() const;
+
void emitNeedRender();
virtual void clearSelection() = 0;
@@ -286,12 +340,16 @@ public:
virtual void handleAxisTitleVisibilityChangedBySender(QObject *sender);
virtual void handleAxisTitleFixedChangedBySender(QObject *sender);
virtual void handleSeriesVisibilityChangedBySender(QObject *sender);
- virtual void handlePendingClick() = 0;
+ virtual void handlePendingClick();
+ virtual void handlePendingGraphPositionQuery();
virtual void adjustAxisRanges() = 0;
void markSeriesItemLabelsDirty();
+ bool isOpenGLES() const;
public slots:
+ void destroyRenderer();
+
void handleAxisTitleChanged(const QString &title);
void handleAxisLabelsChanged();
void handleAxisRangeChanged(float min, float max);
@@ -320,17 +378,8 @@ public slots:
// Renderer callback handlers
void handleRequestShadowQuality(QAbstract3DGraph::ShadowQuality quality);
- void setMeasureFps(bool enable);
- inline bool measureFps() const { return m_measureFps; }
- inline qreal currentFps() const { return m_currentFps; }
-
- QAbstract3DGraph::ElementType selectedElement() const;
-
void updateCustomItem();
- void setAspectRatio(float ratio);
- float aspectRatio();
-
signals:
void shadowQualityChanged(QAbstract3DGraph::ShadowQuality quality);
void activeInputHandlerChanged(QAbstract3DInputHandler *inputHandler);
@@ -344,8 +393,16 @@ signals:
void measureFpsChanged(bool enabled);
void currentFpsChanged(qreal fps);
void orthoProjectionChanged(bool enabled);
- void aspectRatioChanged(float ratio);
+ void aspectRatioChanged(qreal ratio);
+ void horizontalAspectRatioChanged(qreal ratio);
void optimizationHintsChanged(QAbstract3DGraph::OptimizationHints hints);
+ void polarChanged(bool enabled);
+ void radialLabelOffsetChanged(float offset);
+ void reflectionChanged(bool enabled);
+ void reflectivityChanged(qreal reflectivity);
+ void localeChanged(const QLocale &locale);
+ void queriedGraphPositionChanged(const QVector3D &data);
+ void marginChanged(qreal margin);
protected:
virtual QAbstract3DAxis *createDefaultAxis(QAbstract3DAxis::AxisOrientation orientation);
diff --git a/src/datavisualization/engine/abstract3drenderer.cpp b/src/datavisualization/engine/abstract3drenderer.cpp
index 04ede782..cfc691af 100644
--- a/src/datavisualization/engine/abstract3drenderer.cpp
+++ b/src/datavisualization/engine/abstract3drenderer.cpp
@@ -24,9 +24,24 @@
#include "shaderhelper_p.h"
#include "qcustom3ditem_p.h"
#include "qcustom3dlabel_p.h"
+#include "qcustom3dvolume_p.h"
+#include "scatter3drenderer_p.h"
+
+#include <QtCore/qmath.h>
+#include <QtGui/QWindow>
+#include <QtCore/QThread>
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
+// Defined in shaderhelper.cpp
+extern void discardDebugMsgs(QtMsgType type, const QMessageLogContext &context, const QString &msg);
+
+const qreal doublePi(M_PI * 2.0);
+const int polarGridRoundness(64);
+const qreal polarGridAngle(doublePi / qreal(polarGridRoundness));
+const float polarGridAngleDegrees(float(360.0 / qreal(polarGridRoundness)));
+const qreal polarGridHalfAngle(polarGridAngle / 2.0);
+
Abstract3DRenderer::Abstract3DRenderer(Abstract3DController *controller)
: QObject(0),
m_hasNegativeValues(false),
@@ -37,26 +52,83 @@ Abstract3DRenderer::Abstract3DRenderer(Abstract3DController *controller)
m_cachedSelectionMode(QAbstract3DGraph::SelectionNone),
m_cachedOptimizationHint(QAbstract3DGraph::OptimizationDefault),
m_textureHelper(0),
+ m_depthTexture(0),
m_cachedScene(new Q3DScene()),
m_selectionDirty(true),
m_selectionState(SelectNone),
m_devicePixelRatio(1.0f),
m_selectionLabelDirty(true),
- m_clickPending(false),
+ m_clickResolved(false),
+ m_graphPositionQueryPending(false),
+ m_graphPositionQueryResolved(false),
m_clickedSeries(0),
m_clickedType(QAbstract3DGraph::ElementNone),
+ m_selectedLabelIndex(-1),
+ m_selectedCustomItemIndex(-1),
m_selectionLabelItem(0),
m_visibleSeriesCount(0),
m_customItemShader(0),
+ m_volumeTextureShader(0),
+ m_volumeTextureLowDefShader(0),
+ m_volumeTextureSliceShader(0),
+ m_volumeSliceFrameShader(0),
+ m_labelShader(0),
+ m_cursorPositionShader(0),
+ m_cursorPositionFrameBuffer(0),
+ m_cursorPositionTexture(0),
m_useOrthoProjection(false),
m_xFlipped(false),
m_yFlipped(false),
m_zFlipped(false),
+ m_yFlippedForGrid(false),
m_backgroundObj(0),
m_gridLineObj(0),
m_labelObj(0),
- m_graphAspectRatio(2.0f)
+ m_positionMapperObj(0),
+ m_graphAspectRatio(2.0f),
+ m_graphHorizontalAspectRatio(0.0f),
+ m_polarGraph(false),
+ m_radialLabelOffset(1.0f),
+ m_polarRadius(2.0f),
+ m_xRightAngleRotation(QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, 90.0f)),
+ m_yRightAngleRotation(QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, 90.0f)),
+ m_zRightAngleRotation(QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, 90.0f)),
+ m_xRightAngleRotationNeg(QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, -90.0f)),
+ m_yRightAngleRotationNeg(QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, -90.0f)),
+ m_zRightAngleRotationNeg(QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, -90.0f)),
+ m_xFlipRotation(QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, -180.0f)),
+ m_zFlipRotation(QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, -180.0f)),
+ m_requestedMargin(-1.0f),
+ m_vBackgroundMargin(0.1f),
+ m_hBackgroundMargin(0.1f),
+ m_scaleXWithBackground(0.0f),
+ m_scaleYWithBackground(0.0f),
+ m_scaleZWithBackground(0.0f),
+ m_oldCameraTarget(QVector3D(2000.0f, 2000.0f, 2000.0f)), // Just random invalid target
+ m_reflectionEnabled(false),
+ m_reflectivity(0.5),
+#if !defined(QT_OPENGL_ES_2)
+ m_funcs_2_1(0),
+#endif
+ m_context(0),
+ m_dummySurfaceAtDelete(0),
+ m_isOpenGLES(true)
+
{
+ initializeOpenGLFunctions();
+ m_isOpenGLES = Utils::isOpenGLES();
+#if !defined(QT_OPENGL_ES_2)
+ if (!m_isOpenGLES) {
+ // Discard warnings about deprecated functions
+ QtMessageHandler handler = qInstallMessageHandler(discardDebugMsgs);
+
+ m_funcs_2_1 = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_2_1>();
+ m_funcs_2_1->initializeOpenGLFunctions();
+
+ // Restore original message handler
+ qInstallMessageHandler(handler);
+ }
+#endif
QObject::connect(m_drawer, &Drawer::drawerChanged, this, &Abstract3DRenderer::updateTextures);
QObject::connect(this, &Abstract3DRenderer::needRender, controller,
&Abstract3DController::needRender, Qt::QueuedConnection);
@@ -71,6 +143,12 @@ Abstract3DRenderer::~Abstract3DRenderer()
delete m_cachedTheme;
delete m_selectionLabelItem;
delete m_customItemShader;
+ delete m_volumeTextureShader;
+ delete m_volumeTextureLowDefShader;
+ delete m_volumeSliceFrameShader;
+ delete m_volumeTextureSliceShader;
+ delete m_labelShader;
+ delete m_cursorPositionShader;
foreach (SeriesRenderCache *cache, m_renderCacheList) {
cache->cleanup(m_textureHelper);
@@ -88,12 +166,29 @@ Abstract3DRenderer::~Abstract3DRenderer()
ObjectHelper::releaseObjectHelper(this, m_backgroundObj);
ObjectHelper::releaseObjectHelper(this, m_gridLineObj);
ObjectHelper::releaseObjectHelper(this, m_labelObj);
+ ObjectHelper::releaseObjectHelper(this, m_positionMapperObj);
+
+ if (m_textureHelper) {
+ m_textureHelper->deleteTexture(&m_depthTexture);
+ m_textureHelper->deleteTexture(&m_cursorPositionTexture);
+
+ if (QOpenGLContext::currentContext())
+ m_textureHelper->glDeleteFramebuffers(1, &m_cursorPositionFrameBuffer);
+
+ delete m_textureHelper;
+ }
+
+ m_axisCacheX.clearLabels();
+ m_axisCacheY.clearLabels();
+ m_axisCacheZ.clearLabels();
- delete m_textureHelper;
+ restoreContextAfterDelete();
}
void Abstract3DRenderer::initializeOpenGL()
{
+ m_context = QOpenGLContext::currentContext();
+
// Set OpenGL features
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
@@ -101,9 +196,11 @@ void Abstract3DRenderer::initializeOpenGL()
glCullFace(GL_BACK);
#if !defined(QT_OPENGL_ES_2)
- glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
- glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
- glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
+ if (!m_isOpenGLES) {
+ glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
+ glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
+ glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
+ }
#endif
m_textureHelper = new TextureHelper();
@@ -112,6 +209,15 @@ void Abstract3DRenderer::initializeOpenGL()
axisCacheForOrientation(QAbstract3DAxis::AxisOrientationX).setDrawer(m_drawer);
axisCacheForOrientation(QAbstract3DAxis::AxisOrientationY).setDrawer(m_drawer);
axisCacheForOrientation(QAbstract3DAxis::AxisOrientationZ).setDrawer(m_drawer);
+
+ initLabelShaders(QStringLiteral(":/shaders/vertexLabel"),
+ QStringLiteral(":/shaders/fragmentLabel"));
+
+ initCursorPositionShaders(QStringLiteral(":/shaders/vertexPosition"),
+ QStringLiteral(":/shaders/fragmentPositionMap"));
+
+ loadLabelMesh();
+ loadPositionMapperMesh();
}
void Abstract3DRenderer::render(const GLuint defaultFboHandle)
@@ -137,7 +243,7 @@ void Abstract3DRenderer::render(const GLuint defaultFboHandle)
glEnable(GL_SCISSOR_TEST);
QVector4D clearColor = Utils::vectorFromColor(m_cachedTheme->windowColor());
glClearColor(clearColor.x(), clearColor.y(), clearColor.z(), 1.0f);
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glDisable(GL_SCISSOR_TEST);
}
@@ -146,28 +252,87 @@ void Abstract3DRenderer::updateSelectionState(SelectionState state)
m_selectionState = state;
}
-void Abstract3DRenderer::updateInputPosition(const QPoint &position)
+void Abstract3DRenderer::initGradientShaders(const QString &vertexShader,
+ const QString &fragmentShader)
{
- m_inputPosition = position;
+ // Do nothing by default
+ Q_UNUSED(vertexShader)
+ Q_UNUSED(fragmentShader)
}
-void Abstract3DRenderer::initGradientShaders(const QString &vertexShader,
- const QString &fragmentShader)
+void Abstract3DRenderer::initStaticSelectedItemShaders(const QString &vertexShader,
+ const QString &fragmentShader,
+ const QString &gradientVertexShader,
+ const QString &gradientFragmentShader)
{
// Do nothing by default
Q_UNUSED(vertexShader)
Q_UNUSED(fragmentShader)
+ Q_UNUSED(gradientVertexShader)
+ Q_UNUSED(gradientFragmentShader)
}
void Abstract3DRenderer::initCustomItemShaders(const QString &vertexShader,
const QString &fragmentShader)
{
- if (m_customItemShader)
- delete m_customItemShader;
+ delete m_customItemShader;
m_customItemShader = new ShaderHelper(this, vertexShader, fragmentShader);
m_customItemShader->initialize();
}
+void Abstract3DRenderer::initVolumeTextureShaders(const QString &vertexShader,
+ const QString &fragmentShader,
+ const QString &fragmentLowDefShader,
+ const QString &sliceShader,
+ const QString &sliceFrameVertexShader,
+ const QString &sliceFrameShader)
+{
+
+ delete m_volumeTextureShader;
+ m_volumeTextureShader = new ShaderHelper(this, vertexShader, fragmentShader);
+ m_volumeTextureShader->initialize();
+
+ delete m_volumeTextureLowDefShader;
+ m_volumeTextureLowDefShader = new ShaderHelper(this, vertexShader, fragmentLowDefShader);
+ m_volumeTextureLowDefShader->initialize();
+
+ delete m_volumeTextureSliceShader;
+ m_volumeTextureSliceShader = new ShaderHelper(this, vertexShader, sliceShader);
+ m_volumeTextureSliceShader->initialize();
+
+ delete m_volumeSliceFrameShader;
+ m_volumeSliceFrameShader = new ShaderHelper(this, sliceFrameVertexShader, sliceFrameShader);
+ m_volumeSliceFrameShader->initialize();
+}
+
+void Abstract3DRenderer::initLabelShaders(const QString &vertexShader, const QString &fragmentShader)
+{
+ delete m_labelShader;
+ m_labelShader = new ShaderHelper(this, vertexShader, fragmentShader);
+ m_labelShader->initialize();
+}
+
+void Abstract3DRenderer::initCursorPositionShaders(const QString &vertexShader,
+ const QString &fragmentShader)
+{
+ // Init the shader
+ delete m_cursorPositionShader;
+ m_cursorPositionShader = new ShaderHelper(this, vertexShader, fragmentShader);
+ m_cursorPositionShader->initialize();
+}
+
+void Abstract3DRenderer::initCursorPositionBuffer()
+{
+ m_textureHelper->deleteTexture(&m_cursorPositionTexture);
+
+ if (m_primarySubViewport.size().isEmpty())
+ return;
+
+ m_cursorPositionTexture =
+ m_textureHelper->createCursorPositionTexture(m_primarySubViewport.size(),
+ m_cursorPositionFrameBuffer);
+}
+
void Abstract3DRenderer::updateTheme(Q3DTheme *theme)
{
// Synchronize the controller theme with renderer
@@ -193,24 +358,22 @@ void Abstract3DRenderer::updateScene(Q3DScene *scene)
handleResize();
}
- scene->activeCamera()->d_ptr->updateViewMatrix(m_autoScaleAdjustment);
- // Set light position (rotate light with activeCamera, a bit above it (as set in defaultLightPos))
- scene->d_ptr->setLightPositionRelativeToCamera(defaultLightPos);
-
QPoint logicalPixelPosition = scene->selectionQueryPosition();
- updateInputPosition(QPoint(logicalPixelPosition.x() * m_devicePixelRatio,
- logicalPixelPosition.y() * m_devicePixelRatio));
+ m_inputPosition = QPoint(logicalPixelPosition.x() * m_devicePixelRatio,
+ logicalPixelPosition.y() * m_devicePixelRatio);
+
+ QPoint logicalGraphPosition = scene->graphPositionQuery();
+ m_graphPositionQuery = QPoint(logicalGraphPosition.x() * m_devicePixelRatio,
+ logicalGraphPosition.y() * m_devicePixelRatio);
// Synchronize the renderer scene to controller scene
scene->d_ptr->sync(*m_cachedScene->d_ptr);
+ updateCameraViewport();
+
if (Q3DScene::invalidSelectionPoint() == logicalPixelPosition) {
updateSelectionState(SelectNone);
} else {
- // Selections are one-shot, reset selection active to false before processing
- scene->setSelectionQueryPosition(Q3DScene::invalidSelectionPoint());
- m_clickPending = true;
-
if (scene->isSlicingActive()) {
if (scene->isPointInPrimarySubView(logicalPixelPosition))
updateSelectionState(SelectOnOverview);
@@ -222,53 +385,109 @@ void Abstract3DRenderer::updateScene(Q3DScene *scene)
updateSelectionState(SelectOnScene);
}
}
+
+ if (Q3DScene::invalidSelectionPoint() != logicalGraphPosition)
+ m_graphPositionQueryPending = true;
+
+ // Queue up another render when we have a query that needs resolving.
+ // This is needed because QtQuick scene graph can sometimes do a sync without following it up
+ // with a render.
+ if (m_graphPositionQueryPending || m_selectionState != SelectNone)
+ emit needRender();
+}
+
+void Abstract3DRenderer::updateTextures()
+{
+ m_axisCacheX.updateTextures();
+ m_axisCacheY.updateTextures();
+ m_axisCacheZ.updateTextures();
}
void Abstract3DRenderer::reInitShaders()
{
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
- initGradientShaders(QStringLiteral(":/shaders/vertexShadow"),
- QStringLiteral(":/shaders/fragmentShadowNoTexColorOnY"));
- initShaders(QStringLiteral(":/shaders/vertexShadow"),
- QStringLiteral(":/shaders/fragmentShadowNoTex"));
- initBackgroundShaders(QStringLiteral(":/shaders/vertexShadow"),
- QStringLiteral(":/shaders/fragmentShadowNoTex"));
- initCustomItemShaders(QStringLiteral(":/shaders/vertexShadow"),
- QStringLiteral(":/shaders/fragmentShadow"));
- } else {
- initGradientShaders(QStringLiteral(":/shaders/vertex"),
- QStringLiteral(":/shaders/fragmentColorOnY"));
- initShaders(QStringLiteral(":/shaders/vertex"),
- QStringLiteral(":/shaders/fragment"));
+ if (!m_isOpenGLES) {
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ if (m_cachedOptimizationHint.testFlag(QAbstract3DGraph::OptimizationStatic)
+ && qobject_cast<Scatter3DRenderer *>(this)) {
+ initGradientShaders(QStringLiteral(":/shaders/vertexShadow"),
+ QStringLiteral(":/shaders/fragmentShadow"));
+ initStaticSelectedItemShaders(QStringLiteral(":/shaders/vertexShadow"),
+ QStringLiteral(":/shaders/fragmentShadowNoTex"),
+ QStringLiteral(":/shaders/vertexShadow"),
+ QStringLiteral(":/shaders/fragmentShadowNoTexColorOnY"));
+ initShaders(QStringLiteral(":/shaders/vertexShadowNoMatrices"),
+ QStringLiteral(":/shaders/fragmentShadowNoTex"));
+ } else {
+ initGradientShaders(QStringLiteral(":/shaders/vertexShadow"),
+ QStringLiteral(":/shaders/fragmentShadowNoTexColorOnY"));
+ initShaders(QStringLiteral(":/shaders/vertexShadow"),
+ QStringLiteral(":/shaders/fragmentShadowNoTex"));
+ }
+ initBackgroundShaders(QStringLiteral(":/shaders/vertexShadow"),
+ QStringLiteral(":/shaders/fragmentShadowNoTex"));
+ initCustomItemShaders(QStringLiteral(":/shaders/vertexShadow"),
+ QStringLiteral(":/shaders/fragmentShadow"));
+ } else {
+ if (m_cachedOptimizationHint.testFlag(QAbstract3DGraph::OptimizationStatic)
+ && qobject_cast<Scatter3DRenderer *>(this)) {
+ initGradientShaders(QStringLiteral(":/shaders/vertexTexture"),
+ QStringLiteral(":/shaders/fragmentTexture"));
+ initStaticSelectedItemShaders(QStringLiteral(":/shaders/vertex"),
+ QStringLiteral(":/shaders/fragment"),
+ QStringLiteral(":/shaders/vertex"),
+ QStringLiteral(":/shaders/fragmentColorOnY"));
+ initShaders(QStringLiteral(":/shaders/vertexNoMatrices"),
+ QStringLiteral(":/shaders/fragment"));
+ } else {
+ initGradientShaders(QStringLiteral(":/shaders/vertex"),
+ QStringLiteral(":/shaders/fragmentColorOnY"));
+ initShaders(QStringLiteral(":/shaders/vertex"),
+ QStringLiteral(":/shaders/fragment"));
+ }
+ initBackgroundShaders(QStringLiteral(":/shaders/vertex"),
+ QStringLiteral(":/shaders/fragment"));
+ initCustomItemShaders(QStringLiteral(":/shaders/vertexTexture"),
+ QStringLiteral(":/shaders/fragmentTexture"));
+ }
+ initVolumeTextureShaders(QStringLiteral(":/shaders/vertexTexture3D"),
+ QStringLiteral(":/shaders/fragmentTexture3D"),
+ QStringLiteral(":/shaders/fragmentTexture3DLowDef"),
+ QStringLiteral(":/shaders/fragmentTexture3DSlice"),
+ QStringLiteral(":/shaders/vertexPosition"),
+ QStringLiteral(":/shaders/fragment3DSliceFrames"));
+ } else {
+ if (m_cachedOptimizationHint.testFlag(QAbstract3DGraph::OptimizationStatic)
+ && qobject_cast<Scatter3DRenderer *>(this)) {
+ initGradientShaders(QStringLiteral(":/shaders/vertexTexture"),
+ QStringLiteral(":/shaders/fragmentTextureES2"));
+ initStaticSelectedItemShaders(QStringLiteral(":/shaders/vertex"),
+ QStringLiteral(":/shaders/fragmentES2"),
+ QStringLiteral(":/shaders/vertex"),
+ QStringLiteral(":/shaders/fragmentColorOnYES2"));
+ initBackgroundShaders(QStringLiteral(":/shaders/vertexNoMatrices"),
+ QStringLiteral(":/shaders/fragmentES2"));
+ } else {
+ initGradientShaders(QStringLiteral(":/shaders/vertex"),
+ QStringLiteral(":/shaders/fragmentColorOnYES2"));
+ initShaders(QStringLiteral(":/shaders/vertex"),
+ QStringLiteral(":/shaders/fragmentES2"));
+ }
initBackgroundShaders(QStringLiteral(":/shaders/vertex"),
- QStringLiteral(":/shaders/fragment"));
+ QStringLiteral(":/shaders/fragmentES2"));
initCustomItemShaders(QStringLiteral(":/shaders/vertexTexture"),
- QStringLiteral(":/shaders/fragmentTexture"));
+ QStringLiteral(":/shaders/fragmentTextureES2"));
}
-#else
- initGradientShaders(QStringLiteral(":/shaders/vertex"),
- QStringLiteral(":/shaders/fragmentColorOnYES2"));
- initShaders(QStringLiteral(":/shaders/vertex"),
- QStringLiteral(":/shaders/fragmentES2"));
- initBackgroundShaders(QStringLiteral(":/shaders/vertex"),
- QStringLiteral(":/shaders/fragmentES2"));
- initCustomItemShaders(QStringLiteral(":/shaders/vertexTexture"),
- QStringLiteral(":/shaders/fragmentTextureES2"));
-#endif
}
void Abstract3DRenderer::handleShadowQualityChange()
{
reInitShaders();
-#if defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality != QAbstract3DGraph::ShadowQualityNone) {
+ if (m_isOpenGLES && m_cachedShadowQuality != QAbstract3DGraph::ShadowQualityNone) {
emit requestShadowQuality(QAbstract3DGraph::ShadowQualityNone);
qWarning("Shadows are not yet supported for OpenGL ES2");
m_cachedShadowQuality = QAbstract3DGraph::ShadowQualityNone;
}
-#endif
}
void Abstract3DRenderer::updateSelectionMode(QAbstract3DGraph::SelectionFlags mode)
@@ -280,16 +499,39 @@ void Abstract3DRenderer::updateSelectionMode(QAbstract3DGraph::SelectionFlags mo
void Abstract3DRenderer::updateAspectRatio(float ratio)
{
m_graphAspectRatio = ratio;
- calculateZoomLevel();
- m_cachedScene->activeCamera()->d_ptr->updateViewMatrix(m_autoScaleAdjustment);
foreach (SeriesRenderCache *cache, m_renderCacheList)
cache->setDataDirty(true);
- updateCustomItemPositions();
+}
+
+void Abstract3DRenderer::updateHorizontalAspectRatio(float ratio)
+{
+ m_graphHorizontalAspectRatio = ratio;
+ foreach (SeriesRenderCache *cache, m_renderCacheList)
+ cache->setDataDirty(true);
+}
+
+void Abstract3DRenderer::updatePolar(bool enable)
+{
+ m_polarGraph = enable;
+ foreach (SeriesRenderCache *cache, m_renderCacheList)
+ cache->setDataDirty(true);
+}
+
+void Abstract3DRenderer::updateRadialLabelOffset(float offset)
+{
+ m_radialLabelOffset = offset;
+}
+
+void Abstract3DRenderer::updateMargin(float margin)
+{
+ m_requestedMargin = margin;
}
void Abstract3DRenderer::updateOptimizationHint(QAbstract3DGraph::OptimizationHints hint)
{
m_cachedOptimizationHint = hint;
+ foreach (SeriesRenderCache *cache, m_renderCacheList)
+ cache->setDataDirty(true);
}
void Abstract3DRenderer::handleResize()
@@ -303,10 +545,10 @@ void Abstract3DRenderer::handleResize()
// Re-init selection buffer
initSelectionBuffer();
-#if !defined(QT_OPENGL_ES_2)
// Re-init depth buffer
updateDepthBuffer();
-#endif
+
+ initCursorPositionBuffer();
}
void Abstract3DRenderer::calculateZoomLevel()
@@ -315,9 +557,9 @@ void Abstract3DRenderer::calculateZoomLevel()
GLfloat div;
GLfloat zoomAdjustment;
div = qMin(m_primarySubViewport.width(), m_primarySubViewport.height());
- zoomAdjustment = 2.0f * defaultRatio
+ zoomAdjustment = defaultRatio
* ((m_primarySubViewport.width() / div)
- / (m_primarySubViewport.height() / div)) / m_graphAspectRatio;
+ / (m_primarySubViewport.height() / div));
m_autoScaleAdjustment = qMin(zoomAdjustment, 1.0f); // clamp to 1.0f
}
@@ -348,8 +590,6 @@ void Abstract3DRenderer::updateAxisRange(QAbstract3DAxis::AxisOrientation orient
foreach (SeriesRenderCache *cache, m_renderCacheList)
cache->setDataDirty(true);
-
- updateCustomItemPositions();
}
void Abstract3DRenderer::updateAxisSegmentCount(QAbstract3DAxis::AxisOrientation orientation,
@@ -378,8 +618,6 @@ void Abstract3DRenderer::updateAxisReversed(QAbstract3DAxis::AxisOrientation ori
axisCacheForOrientation(orientation).setReversed(enable);
foreach (SeriesRenderCache *cache, m_renderCacheList)
cache->setDataDirty(true);
-
- updateCustomItemPositions();
}
void Abstract3DRenderer::updateAxisFormatter(QAbstract3DAxis::AxisOrientation orientation,
@@ -396,8 +634,6 @@ void Abstract3DRenderer::updateAxisFormatter(QAbstract3DAxis::AxisOrientation or
foreach (SeriesRenderCache *cache, m_renderCacheList)
cache->setDataDirty(true);
-
- updateCustomItemPositions();
}
void Abstract3DRenderer::updateAxisLabelAutoRotation(QAbstract3DAxis::AxisOrientation orientation,
@@ -607,10 +843,9 @@ void Abstract3DRenderer::drawAxisTitleY(const QVector3D &sideLabelRotation,
QQuaternion titleRotation;
if (m_axisCacheY.isTitleFixed()) {
titleRotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, yRotation)
- * QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, 90.0f);
+ * m_zRightAngleRotation;
} else {
- titleRotation = totalRotation
- * QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, 90.0f);
+ titleRotation = totalRotation * m_zRightAngleRotation;
}
dummyItem.setTranslation(titleTrans + titleOffsetVector);
@@ -628,17 +863,22 @@ void Abstract3DRenderer::drawAxisTitleX(const QVector3D &labelRotation,
float labelsMaxWidth,
const QMatrix4x4 &viewMatrix,
const QMatrix4x4 &projectionMatrix,
- ShaderHelper *shader)
+ ShaderHelper *shader,
+ bool radial)
{
float scaleFactor = m_drawer->scaledFontSize() / m_axisCacheX.titleItem().size().height();
- float titleOffset = 2.0f * (labelMargin + (labelsMaxWidth * scaleFactor));
+ float titleOffset;
+ if (radial)
+ titleOffset = -2.0f * (labelMargin + m_drawer->scaledFontSize());
+ else
+ titleOffset = 2.0f * (labelMargin + (labelsMaxWidth * scaleFactor));
float zRotation = 0.0f;
float yRotation = 0.0f;
float xRotation = -90.0f + labelRotation.z();
float offsetRotation = labelRotation.z();
float extraRotation = -90.0f;
Qt::AlignmentFlag alignment = Qt::AlignTop;
- if (m_yFlipped) {
+ if (m_yFlippedForGrid) {
alignment = Qt::AlignBottom;
zRotation = 180.0f;
if (m_zFlipped) {
@@ -678,6 +918,17 @@ void Abstract3DRenderer::drawAxisTitleX(const QVector3D &labelRotation,
}
}
+ if (radial) {
+ if (m_zFlipped) {
+ titleOffset = -titleOffset;
+ } else {
+ if (m_yFlippedForGrid)
+ alignment = Qt::AlignTop;
+ else
+ alignment = Qt::AlignBottom;
+ }
+ }
+
if (offsetRotation == 180.0f || offsetRotation == -180.0f)
offsetRotation = 0.0f;
QQuaternion offsetRotator = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, offsetRotation);
@@ -718,7 +969,7 @@ void Abstract3DRenderer::drawAxisTitleZ(const QVector3D &labelRotation,
float xRotation = -90.0f;
float extraRotation = 90.0f;
Qt::AlignmentFlag alignment = Qt::AlignTop;
- if (m_yFlipped) {
+ if (m_yFlippedForGrid) {
alignment = Qt::AlignBottom;
xRotation = -xRotation;
if (m_zFlipped) {
@@ -793,6 +1044,11 @@ void Abstract3DRenderer::loadLabelMesh()
QStringLiteral(":/defaultMeshes/plane"));
}
+void Abstract3DRenderer::loadPositionMapperMesh()
+{
+ ObjectHelper::resetObjectHelper(this, m_positionMapperObj,
+ QStringLiteral(":/defaultMeshes/barFull"));
+}
void Abstract3DRenderer::generateBaseColorTexture(const QColor &color, GLuint *texture)
{
@@ -846,11 +1102,16 @@ CustomRenderItem *Abstract3DRenderer::addCustomItem(QCustom3DItem *item)
newItem->setRenderer(this);
newItem->setItemPointer(item); // Store pointer for render item updates
newItem->setMesh(item->meshFile());
- QVector3D scaling = item->scaling();
+ newItem->setOrigPosition(item->position());
+ newItem->setOrigScaling(item->scaling());
+ newItem->setScalingAbsolute(item->isScalingAbsolute());
+ newItem->setPositionAbsolute(item->isPositionAbsolute());
QImage textureImage = item->d_ptr->textureImage();
bool facingCamera = false;
+ GLuint texture = 0;
if (item->d_ptr->m_isLabelItem) {
QCustom3DLabel *labelItem = static_cast<QCustom3DLabel *>(item);
+ newItem->setLabelItem(true);
float pointSize = labelItem->font().pointSizeF();
// Check do we have custom visuals or need to use theme
if (!labelItem->dptr()->m_customVisuals) {
@@ -864,22 +1125,52 @@ CustomRenderItem *Abstract3DRenderer::addCustomItem(QCustom3DItem *item)
}
// Calculate scaling based on text (texture size), font size and asked scaling
float scaledFontSize = (0.05f + pointSize / 500.0f) / float(textureImage.height());
+ QVector3D scaling = newItem->origScaling();
scaling.setX(scaling.x() * textureImage.width() * scaledFontSize);
scaling.setY(scaling.y() * textureImage.height() * scaledFontSize);
+ newItem->setOrigScaling(scaling);
// Check if facing camera
facingCamera = labelItem->isFacingCamera();
+ } else if (item->d_ptr->m_isVolumeItem && !m_isOpenGLES) {
+ QCustom3DVolume *volumeItem = static_cast<QCustom3DVolume *>(item);
+ newItem->setTextureWidth(volumeItem->textureWidth());
+ newItem->setTextureHeight(volumeItem->textureHeight());
+ newItem->setTextureDepth(volumeItem->textureDepth());
+ if (volumeItem->textureFormat() == QImage::Format_Indexed8)
+ newItem->setColorTable(volumeItem->colorTable());
+ newItem->setTextureFormat(volumeItem->textureFormat());
+ newItem->setVolume(true);
+ newItem->setBlendNeeded(true);
+ texture = m_textureHelper->create3DTexture(volumeItem->textureData(),
+ volumeItem->textureWidth(),
+ volumeItem->textureHeight(),
+ volumeItem->textureDepth(),
+ volumeItem->textureFormat());
+ newItem->setSliceIndexX(volumeItem->sliceIndexX());
+ newItem->setSliceIndexY(volumeItem->sliceIndexY());
+ newItem->setSliceIndexZ(volumeItem->sliceIndexZ());
+ newItem->setAlphaMultiplier(volumeItem->alphaMultiplier());
+ newItem->setPreserveOpacity(volumeItem->preserveOpacity());
+ newItem->setUseHighDefShader(volumeItem->useHighDefShader());
+
+ newItem->setDrawSlices(volumeItem->drawSlices());
+ newItem->setDrawSliceFrames(volumeItem->drawSliceFrames());
+ newItem->setSliceFrameColor(volumeItem->sliceFrameColor());
+ newItem->setSliceFrameWidths(volumeItem->sliceFrameWidths());
+ newItem->setSliceFrameGaps(volumeItem->sliceFrameGaps());
+ newItem->setSliceFrameThicknesses(volumeItem->sliceFrameThicknesses());
}
- newItem->setScaling(scaling);
+ recalculateCustomItemScalingAndPos(newItem);
newItem->setRotation(item->rotation());
- newItem->setPosition(item->position());
- newItem->setPositionAbsolute(item->isPositionAbsolute());
- newItem->setBlendNeeded(textureImage.hasAlphaChannel());
- GLuint texture = m_textureHelper->create2DTexture(textureImage, true, true, true);
+
+ // In OpenGL ES we simply draw volumes as regular custom item placeholders.
+ if (!item->d_ptr->m_isVolumeItem || m_isOpenGLES)
+ {
+ newItem->setBlendNeeded(textureImage.hasAlphaChannel());
+ texture = m_textureHelper->create2DTexture(textureImage, true, true, true);
+ }
newItem->setTexture(texture);
item->d_ptr->clearTextureImage();
- QVector3D translation = convertPositionToTranslation(item->position(),
- item->isPositionAbsolute());
- newItem->setTranslation(translation);
newItem->setVisible(item->isVisible());
newItem->setShadowCasting(item->isShadowCasting());
newItem->setFacingCamera(facingCamera);
@@ -887,6 +1178,74 @@ CustomRenderItem *Abstract3DRenderer::addCustomItem(QCustom3DItem *item)
return newItem;
}
+void Abstract3DRenderer::recalculateCustomItemScalingAndPos(CustomRenderItem *item)
+{
+ if (!m_polarGraph && !item->isLabel() && !item->isScalingAbsolute()
+ && !item->isPositionAbsolute()) {
+ QVector3D scale = item->origScaling() / 2.0f;
+ QVector3D pos = item->origPosition();
+ QVector3D minBounds(pos.x() - scale.x(),
+ pos.y() - scale.y(),
+ pos.z() + scale.z());
+ QVector3D maxBounds(pos.x() + scale.x(),
+ pos.y() + scale.y(),
+ pos.z() - scale.z());
+ QVector3D minCorner = convertPositionToTranslation(minBounds, false);
+ QVector3D maxCorner = convertPositionToTranslation(maxBounds, false);
+ scale = QVector3D(qAbs(maxCorner.x() - minCorner.x()),
+ qAbs(maxCorner.y() - minCorner.y()),
+ qAbs(maxCorner.z() - minCorner.z())) / 2.0f;
+ if (item->isVolume()) {
+ // Only volume items need to scale and reposition according to bounds
+ QVector3D minBoundsNormal = minCorner;
+ QVector3D maxBoundsNormal = maxCorner;
+ // getVisibleItemBounds returns bounds normalized for fragment shader [-1,1]
+ // Y and Z are also flipped.
+ getVisibleItemBounds(minBoundsNormal, maxBoundsNormal);
+ item->setMinBounds(minBoundsNormal);
+ item->setMaxBounds(maxBoundsNormal);
+ // For scaling calculations, we want [0,1] normalized values
+ minBoundsNormal = item->minBoundsNormal();
+ maxBoundsNormal = item->maxBoundsNormal();
+
+ // Rescale and reposition the item so that it doesn't go over the edges
+ QVector3D adjScaling =
+ QVector3D(scale.x() * (maxBoundsNormal.x() - minBoundsNormal.x()),
+ scale.y() * (maxBoundsNormal.y() - minBoundsNormal.y()),
+ scale.z() * (maxBoundsNormal.z() - minBoundsNormal.z()));
+
+ item->setScaling(adjScaling);
+
+ QVector3D adjPos = item->origPosition();
+ QVector3D dataExtents = QVector3D(maxBounds.x() - minBounds.x(),
+ maxBounds.y() - minBounds.y(),
+ maxBounds.z() - minBounds.z()) / 2.0f;
+ adjPos.setX(adjPos.x() + (dataExtents.x() * minBoundsNormal.x())
+ - (dataExtents.x() * (1.0f - maxBoundsNormal.x())));
+ adjPos.setY(adjPos.y() + (dataExtents.y() * minBoundsNormal.y())
+ - (dataExtents.y() * (1.0f - maxBoundsNormal.y())));
+ adjPos.setZ(adjPos.z() + (dataExtents.z() * minBoundsNormal.z())
+ - (dataExtents.z() * (1.0f - maxBoundsNormal.z())));
+ item->setPosition(adjPos);
+ } else {
+ // Only scale for non-volume items, and do not readjust position
+ item->setScaling(scale);
+ item->setPosition(item->origPosition());
+ }
+ } else {
+ item->setScaling(item->origScaling());
+ item->setPosition(item->origPosition());
+ if (item->isVolume()) {
+ // Y and Z need to be flipped as shader flips those axes
+ item->setMinBounds(QVector3D(-1.0f, 1.0f, 1.0f));
+ item->setMaxBounds(QVector3D(1.0f, -1.0f, -1.0f));
+ }
+ }
+ QVector3D translation = convertPositionToTranslation(item->position(),
+ item->isPositionAbsolute());
+ item->setTranslation(translation);
+}
+
void Abstract3DRenderer::updateCustomItem(CustomRenderItem *renderItem)
{
QCustom3DItem *item = renderItem->itemPointer();
@@ -894,8 +1253,17 @@ void Abstract3DRenderer::updateCustomItem(CustomRenderItem *renderItem)
renderItem->setMesh(item->meshFile());
item->d_ptr->m_dirtyBits.meshDirty = false;
}
+ if (item->d_ptr->m_dirtyBits.positionDirty) {
+ renderItem->setOrigPosition(item->position());
+ renderItem->setPositionAbsolute(item->isPositionAbsolute());
+ if (!item->d_ptr->m_dirtyBits.scalingDirty)
+ recalculateCustomItemScalingAndPos(renderItem);
+ item->d_ptr->m_dirtyBits.positionDirty = false;
+ }
if (item->d_ptr->m_dirtyBits.scalingDirty) {
QVector3D scaling = item->scaling();
+ renderItem->setOrigScaling(scaling);
+ renderItem->setScalingAbsolute(item->isScalingAbsolute());
// In case we have label item, we need to recreate texture for scaling adjustment
if (item->d_ptr->m_isLabelItem) {
QCustom3DLabel *labelItem = static_cast<QCustom3DLabel *>(item);
@@ -918,8 +1286,9 @@ void Abstract3DRenderer::updateCustomItem(CustomRenderItem *renderItem)
scaling.setX(scaling.x() * textureImage.width() * scaledFontSize);
scaling.setY(scaling.y() * textureImage.height() * scaledFontSize);
item->d_ptr->clearTextureImage();
+ renderItem->setOrigScaling(scaling);
}
- renderItem->setScaling(scaling);
+ recalculateCustomItemScalingAndPos(renderItem);
item->d_ptr->m_dirtyBits.scalingDirty = false;
}
if (item->d_ptr->m_dirtyBits.rotationDirty) {
@@ -939,24 +1308,16 @@ void Abstract3DRenderer::updateCustomItem(CustomRenderItem *renderItem)
m_cachedTheme->isLabelBorderEnabled());
textureImage = item->d_ptr->textureImage();
}
+ } else if (!item->d_ptr->m_isVolumeItem || m_isOpenGLES) {
+ renderItem->setBlendNeeded(textureImage.hasAlphaChannel());
+ GLuint oldTexture = renderItem->texture();
+ m_textureHelper->deleteTexture(&oldTexture);
+ GLuint texture = m_textureHelper->create2DTexture(textureImage, true, true, true);
+ renderItem->setTexture(texture);
}
- renderItem->setBlendNeeded(textureImage.hasAlphaChannel());
- GLuint oldTexture = renderItem->texture();
- m_textureHelper->deleteTexture(&oldTexture);
- GLuint texture = m_textureHelper->create2DTexture(textureImage, true, true, true);
- renderItem->setTexture(texture);
item->d_ptr->clearTextureImage();
item->d_ptr->m_dirtyBits.textureDirty = false;
}
- if (item->d_ptr->m_dirtyBits.positionDirty || item->d_ptr->m_dirtyBits.positionAbsoluteDirty) {
- renderItem->setPosition(item->position());
- renderItem->setPositionAbsolute(item->isPositionAbsolute());
- QVector3D translation = convertPositionToTranslation(item->position(),
- item->isPositionAbsolute());
- renderItem->setTranslation(translation);
- item->d_ptr->m_dirtyBits.positionDirty = false;
- item->d_ptr->m_dirtyBits.positionAbsoluteDirty = false;
- }
if (item->d_ptr->m_dirtyBits.visibleDirty) {
renderItem->setVisible(item->isVisible());
item->d_ptr->m_dirtyBits.visibleDirty = false;
@@ -971,130 +1332,649 @@ void Abstract3DRenderer::updateCustomItem(CustomRenderItem *renderItem)
renderItem->setFacingCamera(labelItem->isFacingCamera());
labelItem->dptr()->m_facingCameraDirty = false;
}
+ } else if (item->d_ptr->m_isVolumeItem && !m_isOpenGLES) {
+ QCustom3DVolume *volumeItem = static_cast<QCustom3DVolume *>(item);
+ if (volumeItem->dptr()->m_dirtyBitsVolume.colorTableDirty) {
+ renderItem->setColorTable(volumeItem->colorTable());
+ volumeItem->dptr()->m_dirtyBitsVolume.colorTableDirty = false;
+ }
+ if (volumeItem->dptr()->m_dirtyBitsVolume.textureDimensionsDirty
+ || volumeItem->dptr()->m_dirtyBitsVolume.textureDataDirty
+ || volumeItem->dptr()->m_dirtyBitsVolume.textureFormatDirty) {
+ GLuint oldTexture = renderItem->texture();
+ m_textureHelper->deleteTexture(&oldTexture);
+ GLuint texture = m_textureHelper->create3DTexture(volumeItem->textureData(),
+ volumeItem->textureWidth(),
+ volumeItem->textureHeight(),
+ volumeItem->textureDepth(),
+ volumeItem->textureFormat());
+ renderItem->setTexture(texture);
+ renderItem->setTextureWidth(volumeItem->textureWidth());
+ renderItem->setTextureHeight(volumeItem->textureHeight());
+ renderItem->setTextureDepth(volumeItem->textureDepth());
+ renderItem->setTextureFormat(volumeItem->textureFormat());
+ volumeItem->dptr()->m_dirtyBitsVolume.textureDimensionsDirty = false;
+ volumeItem->dptr()->m_dirtyBitsVolume.textureDataDirty = false;
+ volumeItem->dptr()->m_dirtyBitsVolume.textureFormatDirty = false;
+ }
+ if (volumeItem->dptr()->m_dirtyBitsVolume.slicesDirty) {
+ renderItem->setDrawSlices(volumeItem->drawSlices());
+ renderItem->setDrawSliceFrames(volumeItem->drawSliceFrames());
+ renderItem->setSliceFrameColor(volumeItem->sliceFrameColor());
+ renderItem->setSliceFrameWidths(volumeItem->sliceFrameWidths());
+ renderItem->setSliceFrameGaps(volumeItem->sliceFrameGaps());
+ renderItem->setSliceFrameThicknesses(volumeItem->sliceFrameThicknesses());
+ renderItem->setSliceIndexX(volumeItem->sliceIndexX());
+ renderItem->setSliceIndexY(volumeItem->sliceIndexY());
+ renderItem->setSliceIndexZ(volumeItem->sliceIndexZ());
+ volumeItem->dptr()->m_dirtyBitsVolume.slicesDirty = false;
+ }
+ if (volumeItem->dptr()->m_dirtyBitsVolume.alphaDirty) {
+ renderItem->setAlphaMultiplier(volumeItem->alphaMultiplier());
+ renderItem->setPreserveOpacity(volumeItem->preserveOpacity());
+ volumeItem->dptr()->m_dirtyBitsVolume.alphaDirty = false;
+ }
+ if (volumeItem->dptr()->m_dirtyBitsVolume.shaderDirty) {
+ renderItem->setUseHighDefShader(volumeItem->useHighDefShader());
+ volumeItem->dptr()->m_dirtyBitsVolume.shaderDirty = false;
+ }
}
}
void Abstract3DRenderer::updateCustomItemPositions()
{
- foreach (CustomRenderItem *renderItem, m_customRenderCache) {
- QVector3D translation = convertPositionToTranslation(renderItem->position(),
- renderItem->isPositionAbsolute());
- renderItem->setTranslation(translation);
- }
+ foreach (CustomRenderItem *renderItem, m_customRenderCache)
+ recalculateCustomItemScalingAndPos(renderItem);
}
void Abstract3DRenderer::drawCustomItems(RenderingState state,
- ShaderHelper *shader,
+ ShaderHelper *regularShader,
const QMatrix4x4 &viewMatrix,
const QMatrix4x4 &projectionViewMatrix,
const QMatrix4x4 &depthProjectionViewMatrix,
GLuint depthTexture,
- GLfloat shadowQuality)
+ GLfloat shadowQuality,
+ GLfloat reflection)
{
if (m_customRenderCache.isEmpty())
return;
+ ShaderHelper *shader = regularShader;
+ shader->bind();
+
if (RenderingNormal == state) {
- shader->bind();
shader->setUniformValue(shader->lightP(), m_cachedScene->activeLight()->position());
shader->setUniformValue(shader->ambientS(), m_cachedTheme->ambientLightStrength());
shader->setUniformValue(shader->lightColor(),
Utils::vectorFromColor(m_cachedTheme->lightColor()));
shader->setUniformValue(shader->view(), viewMatrix);
-
- glEnable(GL_TEXTURE_2D);
}
- // Draw custom items
- foreach (CustomRenderItem *item, m_customRenderCache) {
- // Check that the render item is visible, and skip drawing if not
- if (!item->isVisible())
- continue;
-
- // Check if the render item is in data coordinates and not within axis ranges, and skip drawing if it is
- if (!item->isPositionAbsolute()
- && (item->position().x() < m_axisCacheX.min()
- || item->position().x() > m_axisCacheX.max()
- || item->position().z() < m_axisCacheZ.min()
- || item->position().z() > m_axisCacheZ.max()
- || item->position().y() < m_axisCacheY.min()
- || item->position().y() > m_axisCacheY.max())) {
- continue;
- }
+ // Draw custom items - first regular and then volumes
+ bool volumeDetected = false;
+ int loopCount = 0;
+ while (loopCount < 2) {
+ foreach (CustomRenderItem *item, m_customRenderCache) {
+ // Check that the render item is visible, and skip drawing if not
+ // Also check if reflected item is on the "wrong" side, and skip drawing if it is
+ if (!item->isVisible() || ((m_reflectionEnabled && reflection < 0.0f)
+ && (m_yFlipped == (item->translation().y() >= 0.0)))) {
+ continue;
+ }
+ if (loopCount == 0) {
+ if (item->isVolume()) {
+ volumeDetected = true;
+ continue;
+ }
+ } else {
+ if (!item->isVolume())
+ continue;
+ }
- QMatrix4x4 modelMatrix;
- QMatrix4x4 itModelMatrix;
- QMatrix4x4 MVPMatrix;
-
- QQuaternion rotation = item->rotation();
- // Check if the (label) item should be facing camera, and adjust rotation accordingly
- if (item->isFacingCamera()) {
- float camRotationX = m_cachedScene->activeCamera()->xRotation();
- float camRotationY = m_cachedScene->activeCamera()->yRotation();
- rotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, -camRotationX)
- * QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, -camRotationY);
+ // If the render item is in data coordinates and not within axis ranges, skip it
+ if (!item->isPositionAbsolute()
+ && (item->position().x() < m_axisCacheX.min()
+ || item->position().x() > m_axisCacheX.max()
+ || item->position().z() < m_axisCacheZ.min()
+ || item->position().z() > m_axisCacheZ.max()
+ || item->position().y() < m_axisCacheY.min()
+ || item->position().y() > m_axisCacheY.max())) {
+ continue;
+ }
+
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 itModelMatrix;
+ QMatrix4x4 MVPMatrix;
+
+ QQuaternion rotation = item->rotation();
+ // Check if the (label) item should be facing camera, and adjust rotation accordingly
+ if (item->isFacingCamera()) {
+ float camRotationX = m_cachedScene->activeCamera()->xRotation();
+ float camRotationY = m_cachedScene->activeCamera()->yRotation();
+ rotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, -camRotationX)
+ * QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, -camRotationY);
+ }
+
+ if (m_reflectionEnabled) {
+ if (reflection < 0.0f) {
+ if (item->itemPointer()->d_ptr->m_isLabelItem)
+ continue;
+ else
+ glCullFace(GL_FRONT);
+ } else {
+ glCullFace(GL_BACK);
+ }
+ QVector3D trans = item->translation();
+ trans.setY(reflection * trans.y());
+ modelMatrix.translate(trans);
+ if (reflection < 0.0f) {
+ QQuaternion mirror = QQuaternion(rotation.scalar(),
+ -rotation.x(), rotation.y(), -rotation.z());
+ modelMatrix.rotate(mirror);
+ itModelMatrix.rotate(mirror);
+ } else {
+ modelMatrix.rotate(rotation);
+ itModelMatrix.rotate(rotation);
+ }
+ QVector3D scale = item->scaling();
+ scale.setY(reflection * scale.y());
+ modelMatrix.scale(scale);
+ } else {
+ modelMatrix.translate(item->translation());
+ modelMatrix.rotate(rotation);
+ modelMatrix.scale(item->scaling());
+ itModelMatrix.rotate(rotation);
+ }
+ if (!item->isFacingCamera())
+ itModelMatrix.scale(item->scaling());
+ MVPMatrix = projectionViewMatrix * modelMatrix;
+
+ if (RenderingNormal == state) {
+ // Normal render
+ ShaderHelper *prevShader = shader;
+ if (item->isVolume() && !m_isOpenGLES) {
+ if (item->drawSlices() &&
+ (item->sliceIndexX() >= 0
+ || item->sliceIndexY() >= 0
+ || item->sliceIndexZ() >= 0)) {
+ shader = m_volumeTextureSliceShader;
+ } else if (item->useHighDefShader()) {
+ shader = m_volumeTextureShader;
+ } else {
+ shader = m_volumeTextureLowDefShader;
+ }
+ } else if (item->isLabel()) {
+ shader = m_labelShader;
+ } else {
+ shader = regularShader;
+ }
+ if (shader != prevShader)
+ shader->bind();
+ shader->setUniformValue(shader->model(), modelMatrix);
+ shader->setUniformValue(shader->MVP(), MVPMatrix);
+ shader->setUniformValue(shader->nModel(), itModelMatrix.inverted().transposed());
+
+ if (item->isBlendNeeded()) {
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ if (!item->isVolume() && !m_isOpenGLES)
+ glDisable(GL_CULL_FACE);
+ } else {
+ glDisable(GL_BLEND);
+ glEnable(GL_CULL_FACE);
+ }
+
+ if (!m_isOpenGLES && m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone
+ && !item->isVolume()) {
+ // Set shadow shader bindings
+ shader->setUniformValue(shader->shadowQ(), shadowQuality);
+ shader->setUniformValue(shader->depth(), depthProjectionViewMatrix * modelMatrix);
+ shader->setUniformValue(shader->lightS(), m_cachedTheme->lightStrength() / 10.0f);
+ m_drawer->drawObject(shader, item->mesh(), item->texture(), depthTexture);
+ } else {
+ // Set shadowless shader bindings
+ if (item->isVolume() && !m_isOpenGLES) {
+ QVector3D cameraPos = m_cachedScene->activeCamera()->position();
+ cameraPos = MVPMatrix.inverted().map(cameraPos);
+ // Adjust camera position according to min/max bounds
+ cameraPos = -(cameraPos
+ + ((oneVector - cameraPos) * item->minBoundsNormal())
+ - ((oneVector + cameraPos) * (oneVector - item->maxBoundsNormal())));
+ shader->setUniformValue(shader->cameraPositionRelativeToModel(), cameraPos);
+ GLint color8Bit = (item->textureFormat() == QImage::Format_Indexed8) ? 1 : 0;
+ if (color8Bit) {
+ shader->setUniformValueArray(shader->colorIndex(),
+ item->colorTable().constData(), 256);
+ }
+ shader->setUniformValue(shader->color8Bit(), color8Bit);
+ shader->setUniformValue(shader->alphaMultiplier(), item->alphaMultiplier());
+ shader->setUniformValue(shader->preserveOpacity(),
+ item->preserveOpacity() ? 1 : 0);
+
+ shader->setUniformValue(shader->minBounds(), item->minBounds());
+ shader->setUniformValue(shader->maxBounds(), item->maxBounds());
+
+ if (shader == m_volumeTextureSliceShader) {
+ shader->setUniformValue(shader->volumeSliceIndices(),
+ item->sliceFractions());
+ } else {
+ // Precalculate texture dimensions so we can optimize
+ // ray stepping to hit every texture layer.
+ QVector3D textureDimensions(1.0f / float(item->textureWidth()),
+ 1.0f / float(item->textureHeight()),
+ 1.0f / float(item->textureDepth()));
+
+ // Worst case scenario sample count
+ int sampleCount;
+ if (shader == m_volumeTextureLowDefShader) {
+ sampleCount = qMax(item->textureWidth(),
+ qMax(item->textureDepth(), item->textureHeight()));
+ // Further improve speed with big textures by simply dropping every
+ // other sample:
+ if (sampleCount > 256)
+ sampleCount /= 2;
+ } else {
+ sampleCount = item->textureWidth() + item->textureHeight()
+ + item->textureDepth();
+ }
+ shader->setUniformValue(shader->textureDimensions(), textureDimensions);
+ shader->setUniformValue(shader->sampleCount(), sampleCount);
+ }
+ if (item->drawSliceFrames()) {
+ // Set up the slice frame shader
+ glDisable(GL_CULL_FACE);
+ m_volumeSliceFrameShader->bind();
+ m_volumeSliceFrameShader->setUniformValue(
+ m_volumeSliceFrameShader->color(), item->sliceFrameColor());
+
+ // Draw individual slice frames.
+ if (item->sliceIndexX() >= 0)
+ drawVolumeSliceFrame(item, Qt::XAxis, projectionViewMatrix);
+ if (item->sliceIndexY() >= 0)
+ drawVolumeSliceFrame(item, Qt::YAxis, projectionViewMatrix);
+ if (item->sliceIndexZ() >= 0)
+ drawVolumeSliceFrame(item, Qt::ZAxis, projectionViewMatrix);
+
+ glEnable(GL_CULL_FACE);
+ shader->bind();
+ }
+ m_drawer->drawObject(shader, item->mesh(), 0, 0, item->texture());
+ } else {
+ shader->setUniformValue(shader->lightS(), m_cachedTheme->lightStrength());
+ m_drawer->drawObject(shader, item->mesh(), item->texture());
+ }
+ }
+ } else if (RenderingSelection == state) {
+ // Selection render
+ shader->setUniformValue(shader->MVP(), MVPMatrix);
+ QVector4D itemColor = indexToSelectionColor(item->index());
+ itemColor.setW(customItemAlpha);
+ itemColor /= 255.0f;
+ shader->setUniformValue(shader->color(), itemColor);
+ m_drawer->drawObject(shader, item->mesh());
+ } else if (item->isShadowCasting()) {
+ // Depth render
+ shader->setUniformValue(shader->MVP(), depthProjectionViewMatrix * modelMatrix);
+ m_drawer->drawObject(shader, item->mesh());
+ }
}
+ loopCount++;
+ if (!volumeDetected)
+ loopCount++; // Skip second run if no volumes detected
+ }
+
+ if (RenderingNormal == state) {
+ glDisable(GL_BLEND);
+ glEnable(GL_CULL_FACE);
+ }
+}
+
+void Abstract3DRenderer::drawVolumeSliceFrame(const CustomRenderItem *item, Qt::Axis axis,
+ const QMatrix4x4 &projectionViewMatrix)
+{
+ QVector2D frameWidth;
+ QVector3D frameScaling;
+ QVector3D translation = item->translation();
+ QQuaternion rotation = item->rotation();
+ float fracTrans;
+ bool needRotate = !rotation.isIdentity();
+ QMatrix4x4 rotationMatrix;
+ if (needRotate)
+ rotationMatrix.rotate(rotation);
+
+ if (axis == Qt::XAxis) {
+ fracTrans = item->sliceFractions().x();
+ float range = item->maxBoundsNormal().x() - item->minBoundsNormal().x();
+ float minMult = item->minBoundsNormal().x() / range;
+ float maxMult = (1.0f - item->maxBoundsNormal().x()) / range;
+ fracTrans = fracTrans - ((1.0f - fracTrans) * minMult) + ((1.0f + fracTrans) * maxMult);
+ if (needRotate)
+ translation += rotationMatrix.map(QVector3D(fracTrans * item->scaling().x(), 0.0f, 0.0f));
+ else
+ translation.setX(translation.x() + fracTrans * item->scaling().x());
+ frameScaling = QVector3D(item->scaling().z()
+ + (item->scaling().z() * item->sliceFrameGaps().z())
+ + (item->scaling().z() * item->sliceFrameWidths().z()),
+ item->scaling().y()
+ + (item->scaling().y() * item->sliceFrameGaps().y())
+ + (item->scaling().y() * item->sliceFrameWidths().y()),
+ item->scaling().x() * item->sliceFrameThicknesses().x());
+ frameWidth = QVector2D(item->scaling().z() * item->sliceFrameWidths().z(),
+ item->scaling().y() * item->sliceFrameWidths().y());
+ rotation *= m_yRightAngleRotation;
+ } else if (axis == Qt::YAxis) {
+ fracTrans = item->sliceFractions().y();
+ float range = item->maxBoundsNormal().y() - item->minBoundsNormal().y();
+ // Y axis is logically flipped, so we need to swam min and max bounds
+ float minMult = (1.0f - item->maxBoundsNormal().y()) / range;
+ float maxMult = item->minBoundsNormal().y() / range;
+ fracTrans = fracTrans - ((1.0f - fracTrans) * minMult) + ((1.0f + fracTrans) * maxMult);
+ if (needRotate)
+ translation -= rotationMatrix.map(QVector3D(0.0f, fracTrans * item->scaling().y(), 0.0f));
+ else
+ translation.setY(translation.y() - fracTrans * item->scaling().y());
+ frameScaling = QVector3D(item->scaling().x()
+ + (item->scaling().x() * item->sliceFrameGaps().x())
+ + (item->scaling().x() * item->sliceFrameWidths().x()),
+ item->scaling().z()
+ + (item->scaling().z() * item->sliceFrameGaps().z())
+ + (item->scaling().z() * item->sliceFrameWidths().z()),
+ item->scaling().y() * item->sliceFrameThicknesses().y());
+ frameWidth = QVector2D(item->scaling().x() * item->sliceFrameWidths().x(),
+ item->scaling().z() * item->sliceFrameWidths().z());
+ rotation *= m_xRightAngleRotation;
+ } else { // Z axis
+ fracTrans = item->sliceFractions().z();
+ float range = item->maxBoundsNormal().z() - item->minBoundsNormal().z();
+ // Z axis is logically flipped, so we need to swam min and max bounds
+ float minMult = (1.0f - item->maxBoundsNormal().z()) / range;
+ float maxMult = item->minBoundsNormal().z() / range;
+ fracTrans = fracTrans - ((1.0f - fracTrans) * minMult) + ((1.0f + fracTrans) * maxMult);
+ if (needRotate)
+ translation -= rotationMatrix.map(QVector3D(0.0f, 0.0f, fracTrans * item->scaling().z()));
+ else
+ translation.setZ(translation.z() - fracTrans * item->scaling().z());
+ frameScaling = QVector3D(item->scaling().x()
+ + (item->scaling().x() * item->sliceFrameGaps().x())
+ + (item->scaling().x() * item->sliceFrameWidths().x()),
+ item->scaling().y()
+ + (item->scaling().y() * item->sliceFrameGaps().y())
+ + (item->scaling().y() * item->sliceFrameWidths().y()),
+ item->scaling().z() * item->sliceFrameThicknesses().z());
+ frameWidth = QVector2D(item->scaling().x() * item->sliceFrameWidths().x(),
+ item->scaling().y() * item->sliceFrameWidths().y());
+ }
+
+ // If the slice is outside the shown area, don't show the frame
+ if (fracTrans < -1.0 || fracTrans > 1.0)
+ return;
+
+ // Shader needs the width of clear space in the middle.
+ frameWidth.setX(1.0f - (frameWidth.x() / frameScaling.x()));
+ frameWidth.setY(1.0f - (frameWidth.y() / frameScaling.y()));
- modelMatrix.translate(item->translation());
- modelMatrix.rotate(rotation);
- modelMatrix.scale(item->scaling());
- itModelMatrix.rotate(rotation);
- if (!item->isFacingCamera())
- itModelMatrix.scale(item->scaling());
- MVPMatrix = projectionViewMatrix * modelMatrix;
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 mvpMatrix;
+
+ modelMatrix.translate(translation);
+ modelMatrix.rotate(rotation);
+ modelMatrix.scale(frameScaling);
+ mvpMatrix = projectionViewMatrix * modelMatrix;
+ m_volumeSliceFrameShader->setUniformValue(m_volumeSliceFrameShader->MVP(), mvpMatrix);
+ m_volumeSliceFrameShader->setUniformValue(m_volumeSliceFrameShader->sliceFrameWidth(),
+ frameWidth);
+
+ m_drawer->drawObject(m_volumeSliceFrameShader, item->mesh());
+
+}
+
+void Abstract3DRenderer::queriedGraphPosition(const QMatrix4x4 &projectionViewMatrix,
+ const QVector3D &scaling,
+ GLuint defaultFboHandle)
+{
+ m_cursorPositionShader->bind();
+
+ // Set up mapper framebuffer
+ glBindFramebuffer(GL_FRAMEBUFFER, m_cursorPositionFrameBuffer);
+ glViewport(0, 0,
+ m_primarySubViewport.width(),
+ m_primarySubViewport.height());
+ glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+ glDisable(GL_DITHER); // Dither may affect colors if enabled
+ glEnable(GL_CULL_FACE);
+ glCullFace(GL_FRONT);
+
+ // Draw a cube scaled to the graph dimensions
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 MVPMatrix;
+
+ modelMatrix.scale(scaling);
+
+ MVPMatrix = projectionViewMatrix * modelMatrix;
+ m_cursorPositionShader->setUniformValue(m_cursorPositionShader->MVP(), MVPMatrix);
+ m_drawer->drawObject(m_cursorPositionShader, m_positionMapperObj);
+
+ QVector4D dataColor = Utils::getSelection(m_graphPositionQuery,
+ m_primarySubViewport.height());
+ if (dataColor.w() > 0.0f) {
+ // If position is outside the graph, set the position well outside the graph boundaries
+ dataColor = QVector4D(-10000.0f, -10000.0f, -10000.0f, 0.0f);
+ } else {
+ // Normalize to range [0.0, 1.0]
+ dataColor /= 255.0f;
+ }
+
+ // Restore state
+ glEnable(GL_DITHER);
+ glCullFace(GL_BACK);
+ glBindFramebuffer(GL_FRAMEBUFFER, defaultFboHandle);
+ glViewport(m_primarySubViewport.x(),
+ m_primarySubViewport.y(),
+ m_primarySubViewport.width(),
+ m_primarySubViewport.height());
+
+ QVector3D normalizedValues = dataColor.toVector3D() * 2.0f;
+ normalizedValues -= oneVector;
+ m_queriedGraphPosition = QVector3D(normalizedValues.x(),
+ normalizedValues.y(),
+ normalizedValues.z());
+ m_graphPositionQueryResolved = true;
+ m_graphPositionQueryPending = false;
+}
+
+void Abstract3DRenderer::fixContextBeforeDelete()
+{
+ // Only need to fix context if the current context is null.
+ // Otherwise we expect it to be our shared context, so we can use it for cleanup.
+ if (!QOpenGLContext::currentContext() && !m_context.isNull()
+ && QThread::currentThread() == this->thread()) {
+ m_dummySurfaceAtDelete = new QWindow();
+ m_dummySurfaceAtDelete->setSurfaceType(QWindow::OpenGLSurface);
+ m_dummySurfaceAtDelete->setFormat(m_context->format());
+ m_dummySurfaceAtDelete->create();
+
+ m_context->makeCurrent(m_dummySurfaceAtDelete);
+ }
+}
+
+void Abstract3DRenderer::restoreContextAfterDelete()
+{
+ if (m_dummySurfaceAtDelete)
+ m_context->doneCurrent();
+
+ delete m_dummySurfaceAtDelete;
+ m_dummySurfaceAtDelete = 0;
+}
+
+void Abstract3DRenderer::calculatePolarXZ(const QVector3D &dataPos, float &x, float &z) const
+{
+ // x is angular, z is radial
+ qreal angle = m_axisCacheX.formatter()->positionAt(dataPos.x()) * doublePi;
+ qreal radius = m_axisCacheZ.formatter()->positionAt(dataPos.z());
+
+ // Convert angle & radius to X and Z coords
+ x = float(radius * qSin(angle)) * m_polarRadius;
+ z = -float(radius * qCos(angle)) * m_polarRadius;
+}
+
+void Abstract3DRenderer::drawRadialGrid(ShaderHelper *shader, float yFloorLinePos,
+ const QMatrix4x4 &projectionViewMatrix,
+ const QMatrix4x4 &depthMatrix)
+{
+ static QVector<QQuaternion> lineRotations;
+ if (!lineRotations.size()) {
+ lineRotations.resize(polarGridRoundness);
+ for (int j = 0; j < polarGridRoundness; j++) {
+ lineRotations[j] = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f,
+ polarGridAngleDegrees * float(j));
+ }
+ }
+ int gridLineCount = m_axisCacheZ.gridLineCount();
+ const QVector<float> &gridPositions = m_axisCacheZ.formatter()->gridPositions();
+ const QVector<float> &subGridPositions = m_axisCacheZ.formatter()->subGridPositions();
+ int mainSize = gridPositions.size();
+ QVector3D translateVector(0.0f, yFloorLinePos, 0.0f);
+ QQuaternion finalRotation = m_xRightAngleRotationNeg;
+ if (m_yFlippedForGrid)
+ finalRotation *= m_xFlipRotation;
+
+ for (int i = 0; i < gridLineCount; i++) {
+ float gridPosition = (i >= mainSize)
+ ? subGridPositions.at(i - mainSize) : gridPositions.at(i);
+ float radiusFraction = m_polarRadius * gridPosition;
+ QVector3D gridLineScaler(radiusFraction * float(qSin(polarGridHalfAngle)),
+ gridLineWidth, gridLineWidth);
+ translateVector.setZ(gridPosition * m_polarRadius);
+ for (int j = 0; j < polarGridRoundness; j++) {
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 itModelMatrix;
+ modelMatrix.rotate(lineRotations.at(j));
+ itModelMatrix.rotate(lineRotations.at(j));
+ modelMatrix.translate(translateVector);
+ modelMatrix.scale(gridLineScaler);
+ itModelMatrix.scale(gridLineScaler);
+ modelMatrix.rotate(finalRotation);
+ itModelMatrix.rotate(finalRotation);
+ QMatrix4x4 MVPMatrix = projectionViewMatrix * modelMatrix;
- if (RenderingNormal == state) {
- // Normal render
shader->setUniformValue(shader->model(), modelMatrix);
- shader->setUniformValue(shader->MVP(), MVPMatrix);
shader->setUniformValue(shader->nModel(), itModelMatrix.inverted().transposed());
-
- if (item->isBlendNeeded()) {
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- glDisable(GL_CULL_FACE);
+ shader->setUniformValue(shader->MVP(), MVPMatrix);
+ if (!m_isOpenGLES) {
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ // Set shadow shader bindings
+ QMatrix4x4 depthMVPMatrix = depthMatrix * modelMatrix;
+ shader->setUniformValue(shader->depth(), depthMVPMatrix);
+ // Draw the object
+ m_drawer->drawObject(shader, m_gridLineObj, 0, m_depthTexture);
+ } else {
+ // Draw the object
+ m_drawer->drawObject(shader, m_gridLineObj);
+ }
} else {
- glDisable(GL_BLEND);
- glEnable(GL_CULL_FACE);
+ m_drawer->drawLine(shader);
}
+ }
+ }
+}
-#if !defined(QT_OPENGL_ES_2)
+void Abstract3DRenderer::drawAngularGrid(ShaderHelper *shader, float yFloorLinePos,
+ const QMatrix4x4 &projectionViewMatrix,
+ const QMatrix4x4 &depthMatrix)
+{
+ float halfRatio((m_polarRadius + (labelMargin / 2.0f)) / 2.0f);
+ QVector3D gridLineScaler(gridLineWidth, gridLineWidth, halfRatio);
+ int gridLineCount = m_axisCacheX.gridLineCount();
+ const QVector<float> &gridPositions = m_axisCacheX.formatter()->gridPositions();
+ const QVector<float> &subGridPositions = m_axisCacheX.formatter()->subGridPositions();
+ int mainSize = gridPositions.size();
+ QVector3D translateVector(0.0f, yFloorLinePos, -halfRatio);
+ QQuaternion finalRotation;
+ if (m_isOpenGLES)
+ finalRotation = m_yRightAngleRotationNeg;
+ else
+ finalRotation = m_xRightAngleRotationNeg;
+ if (m_yFlippedForGrid)
+ finalRotation *= m_xFlipRotation;
+ for (int i = 0; i < gridLineCount; i++) {
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 itModelMatrix;
+ float gridPosition = (i >= mainSize)
+ ? subGridPositions.at(i - mainSize) : gridPositions.at(i);
+ QQuaternion lineRotation = QQuaternion::fromAxisAndAngle(upVector, gridPosition * 360.0f);
+ modelMatrix.rotate(lineRotation);
+ itModelMatrix.rotate(lineRotation);
+ modelMatrix.translate(translateVector);
+ modelMatrix.scale(gridLineScaler);
+ itModelMatrix.scale(gridLineScaler);
+ modelMatrix.rotate(finalRotation);
+ itModelMatrix.rotate(finalRotation);
+ QMatrix4x4 MVPMatrix = projectionViewMatrix * modelMatrix;
+
+ shader->setUniformValue(shader->model(), modelMatrix);
+ shader->setUniformValue(shader->nModel(), itModelMatrix.inverted().transposed());
+ shader->setUniformValue(shader->MVP(), MVPMatrix);
+ if (m_isOpenGLES) {
+ m_drawer->drawLine(shader);
+ } else {
if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
// Set shadow shader bindings
- shader->setUniformValue(shader->shadowQ(), shadowQuality);
- shader->setUniformValue(shader->depth(), depthProjectionViewMatrix * modelMatrix);
- shader->setUniformValue(shader->lightS(), m_cachedTheme->lightStrength() / 10.0f);
- m_drawer->drawObject(shader, item->mesh(), item->texture(), depthTexture);
- } else
-#else
- Q_UNUSED(depthTexture)
- Q_UNUSED(shadowQuality)
-#endif
- {
- // Set shadowless shader bindings
- shader->setUniformValue(shader->lightS(), m_cachedTheme->lightStrength());
- m_drawer->drawObject(shader, item->mesh(), item->texture());
+ QMatrix4x4 depthMVPMatrix = depthMatrix * modelMatrix;
+ shader->setUniformValue(shader->depth(), depthMVPMatrix);
+ // Draw the object
+ m_drawer->drawObject(shader, m_gridLineObj, 0, m_depthTexture);
+ } else {
+ // Draw the object
+ m_drawer->drawObject(shader, m_gridLineObj);
}
- } else if (RenderingSelection == state) {
- // Selection render
- shader->setUniformValue(shader->MVP(), MVPMatrix);
- QVector4D itemColor = indexToSelectionColor(item->index());
- itemColor.setW(customItemAlpha);
- itemColor /= 255.0f;
- shader->setUniformValue(shader->color(), itemColor);
- m_drawer->drawObject(shader, item->mesh());
- } else if (item->isShadowCasting()) {
- // Depth render
- shader->setUniformValue(shader->MVP(), depthProjectionViewMatrix * modelMatrix);
- m_drawer->drawObject(shader, item->mesh());
}
}
+}
- if (RenderingNormal == state) {
- glDisable(GL_TEXTURE_2D);
- glDisable(GL_BLEND);
- glEnable(GL_CULL_FACE);
+float Abstract3DRenderer::calculatePolarBackgroundMargin()
+{
+ // Check each extents of each angular label
+ // Calculate angular position
+ QVector<float> &labelPositions = m_axisCacheX.formatter()->labelPositions();
+ float actualLabelHeight = m_drawer->scaledFontSize() * 2.0f; // All labels are same height
+ float maxNeededMargin = 0.0f;
+
+ // Axis title needs to be accounted for
+ if (m_axisCacheX.isTitleVisible())
+ maxNeededMargin = 2.0f * actualLabelHeight + 3.0f * labelMargin;
+
+ for (int label = 0; label < labelPositions.size(); label++) {
+ QSize labelSize = m_axisCacheX.labelItems().at(label)->size();
+ float actualLabelWidth = actualLabelHeight / labelSize.height() * labelSize.width();
+ float labelPosition = labelPositions.at(label);
+ qreal angle = labelPosition * M_PI * 2.0;
+ float x = qAbs((m_polarRadius + labelMargin) * float(qSin(angle)))
+ + actualLabelWidth - m_polarRadius + labelMargin;
+ float z = qAbs(-(m_polarRadius + labelMargin) * float(qCos(angle)))
+ + actualLabelHeight - m_polarRadius + labelMargin;
+ float neededMargin = qMax(x, z);
+ maxNeededMargin = qMax(maxNeededMargin, neededMargin);
}
+
+ return maxNeededMargin;
+}
+
+void Abstract3DRenderer::updateCameraViewport()
+{
+ QVector3D adjustedTarget = m_cachedScene->activeCamera()->target();
+ fixCameraTarget(adjustedTarget);
+ if (m_oldCameraTarget != adjustedTarget) {
+ QVector3D cameraBase = cameraDistanceVector + adjustedTarget;
+
+ m_cachedScene->activeCamera()->d_ptr->setBaseOrientation(cameraBase,
+ adjustedTarget,
+ upVector);
+ m_oldCameraTarget = adjustedTarget;
+ }
+ m_cachedScene->activeCamera()->d_ptr->updateViewMatrix(m_autoScaleAdjustment);
+ // Set light position (i.e rotate light with activeCamera, a bit above it)
+ m_cachedScene->d_ptr->setLightPositionRelativeToCamera(defaultLightPos);
}
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/engine/abstract3drenderer_p.h b/src/datavisualization/engine/abstract3drenderer_p.h
index 0dfc7367..1e38023d 100644
--- a/src/datavisualization/engine/abstract3drenderer_p.h
+++ b/src/datavisualization/engine/abstract3drenderer_p.h
@@ -30,13 +30,17 @@
#define ABSTRACT3DRENDERER_P_H
#include <QtGui/QOpenGLFunctions>
-
+#if !defined(QT_OPENGL_ES_2)
+# include <QtGui/QOpenGLFunctions_2_1>
+#endif
#include "datavisualizationglobal_p.h"
#include "abstract3dcontroller_p.h"
#include "axisrendercache_p.h"
#include "seriesrendercache_p.h"
#include "customrenderitem_p.h"
+class QSurface;
+
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
class TextureHelper;
@@ -77,21 +81,33 @@ public:
virtual void updateSelectionMode(QAbstract3DGraph::SelectionFlags newMode);
virtual void updateOptimizationHint(QAbstract3DGraph::OptimizationHints hint);
virtual void updateScene(Q3DScene *scene);
- virtual void updateTextures() = 0;
+ virtual void updateTextures();
virtual void initSelectionBuffer() = 0;
virtual void updateSelectionState(SelectionState state);
- virtual void updateInputPosition(const QPoint &position);
-#if !defined(QT_OPENGL_ES_2)
virtual void updateDepthBuffer() = 0;
-#endif
virtual void updateShadowQuality(QAbstract3DGraph::ShadowQuality quality) = 0;
virtual void initShaders(const QString &vertexShader, const QString &fragmentShader) = 0;
virtual void initGradientShaders(const QString &vertexShader, const QString &fragmentShader);
+ virtual void initStaticSelectedItemShaders(const QString &vertexShader,
+ const QString &fragmentShader,
+ const QString &gradientVertexShader,
+ const QString &gradientFragmentShader);
virtual void initBackgroundShaders(const QString &vertexShader,
const QString &fragmentShader) = 0;
virtual void initCustomItemShaders(const QString &vertexShader,
const QString &fragmentShader);
+ virtual void initVolumeTextureShaders(const QString &vertexShader,
+ const QString &fragmentShader,
+ const QString &fragmentLowDefShader,
+ const QString &sliceShader,
+ const QString &sliceFrameVertexShader,
+ const QString &sliceFrameShader);
+ virtual void initLabelShaders(const QString &vertexShader, const QString &fragmentShader);
+ virtual void initCursorPositionShaders(const QString &vertexShader,
+ const QString &fragmentShader);
+ virtual void initCursorPositionBuffer();
+
virtual void updateAxisType(QAbstract3DAxis::AxisOrientation orientation,
QAbstract3DAxis::AxisType type);
virtual void updateAxisTitle(QAbstract3DAxis::AxisOrientation orientation,
@@ -112,7 +128,7 @@ public:
virtual void updateAxisLabelAutoRotation(QAbstract3DAxis::AxisOrientation orientation,
float angle);
virtual void updateAxisTitleVisibility(QAbstract3DAxis::AxisOrientation orientation,
- bool visible);
+ bool visible);
virtual void updateAxisTitleFixed(QAbstract3DAxis::AxisOrientation orientation,
bool fixed);
virtual void modifiedSeriesList(const QVector<QAbstract3DSeries *> &seriesList);
@@ -123,6 +139,10 @@ public:
virtual void updateCustomItem(CustomRenderItem *renderItem);
virtual void updateAspectRatio(float ratio);
+ virtual void updateHorizontalAspectRatio(float ratio);
+ virtual void updatePolar(bool enable);
+ virtual void updateRadialLabelOffset(float offset);
+ virtual void updateMargin(float margin);
virtual QVector3D convertPositionToTranslation(const QVector3D &position,
bool isAbsolute) = 0;
@@ -130,21 +150,28 @@ public:
void generateBaseColorTexture(const QColor &color, GLuint *texture);
void fixGradientAndGenerateTexture(QLinearGradient *gradient, GLuint *gradientTexture);
- inline bool isClickPending() { return m_clickPending; }
- inline void clearClickPending() { m_clickPending = false; }
+ inline bool isClickQueryResolved() const { return m_clickResolved; }
+ inline void clearClickQueryResolved() { m_clickResolved = false; }
+ inline QPoint cachedClickQuery() const { return m_cachedScene->selectionQueryPosition(); }
inline QAbstract3DSeries *clickedSeries() const { return m_clickedSeries; }
inline QAbstract3DGraph::ElementType clickedType() { return m_clickedType; }
+ inline bool isGraphPositionQueryResolved() const { return m_graphPositionQueryResolved; }
+ inline void clearGraphPositionQueryResolved() { m_graphPositionQueryResolved = false; }
+ inline QVector3D queriedGraphPosition() const { return m_queriedGraphPosition; }
+ inline QPoint cachedGraphPositionQuery() const { return m_cachedScene->graphPositionQuery(); }
LabelItem &selectionLabelItem();
void setSelectionLabel(const QString &label);
QString &selectionLabel();
- void drawCustomItems(RenderingState state, ShaderHelper *shader,
+ void drawCustomItems(RenderingState state, ShaderHelper *regularShader,
const QMatrix4x4 &viewMatrix,
const QMatrix4x4 &projectionViewMatrix,
const QMatrix4x4 &depthProjectionViewMatrix,
- GLuint depthTexture, GLfloat shadowQuality);
+ GLuint depthTexture, GLfloat shadowQuality, GLfloat reflection = 1.0f);
+
QVector4D indexToSelectionColor(GLint index);
+ void calculatePolarXZ(const QVector3D &dataPos, float &x, float &z) const;
signals:
void needRender(); // Emit this if something in renderer causes need for another render pass.
@@ -177,7 +204,7 @@ protected:
const QQuaternion &totalRotation, AbstractRenderItem &dummyItem,
const Q3DCamera *activeCamera, float labelsMaxWidth,
const QMatrix4x4 &viewMatrix, const QMatrix4x4 &projectionMatrix,
- ShaderHelper *shader);
+ ShaderHelper *shader, bool radial = false);
void drawAxisTitleZ(const QVector3D &labelRotation, const QVector3D &labelTrans,
const QQuaternion &totalRotation, AbstractRenderItem &dummyItem,
const Q3DCamera *activeCamera, float labelsMaxWidth,
@@ -186,6 +213,26 @@ protected:
void loadGridLineMesh();
void loadLabelMesh();
+ void loadPositionMapperMesh();
+
+ void drawRadialGrid(ShaderHelper *shader, float yFloorLinePos,
+ const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &depthMatrix);
+ void drawAngularGrid(ShaderHelper *shader, float yFloorLinePos,
+ const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &depthMatrix);
+
+ float calculatePolarBackgroundMargin();
+ virtual void fixCameraTarget(QVector3D &target) = 0;
+ void updateCameraViewport();
+
+ void recalculateCustomItemScalingAndPos(CustomRenderItem *item);
+ virtual void getVisibleItemBounds(QVector3D &minBounds, QVector3D &maxBounds) = 0;
+ void drawVolumeSliceFrame(const CustomRenderItem *item, Qt::Axis axis,
+ const QMatrix4x4 &projectionViewMatrix);
+ void queriedGraphPosition(const QMatrix4x4 &projectionViewMatrix, const QVector3D &scaling,
+ GLuint defaultFboHandle);
+
+ void fixContextBeforeDelete();
+ void restoreContextAfterDelete();
bool m_hasNegativeValues;
Q3DTheme *m_cachedTheme;
@@ -201,6 +248,7 @@ protected:
AxisRenderCache m_axisCacheY;
AxisRenderCache m_axisCacheZ;
TextureHelper *m_textureHelper;
+ GLuint m_depthTexture;
Q3DScene *m_cachedScene;
bool m_selectionDirty;
@@ -212,28 +260,75 @@ protected:
QRect m_secondarySubViewport;
float m_devicePixelRatio;
bool m_selectionLabelDirty;
- bool m_clickPending;
+ bool m_clickResolved;
+ bool m_graphPositionQueryPending;
+ bool m_graphPositionQueryResolved;
QAbstract3DSeries *m_clickedSeries;
QAbstract3DGraph::ElementType m_clickedType;
int m_selectedLabelIndex;
int m_selectedCustomItemIndex;
+ QVector3D m_queriedGraphPosition;
+ QPoint m_graphPositionQuery;
QString m_selectionLabel;
LabelItem *m_selectionLabelItem;
int m_visibleSeriesCount;
ShaderHelper *m_customItemShader;
+ ShaderHelper *m_volumeTextureShader;
+ ShaderHelper *m_volumeTextureLowDefShader;
+ ShaderHelper *m_volumeTextureSliceShader;
+ ShaderHelper *m_volumeSliceFrameShader;
+ ShaderHelper *m_labelShader;
+ ShaderHelper *m_cursorPositionShader;
+ GLuint m_cursorPositionFrameBuffer;
+ GLuint m_cursorPositionTexture;
bool m_useOrthoProjection;
bool m_xFlipped;
bool m_yFlipped;
bool m_zFlipped;
+ bool m_yFlippedForGrid;
ObjectHelper *m_backgroundObj; // Shared reference
ObjectHelper *m_gridLineObj; // Shared reference
ObjectHelper *m_labelObj; // Shared reference
+ ObjectHelper *m_positionMapperObj; // Shared reference
float m_graphAspectRatio;
+ float m_graphHorizontalAspectRatio;
+ bool m_polarGraph;
+ float m_radialLabelOffset;
+ float m_polarRadius;
+
+ QQuaternion m_xRightAngleRotation;
+ QQuaternion m_yRightAngleRotation;
+ QQuaternion m_zRightAngleRotation;
+ QQuaternion m_xRightAngleRotationNeg;
+ QQuaternion m_yRightAngleRotationNeg;
+ QQuaternion m_zRightAngleRotationNeg;
+ QQuaternion m_xFlipRotation;
+ QQuaternion m_zFlipRotation;
+
+ float m_requestedMargin;
+ float m_vBackgroundMargin;
+ float m_hBackgroundMargin;
+ float m_scaleXWithBackground;
+ float m_scaleYWithBackground;
+ float m_scaleZWithBackground;
+
+ QVector3D m_oldCameraTarget;
+
+ bool m_reflectionEnabled;
+ qreal m_reflectivity;
+
+ QLocale m_locale;
+#if !defined(QT_OPENGL_ES_2)
+ QOpenGLFunctions_2_1 *m_funcs_2_1; // Not owned
+#endif
+ QPointer<QOpenGLContext> m_context; // Not owned
+ QWindow *m_dummySurfaceAtDelete;
+ bool m_isOpenGLES;
private:
friend class Abstract3DController;
diff --git a/src/datavisualization/engine/axisrendercache.cpp b/src/datavisualization/engine/axisrendercache.cpp
index 0b7a5c7a..02e3b7f6 100644
--- a/src/datavisualization/engine/axisrendercache.cpp
+++ b/src/datavisualization/engine/axisrendercache.cpp
@@ -46,6 +46,7 @@ AxisRenderCache::~AxisRenderCache()
{
foreach (LabelItem *label, m_labelItems)
delete label;
+ m_titleItem.clear();
delete m_formatter;
}
@@ -54,10 +55,8 @@ void AxisRenderCache::setDrawer(Drawer *drawer)
{
m_drawer = drawer;
m_font = m_drawer->font();
- if (m_drawer) {
- QObject::connect(m_drawer, &Drawer::drawerChanged, this, &AxisRenderCache::updateTextures);
+ if (m_drawer)
updateTextures();
- }
}
void AxisRenderCache::setType(QAbstract3DAxis::AxisType type)
@@ -106,10 +105,12 @@ void AxisRenderCache::setLabels(const QStringList &labels)
if (i >= oldSize)
m_labelItems.append(new LabelItem);
if (m_drawer) {
- if (labels.at(i).isEmpty())
+ if (labels.at(i).isEmpty()) {
m_labelItems[i]->clear();
- else if (i >= oldSize || labels.at(i) != m_labels.at(i))
+ } else if (i >= oldSize || labels.at(i) != m_labels.at(i)
+ || m_labelItems[i]->size().width() != widest) {
m_drawer->generateLabelItem(*m_labelItems[i], labels.at(i), widest);
+ }
}
}
m_labels = labels;
@@ -175,6 +176,13 @@ void AxisRenderCache::updateTextures()
}
}
+void AxisRenderCache::clearLabels()
+{
+ m_titleItem.clear();
+ for (int i = 0; i < m_labels.size(); i++)
+ m_labelItems[i]->clear();
+}
+
int AxisRenderCache::maxLabelWidth(const QStringList &labels) const
{
int labelWidth = 0;
diff --git a/src/datavisualization/engine/axisrendercache_p.h b/src/datavisualization/engine/axisrendercache_p.h
index 90321740..e32c590e 100644
--- a/src/datavisualization/engine/axisrendercache_p.h
+++ b/src/datavisualization/engine/axisrendercache_p.h
@@ -107,8 +107,8 @@ public:
inline bool isTitleFixed() const { return m_titleFixed; }
inline void setTitleFixed(bool fixed) { m_titleFixed = fixed; }
-public slots:
void updateTextures();
+ void clearLabels();
private:
int maxLabelWidth(const QStringList &labels) const;
diff --git a/src/datavisualization/engine/bars3dcontroller.cpp b/src/datavisualization/engine/bars3dcontroller.cpp
index 3a240c24..2092dd60 100644
--- a/src/datavisualization/engine/bars3dcontroller.cpp
+++ b/src/datavisualization/engine/bars3dcontroller.cpp
@@ -36,6 +36,7 @@ Bars3DController::Bars3DController(QRect boundRect, Q3DScene *scene)
m_isBarSpecRelative(true),
m_barThicknessRatio(1.0f),
m_barSpacing(QSizeF(1.0, 1.0)),
+ m_floorLevel(0.0f),
m_renderer(0)
{
// Setting a null axis creates a new default axis according to orientation and graph type.
@@ -83,6 +84,12 @@ void Bars3DController::synchDataToRenderer()
needSceneUpdate = true;
}
+ // Floor level update requires data update, so do before abstract sync
+ if (m_changeTracker.floorLevelChanged) {
+ m_renderer->updateFloorLevel(m_floorLevel);
+ m_changeTracker.floorLevelChanged = false;
+ }
+
Abstract3DController::synchDataToRenderer();
// Notify changes to renderer
@@ -114,12 +121,10 @@ void Bars3DController::synchDataToRenderer()
m_changeTracker.selectedBarChanged = false;
}
- if (needSceneUpdate) {
- // Since scene is updated before axis updates are handled,
- // do another render pass for scene update
- m_scene->d_ptr->m_sceneDirty = true;
- emitNeedRender();
- }
+ // Since scene is updated before axis updates are handled, do another render pass to
+ // properly update controller side camera limits.
+ if (needSceneUpdate)
+ m_scene->d_ptr->markDirty();
}
void Bars3DController::handleArrayReset()
@@ -487,6 +492,19 @@ bool Bars3DController::isBarSpecRelative()
return m_isBarSpecRelative;
}
+void Bars3DController::setFloorLevel(float level)
+{
+ m_floorLevel = level;
+ m_isDataDirty = true;
+ m_changeTracker.floorLevelChanged = true;
+ emitNeedRender();
+}
+
+float Bars3DController::floorLevel() const
+{
+ return m_floorLevel;
+}
+
void Bars3DController::setSelectionMode(QAbstract3DGraph::SelectionFlags mode)
{
if (mode.testFlag(QAbstract3DGraph::SelectionSlice)
diff --git a/src/datavisualization/engine/bars3dcontroller_p.h b/src/datavisualization/engine/bars3dcontroller_p.h
index 4f0e1f20..ac62f37d 100644
--- a/src/datavisualization/engine/bars3dcontroller_p.h
+++ b/src/datavisualization/engine/bars3dcontroller_p.h
@@ -43,13 +43,15 @@ struct Bars3DChangeBitField {
bool selectedBarChanged : 1;
bool rowsChanged : 1;
bool itemChanged : 1;
+ bool floorLevelChanged : 1;
Bars3DChangeBitField() :
multiSeriesScalingChanged(true),
barSpecsChanged(true),
selectedBarChanged(true),
rowsChanged(false),
- itemChanged(false)
+ itemChanged(false),
+ floorLevelChanged(false)
{
}
};
@@ -84,6 +86,7 @@ private:
bool m_isBarSpecRelative;
GLfloat m_barThicknessRatio;
QSizeF m_barSpacing;
+ float m_floorLevel;
// Rendering
Bars3DRenderer *m_renderer;
@@ -107,6 +110,8 @@ public:
GLfloat barThickness();
QSizeF barSpacing();
bool isBarSpecRelative();
+ void setFloorLevel(float level);
+ float floorLevel() const;
inline QBar3DSeries *selectedSeries() const { return m_selectedBarSeries; }
diff --git a/src/datavisualization/engine/bars3drenderer.cpp b/src/datavisualization/engine/bars3drenderer.cpp
index 689f3f5d..b6a191a9 100644
--- a/src/datavisualization/engine/bars3drenderer.cpp
+++ b/src/datavisualization/engine/bars3drenderer.cpp
@@ -31,7 +31,6 @@
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
-const GLfloat gridLineWidth = 0.005f;
const bool sliceGridLabels = true;
Bars3DRenderer::Bars3DRenderer(Bars3DController *controller)
@@ -48,9 +47,7 @@ Bars3DRenderer::Bars3DRenderer(Bars3DController *controller)
m_depthShader(0),
m_selectionShader(0),
m_backgroundShader(0),
- m_labelShader(0),
m_bgrTexture(0),
- m_depthTexture(0),
m_selectionTexture(0),
m_depthFrameBuffer(0),
m_selectionFrameBuffer(0),
@@ -67,7 +64,6 @@ Bars3DRenderer::Bars3DRenderer(Bars3DController *controller)
m_scaleFactor(0),
m_maxSceneSize(40.0f),
m_visualSelectedBarPos(Bars3DController::invalidSelectionPosition()),
- m_resetCameraBaseOrientation(true),
m_selectedBarPos(Bars3DController::invalidSelectionPosition()),
m_selectedSeriesCache(0),
m_noZeroInRange(false),
@@ -79,17 +75,22 @@ Bars3DRenderer::Bars3DRenderer(Bars3DController *controller)
m_keepSeriesUniform(false),
m_haveUniformColorSeries(false),
m_haveGradientSeries(false),
- m_zeroPosition(0.0f)
+ m_zeroPosition(0.0f),
+ m_xScaleFactor(1.0f),
+ m_zScaleFactor(1.0f),
+ m_floorLevel(0.0f),
+ m_actualFloorLevel(0.0f)
{
m_axisCacheY.setScale(2.0f);
m_axisCacheY.setTranslate(-1.0f);
- initializeOpenGLFunctions();
initializeOpenGL();
}
Bars3DRenderer::~Bars3DRenderer()
{
+ fixContextBeforeDelete();
+
if (QOpenGLContext::currentContext()) {
m_textureHelper->glDeleteFramebuffers(1, &m_selectionFrameBuffer);
m_textureHelper->glDeleteRenderbuffers(1, &m_selectionDepthBuffer);
@@ -103,7 +104,6 @@ Bars3DRenderer::~Bars3DRenderer()
delete m_depthShader;
delete m_selectionShader;
delete m_backgroundShader;
- delete m_labelShader;
}
void Bars3DRenderer::initializeOpenGL()
@@ -111,13 +111,9 @@ void Bars3DRenderer::initializeOpenGL()
Abstract3DRenderer::initializeOpenGL();
// Initialize shaders
- initLabelShaders(QStringLiteral(":/shaders/vertexLabel"),
- QStringLiteral(":/shaders/fragmentLabel"));
-#if !defined(QT_OPENGL_ES_2)
// Init depth shader (for shadows). Init in any case, easier to handle shadow activation if done via api.
initDepthShader();
-#endif
// Init selection shader
initSelectionShader();
@@ -125,13 +121,57 @@ void Bars3DRenderer::initializeOpenGL()
// Load grid line mesh
loadGridLineMesh();
- // Load label mesh
- loadLabelMesh();
-
// Load background mesh (we need to be initialized first)
loadBackgroundMesh();
}
+void Bars3DRenderer::fixCameraTarget(QVector3D &target)
+{
+ target.setX(target.x() * m_xScaleFactor);
+ target.setY(0.0f);
+ target.setZ(target.z() * -m_zScaleFactor);
+}
+
+void Bars3DRenderer::getVisibleItemBounds(QVector3D &minBounds, QVector3D &maxBounds)
+{
+ // The inputs are the item bounds in OpenGL coordinates.
+ // The outputs limit these bounds to visible ranges, normalized to range [-1, 1]
+ // Volume shader flips the Y and Z axes, so we need to set negatives of actual values to those
+ float itemRangeX = (maxBounds.x() - minBounds.x());
+ float itemRangeY = (maxBounds.y() - minBounds.y());
+ float itemRangeZ = (maxBounds.z() - minBounds.z());
+
+ if (minBounds.x() < -m_xScaleFactor)
+ minBounds.setX(-1.0f + (2.0f * qAbs(minBounds.x() + m_xScaleFactor) / itemRangeX));
+ else
+ minBounds.setX(-1.0f);
+
+ if (minBounds.y() < -1.0f + m_backgroundAdjustment)
+ minBounds.setY(-(-1.0f + (2.0f * qAbs(minBounds.y() + 1.0f - m_backgroundAdjustment) / itemRangeY)));
+ else
+ minBounds.setY(1.0f);
+
+ if (minBounds.z() < -m_zScaleFactor)
+ minBounds.setZ(-(-1.0f + (2.0f * qAbs(minBounds.z() + m_zScaleFactor) / itemRangeZ)));
+ else
+ minBounds.setZ(1.0f);
+
+ if (maxBounds.x() > m_xScaleFactor)
+ maxBounds.setX(1.0f - (2.0f * qAbs(maxBounds.x() - m_xScaleFactor) / itemRangeX));
+ else
+ maxBounds.setX(1.0f);
+
+ if (maxBounds.y() > 1.0f + m_backgroundAdjustment)
+ maxBounds.setY(-(1.0f - (2.0f * qAbs(maxBounds.y() - 1.0f - m_backgroundAdjustment) / itemRangeY)));
+ else
+ maxBounds.setY(-1.0f);
+
+ if (maxBounds.z() > m_zScaleFactor)
+ maxBounds.setZ(-(1.0f - (2.0f * qAbs(maxBounds.z() - m_zScaleFactor) / itemRangeZ)));
+ else
+ maxBounds.setZ(-1.0f);
+}
+
void Bars3DRenderer::updateData()
{
int minRow = m_axisCacheZ.min();
@@ -163,11 +203,11 @@ void Bars3DRenderer::updateData()
GLfloat sceneRatio = qMin(GLfloat(newColumns) / GLfloat(newRows),
GLfloat(newRows) / GLfloat(newColumns));
m_maxSceneSize = 2.0f * qSqrt(sceneRatio * newColumns * newRows);
- // Calculate here and at setting bar specs
- calculateSceneScalingFactors();
}
- m_zeroPosition = m_axisCacheY.formatter()->positionAt(0.0f);
+ calculateSceneScalingFactors();
+
+ m_zeroPosition = m_axisCacheY.formatter()->positionAt(m_actualFloorLevel);
foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
BarSeriesRenderCache *cache = static_cast<BarSeriesRenderCache *>(baseCache);
@@ -390,14 +430,6 @@ void Bars3DRenderer::updateScene(Q3DScene *scene)
}
}
- if (m_resetCameraBaseOrientation) {
- // Set initial camera position. Also update if height adjustment has changed.
- scene->activeCamera()->d_ptr->setBaseOrientation(cameraDistanceVector,
- zeroVector,
- upVector);
- m_resetCameraBaseOrientation = false;
- }
-
Abstract3DRenderer::updateScene(scene);
updateSlicingActive(scene->isSlicingActive());
@@ -418,10 +450,17 @@ void Bars3DRenderer::render(GLuint defaultFboHandle)
void Bars3DRenderer::drawSlicedScene()
{
+ if (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionRow)
+ == m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionColumn)) {
+ qWarning("Invalid selection mode. Either QAbstract3DGraph::SelectionRow or"
+ " QAbstract3DGraph::SelectionColumn must be set before calling"
+ " setSlicingActive(true).");
+ return;
+ }
+
GLfloat barPosX = 0;
QVector3D lightPos;
QVector4D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor());
- static QQuaternion ninetyDegreeRotation = QQuaternion::fromAxisAndAngle(upVector, 90.0f);
// Specify viewport
glViewport(m_secondarySubViewport.x(),
@@ -482,11 +521,12 @@ void Bars3DRenderer::drawSlicedScene()
// Draw grid lines
if (m_cachedTheme->isGridEnabled()) {
glDisable(GL_DEPTH_TEST);
-#if !(defined QT_OPENGL_ES_2)
- ShaderHelper *lineShader = m_backgroundShader;
-#else
- ShaderHelper *lineShader = m_selectionShader; // Plain color shader for GL_LINES
-#endif
+ ShaderHelper *lineShader;
+ if (m_isOpenGLES)
+ lineShader = m_selectionShader; // Plain color shader for GL_LINES
+ else
+ lineShader = m_backgroundShader;
+
// Bind line shader
lineShader->bind();
@@ -496,7 +536,8 @@ void Bars3DRenderer::drawSlicedScene()
lineShader->setUniformValue(lineShader->view(), viewMatrix);
lineShader->setUniformValue(lineShader->color(), lineColor);
lineShader->setUniformValue(lineShader->ambientS(),
- m_cachedTheme->ambientLightStrength() * 2.3f);
+ m_cachedTheme->ambientLightStrength()
+ + m_cachedTheme->lightStrength() / 7.0f);
lineShader->setUniformValue(lineShader->lightS(), 0.0f);
lineShader->setUniformValue(lineShader->lightColor(), lightColor);
@@ -524,11 +565,10 @@ void Bars3DRenderer::drawSlicedScene()
lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
// Draw the object
-#if !(defined QT_OPENGL_ES_2)
- m_drawer->drawObject(lineShader, m_gridLineObj);
-#else
- m_drawer->drawLine(lineShader);
-#endif
+ if (m_isOpenGLES)
+ m_drawer->drawLine(lineShader);
+ else
+ m_drawer->drawObject(lineShader, m_gridLineObj);
// Check if we have a line at zero position already
if (gridPos == (barPosYAdjustment + zeroPosAdjustment))
@@ -553,18 +593,16 @@ void Bars3DRenderer::drawSlicedScene()
m_cachedTheme->labelTextColor()));
// Draw the object
-#if !(defined QT_OPENGL_ES_2)
- m_drawer->drawObject(lineShader, m_gridLineObj);
-#else
- m_drawer->drawLine(lineShader);
-#endif
+ if (m_isOpenGLES)
+ m_drawer->drawLine(lineShader);
+ else
+ m_drawer->drawObject(lineShader, m_gridLineObj);
}
}
if (sliceGridLabels) {
// Bind label shader
m_labelShader->bind();
- glEnable(GL_TEXTURE_2D);
glCullFace(GL_BACK);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
@@ -587,7 +625,6 @@ void Bars3DRenderer::drawSlicedScene()
}
labelNbr++;
}
- glDisable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
}
@@ -606,14 +643,16 @@ void Bars3DRenderer::drawSlicedScene()
m_barShader->setUniformValue(m_barShader->view(), viewMatrix);
m_barShader->setUniformValue(m_barShader->lightS(), 0.15f);
m_barShader->setUniformValue(m_barShader->ambientS(),
- m_cachedTheme->ambientLightStrength() * 2.3f);
+ m_cachedTheme->ambientLightStrength()
+ + m_cachedTheme->lightStrength() / 7.0f);
m_barShader->setUniformValue(m_barShader->lightColor(), lightColor);
m_barGradientShader->bind();
m_barGradientShader->setUniformValue(m_barGradientShader->lightP(), lightPos);
m_barGradientShader->setUniformValue(m_barGradientShader->view(), viewMatrix);
m_barGradientShader->setUniformValue(m_barGradientShader->lightS(), 0.15f);
m_barGradientShader->setUniformValue(m_barGradientShader->ambientS(),
- m_cachedTheme->ambientLightStrength() * 2.3f);
+ m_cachedTheme->ambientLightStrength()
+ + m_cachedTheme->lightStrength() / 7.0f);
m_barGradientShader->setUniformValue(m_barGradientShader->gradientMin(), 0.0f);
m_barGradientShader->setUniformValue(m_barGradientShader->lightColor(), lightColor);
@@ -700,7 +739,7 @@ void Bars3DRenderer::drawSlicedScene()
barPosX = item.translation().x();
} else {
barPosX = -(item.translation().z()); // flip z; frontmost bar to the left
- barRotation *= ninetyDegreeRotation;
+ barRotation *= m_yRightAngleRotation;
}
modelMatrix.translate(barPosX, barPosY, 0.0f);
@@ -760,7 +799,6 @@ void Bars3DRenderer::drawSlicedScene()
// Draw labels
m_labelShader->bind();
glDisable(GL_DEPTH_TEST);
- glEnable(GL_TEXTURE_2D);
glCullFace(GL_BACK);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
@@ -778,6 +816,12 @@ void Bars3DRenderer::drawSlicedScene()
int labelCount = m_sliceCache->labelItems().size();
for (int labelNo = 0; labelNo < labelCount; labelNo++) {
+ // Check for invalid usage (no selection when setting slicing active)
+ if (!firstVisualSliceArray) {
+ qWarning("No slice data found. Make sure there is a valid selection.");
+ continue;
+ }
+
// Get labels from first series only
const BarRenderSliceItem &item = firstVisualSliceArray->at(labelNo);
m_dummyBarRenderItem.setTranslation(QVector3D(item.translation().x(),
@@ -889,7 +933,6 @@ void Bars3DRenderer::drawSlicedScene()
m_cachedSelectionMode, m_labelShader, m_labelObj, activeCamera,
false, false, Drawer::LabelMid, Qt::AlignBottom);
- glDisable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
@@ -912,8 +955,6 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
GLfloat colPos = 0;
GLfloat rowPos = 0;
- QVector4D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor());
-
const Q3DCamera *activeCamera = m_cachedScene->activeCamera();
glViewport(m_primarySubViewport.x(),
@@ -990,15 +1031,9 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
QMatrix4x4 projectionViewMatrix = projectionMatrix * viewMatrix;
- bool rowMode = m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionRow);
-
- GLfloat rowScaleFactor = m_rowWidth / m_scaleFactor;
- GLfloat columnScaleFactor = m_columnDepth / m_scaleFactor;
-
BarRenderItem *selectedBar(0);
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) {
// Render scene into a depth texture for using with shadow mapping
// Enable drawing to depth framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, m_depthFrameBuffer);
@@ -1052,6 +1087,12 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
shadowOffset = -0.015f;
}
+ if (m_cachedTheme->isBackgroundEnabled() && m_reflectionEnabled
+ && ((m_yFlipped && item.height() > 0.0)
+ || (!m_yFlipped && item.height() < 0.0))) {
+ continue;
+ }
+
QMatrix4x4 modelMatrix;
QMatrix4x4 MVPMatrix;
@@ -1097,8 +1138,9 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
}
Abstract3DRenderer::drawCustomItems(RenderingDepth, m_depthShader, viewMatrix,
- projectionViewMatrix, depthProjectionViewMatrix,
- m_depthTexture, m_shadowQualityToShader);
+ projectionViewMatrix,
+ depthProjectionViewMatrix, m_depthTexture,
+ m_shadowQualityToShader);
// Disable drawing to depth framebuffer (= enable drawing to screen)
glBindFramebuffer(GL_FRAMEBUFFER, defaultFboHandle);
@@ -1112,12 +1154,22 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
m_primarySubViewport.width(),
m_primarySubViewport.height());
}
-#endif
+
+ // Do position mapping when necessary
+ if (m_graphPositionQueryPending) {
+ QVector3D graphDimensions(m_xScaleFactor, 0.0f, m_zScaleFactor);
+ queriedGraphPosition(projectionViewMatrix, graphDimensions, defaultFboHandle);
+
+ // Y is always at floor level
+ m_queriedGraphPosition.setY(0.0f);
+ emit needRender();
+ }
// Skip selection mode drawing if we're slicing or have no selection mode
if (!m_cachedIsSlicingActivated && m_cachedSelectionMode > QAbstract3DGraph::SelectionNone
&& m_selectionState == SelectOnScene
- && (m_visibleSeriesCount > 0 || !m_customRenderCache.isEmpty())) {
+ && (m_visibleSeriesCount > 0 || !m_customRenderCache.isEmpty())
+ && m_selectionTexture) {
// Bind selection shader
m_selectionShader->bind();
@@ -1181,18 +1233,20 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
}
}
glCullFace(GL_BACK);
- Abstract3DRenderer::drawCustomItems(RenderingSelection, m_selectionShader, viewMatrix,
+ Abstract3DRenderer::drawCustomItems(RenderingSelection, m_selectionShader,
+ viewMatrix,
projectionViewMatrix, depthProjectionViewMatrix,
m_depthTexture, m_shadowQualityToShader);
- drawLabels(true, activeCamera, viewMatrix, projectionMatrix, rowScaleFactor,
- columnScaleFactor);
+ drawLabels(true, activeCamera, viewMatrix, projectionMatrix);
+ drawBackground(backgroundRotation, depthProjectionViewMatrix, projectionViewMatrix,
+ viewMatrix, false, true);
glEnable(GL_DITHER);
// Read color under cursor
- QVector4D clickedColor = Utils::getSelection(m_inputPosition,
- m_viewport.height());
+ QVector4D clickedColor = Utils::getSelection(m_inputPosition, m_viewport.height());
m_clickedPosition = selectionColorToArrayPosition(clickedColor);
m_clickedSeries = selectionColorToSeries(clickedColor);
+ m_clickResolved = true;
emit needRender();
@@ -1204,8 +1258,133 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
m_primarySubViewport.height());
}
- // Enable texturing
- glEnable(GL_TEXTURE_2D);
+ if (m_reflectionEnabled) {
+ //
+ // Draw reflections
+ //
+ glDisable(GL_DEPTH_TEST);
+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+ glEnable(GL_STENCIL_TEST);
+ glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
+ glStencilFunc(GL_ALWAYS, 1, 0xffffffff);
+
+ // Draw background stencil
+ drawBackground(backgroundRotation, depthProjectionViewMatrix, projectionViewMatrix,
+ viewMatrix);
+
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ glEnable(GL_DEPTH_TEST);
+
+ glStencilFunc(GL_EQUAL, 1, 0xffffffff);
+ glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
+
+ // Set light
+ QVector3D reflectionLightPos = lightPos;
+ reflectionLightPos.setY(-(lightPos.y()));
+ m_cachedScene->activeLight()->setPosition(reflectionLightPos);
+
+ // Draw bar reflections
+ (void)drawBars(&selectedBar, depthProjectionViewMatrix,
+ projectionViewMatrix, viewMatrix,
+ startRow, stopRow, stepRow,
+ startBar, stopBar, stepBar, -1.0f);
+
+ Abstract3DRenderer::drawCustomItems(RenderingNormal, m_customItemShader,
+ viewMatrix, projectionViewMatrix,
+ depthProjectionViewMatrix, m_depthTexture,
+ m_shadowQualityToShader, -1.0f);
+
+ // Reset light
+ m_cachedScene->activeLight()->setPosition(lightPos);
+
+ glDisable(GL_STENCIL_TEST);
+
+ glCullFace(GL_BACK);
+ }
+
+ //
+ // Draw the real scene
+ //
+ // Draw background
+ if (m_reflectionEnabled) {
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ drawBackground(backgroundRotation, depthProjectionViewMatrix, projectionViewMatrix,
+ viewMatrix, true);
+ glDisable(GL_BLEND);
+ } else {
+ drawBackground(backgroundRotation, depthProjectionViewMatrix, projectionViewMatrix,
+ viewMatrix);
+ }
+
+ // Draw bars
+ bool barSelectionFound = drawBars(&selectedBar, depthProjectionViewMatrix,
+ projectionViewMatrix, viewMatrix,
+ startRow, stopRow, stepRow,
+ startBar, stopBar, stepBar);
+
+ // Draw grid lines
+ drawGridLines(depthProjectionViewMatrix, projectionViewMatrix, viewMatrix);
+
+ // Draw custom items
+ Abstract3DRenderer::drawCustomItems(RenderingNormal, m_customItemShader, viewMatrix,
+ projectionViewMatrix, depthProjectionViewMatrix,
+ m_depthTexture, m_shadowQualityToShader);
+
+ // Draw labels
+ drawLabels(false, activeCamera, viewMatrix, projectionMatrix);
+
+ // Handle selected bar label generation
+ if (barSelectionFound) {
+ // Print value of selected bar
+ glDisable(GL_DEPTH_TEST);
+ // Draw the selection label
+ LabelItem &labelItem = selectionLabelItem();
+ if (m_selectedBar != selectedBar || m_updateLabels || !labelItem.textureId()
+ || m_selectionLabelDirty) {
+ QString labelText = selectionLabel();
+ if (labelText.isNull() || m_selectionLabelDirty) {
+ labelText = m_selectedSeriesCache->itemLabel();
+ setSelectionLabel(labelText);
+ m_selectionLabelDirty = false;
+ }
+ m_drawer->generateLabelItem(labelItem, labelText);
+ m_selectedBar = selectedBar;
+ }
+
+ Drawer::LabelPosition position =
+ m_selectedBar->height() >= 0 ? Drawer::LabelOver : Drawer::LabelBelow;
+
+ m_drawer->drawLabel(*selectedBar, labelItem, viewMatrix, projectionMatrix,
+ zeroVector, identityQuaternion, selectedBar->height(),
+ m_cachedSelectionMode, m_labelShader,
+ m_labelObj, activeCamera, true, false, position);
+
+ // Reset label update flag; they should have been updated when we get here
+ m_updateLabels = false;
+
+ glEnable(GL_DEPTH_TEST);
+ } else {
+ m_selectedBar = 0;
+ }
+
+ glDisable(GL_BLEND);
+
+ // Release shader
+ glUseProgram(0);
+ m_selectionDirty = false;
+}
+
+bool Bars3DRenderer::drawBars(BarRenderItem **selectedBar,
+ const QMatrix4x4 &depthProjectionViewMatrix,
+ const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &viewMatrix,
+ GLint startRow, GLint stopRow, GLint stepRow,
+ GLint startBar, GLint stopBar, GLint stepBar, GLfloat reflection)
+{
+ QVector3D lightPos = m_cachedScene->activeLight()->position();
+ QVector4D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor());
+
+ bool rowMode = m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionRow);
ShaderHelper *barShader = 0;
GLuint gradientTexture = 0;
@@ -1251,7 +1430,6 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
m_sliceTitleItem = 0;
}
- // Draw bars
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(0.5f, 1.0f);
@@ -1302,12 +1480,12 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
}
previousColorStyle = colorStyle;
-
for (int row = startRow; row != stopRow; row += stepRow) {
BarRenderItemRow &renderRow = renderArray[row];
for (int bar = startBar; bar != stopBar; bar += stepBar) {
BarRenderItem &item = renderRow[bar];
- if (item.height() < 0)
+ float adjustedHeight = reflection * item.height();
+ if (adjustedHeight < 0)
glCullFace(GL_FRONT);
else
glCullFace(GL_BACK);
@@ -1316,13 +1494,13 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
QMatrix4x4 itModelMatrix;
QMatrix4x4 MVPMatrix;
- colPos = (bar + seriesPos) * (m_cachedBarSpacing.width());
- rowPos = (row + 0.5f) * (m_cachedBarSpacing.height());
+ GLfloat colPos = (bar + seriesPos) * (m_cachedBarSpacing.width());
+ GLfloat rowPos = (row + 0.5f) * (m_cachedBarSpacing.height());
modelMatrix.translate((colPos - m_rowWidth) / m_scaleFactor,
- item.height(),
+ adjustedHeight,
(m_columnDepth - rowPos) / m_scaleFactor);
- modelScaler.setY(item.height());
+ modelScaler.setY(adjustedHeight);
if (!seriesRotation.isIdentity() || !item.rotation().isIdentity()) {
QQuaternion totalRotation = seriesRotation * item.rotation();
modelMatrix.rotate(totalRotation);
@@ -1357,8 +1535,8 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
// We have no ownership, don't delete the previous one
if (!m_cachedIsSlicingActivated
&& m_selectedSeriesCache == cache) {
- selectedBar = &item;
- selectedBar->setPosition(QPoint(row, bar));
+ *selectedBar = &item;
+ (*selectedBar)->setPosition(QPoint(row, bar));
item.setTranslation(modelMatrix.column(3).toVector3D());
barSelectionFound = true;
}
@@ -1438,8 +1616,15 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
}
}
- // Skip drawing of 0-height bars
- if (item.height() != 0) {
+ if (item.height() == 0) {
+ continue;
+ } else if ((m_reflectionEnabled
+ && (reflection == 1.0f
+ || (reflection != 1.0f
+ && ((m_yFlipped && item.height() < 0.0)
+ || (!m_yFlipped && item.height() > 0.0)))))
+ || !m_reflectionEnabled) {
+ // Skip drawing of 0-height bars and reflections of bars on the "wrong side"
// Set shader bindings
barShader->setUniformValue(barShader->model(), modelMatrix);
barShader->setUniformValue(barShader->nModel(),
@@ -1452,8 +1637,10 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
qAbs(item.height()) / m_gradientFraction);
}
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ if (((m_reflectionEnabled && reflection == 1.0f
+ && m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone)
+ || m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone)
+ && !m_isOpenGLES) {
// Set shadow shader bindings
QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
barShader->setUniformValue(barShader->shadowQ(),
@@ -1465,13 +1652,15 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
// Draw the object
m_drawer->drawObject(barShader, barObj, gradientTexture,
m_depthTexture);
- } else
-#else
- Q_UNUSED(shadowLightStrength);
-#endif
- {
+ } else {
// Set shadowless shader bindings
- barShader->setUniformValue(barShader->lightS(), lightStrength);
+ if (m_reflectionEnabled && reflection != 1.0f
+ && m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ barShader->setUniformValue(barShader->lightS(),
+ adjustedLightStrength);
+ } else {
+ barShader->setUniformValue(barShader->lightS(), lightStrength);
+ }
// Draw the object
m_drawer->drawObject(barShader, barObj, gradientTexture);
@@ -1481,122 +1670,145 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
}
}
}
-
glDisable(GL_POLYGON_OFFSET_FILL);
// Reset culling
glCullFace(GL_BACK);
- // Bind background shader
- m_backgroundShader->bind();
+ return barSelectionFound;
+}
+void Bars3DRenderer::drawBackground(GLfloat backgroundRotation,
+ const QMatrix4x4 &depthProjectionViewMatrix,
+ const QMatrix4x4 &projectionViewMatrix,
+ const QMatrix4x4 &viewMatrix, bool reflectingDraw,
+ bool drawingSelectionBuffer)
+{
// Draw background
if (m_cachedTheme->isBackgroundEnabled() && m_backgroundObj) {
+ QVector3D lightPos = m_cachedScene->activeLight()->position();
+ QVector4D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor());
+ GLfloat adjustedLightStrength = m_cachedTheme->lightStrength() / 10.0f;
+ ShaderHelper *shader = 0;
+
+ // Bind background shader
+ if (drawingSelectionBuffer)
+ shader = m_selectionShader; // Use single color shader when drawing to selection buffer
+ else
+ shader = m_backgroundShader;
+ shader->bind();
+
QMatrix4x4 modelMatrix;
QMatrix4x4 MVPMatrix;
QMatrix4x4 itModelMatrix;
- QVector3D backgroundScaler(rowScaleFactor, 1.0f, columnScaleFactor);
- modelMatrix.translate(0.0f, m_backgroundAdjustment, 0.0f);
+ QVector3D backgroundScaler(m_scaleXWithBackground, m_scaleYWithBackground,
+ m_scaleZWithBackground);
+ QVector4D backgroundColor = Utils::vectorFromColor(m_cachedTheme->backgroundColor());
+ if (m_reflectionEnabled)
+ backgroundColor.setW(backgroundColor.w() * m_reflectivity);
+ // Set shader bindings
+ shader->setUniformValue(shader->lightP(), lightPos);
+ shader->setUniformValue(shader->view(), viewMatrix);
+ if (drawingSelectionBuffer) {
+ // Use selectionSkipColor for background when drawing to selection buffer
+ shader->setUniformValue(shader->color(), selectionSkipColor);
+ } else {
+ shader->setUniformValue(shader->color(), backgroundColor);
+ }
+ shader->setUniformValue(shader->ambientS(),
+ m_cachedTheme->ambientLightStrength() * 2.0f);
+ shader->setUniformValue(shader->lightColor(), lightColor);
+
+ // Draw floor
modelMatrix.scale(backgroundScaler);
- itModelMatrix.scale(backgroundScaler);
- modelMatrix.rotate(backgroundRotation, 0.0f, 1.0f, 0.0f);
- itModelMatrix.rotate(backgroundRotation, 0.0f, 1.0f, 0.0f);
+
+ if (m_yFlipped)
+ modelMatrix.rotate(m_xRightAngleRotation);
+ else
+ modelMatrix.rotate(m_xRightAngleRotationNeg);
+
+ itModelMatrix = modelMatrix;
#ifdef SHOW_DEPTH_TEXTURE_SCENE
MVPMatrix = depthProjectionViewMatrix * modelMatrix;
#else
MVPMatrix = projectionViewMatrix * modelMatrix;
#endif
- QVector4D backgroundColor = Utils::vectorFromColor(m_cachedTheme->backgroundColor());
-
- // Set shader bindings
- m_backgroundShader->setUniformValue(m_backgroundShader->lightP(), lightPos);
- m_backgroundShader->setUniformValue(m_backgroundShader->view(), viewMatrix);
- m_backgroundShader->setUniformValue(m_backgroundShader->model(), modelMatrix);
- m_backgroundShader->setUniformValue(m_backgroundShader->nModel(),
- itModelMatrix.inverted().transposed());
- m_backgroundShader->setUniformValue(m_backgroundShader->MVP(), MVPMatrix);
- m_backgroundShader->setUniformValue(m_backgroundShader->color(), backgroundColor);
- m_backgroundShader->setUniformValue(m_backgroundShader->ambientS(),
- m_cachedTheme->ambientLightStrength() * 2.0f);
- m_backgroundShader->setUniformValue(m_backgroundShader->lightColor(), lightColor);
+ // Set changed shader bindings
+ shader->setUniformValue(shader->model(), modelMatrix);
+ shader->setUniformValue(shader->nModel(), itModelMatrix.inverted().transposed());
+ shader->setUniformValue(shader->MVP(), MVPMatrix);
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) {
// Set shadow shader bindings
QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
- m_backgroundShader->setUniformValue(m_backgroundShader->shadowQ(),
- m_shadowQualityToShader);
- m_backgroundShader->setUniformValue(m_backgroundShader->depth(), depthMVPMatrix);
- m_backgroundShader->setUniformValue(m_backgroundShader->lightS(),
- adjustedLightStrength);
-
+ shader->setUniformValue(shader->depth(), depthMVPMatrix);
// Draw the object
- m_drawer->drawObject(m_backgroundShader, m_backgroundObj, 0, m_depthTexture);
- } else
-#endif
- {
- // Set shadowless shader bindings
- m_backgroundShader->setUniformValue(m_backgroundShader->lightS(),
- m_cachedTheme->lightStrength());
-
+ m_drawer->drawObject(shader, m_gridLineObj, 0, m_depthTexture);
+ } else {
// Draw the object
- m_drawer->drawObject(m_backgroundShader, m_backgroundObj);
+ m_drawer->drawObject(shader, m_gridLineObj);
}
- // Draw floor
+ // Draw walls
modelMatrix = QMatrix4x4();
itModelMatrix = QMatrix4x4();
+ modelMatrix.translate(0.0f, m_backgroundAdjustment, 0.0f);
modelMatrix.scale(backgroundScaler);
-
- if (m_yFlipped)
- modelMatrix.rotate(90.0f, 1.0f, 0.0f, 0.0f);
- else
- modelMatrix.rotate(-90.0f, 1.0f, 0.0f, 0.0f);
-
- itModelMatrix = modelMatrix;
+ itModelMatrix.scale(backgroundScaler);
+ modelMatrix.rotate(backgroundRotation, 0.0f, 1.0f, 0.0f);
+ itModelMatrix.rotate(backgroundRotation, 0.0f, 1.0f, 0.0f);
#ifdef SHOW_DEPTH_TEXTURE_SCENE
MVPMatrix = depthProjectionViewMatrix * modelMatrix;
#else
MVPMatrix = projectionViewMatrix * modelMatrix;
#endif
+
// Set changed shader bindings
- m_backgroundShader->setUniformValue(m_backgroundShader->model(), modelMatrix);
- m_backgroundShader->setUniformValue(m_backgroundShader->nModel(),
- itModelMatrix.inverted().transposed());
- m_backgroundShader->setUniformValue(m_backgroundShader->MVP(), MVPMatrix);
+ shader->setUniformValue(shader->model(), modelMatrix);
+ shader->setUniformValue(shader->nModel(), itModelMatrix.inverted().transposed());
+ shader->setUniformValue(shader->MVP(), MVPMatrix);
+ if (!m_reflectionEnabled || (m_reflectionEnabled && reflectingDraw)) {
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) {
+ // Set shadow shader bindings
+ QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
+ shader->setUniformValue(shader->shadowQ(), m_shadowQualityToShader);
+ shader->setUniformValue(shader->depth(), depthMVPMatrix);
+ shader->setUniformValue(shader->lightS(), adjustedLightStrength);
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
- // Set shadow shader bindings
- QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
- m_backgroundShader->setUniformValue(m_backgroundShader->depth(), depthMVPMatrix);
- // Draw the object
- m_drawer->drawObject(m_backgroundShader, m_gridLineObj, 0, m_depthTexture);
- } else
-#endif
- {
- // Draw the object
- m_drawer->drawObject(m_backgroundShader, m_gridLineObj);
+ // Draw the object
+ m_drawer->drawObject(shader, m_backgroundObj, 0, m_depthTexture);
+ } else {
+ // Set shadowless shader bindings
+ shader->setUniformValue(shader->lightS(), m_cachedTheme->lightStrength());
+
+ // Draw the object
+ m_drawer->drawObject(shader, m_backgroundObj);
+ }
}
}
+}
- // Disable textures
- glDisable(GL_TEXTURE_2D);
-
- // Draw grid lines
+void Bars3DRenderer::drawGridLines(const QMatrix4x4 &depthProjectionViewMatrix,
+ const QMatrix4x4 &projectionViewMatrix,
+ const QMatrix4x4 &viewMatrix)
+{
if (m_cachedTheme->isGridEnabled()) {
-#if !(defined QT_OPENGL_ES_2)
- ShaderHelper *lineShader = m_backgroundShader;
-#else
- ShaderHelper *lineShader = m_selectionShader; // Plain color shader for GL_LINES
-#endif
+ ShaderHelper *lineShader;
+ if (m_isOpenGLES)
+ lineShader = m_selectionShader; // Plain color shader for GL_LINES
+ else
+ lineShader = m_backgroundShader;
+
QQuaternion lineRotation;
+ QVector3D lightPos = m_cachedScene->activeLight()->position();
+ QVector4D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor());
+
// Bind bar shader
lineShader->bind();
@@ -1607,15 +1819,12 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
lineShader->setUniformValue(lineShader->color(), barColor);
lineShader->setUniformValue(lineShader->ambientS(), m_cachedTheme->ambientLightStrength());
lineShader->setUniformValue(lineShader->lightColor(), lightColor);
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) {
// Set shadowed shader bindings
lineShader->setUniformValue(lineShader->shadowQ(), m_shadowQualityToShader);
lineShader->setUniformValue(lineShader->lightS(),
m_cachedTheme->lightStrength() / 20.0f);
- } else
-#endif
- {
+ } else {
// Set shadowless shader bindings
lineShader->setUniformValue(lineShader->lightS(),
m_cachedTheme->lightStrength() / 2.5f);
@@ -1625,12 +1834,12 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
if (m_yFlipped)
yFloorLinePosition = -yFloorLinePosition;
- QVector3D gridLineScaler(rowScaleFactor, gridLineWidth, gridLineWidth);
+ QVector3D gridLineScaler(m_scaleXWithBackground, gridLineWidth, gridLineWidth);
if (m_yFlipped)
- lineRotation = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, 90.0f);
+ lineRotation = m_xRightAngleRotation;
else
- lineRotation = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, -90.0f);
+ lineRotation = m_xRightAngleRotationNeg;
// Floor lines: rows
for (GLfloat row = 0.0f; row <= m_cachedRowCount; row++) {
@@ -1638,7 +1847,7 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
QMatrix4x4 MVPMatrix;
QMatrix4x4 itModelMatrix;
- rowPos = row * m_cachedBarSpacing.height();
+ GLfloat rowPos = row * m_cachedBarSpacing.height();
modelMatrix.translate(0.0f, yFloorLinePosition,
(m_columnDepth - rowPos) / m_scaleFactor);
modelMatrix.scale(gridLineScaler);
@@ -1654,33 +1863,32 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
itModelMatrix.inverted().transposed());
lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
- // Set shadow shader bindings
- QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
- lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ if (m_isOpenGLES) {
+ m_drawer->drawLine(lineShader);
} else {
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj);
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ // Set shadow shader bindings
+ QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
+ lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ } else {
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj);
+ }
}
-#else
- m_drawer->drawLine(lineShader);
-#endif
}
// Floor lines: columns
-#if defined(QT_OPENGL_ES_2)
- lineRotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, 90.0f);
-#endif
- gridLineScaler = QVector3D(gridLineWidth, gridLineWidth, columnScaleFactor);
+ if (m_isOpenGLES)
+ lineRotation = m_yRightAngleRotation;
+ gridLineScaler = QVector3D(gridLineWidth, gridLineWidth, m_scaleZWithBackground);
for (GLfloat bar = 0.0f; bar <= m_cachedColumnCount; bar++) {
QMatrix4x4 modelMatrix;
QMatrix4x4 MVPMatrix;
QMatrix4x4 itModelMatrix;
- colPos = bar * m_cachedBarSpacing.width();
+ GLfloat colPos = bar * m_cachedBarSpacing.width();
modelMatrix.translate((m_rowWidth - colPos) / m_scaleFactor,
yFloorLinePosition, 0.0f);
modelMatrix.scale(gridLineScaler);
@@ -1696,31 +1904,31 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
itModelMatrix.inverted().transposed());
lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
- // Set shadow shader bindings
- QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
- lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ if (m_isOpenGLES) {
+ m_drawer->drawLine(lineShader);
} else {
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj);
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ // Set shadow shader bindings
+ QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
+ lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ } else {
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj);
+ }
}
-#else
- m_drawer->drawLine(lineShader);
-#endif
}
if (m_axisCacheY.segmentCount() > 0) {
// Wall lines: back wall
int gridLineCount = m_axisCacheY.gridLineCount();
- GLfloat zWallLinePosition = -columnScaleFactor + gridLineOffset;
+ GLfloat zWallLinePosition = -m_scaleZWithBackground + gridLineOffset;
if (m_zFlipped)
zWallLinePosition = -zWallLinePosition;
- gridLineScaler = QVector3D(rowScaleFactor, gridLineWidth, gridLineWidth);
+ gridLineScaler = QVector3D(m_scaleXWithBackground, gridLineWidth, gridLineWidth);
for (int line = 0; line < gridLineCount; line++) {
QMatrix4x4 modelMatrix;
QMatrix4x4 MVPMatrix;
@@ -1732,8 +1940,8 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
modelMatrix.scale(gridLineScaler);
itModelMatrix.scale(gridLineScaler);
if (m_zFlipped) {
- modelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f);
- itModelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f);
+ modelMatrix.rotate(m_xFlipRotation);
+ itModelMatrix.rotate(m_xFlipRotation);
}
MVPMatrix = projectionViewMatrix * modelMatrix;
@@ -1744,33 +1952,33 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
itModelMatrix.inverted().transposed());
lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
- // Set shadow shader bindings
- QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
- lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ if (m_isOpenGLES) {
+ m_drawer->drawLine(lineShader);
} else {
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj);
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ // Set shadow shader bindings
+ QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
+ lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ } else {
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj);
+ }
}
-#else
- m_drawer->drawLine(lineShader);
-#endif
}
// Wall lines: side wall
- GLfloat xWallLinePosition = -rowScaleFactor + gridLineOffset;
+ GLfloat xWallLinePosition = -m_scaleXWithBackground + gridLineOffset;
if (m_xFlipped)
xWallLinePosition = -xWallLinePosition;
if (m_xFlipped)
- lineRotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, -90.0f);
+ lineRotation = m_yRightAngleRotationNeg;
else
- lineRotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, 90.0f);
+ lineRotation = m_yRightAngleRotation;
- gridLineScaler = QVector3D(gridLineWidth, gridLineWidth, columnScaleFactor);
+ gridLineScaler = QVector3D(gridLineWidth, gridLineWidth, m_scaleZWithBackground);
for (int line = 0; line < gridLineCount; line++) {
QMatrix4x4 modelMatrix;
QMatrix4x4 MVPMatrix;
@@ -1792,76 +2000,27 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
itModelMatrix.inverted().transposed());
lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
- // Set shadow shader bindings
- QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
- lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ if (m_isOpenGLES) {
+ m_drawer->drawLine(lineShader);
} else {
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj);
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ // Set shadow shader bindings
+ QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
+ lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ } else {
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj);
+ }
}
-#else
- m_drawer->drawLine(lineShader);
-#endif
}
}
}
-
- Abstract3DRenderer::drawCustomItems(RenderingNormal, m_customItemShader, viewMatrix,
- projectionViewMatrix, depthProjectionViewMatrix,
- m_depthTexture, m_shadowQualityToShader);
-
- drawLabels(false, activeCamera, viewMatrix, projectionMatrix, rowScaleFactor,
- columnScaleFactor);
-
- // Handle selected bar label generation
- if (barSelectionFound) {
- // Print value of selected bar
- glDisable(GL_DEPTH_TEST);
- // Draw the selection label
- LabelItem &labelItem = selectionLabelItem();
- if (m_selectedBar != selectedBar || m_updateLabels || !labelItem.textureId()
- || m_selectionLabelDirty) {
- QString labelText = selectionLabel();
- if (labelText.isNull() || m_selectionLabelDirty) {
- labelText = m_selectedSeriesCache->itemLabel();
- setSelectionLabel(labelText);
- m_selectionLabelDirty = false;
- }
- m_drawer->generateLabelItem(labelItem, labelText);
- m_selectedBar = selectedBar;
- }
-
- Drawer::LabelPosition position =
- m_selectedBar->height() >= 0 ? Drawer::LabelOver : Drawer::LabelBelow;
-
- m_drawer->drawLabel(*selectedBar, labelItem, viewMatrix, projectionMatrix,
- zeroVector, identityQuaternion, selectedBar->height(),
- m_cachedSelectionMode, m_labelShader,
- m_labelObj, activeCamera, true, false, position);
-
- // Reset label update flag; they should have been updated when we get here
- m_updateLabels = false;
-
- glEnable(GL_DEPTH_TEST);
- } else {
- m_selectedBar = 0;
- }
-
- glDisable(GL_TEXTURE_2D);
- glDisable(GL_BLEND);
-
- // Release shader
- glUseProgram(0);
- m_selectionDirty = false;
}
void Bars3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCamera,
- const QMatrix4x4 &viewMatrix, const QMatrix4x4 &projectionMatrix,
- GLfloat rowScaleFactor, GLfloat columnScaleFactor) {
+ const QMatrix4x4 &viewMatrix, const QMatrix4x4 &projectionMatrix) {
ShaderHelper *shader = 0;
GLfloat alphaForValueSelection = labelValueAlpha / 255.0f;
GLfloat alphaForRowSelection = labelRowAlpha / 255.0f;
@@ -1873,7 +2032,6 @@ void Bars3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCamer
shader = m_labelShader;
shader->bind();
- glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
@@ -1894,8 +2052,8 @@ void Bars3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCamer
int labelCount = m_axisCacheY.labelCount();
GLfloat labelMarginXTrans = labelMargin;
GLfloat labelMarginZTrans = labelMargin;
- GLfloat labelXTrans = rowScaleFactor;
- GLfloat labelZTrans = columnScaleFactor;
+ GLfloat labelXTrans = m_scaleXWithBackground;
+ GLfloat labelZTrans = m_scaleZWithBackground;
QVector3D backLabelRotation(0.0f, -90.0f, 0.0f);
QVector3D sideLabelRotation(0.0f, 0.0f, 0.0f);
Qt::AlignmentFlag backAlignment = (m_xFlipped != m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight;
@@ -1999,10 +2157,8 @@ void Bars3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCamer
fractionCamY = activeCamera->yRotation() * labelAngleFraction;
fractionCamX = activeCamera->xRotation() * labelAngleFraction;
GLfloat labelYAdjustment = 0.005f;
- GLfloat scaledRowWidth = rowScaleFactor;
- GLfloat scaledColumnDepth = columnScaleFactor;
- GLfloat colPosValue = scaledRowWidth + labelMargin;
- GLfloat rowPosValue = scaledColumnDepth + labelMargin;
+ GLfloat colPosValue = m_scaleXWithBackground + labelMargin;
+ GLfloat rowPosValue = m_scaleZWithBackground + labelMargin;
GLfloat rowPos = 0.0f;
GLfloat colPos = 0.0f;
Qt::AlignmentFlag alignment = (m_xFlipped == m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight;
@@ -2296,14 +2452,8 @@ void Bars3DRenderer::updateAxisRange(QAbstract3DAxis::AxisOrientation orientatio
{
Abstract3DRenderer::updateAxisRange(orientation, min, max);
- if (orientation == QAbstract3DAxis::AxisOrientationY) {
- // Check if we have negative values
- if (min < 0)
- m_hasNegativeValues = true;
- else if (min >= 0)
- m_hasNegativeValues = false;
+ if (orientation == QAbstract3DAxis::AxisOrientationY)
calculateHeightAdjustment();
- }
}
void Bars3DRenderer::updateAxisReversed(QAbstract3DAxis::AxisOrientation orientation, bool enable)
@@ -2384,10 +2534,12 @@ void Bars3DRenderer::updateShadowQuality(QAbstract3DGraph::ShadowQuality quality
handleShadowQualityChange();
-#if !defined(QT_OPENGL_ES_2)
// Re-init depth buffer
updateDepthBuffer();
-#endif
+
+ // Redraw to handle both reflections and shadows on background
+ if (m_reflectionEnabled)
+ needRender();
}
void Bars3DRenderer::loadBackgroundMesh()
@@ -2398,6 +2550,8 @@ void Bars3DRenderer::loadBackgroundMesh()
void Bars3DRenderer::updateTextures()
{
+ Abstract3DRenderer::updateTextures();
+
// Drawer has changed; this flag needs to be checked when checking if we need to update labels
m_updateLabels = true;
}
@@ -2420,30 +2574,60 @@ void Bars3DRenderer::calculateSceneScalingFactors()
m_maxDimension = qMax(m_rowWidth, m_columnDepth);
m_scaleFactor = qMin((m_cachedColumnCount * (m_maxDimension / m_maxSceneSize)),
(m_cachedRowCount * (m_maxDimension / m_maxSceneSize)));
+
+ // Single bar scaling
m_scaleX = m_cachedBarThickness.width() / m_scaleFactor;
m_scaleZ = m_cachedBarThickness.height() / m_scaleFactor;
+
+ // Whole graph scale factors
+ m_xScaleFactor = m_rowWidth / m_scaleFactor;
+ m_zScaleFactor = m_columnDepth / m_scaleFactor;
+
+ if (m_requestedMargin < 0.0f) {
+ m_hBackgroundMargin = 0.0f;
+ m_vBackgroundMargin = 0.0f;
+ } else {
+ m_hBackgroundMargin = m_requestedMargin;
+ m_vBackgroundMargin = m_requestedMargin;
+ }
+
+ m_scaleXWithBackground = m_xScaleFactor + m_hBackgroundMargin;
+ m_scaleYWithBackground = 1.0f + m_vBackgroundMargin;
+ m_scaleZWithBackground = m_zScaleFactor + m_hBackgroundMargin;
+
+ updateCameraViewport();
+ updateCustomItemPositions();
}
void Bars3DRenderer::calculateHeightAdjustment()
{
+ float min = m_axisCacheY.min();
+ float max = m_axisCacheY.max();
GLfloat newAdjustment = 1.0f;
- GLfloat maxAbs = qFabs(m_axisCacheY.max());
-
- if (m_axisCacheY.max() < 0.0f) {
- m_heightNormalizer = GLfloat(qFabs(m_axisCacheY.min()) - qFabs(m_axisCacheY.max()));
- maxAbs = qFabs(m_axisCacheY.max()) - qFabs(m_axisCacheY.min());
+ m_actualFloorLevel = qBound(min, m_floorLevel, max);
+ GLfloat maxAbs = qFabs(max - m_actualFloorLevel);
+
+ // Check if we have negative values
+ if (min < m_actualFloorLevel)
+ m_hasNegativeValues = true;
+ else if (min >= m_actualFloorLevel)
+ m_hasNegativeValues = false;
+
+ if (max < m_actualFloorLevel) {
+ m_heightNormalizer = GLfloat(qFabs(min) - qFabs(max));
+ maxAbs = qFabs(max) - qFabs(min);
} else {
- m_heightNormalizer = GLfloat(m_axisCacheY.max() - m_axisCacheY.min());
+ m_heightNormalizer = GLfloat(max - min);
}
// Height fractions are used in gradient calculations and are therefore doubled
// Note that if max or min is exactly zero, we still consider it outside the range
- if (m_axisCacheY.max() <= 0.0f || m_axisCacheY.min() >= 0.0f) {
+ if (max <= m_actualFloorLevel || min >= m_actualFloorLevel) {
m_noZeroInRange = true;
m_gradientFraction = 2.0f;
} else {
m_noZeroInRange = false;
- GLfloat minAbs = qFabs(m_axisCacheY.min());
+ GLfloat minAbs = qFabs(min - m_actualFloorLevel);
m_gradientFraction = qMax(minAbs, maxAbs) / m_heightNormalizer * 2.0f;
}
@@ -2551,13 +2735,13 @@ void Bars3DRenderer::updateSlicingActive(bool isSlicing)
m_cachedIsSlicingActivated = isSlicing;
- if (!m_cachedIsSlicingActivated)
- initSelectionBuffer(); // We need to re-init selection buffer in case there has been a resize
+ if (!m_cachedIsSlicingActivated) {
+ // We need to re-init selection buffer in case there has been a resize
+ initSelectionBuffer();
+ initCursorPositionBuffer();
+ }
-#if !defined(QT_OPENGL_ES_2)
updateDepthBuffer(); // Re-init depth buffer as well
-#endif
-
m_selectionDirty = true;
}
@@ -2598,32 +2782,35 @@ void Bars3DRenderer::initSelectionBuffer()
m_selectionDepthBuffer);
}
-#if !defined(QT_OPENGL_ES_2)
void Bars3DRenderer::initDepthShader()
{
- if (m_depthShader)
- delete m_depthShader;
- m_depthShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexDepth"),
- QStringLiteral(":/shaders/fragmentDepth"));
- m_depthShader->initialize();
+ if (!m_isOpenGLES) {
+ if (m_depthShader)
+ delete m_depthShader;
+ m_depthShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexDepth"),
+ QStringLiteral(":/shaders/fragmentDepth"));
+ m_depthShader->initialize();
+ }
}
void Bars3DRenderer::updateDepthBuffer()
{
- m_textureHelper->deleteTexture(&m_depthTexture);
+ if (!m_isOpenGLES) {
+ m_textureHelper->deleteTexture(&m_depthTexture);
- if (m_primarySubViewport.size().isEmpty())
- return;
+ if (m_primarySubViewport.size().isEmpty())
+ return;
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
- m_depthTexture = m_textureHelper->createDepthTextureFrameBuffer(m_primarySubViewport.size(),
- m_depthFrameBuffer,
- m_shadowQualityMultiplier);
- if (!m_depthTexture)
- lowerShadowQuality();
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ m_depthTexture =
+ m_textureHelper->createDepthTextureFrameBuffer(m_primarySubViewport.size(),
+ m_depthFrameBuffer,
+ m_shadowQualityMultiplier);
+ if (!m_depthTexture)
+ lowerShadowQuality();
+ }
}
}
-#endif
void Bars3DRenderer::initBackgroundShaders(const QString &vertexShader,
const QString &fragmentShader)
@@ -2634,14 +2821,6 @@ void Bars3DRenderer::initBackgroundShaders(const QString &vertexShader,
m_backgroundShader->initialize();
}
-void Bars3DRenderer::initLabelShaders(const QString &vertexShader, const QString &fragmentShader)
-{
- if (m_labelShader)
- delete m_labelShader;
- m_labelShader = new ShaderHelper(this, vertexShader, fragmentShader);
- m_labelShader->initialize();
-}
-
QVector3D Bars3DRenderer::convertPositionToTranslation(const QVector3D &position, bool isAbsolute)
{
float xTrans = 0.0f;
@@ -2649,15 +2828,15 @@ QVector3D Bars3DRenderer::convertPositionToTranslation(const QVector3D &position
float zTrans = 0.0f;
if (!isAbsolute) {
// Convert row and column to translation on graph
- xTrans = (((position.x() + 0.5f) * m_cachedBarSpacing.width()) - m_rowWidth)
- / m_scaleFactor;
- zTrans = (m_columnDepth - ((position.z() + 0.5f) * m_cachedBarSpacing.height()))
- / m_scaleFactor;
+ xTrans = (((position.x() - m_axisCacheX.min() + 0.5f) * m_cachedBarSpacing.width())
+ - m_rowWidth) / m_scaleFactor;
+ zTrans = (m_columnDepth - ((position.z() - m_axisCacheZ.min() + 0.5f)
+ * m_cachedBarSpacing.height())) / m_scaleFactor;
yTrans = m_axisCacheY.positionAt(position.y());
} else {
- xTrans = position.x() * m_scaleX;
+ xTrans = position.x() * m_xScaleFactor;
yTrans = position.y() + m_backgroundAdjustment;
- zTrans = position.z() * m_scaleZ;
+ zTrans = position.z() * -m_zScaleFactor;
}
return QVector3D(xTrans, yTrans, zTrans);
}
@@ -2667,4 +2846,18 @@ void Bars3DRenderer::updateAspectRatio(float ratio)
Q_UNUSED(ratio)
}
+void Bars3DRenderer::updateFloorLevel(float level)
+{
+ foreach (SeriesRenderCache *cache, m_renderCacheList)
+ cache->setDataDirty(true);
+ m_floorLevel = level;
+ calculateHeightAdjustment();
+}
+
+void Bars3DRenderer::updateMargin(float margin)
+{
+ Abstract3DRenderer::updateMargin(margin);
+ calculateSceneScalingFactors();
+}
+
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/engine/bars3drenderer_p.h b/src/datavisualization/engine/bars3drenderer_p.h
index 3a0ab3b8..094b8fd9 100644
--- a/src/datavisualization/engine/bars3drenderer_p.h
+++ b/src/datavisualization/engine/bars3drenderer_p.h
@@ -66,9 +66,7 @@ private:
ShaderHelper *m_depthShader;
ShaderHelper *m_selectionShader;
ShaderHelper *m_backgroundShader;
- ShaderHelper *m_labelShader;
GLuint m_bgrTexture;
- GLuint m_depthTexture;
GLuint m_selectionTexture;
GLuint m_depthFrameBuffer;
GLuint m_selectionFrameBuffer;
@@ -86,7 +84,6 @@ private:
GLfloat m_scaleFactor;
GLfloat m_maxSceneSize;
QPoint m_visualSelectedBarPos;
- bool m_resetCameraBaseOrientation;
QPoint m_selectedBarPos;
BarSeriesRenderCache *m_selectedSeriesCache;
BarRenderItem m_dummyBarRenderItem;
@@ -100,6 +97,10 @@ private:
bool m_haveUniformColorSeries;
bool m_haveGradientSeries;
float m_zeroPosition;
+ float m_xScaleFactor;
+ float m_zScaleFactor;
+ float m_floorLevel;
+ float m_actualFloorLevel;
public:
explicit Bars3DRenderer(Bars3DController *controller);
@@ -116,9 +117,13 @@ public:
QVector3D convertPositionToTranslation(const QVector3D &position, bool isAbsolute);
void updateAspectRatio(float ratio);
+ void updateFloorLevel(float level);
+ void updateMargin(float margin);
protected:
virtual void initializeOpenGL();
+ virtual void fixCameraTarget(QVector3D &target);
+ virtual void getVisibleItemBounds(QVector3D &minBounds, QVector3D &maxBounds);
public slots:
void updateMultiSeriesScaling(bool uniform);
@@ -146,18 +151,25 @@ private:
void drawSlicedScene();
void drawScene(GLuint defaultFboHandle);
void drawLabels(bool drawSelection, const Q3DCamera *activeCamera,
- const QMatrix4x4 &viewMatrix, const QMatrix4x4 &projectionMatrix,
- GLfloat rowScaleFactor, GLfloat columnScaleFactor);
+ const QMatrix4x4 &viewMatrix, const QMatrix4x4 &projectionMatrix);
+
+ bool drawBars(BarRenderItem **selectedBar, const QMatrix4x4 &depthProjectionViewMatrix,
+ const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &viewMatrix,
+ GLint startRow, GLint stopRow, GLint stepRow,
+ GLint startBar, GLint stopBar, GLint stepBar, GLfloat reflection = 1.0f);
+ void drawBackground(GLfloat backgroundRotation, const QMatrix4x4 &depthProjectionViewMatrix,
+ const QMatrix4x4 &projectionViewMatrix, const QMatrix4x4 &viewMatrix,
+ bool reflectingDraw = false, bool drawingSelectionBuffer = false);
+ void drawGridLines(const QMatrix4x4 &depthProjectionViewMatrix,
+ const QMatrix4x4 &projectionViewMatrix,
+ const QMatrix4x4 &viewMatrix);
void loadBackgroundMesh();
void initSelectionShader();
void initBackgroundShaders(const QString &vertexShader, const QString &fragmentShader);
- void initLabelShaders(const QString &vertexShader, const QString &fragmentShader);
void initSelectionBuffer();
-#if !defined(QT_OPENGL_ES_2)
void initDepthShader();
void updateDepthBuffer();
-#endif
void calculateSceneScalingFactors();
void calculateHeightAdjustment();
Abstract3DController::SelectionType isSelected(int row, int bar,
diff --git a/src/datavisualization/engine/drawer.cpp b/src/datavisualization/engine/drawer.cpp
index b8726840..d4352702 100644
--- a/src/datavisualization/engine/drawer.cpp
+++ b/src/datavisualization/engine/drawer.cpp
@@ -69,10 +69,9 @@ Drawer::~Drawer()
void Drawer::initializeOpenGL()
{
- if (!m_textureHelper) {
- initializeOpenGLFunctions();
+ initializeOpenGLFunctions();
+ if (!m_textureHelper)
m_textureHelper = new TextureHelper();
- }
}
void Drawer::setTheme(Q3DTheme *theme)
@@ -93,8 +92,11 @@ QFont Drawer::font() const
}
void Drawer::drawObject(ShaderHelper *shader, AbstractObjectHelper *object, GLuint textureId,
- GLuint depthTextureId)
+ GLuint depthTextureId, GLuint textureId3D)
{
+#if defined(QT_OPENGL_ES_2)
+ Q_UNUSED(textureId3D)
+#endif
if (textureId) {
// Activate texture
glActiveTexture(GL_TEXTURE0);
@@ -108,6 +110,14 @@ void Drawer::drawObject(ShaderHelper *shader, AbstractObjectHelper *object, GLui
glBindTexture(GL_TEXTURE_2D, depthTextureId);
shader->setUniformValue(shader->shadow(), 1);
}
+#if !defined(QT_OPENGL_ES_2)
+ if (textureId3D) {
+ // Activate texture
+ glActiveTexture(GL_TEXTURE2);
+ glBindTexture(GL_TEXTURE_3D, textureId3D);
+ shader->setUniformValue(shader->texture(), 2);
+ }
+#endif
// 1st attribute buffer : vertices
glEnableVertexAttribArray(shader->posAtt());
@@ -115,14 +125,18 @@ void Drawer::drawObject(ShaderHelper *shader, AbstractObjectHelper *object, GLui
glVertexAttribPointer(shader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
// 2nd attribute buffer : normals
- glEnableVertexAttribArray(shader->normalAtt());
- glBindBuffer(GL_ARRAY_BUFFER, object->normalBuf());
- glVertexAttribPointer(shader->normalAtt(), 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
+ if (shader->normalAtt() >= 0) {
+ glEnableVertexAttribArray(shader->normalAtt());
+ glBindBuffer(GL_ARRAY_BUFFER, object->normalBuf());
+ glVertexAttribPointer(shader->normalAtt(), 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
+ }
// 3rd attribute buffer : UVs
- glEnableVertexAttribArray(shader->uvAtt());
- glBindBuffer(GL_ARRAY_BUFFER, object->uvBuf());
- glVertexAttribPointer(shader->uvAtt(), 2, GL_FLOAT, GL_FALSE, 0, (void*)0);
+ if (shader->uvAtt() >= 0) {
+ glEnableVertexAttribArray(shader->uvAtt());
+ glBindBuffer(GL_ARRAY_BUFFER, object->uvBuf());
+ glVertexAttribPointer(shader->uvAtt(), 2, GL_FLOAT, GL_FALSE, 0, (void*)0);
+ }
// Index buffer
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, object->elementBuf());
@@ -134,11 +148,19 @@ void Drawer::drawObject(ShaderHelper *shader, AbstractObjectHelper *object, GLui
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
- glDisableVertexAttribArray(shader->uvAtt());
- glDisableVertexAttribArray(shader->normalAtt());
+ if (shader->uvAtt() >= 0)
+ glDisableVertexAttribArray(shader->uvAtt());
+ if (shader->normalAtt() >= 0)
+ glDisableVertexAttribArray(shader->normalAtt());
glDisableVertexAttribArray(shader->posAtt());
// Release textures
+#if !defined(QT_OPENGL_ES_2)
+ if (textureId3D) {
+ glActiveTexture(GL_TEXTURE2);
+ glBindTexture(GL_TEXTURE_3D, 0);
+ }
+#endif
if (depthTextureId) {
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, 0);
@@ -206,13 +228,27 @@ void Drawer::drawPoint(ShaderHelper *shader)
glDisableVertexAttribArray(shader->posAtt());
}
-void Drawer::drawPoints(ShaderHelper *shader, ScatterPointBufferHelper *object)
+void Drawer::drawPoints(ShaderHelper *shader, ScatterPointBufferHelper *object, GLuint textureId)
{
+ if (textureId) {
+ // Activate texture
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, textureId);
+ shader->setUniformValue(shader->texture(), 0);
+ }
+
// 1st attribute buffer : vertices
glEnableVertexAttribArray(shader->posAtt());
glBindBuffer(GL_ARRAY_BUFFER, object->pointBuf());
glVertexAttribPointer(shader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
+ // 2nd attribute buffer : UVs
+ if (textureId) {
+ glEnableVertexAttribArray(shader->uvAtt());
+ glBindBuffer(GL_ARRAY_BUFFER, object->uvBuf());
+ glVertexAttribPointer(shader->uvAtt(), 2, GL_FLOAT, GL_FALSE, 0, (void*)0);
+ }
+
// Draw the points
glDrawArrays(GL_POINTS, 0, object->indexCount());
@@ -220,6 +256,12 @@ void Drawer::drawPoints(ShaderHelper *shader, ScatterPointBufferHelper *object)
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDisableVertexAttribArray(shader->posAtt());
+
+ if (textureId) {
+ glDisableVertexAttribArray(shader->uvAtt());
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, 0);
+ }
}
void Drawer::drawLine(ShaderHelper *shader)
diff --git a/src/datavisualization/engine/drawer_p.h b/src/datavisualization/engine/drawer_p.h
index ffcea315..0c96724c 100644
--- a/src/datavisualization/engine/drawer_p.h
+++ b/src/datavisualization/engine/drawer_p.h
@@ -75,11 +75,11 @@ public:
inline GLfloat scaledFontSize() const { return m_scaledFontSize; }
void drawObject(ShaderHelper *shader, AbstractObjectHelper *object, GLuint textureId = 0,
- GLuint depthTextureId = 0);
+ GLuint depthTextureId = 0, GLuint textureId3D = 0);
void drawSelectionObject(ShaderHelper *shader, AbstractObjectHelper *object);
void drawSurfaceGrid(ShaderHelper *shader, SurfaceObject *object);
void drawPoint(ShaderHelper *shader);
- void drawPoints(ShaderHelper *shader, ScatterPointBufferHelper *object);
+ void drawPoints(ShaderHelper *shader, ScatterPointBufferHelper *object, GLuint textureId);
void drawLine(ShaderHelper *shader);
void drawLabel(const AbstractRenderItem &item, const LabelItem &labelItem,
const QMatrix4x4 &viewmatrix, const QMatrix4x4 &projectionmatrix,
diff --git a/src/datavisualization/engine/engine.pri b/src/datavisualization/engine/engine.pri
index 96fa7fa9..60703e96 100644
--- a/src/datavisualization/engine/engine.pri
+++ b/src/datavisualization/engine/engine.pri
@@ -57,3 +57,5 @@ SOURCES += $$PWD/qabstract3dgraph.cpp \
RESOURCES += engine/engine.qrc
OTHER_FILES += $$PWD/meshes/* $$PWD/shaders/*
+
+INCLUDEPATH += $$PWD
diff --git a/src/datavisualization/engine/engine.qrc b/src/datavisualization/engine/engine.qrc
index 673b6ee0..0ef169c9 100644
--- a/src/datavisualization/engine/engine.qrc
+++ b/src/datavisualization/engine/engine.qrc
@@ -57,5 +57,18 @@
<file alias="fragmentTexture">shaders/texture.frag</file>
<file alias="fragmentTextureES2">shaders/texture_ES2.frag</file>
<file alias="vertexTexture">shaders/texture.vert</file>
+ <file alias="fragmentTexturedSurfaceShadowFlat">shaders/surfaceTexturedShadowFlat.frag</file>
+ <file alias="fragmentSurfaceTexturedFlat">shaders/surfaceTexturedFlat.frag</file>
+ <file alias="fragmentTexture3D">shaders/texture3d.frag</file>
+ <file alias="vertexTexture3D">shaders/texture3d.vert</file>
+ <file alias="fragmentTexture3DSlice">shaders/texture3dslice.frag</file>
+ <file alias="vertexShadowNoMatrices">shaders/shadowNoMatrices.vert</file>
+ <file alias="vertexNoMatrices">shaders/defaultNoMatrices.vert</file>
+ <file alias="fragmentTexture3DLowDef">shaders/texture3dlowdef.frag</file>
+ <file alias="vertexPointES2_UV">shaders/point_ES2_UV.vert</file>
+ <file alias="fragment3DSliceFrames">shaders/3dsliceframes.frag</file>
+ <file alias="vertexPosition">shaders/position.vert</file>
+ <file alias="fragmentPositionMap">shaders/positionmap.frag</file>
+ <file alias="fragmentTexturedSurfaceShadow">shaders/surfaceTexturedShadow.frag</file>
</qresource>
</RCC>
diff --git a/src/datavisualization/engine/q3dbars.cpp b/src/datavisualization/engine/q3dbars.cpp
index bb7aca89..d42c11b7 100644
--- a/src/datavisualization/engine/q3dbars.cpp
+++ b/src/datavisualization/engine/q3dbars.cpp
@@ -331,6 +331,26 @@ QBar3DSeries *Q3DBars::selectedSeries() const
}
/*!
+ * \property Q3DBars::floorLevel
+ *
+ * The desired floor level for the bar graph in Y-axis data coordinates.
+ * The actual floor level cannot go below Y-axis minimum or above Y-axis maximum.
+ * Defaults to zero.
+ */
+void Q3DBars::setFloorLevel(float level)
+{
+ if (level != floorLevel()) {
+ dptr()->m_shared->setFloorLevel(level);
+ emit floorLevelChanged(level);
+ }
+}
+
+float Q3DBars::floorLevel() const
+{
+ return dptrc()->m_shared->floorLevel();
+}
+
+/*!
* Adds \a axis to the graph. The axes added via addAxis are not yet taken to use,
* addAxis is simply used to give the ownership of the \a axis to the graph.
* The \a axis must not be null or added to another graph.
diff --git a/src/datavisualization/engine/q3dbars.h b/src/datavisualization/engine/q3dbars.h
index 7f9c981f..df1c846e 100644
--- a/src/datavisualization/engine/q3dbars.h
+++ b/src/datavisualization/engine/q3dbars.h
@@ -40,6 +40,7 @@ class QT_DATAVISUALIZATION_EXPORT Q3DBars : public QAbstract3DGraph
Q_PROPERTY(QValue3DAxis *valueAxis READ valueAxis WRITE setValueAxis NOTIFY valueAxisChanged)
Q_PROPERTY(QBar3DSeries *primarySeries READ primarySeries WRITE setPrimarySeries NOTIFY primarySeriesChanged)
Q_PROPERTY(QBar3DSeries *selectedSeries READ selectedSeries NOTIFY selectedSeriesChanged)
+ Q_PROPERTY(float floorLevel READ floorLevel WRITE setFloorLevel NOTIFY floorLevelChanged)
public:
explicit Q3DBars(const QSurfaceFormat *format = 0, QWindow *parent = 0);
@@ -75,6 +76,8 @@ public:
QList<QAbstract3DAxis *> axes() const;
QBar3DSeries *selectedSeries() const;
+ void setFloorLevel(float level);
+ float floorLevel() const;
signals:
void multiSeriesUniformChanged(bool uniform);
@@ -86,6 +89,7 @@ signals:
void valueAxisChanged(QValue3DAxis *axis);
void primarySeriesChanged(QBar3DSeries *series);
void selectedSeriesChanged(QBar3DSeries *series);
+ void floorLevelChanged(float level);
private:
Q3DBarsPrivate *dptr();
diff --git a/src/datavisualization/engine/q3dcamera.cpp b/src/datavisualization/engine/q3dcamera.cpp
index 06fd570c..897c3779 100644
--- a/src/datavisualization/engine/q3dcamera.cpp
+++ b/src/datavisualization/engine/q3dcamera.cpp
@@ -110,8 +110,36 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
/*!
* \qmlproperty float Camera3D::zoomLevel
*
- * This property contains the the camera zoom level in percentage. 100.0 means there is no zoom
- * in or out set in the camera.
+ * This property contains the the camera zoom level in percentage. The default value of \c{100.0}
+ * means there is no zoom in or out set in the camera.
+ * The value is limited within the bounds defined by minZoomLevel and maxZoomLevel properties.
+ *
+ * \sa minZoomLevel, maxZoomLevel
+ */
+
+/*!
+ * \qmlproperty float Camera3D::minZoomLevel
+ *
+ * This property contains the the minimum allowed camera zoom level.
+ * If the new minimum level is more than the existing maximum level, the maximum level is
+ * adjusted to the new minimum as well.
+ * If current zoomLevel is outside the new bounds, it is adjusted as well.
+ * The minZoomLevel cannot be set below \c{1.0}.
+ * Defaults to \c{10.0}.
+ *
+ * \sa zoomLevel, maxZoomLevel
+ */
+
+/*!
+ * \qmlproperty float Camera3D::maxZoomLevel
+ *
+ * This property contains the the maximum allowed camera zoom level.
+ * If the new maximum level is less than the existing minimum level, the minimum level is
+ * adjusted to the new maximum as well.
+ * If current zoomLevel is outside the new bounds, it is adjusted as well.
+ * Defaults to \c{500.0f}.
+ *
+ * \sa zoomLevel, minZoomLevel
*/
/*!
@@ -137,6 +165,19 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
*/
/*!
+ * \qmlproperty vector3d Camera3D::target
+ * \since QtDataVisualization 1.2
+ *
+ * Holds the camera \a target as a vector3d. Defaults to \c {vector3d(0.0, 0.0, 0.0)}.
+ *
+ * Valid coordinate values are between \c{-1.0...1.0}, where the edge values indicate
+ * the edges of the corresponding axis range. Any values outside this range are clamped to the edge.
+ *
+ * \note For bar graphs, the Y-coordinate is ignored and camera always targets a point on
+ * the horizontal background.
+ */
+
+/*!
* Constructs a new 3D camera with position set to origin, up direction facing towards the Y-axis
* and looking at origin by default. An optional \a parent parameter can be given and is then passed
* to QObject constructor.
@@ -160,22 +201,11 @@ Q3DCamera::~Q3DCamera()
*/
void Q3DCamera::copyValuesFrom(const Q3DObject &source)
{
- Q3DObject::copyValuesFrom(source);
+ // Note: Do not copy values from parent, as we are handling the position internally
const Q3DCamera &sourceCamera = static_cast<const Q3DCamera &>(source);
- d_ptr->m_target.setX(sourceCamera.d_ptr->m_target.x());
- d_ptr->m_target.setY(sourceCamera.d_ptr->m_target.y());
- d_ptr->m_target.setZ(sourceCamera.d_ptr->m_target.z());
-
- d_ptr->m_up.setX(sourceCamera.d_ptr->m_up.x());
- d_ptr->m_up.setY(sourceCamera.d_ptr->m_up.y());
- d_ptr->m_up.setZ(sourceCamera.d_ptr->m_up.z());
-
- float *values = new float[16];
- sourceCamera.d_ptr->m_viewMatrix.copyDataTo(values);
- d_ptr->m_viewMatrix = QMatrix4x4(values);
- delete[] values;
+ d_ptr->m_requestedTarget = sourceCamera.d_ptr->m_requestedTarget;
d_ptr->m_xRotation = sourceCamera.d_ptr->m_xRotation;
d_ptr->m_yRotation = sourceCamera.d_ptr->m_yRotation;
@@ -189,6 +219,8 @@ void Q3DCamera::copyValuesFrom(const Q3DObject &source)
d_ptr->m_wrapYRotation = sourceCamera.d_ptr->m_wrapYRotation;
d_ptr->m_zoomLevel = sourceCamera.d_ptr->m_zoomLevel;
+ d_ptr->m_minZoomLevel = sourceCamera.d_ptr->m_minZoomLevel;
+ d_ptr->m_maxZoomLevel = sourceCamera.d_ptr->m_maxZoomLevel;
d_ptr->m_activePreset = sourceCamera.d_ptr->m_activePreset;
}
@@ -389,6 +421,9 @@ void Q3DCamera::setCameraPreset(CameraPreset preset)
break;
}
+ // All presets target the center of the graph
+ setTarget(zeroVector);
+
if (d_ptr->m_activePreset != preset) {
d_ptr->m_activePreset = preset;
setDirty(true);
@@ -399,8 +434,11 @@ void Q3DCamera::setCameraPreset(CameraPreset preset)
/*!
* \property Q3DCamera::zoomLevel
*
- * This property contains the the camera zoom level in percentage. \c 100.0f means there is no zoom
- * in or out set in the camera.
+ * This property contains the the camera zoom level in percentage. The default value of \c{100.0f}
+ * means there is no zoom in or out set in the camera.
+ * The value is limited within the bounds defined by minZoomLevel and maxZoomLevel properties.
+ *
+ * \sa minZoomLevel, maxZoomLevel
*/
float Q3DCamera::zoomLevel() const
{
@@ -409,10 +447,73 @@ float Q3DCamera::zoomLevel() const
void Q3DCamera::setZoomLevel(float zoomLevel)
{
- if (d_ptr->m_zoomLevel != zoomLevel) {
- d_ptr->m_zoomLevel = zoomLevel;
+ float newZoomLevel = qBound(d_ptr->m_minZoomLevel, zoomLevel, d_ptr->m_maxZoomLevel);
+
+ if (d_ptr->m_zoomLevel != newZoomLevel) {
+ d_ptr->m_zoomLevel = newZoomLevel;
+ setDirty(true);
+ emit zoomLevelChanged(newZoomLevel);
+ }
+}
+
+/*!
+ * \property Q3DCamera::minZoomLevel
+ *
+ * This property contains the the minimum allowed camera zoom level.
+ * If the new minimum level is more than the existing maximum level, the maximum level is
+ * adjusted to the new minimum as well.
+ * If current zoomLevel is outside the new bounds, it is adjusted as well.
+ * The minZoomLevel cannot be set below \c{1.0f}.
+ * Defaults to \c{10.0f}.
+ *
+ * \sa zoomLevel, maxZoomLevel
+ */
+float Q3DCamera::minZoomLevel() const
+{
+ return d_ptr->m_minZoomLevel;
+}
+
+void Q3DCamera::setMinZoomLevel(float zoomLevel)
+{
+ // Don't allow minimum to be below one, as that can cause zoom to break.
+ float newMinLevel = qMax(zoomLevel, 1.0f);
+ if (d_ptr->m_minZoomLevel != newMinLevel) {
+ d_ptr->m_minZoomLevel = newMinLevel;
+ if (d_ptr->m_maxZoomLevel < newMinLevel)
+ setMaxZoomLevel(newMinLevel);
+ setZoomLevel(d_ptr->m_zoomLevel);
setDirty(true);
- emit zoomLevelChanged(zoomLevel);
+ emit minZoomLevelChanged(newMinLevel);
+ }
+}
+
+/*!
+ * \property Q3DCamera::maxZoomLevel
+ *
+ * This property contains the the maximum allowed camera zoom level.
+ * If the new maximum level is less than the existing minimum level, the minimum level is
+ * adjusted to the new maximum as well.
+ * If current zoomLevel is outside the new bounds, it is adjusted as well.
+ * Defaults to \c{500.0f}.
+ *
+ * \sa zoomLevel, minZoomLevel
+ */
+float Q3DCamera::maxZoomLevel() const
+{
+ return d_ptr->m_maxZoomLevel;
+}
+
+void Q3DCamera::setMaxZoomLevel(float zoomLevel)
+{
+ // Don't allow maximum to be below one, as that can cause zoom to break.
+ float newMaxLevel = qMax(zoomLevel, 1.0f);
+ if (d_ptr->m_maxZoomLevel != newMaxLevel) {
+ d_ptr->m_maxZoomLevel = newMaxLevel;
+ if (d_ptr->m_minZoomLevel > newMaxLevel)
+ setMinZoomLevel(newMaxLevel);
+ setZoomLevel(d_ptr->m_zoomLevel);
+ setDirty(true);
+ emit maxZoomLevelChanged(newMaxLevel);
}
}
@@ -459,16 +560,61 @@ void Q3DCamera::setWrapYRotation(bool isEnabled)
/*!
* Utility function that sets the camera rotations and distance.\a horizontal and \a vertical
* define the camera rotations to be used.
- * Optional \a zoom parameter can be given to set the zoom percentage of the camera in range of
- * \c{10.0f - 500.0f}.
+ * Optional \a zoom parameter can be given to set the zoom percentage of the camera within
+ * the bounds defined by minZoomLevel and maxZoomLevel properties.
*/
void Q3DCamera::setCameraPosition(float horizontal, float vertical, float zoom)
{
- setZoomLevel(qBound(10.0f, zoom, 500.0f));
+ setZoomLevel(zoom);
setXRotation(horizontal);
setYRotation(vertical);
}
+/*!
+ * \property Q3DCamera::target
+ * \since QtDataVisualization 1.2
+ *
+ * Holds the camera \a target as a QVector3D. Defaults to \c {QVector3D(0.0, 0.0, 0.0)}.
+ *
+ * Valid coordinate values are between \c{-1.0...1.0}, where the edge values indicate
+ * the edges of the corresponding axis range. Any values outside this range are clamped to the edge.
+ *
+ * \note For bar graphs, the Y-coordinate is ignored and camera always targets a point on
+ * the horizontal background.
+ */
+QVector3D Q3DCamera::target() const
+{
+ return d_ptr->m_requestedTarget;
+}
+
+void Q3DCamera::setTarget(const QVector3D &target)
+{
+ QVector3D newTarget = target;
+
+ if (newTarget.x() < -1.0f)
+ newTarget.setX(-1.0f);
+ else if (newTarget.x() > 1.0f)
+ newTarget.setX(1.0f);
+
+ if (newTarget.y() < -1.0f)
+ newTarget.setY(-1.0f);
+ else if (newTarget.y() > 1.0f)
+ newTarget.setY(1.0f);
+
+ if (newTarget.z() < -1.0f)
+ newTarget.setZ(-1.0f);
+ else if (newTarget.z() > 1.0f)
+ newTarget.setZ(1.0f);
+
+ if (d_ptr->m_requestedTarget != newTarget) {
+ if (d_ptr->m_activePreset != CameraPresetNone)
+ d_ptr->m_activePreset = CameraPresetNone;
+ d_ptr->m_requestedTarget = newTarget;
+ setDirty(true);
+ emit targetChanged(newTarget);
+ }
+}
+
Q3DCameraPrivate::Q3DCameraPrivate(Q3DCamera *q) :
q_ptr(q),
m_isViewMatrixUpdateActive(true),
@@ -479,6 +625,8 @@ Q3DCameraPrivate::Q3DCameraPrivate(Q3DCamera *q) :
m_maxXRotation(180.0f),
m_maxYRotation(90.0f),
m_zoomLevel(100.0f),
+ m_minZoomLevel(10.0f),
+ m_maxZoomLevel(500.0f),
m_wrapXRotation(true),
m_wrapYRotation(false),
m_activePreset(Q3DCamera::CameraPresetNone)
@@ -645,9 +793,9 @@ void Q3DCameraPrivate::updateViewMatrix(float zoomAdjustment)
QMatrix4x4 viewMatrix;
// Apply to view matrix
- viewMatrix.lookAt(q_ptr->position(), m_target, m_up);
+ viewMatrix.lookAt(q_ptr->position(), m_actualTarget, m_up);
// Compensate for translation (if d_ptr->m_target is off origin)
- viewMatrix.translate(m_target.x(), m_target.y(), m_target.z());
+ viewMatrix.translate(m_actualTarget.x(), m_actualTarget.y(), m_actualTarget.z());
// Apply rotations
// Handle x and z rotation when y -angle is other than 0
viewMatrix.rotate(m_xRotation, 0, qCos(qDegreesToRadians(m_yRotation)),
@@ -657,7 +805,7 @@ void Q3DCameraPrivate::updateViewMatrix(float zoomAdjustment)
// handle zoom by scaling
viewMatrix.scale(zoom / 100.0f);
// Compensate for translation (if d_ptr->m_target is off origin)
- viewMatrix.translate(-m_target.x(), -m_target.y(), -m_target.z());
+ viewMatrix.translate(-m_actualTarget.x(), -m_actualTarget.y(), -m_actualTarget.z());
setViewMatrix(viewMatrix);
}
@@ -713,9 +861,9 @@ void Q3DCameraPrivate::setBaseOrientation(const QVector3D &basePosition,
const QVector3D &target,
const QVector3D &baseUp)
{
- if (q_ptr->position() != basePosition || m_target != target || m_up != baseUp) {
+ if (q_ptr->position() != basePosition || m_actualTarget != target || m_up != baseUp) {
q_ptr->setPosition(basePosition);
- m_target = target;
+ m_actualTarget = target;
m_up = baseUp;
q_ptr->setDirty(true);
}
@@ -737,20 +885,33 @@ QVector3D Q3DCameraPrivate::calculatePositionRelativeToCamera(const QVector3D &r
float distanceModifier) const
{
// Move the position with camera
- GLfloat radiusFactor = cameraDistance * (1.5f + distanceModifier);
- GLfloat xAngle;
- GLfloat yAngle;
+ const float radiusFactor = cameraDistance * (1.5f + distanceModifier);
+ float xAngle;
+ float yAngle;
+
if (!fixedRotation) {
xAngle = qDegreesToRadians(m_xRotation);
- yAngle = qDegreesToRadians(m_yRotation);
+ float yRotation = m_yRotation;
+ // Light must not be paraller to eye vector, so fudge the y rotation a bit.
+ // Note: This needs redoing if we ever allow arbitrary light positioning.
+ const float yMargin = 0.1f; // Smaller margins cause weird shadow artifacts on tops of bars
+ const float absYRotation = qAbs(yRotation);
+ if (absYRotation < 90.0f + yMargin && absYRotation > 90.0f - yMargin) {
+ if (yRotation < 0.0f)
+ yRotation = -90.0f + yMargin;
+ else
+ yRotation = 90.0f - yMargin;
+ }
+ yAngle = qDegreesToRadians(yRotation);
} else {
xAngle = qDegreesToRadians(fixedRotation);
yAngle = 0;
}
- GLfloat radius = (radiusFactor + relativePosition.y()); // set radius to match the highest height of the position
- GLfloat zPos = radius * qCos(xAngle) * qCos(yAngle);
- GLfloat xPos = radius * qSin(xAngle) * qCos(yAngle);
- GLfloat yPos = (radiusFactor + relativePosition.y()) * qSin(yAngle);
+ // Set radius to match the highest height of the position
+ const float radius = (radiusFactor + relativePosition.y());
+ const float zPos = radius * qCos(xAngle) * qCos(yAngle);
+ const float xPos = radius * qSin(xAngle) * qCos(yAngle);
+ const float yPos = radius * qSin(yAngle);
// Keep in the set position in relation to camera
return QVector3D(-xPos + relativePosition.x(),
diff --git a/src/datavisualization/engine/q3dcamera.h b/src/datavisualization/engine/q3dcamera.h
index e9ad6dd2..5b539ccf 100644
--- a/src/datavisualization/engine/q3dcamera.h
+++ b/src/datavisualization/engine/q3dcamera.h
@@ -35,6 +35,9 @@ class QT_DATAVISUALIZATION_EXPORT Q3DCamera : public Q3DObject
Q_PROPERTY(CameraPreset cameraPreset READ cameraPreset WRITE setCameraPreset NOTIFY cameraPresetChanged)
Q_PROPERTY(bool wrapXRotation READ wrapXRotation WRITE setWrapXRotation NOTIFY wrapXRotationChanged)
Q_PROPERTY(bool wrapYRotation READ wrapYRotation WRITE setWrapYRotation NOTIFY wrapYRotationChanged)
+ Q_PROPERTY(QVector3D target READ target WRITE setTarget NOTIFY targetChanged REVISION 1)
+ Q_PROPERTY(float minZoomLevel READ minZoomLevel WRITE setMinZoomLevel NOTIFY minZoomLevelChanged REVISION 1)
+ Q_PROPERTY(float maxZoomLevel READ maxZoomLevel WRITE setMaxZoomLevel NOTIFY maxZoomLevelChanged REVISION 1)
public:
enum CameraPreset {
@@ -86,9 +89,16 @@ public:
float zoomLevel() const;
void setZoomLevel(float zoomLevel);
+ float minZoomLevel() const;
+ void setMinZoomLevel(float zoomLevel);
+ float maxZoomLevel() const;
+ void setMaxZoomLevel(float zoomLevel);
void setCameraPosition(float horizontal, float vertical, float zoom = 100.0f);
+ QVector3D target() const;
+ void setTarget(const QVector3D &target);
+
signals:
void xRotationChanged(float rotation);
void yRotationChanged(float rotation);
@@ -96,6 +106,9 @@ signals:
void cameraPresetChanged(Q3DCamera::CameraPreset preset);
void wrapXRotationChanged(bool isEnabled);
void wrapYRotationChanged(bool isEnabled);
+ Q_REVISION(1) void targetChanged(const QVector3D &target);
+ Q_REVISION(1) void minZoomLevelChanged(float zoomLevel);
+ Q_REVISION(1) void maxZoomLevelChanged(float zoomLevel);
private:
QScopedPointer<Q3DCameraPrivate> d_ptr;
diff --git a/src/datavisualization/engine/q3dcamera_p.h b/src/datavisualization/engine/q3dcamera_p.h
index 01e7a508..b7826a5b 100644
--- a/src/datavisualization/engine/q3dcamera_p.h
+++ b/src/datavisualization/engine/q3dcamera_p.h
@@ -84,22 +84,25 @@ signals:
public:
Q3DCamera *q_ptr;
- QVector3D m_target;
+ QVector3D m_actualTarget;
QVector3D m_up;
QMatrix4x4 m_viewMatrix;
bool m_isViewMatrixUpdateActive;
- GLfloat m_xRotation;
- GLfloat m_yRotation;
- GLfloat m_minXRotation;
- GLfloat m_minYRotation;
- GLfloat m_maxXRotation;
- GLfloat m_maxYRotation;
- GLfloat m_zoomLevel;
+ float m_xRotation;
+ float m_yRotation;
+ float m_minXRotation;
+ float m_minYRotation;
+ float m_maxXRotation;
+ float m_maxYRotation;
+ float m_zoomLevel;
+ float m_minZoomLevel;
+ float m_maxZoomLevel;
bool m_wrapXRotation;
bool m_wrapYRotation;
Q3DCamera::CameraPreset m_activePreset;
+ QVector3D m_requestedTarget;
friend class Bars3DRenderer;
friend class Surface3DRenderer;
diff --git a/src/datavisualization/engine/q3dlight.cpp b/src/datavisualization/engine/q3dlight.cpp
index 03f094cb..92b7bd07 100644
--- a/src/datavisualization/engine/q3dlight.cpp
+++ b/src/datavisualization/engine/q3dlight.cpp
@@ -68,13 +68,8 @@ Q3DLightPrivate::~Q3DLightPrivate()
void Q3DLightPrivate::sync(Q3DLight &other)
{
- // Copies changed values from this light to the other light. If the other light had same changes,
- // those changes are discarded.
- if (q_ptr->isDirty()) {
- other.copyValuesFrom(*q_ptr);
- q_ptr->setDirty(false);
- other.setDirty(false);
- }
+ Q_UNUSED(other);
+ // Do nothing. Only value light has to sync is the position, which we handle internally.
}
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/engine/q3dobject.cpp b/src/datavisualization/engine/q3dobject.cpp
index 56edb098..6b442ef8 100644
--- a/src/datavisualization/engine/q3dobject.cpp
+++ b/src/datavisualization/engine/q3dobject.cpp
@@ -53,9 +53,7 @@ Q3DObject::~Q3DObject()
*/
void Q3DObject::copyValuesFrom(const Q3DObject &source)
{
- d_ptr->m_position.setX(source.d_ptr->m_position.x());
- d_ptr->m_position.setY(source.d_ptr->m_position.y());
- d_ptr->m_position.setZ(source.d_ptr->m_position.z());
+ d_ptr->m_position = source.d_ptr->m_position;
setDirty(true);
}
@@ -74,6 +72,9 @@ Q3DScene *Q3DObject::parentScene()
* \property Q3DObject::position
*
* This property contains the 3D position of the object.
+ *
+ * \note Currently setting this property has no effect, as the positions of Q3DObjects in the
+ * scene are handled internally.
*/
QVector3D Q3DObject::position() const
{
@@ -97,7 +98,7 @@ void Q3DObject::setDirty(bool dirty)
{
d_ptr->m_isDirty = dirty;
if (parentScene())
- parentScene()->d_ptr->m_sceneDirty = true;
+ parentScene()->d_ptr->markDirty();
}
/*!
diff --git a/src/datavisualization/engine/q3dscene.cpp b/src/datavisualization/engine/q3dscene.cpp
index 9464bc8d..e4015c2b 100644
--- a/src/datavisualization/engine/q3dscene.cpp
+++ b/src/datavisualization/engine/q3dscene.cpp
@@ -96,9 +96,33 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
*/
/*!
+ * \qmlproperty point Scene3D::graphPositionQuery
+ *
+ * This property contains the coordinates for the user input that should be processed
+ * by the scene as a graph position query. If this is set to value other than
+ * invalidSelectionPoint, the graph tries to match a graph position to the given \a point
+ * within the primary viewport.
+ * After the rendering pass this property is returned to its default state of
+ * invalidSelectionPoint. The queried graph position can be read from
+ * AbstractGraph3D::queriedGraphPosition property after the next render pass.
+ *
+ * There isn't a single correct 3D coordinate to match to each specific screen position, so to be
+ * consistent, the queries are always done against the inner sides of an invisible box surrounding
+ * the graph.
+ *
+ * \note Bar graphs allow graph position queries only at the graph floor level.
+ *
+ * \sa AbstractGraph3D::queriedGraphPosition
+ */
+
+/*!
* \qmlproperty bool Scene3D::slicingActive
*
- * This property contains whether 2D slicing view is currently active or not.
+ * This property contains whether 2D slicing view is currently active or not. If setting it, you
+ * must make sure AbstractGraph3D::selectionMode has either
+ * \l{QAbstract3DGraph::SelectionRow}{AbstractGraph3D.SelectionRow} or
+ * \l{QAbstract3DGraph::SelectionColumn}{AbstractGraph3D.SelectionColumn} flag set, and there is a
+ * valid selection.
* \note Not all visualizations support the 2D slicing view.
*/
@@ -135,7 +159,6 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
* A constant property providing an invalid point for selection.
*/
-
/*!
* Constructs a basic scene with one light and one camera in it. An
* optional \a parent parameter can be given and is then passed to QObject constructor.
@@ -195,33 +218,35 @@ void Q3DScene::setPrimarySubViewport(const QRect &primarySubViewport)
/*!
* Returns whether the given \a point resides inside the primary subview or not.
* \return \c true if the point is inside the primary subview.
+ * \note If subviews are superimposed, and the given \a point resides inside both, result is
+ * \c true only when the primary subview is on top.
*/
bool Q3DScene::isPointInPrimarySubView(const QPoint &point)
{
int x = point.x();
int y = point.y();
- int areaMinX = d_ptr->m_primarySubViewport.x();
- int areaMaxX = d_ptr->m_primarySubViewport.x() + d_ptr->m_primarySubViewport.width();
- int areaMinY = d_ptr->m_primarySubViewport.y();
- int areaMaxY = d_ptr->m_primarySubViewport.y() + d_ptr->m_primarySubViewport.height();
-
- return ( x > areaMinX && x < areaMaxX && y > areaMinY && y < areaMaxY );
+ bool isInSecondary = d_ptr->isInArea(d_ptr->m_secondarySubViewport, x, y);
+ if (!isInSecondary || (isInSecondary && !d_ptr->m_isSecondarySubviewOnTop))
+ return d_ptr->isInArea(d_ptr->m_primarySubViewport, x, y);
+ else
+ return false;
}
/*!
* Returns whether the given \a point resides inside the secondary subview or not.
* \return \c true if the point is inside the secondary subview.
+ * \note If subviews are superimposed, and the given \a point resides inside both, result is
+ * \c true only when the secondary subview is on top.
*/
bool Q3DScene::isPointInSecondarySubView(const QPoint &point)
{
int x = point.x();
int y = point.y();
- int areaMinX = d_ptr->m_secondarySubViewport.x();
- int areaMaxX = d_ptr->m_secondarySubViewport.x() + d_ptr->m_secondarySubViewport.width();
- int areaMinY = d_ptr->m_secondarySubViewport.y();
- int areaMaxY = d_ptr->m_secondarySubViewport.y() + d_ptr->m_secondarySubViewport.height();
-
- return ( x > areaMinX && x < areaMaxX && y > areaMinY && y < areaMaxY );
+ bool isInPrimary = d_ptr->isInArea(d_ptr->m_primarySubViewport, x, y);
+ if (!isInPrimary || (isInPrimary && d_ptr->m_isSecondarySubviewOnTop))
+ return d_ptr->isInArea(d_ptr->m_secondarySubViewport, x, y);
+ else
+ return false;
}
/*!
@@ -254,7 +279,7 @@ void Q3DScene::setSecondarySubViewport(const QRect &secondarySubViewport)
* \property Q3DScene::selectionQueryPosition
*
* This property contains the coordinates for the user input that should be processed
- * by the scene as selection. If this is set to value other than invalidSelectionPoint() the
+ * by the scene as a selection. If this is set to value other than invalidSelectionPoint(), the
* graph tries to select a data item, axis label, or a custom item at the given \a point within
* the primary viewport.
* After the rendering pass the property is returned to its default state of
@@ -289,9 +314,47 @@ QPoint Q3DScene::invalidSelectionPoint()
}
/*!
+ * \property Q3DScene::graphPositionQuery
+ *
+ * This property contains the coordinates for the user input that should be processed
+ * by the scene as a graph position query. If this is set to value other than
+ * invalidSelectionPoint(), the graph tries to match a graph position to the given \a point
+ * within the primary viewport.
+ * After the rendering pass this property is returned to its default state of
+ * invalidSelectionPoint(). The queried graph position can be read from
+ * QAbstract3DGraph::queriedGraphPosition property after the next render pass.
+ *
+ * There isn't a single correct 3D coordinate to match to each specific screen position, so to be
+ * consistent, the queries are always done against the inner sides of an invisible box surrounding
+ * the graph.
+ *
+ * \note Bar graphs allow graph position queries only at the graph floor level.
+ *
+ * \sa QAbstract3DGraph::queriedGraphPosition
+ */
+void Q3DScene::setGraphPositionQuery(const QPoint &point)
+{
+ if (point != d_ptr->m_graphPositionQueryPosition) {
+ d_ptr->m_graphPositionQueryPosition = point;
+ d_ptr->m_changeTracker.graphPositionQueryPositionChanged = true;
+ d_ptr->m_sceneDirty = true;
+
+ emit graphPositionQueryChanged(point);
+ emit d_ptr->needRender();
+ }
+}
+
+QPoint Q3DScene::graphPositionQuery() const
+{
+ return d_ptr->m_graphPositionQueryPosition;
+}
+
+/*!
* \property Q3DScene::slicingActive
*
- * This property contains whether 2D slicing view is currently active or not.
+ * This property contains whether 2D slicing view is currently active or not. If setting it, you
+ * must make sure QAbstract3DGraph::selectionMode has either QAbstract3DGraph::SelectionRow or
+ * QAbstract3DGraph::SelectionColumn flag set, and there is a valid selection.
* \note Not all visualizations support the 2D slicing view.
*/
bool Q3DScene::isSlicingActive() const
@@ -306,6 +369,10 @@ void Q3DScene::setSlicingActive(bool isSlicing)
d_ptr->m_changeTracker.slicingActivatedChanged = true;
d_ptr->m_sceneDirty = true;
+ // Set secondary subview behind primary to achieve default functionality (= clicking on
+ // primary disables slice)
+ setSecondarySubviewOnTop(!isSlicing);
+
d_ptr->calculateSubViewports();
emit slicingActiveChanged(isSlicing);
emit d_ptr->needRender();
@@ -447,7 +514,9 @@ Q3DScenePrivate::Q3DScenePrivate(Q3DScene *q) :
m_light(),
m_isUnderSideCameraEnabled(false),
m_isSlicingActive(false),
- m_selectionQueryPosition(Q3DScene::invalidSelectionPoint())
+ m_selectionQueryPosition(Q3DScene::invalidSelectionPoint()),
+ m_graphPositionQueryPosition(Q3DScene::invalidSelectionPoint()),
+ m_sceneDirty(true)
{
}
@@ -491,6 +560,11 @@ void Q3DScenePrivate::sync(Q3DScenePrivate &other)
m_changeTracker.selectionQueryPositionChanged = false;
other.m_changeTracker.selectionQueryPositionChanged = false;
}
+ if (m_changeTracker.graphPositionQueryPositionChanged) {
+ other.q_ptr->setGraphPositionQuery(q_ptr->graphPositionQuery());
+ m_changeTracker.graphPositionQueryPositionChanged = false;
+ other.m_changeTracker.graphPositionQueryPositionChanged = false;
+ }
if (m_changeTracker.cameraChanged) {
m_camera->setDirty(true);
m_changeTracker.cameraChanged = false;
@@ -649,4 +723,19 @@ void Q3DScenePrivate::setLightPositionRelativeToCamera(const QVector3D &relative
distanceModifier));
}
+void Q3DScenePrivate::markDirty()
+{
+ m_sceneDirty = true;
+ emit needRender();
+}
+
+bool Q3DScenePrivate::isInArea(const QRect &area, int x, int y) const
+{
+ int areaMinX = area.x();
+ int areaMaxX = area.x() + area.width();
+ int areaMinY = area.y();
+ int areaMaxY = area.y() + area.height();
+ return ( x >= areaMinX && x <= areaMaxX && y >= areaMinY && y <= areaMaxY );
+}
+
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/engine/q3dscene.h b/src/datavisualization/engine/q3dscene.h
index 1699b125..a46b4d7b 100644
--- a/src/datavisualization/engine/q3dscene.h
+++ b/src/datavisualization/engine/q3dscene.h
@@ -41,6 +41,7 @@ class QT_DATAVISUALIZATION_EXPORT Q3DScene : public QObject
Q_PROPERTY(Q3DCamera* activeCamera READ activeCamera WRITE setActiveCamera NOTIFY activeCameraChanged)
Q_PROPERTY(Q3DLight* activeLight READ activeLight WRITE setActiveLight NOTIFY activeLightChanged)
Q_PROPERTY(float devicePixelRatio READ devicePixelRatio WRITE setDevicePixelRatio NOTIFY devicePixelRatioChanged)
+ Q_PROPERTY(QPoint graphPositionQuery READ graphPositionQuery WRITE setGraphPositionQuery NOTIFY graphPositionQueryChanged REVISION 1)
public:
Q3DScene(QObject *parent = 0);
@@ -60,6 +61,9 @@ public:
QPoint selectionQueryPosition() const;
static QPoint invalidSelectionPoint();
+ void setGraphPositionQuery(const QPoint &point);
+ QPoint graphPositionQuery() const;
+
void setSlicingActive(bool isSlicing);
bool isSlicingActive() const;
@@ -85,6 +89,7 @@ signals:
void activeLightChanged(Q3DLight *light);
void devicePixelRatioChanged(float pixelRatio);
void selectionQueryPositionChanged(const QPoint &position);
+ Q_REVISION(1) void graphPositionQueryChanged(const QPoint &position);
private:
QScopedPointer<Q3DScenePrivate> d_ptr;
diff --git a/src/datavisualization/engine/q3dscene_p.h b/src/datavisualization/engine/q3dscene_p.h
index 2c69e5e0..d0f6e735 100644
--- a/src/datavisualization/engine/q3dscene_p.h
+++ b/src/datavisualization/engine/q3dscene_p.h
@@ -47,6 +47,7 @@ struct Q3DSceneChangeBitField {
bool slicingActivatedChanged : 1;
bool devicePixelRatioChanged : 1;
bool selectionQueryPositionChanged : 1;
+ bool graphPositionQueryPositionChanged : 1;
bool windowSizeChanged : 1;
Q3DSceneChangeBitField()
@@ -59,6 +60,7 @@ struct Q3DSceneChangeBitField {
slicingActivatedChanged(true),
devicePixelRatioChanged(true),
selectionQueryPositionChanged(false),
+ graphPositionQueryPositionChanged(false),
windowSizeChanged(true)
{
}
@@ -89,6 +91,10 @@ public:
float fixedRotation = 0.0f,
float distanceModifier = 0.0f);
+ void markDirty();
+
+ bool isInArea(const QRect &area, int x, int y) const;
+
signals:
void needRender();
@@ -106,6 +112,7 @@ public:
bool m_isUnderSideCameraEnabled;
bool m_isSlicingActive;
QPoint m_selectionQueryPosition;
+ QPoint m_graphPositionQueryPosition;
QSize m_windowSize;
QRect m_glViewport;
QRect m_glPrimarySubViewport;
diff --git a/src/datavisualization/engine/q3dsurface.cpp b/src/datavisualization/engine/q3dsurface.cpp
index c5ce29d7..ce3e7f4c 100644
--- a/src/datavisualization/engine/q3dsurface.cpp
+++ b/src/datavisualization/engine/q3dsurface.cpp
@@ -98,6 +98,8 @@ Q3DSurface::Q3DSurface(const QSurfaceFormat *format, QWindow *parent)
dptr()->m_shared->initializeOpenGL();
QObject::connect(dptr()->m_shared, &Surface3DController::selectedSeriesChanged,
this, &Q3DSurface::selectedSeriesChanged);
+ QObject::connect(dptr()->m_shared, &Surface3DController::flipHorizontalGridChanged,
+ this, &Q3DSurface::flipHorizontalGridChanged);
}
/*!
@@ -230,6 +232,31 @@ QSurface3DSeries *Q3DSurface::selectedSeries() const
}
/*!
+ * \property Q3DSurface::flipHorizontalGrid
+ * \since QtDataVisualization 1.2
+ *
+ * In some use cases the horizontal axis grid is mostly covered by the surface, so it can be more
+ * useful to display the horizontal axis grid on top of the graph rather than on the bottom.
+ * A typical use case for this is showing 2D spectrograms using orthoGraphic projection with
+ * a top-down viewpoint.
+ *
+ * If \c{false}, the horizontal axis grid and labels are drawn on the horizontal background
+ * of the graph.
+ * If \c{true}, the horizontal axis grid and labels are drawn on the opposite side of the graph
+ * from the horizontal background.
+ * Defaults to \c{false}.
+ */
+void Q3DSurface::setFlipHorizontalGrid(bool flip)
+{
+ dptr()->m_shared->setFlipHorizontalGrid(flip);
+}
+
+bool Q3DSurface::flipHorizontalGrid() const
+{
+ return dptrc()->m_shared->flipHorizontalGrid();
+}
+
+/*!
* Adds \a axis to the graph. The axes added via addAxis are not yet taken to use,
* addAxis is simply used to give the ownership of the \a axis to the graph.
* The \a axis must not be null or added to another graph.
diff --git a/src/datavisualization/engine/q3dsurface.h b/src/datavisualization/engine/q3dsurface.h
index 9868c844..86740519 100644
--- a/src/datavisualization/engine/q3dsurface.h
+++ b/src/datavisualization/engine/q3dsurface.h
@@ -34,6 +34,7 @@ class QT_DATAVISUALIZATION_EXPORT Q3DSurface : public QAbstract3DGraph
Q_PROPERTY(QValue3DAxis *axisY READ axisY WRITE setAxisY NOTIFY axisYChanged)
Q_PROPERTY(QValue3DAxis *axisZ READ axisZ WRITE setAxisZ NOTIFY axisZChanged)
Q_PROPERTY(QSurface3DSeries *selectedSeries READ selectedSeries NOTIFY selectedSeriesChanged)
+ Q_PROPERTY(bool flipHorizontalGrid READ flipHorizontalGrid WRITE setFlipHorizontalGrid NOTIFY flipHorizontalGridChanged)
public:
explicit Q3DSurface(const QSurfaceFormat *format = 0, QWindow *parent = 0);
@@ -55,12 +56,15 @@ public:
QList<QValue3DAxis *> axes() const;
QSurface3DSeries *selectedSeries() const;
+ void setFlipHorizontalGrid(bool flip);
+ bool flipHorizontalGrid() const;
signals:
void axisXChanged(QValue3DAxis *axis);
void axisYChanged(QValue3DAxis *axis);
void axisZChanged(QValue3DAxis *axis);
void selectedSeriesChanged(QSurface3DSeries *series);
+ void flipHorizontalGridChanged(bool flip);
private:
Q3DSurfacePrivate *dptr();
diff --git a/src/datavisualization/engine/qabstract3dgraph.cpp b/src/datavisualization/engine/qabstract3dgraph.cpp
index b8fa92e8..e51d9ce4 100644
--- a/src/datavisualization/engine/qabstract3dgraph.cpp
+++ b/src/datavisualization/engine/qabstract3dgraph.cpp
@@ -22,6 +22,7 @@
#include "qabstract3dinputhandler_p.h"
#include "q3dscene_p.h"
#include "qutils.h"
+#include "utils_p.h"
#include <QtGui/QGuiApplication>
#include <QtGui/QOpenGLContext>
@@ -29,6 +30,9 @@
#include <QtGui/QPainter>
#include <QtGui/QOpenGLFramebufferObject>
#include <QtGui/QOffscreenSurface>
+#if defined(Q_OS_OSX)
+#include <qpa/qplatformnativeinterface.h>
+#endif
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
@@ -148,7 +152,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
\value OptimizationDefault
Provides the full feature set at a reasonable performance.
\value OptimizationStatic
- Beta level feature. Optimizes the rendering of static data sets at the expense of some features.
+ Optimizes the rendering of static data sets at the expense of some features.
*/
/*!
@@ -169,11 +173,7 @@ QAbstract3DGraph::QAbstract3DGraph(QAbstract3DGraphPrivate *d, const QSurfaceFor
if (format) {
surfaceFormat = *format;
// Make sure renderable type is correct
-#if !defined(QT_OPENGL_ES_2)
- surfaceFormat.setRenderableType(QSurfaceFormat::OpenGL);
-#else
- surfaceFormat.setRenderableType(QSurfaceFormat::OpenGLES);
-#endif
+ surfaceFormat.setRenderableType(QSurfaceFormat::DefaultRenderableType);
} else {
surfaceFormat = qDefaultSurfaceFormat();
}
@@ -197,17 +197,24 @@ QAbstract3DGraph::QAbstract3DGraph(QAbstract3DGraphPrivate *d, const QSurfaceFor
qDebug() << "GLSL version:" << (const char *)shaderVersion;
#endif
-#if !defined(QT_OPENGL_ES_2)
- // If we have real OpenGL, GLSL version must be 1.2 or over. Quit if not.
- QStringList splitversionstr =
- QString::fromLatin1((const char *)shaderVersion).split(QChar::fromLatin1(' '));
- if (splitversionstr[0].toFloat() < 1.2)
- qFatal("GLSL version must be 1.20 or higher. Try installing latest display drivers.");
-#else
- Q_UNUSED(shaderVersion)
-#endif
+ if (!Utils::isOpenGLES()) {
+ // If we have real OpenGL, GLSL version must be 1.2 or over. Quit if not.
+ QStringList splitversionstr =
+ QString::fromLatin1((const char *)shaderVersion).split(QChar::fromLatin1(' '));
+ if (splitversionstr[0].toFloat() < 1.2)
+ qFatal("GLSL version must be 1.20 or higher. Try installing latest display drivers.");
+ }
d_ptr->renderLater();
+
+#if defined(Q_OS_OSX)
+ // Enable touch events for Mac touchpads
+ typedef void * (*EnableTouch)(QWindow*, bool);
+ EnableTouch enableTouch =
+ (EnableTouch)QGuiApplication::platformNativeInterface()->nativeResourceFunctionForIntegration("registertouchwindow");
+ if (enableTouch)
+ enableTouch(this, true);
+#endif
}
/*!
@@ -400,7 +407,7 @@ void QAbstract3DGraph::clearSelection()
* \return index to the added item if add was successful, -1 if trying to add a null item, and
* index of the item if trying to add an already added item.
*
- * \sa removeCustomItems(), removeCustomItem(), removeCustomItemAt()
+ * \sa removeCustomItems(), removeCustomItem(), removeCustomItemAt(), customItems()
*
* \since QtDataVisualization 1.1
*/
@@ -455,6 +462,16 @@ void QAbstract3DGraph::releaseCustomItem(QCustom3DItem *item)
}
/*!
+ * \return list of all added custom items.
+ * \since QtDataVisualization 1.2
+ * \sa addCustomItem()
+ */
+QList<QCustom3DItem *> QAbstract3DGraph::customItems() const
+{
+ return d_ptr->m_visualController->customItems();
+}
+
+/*!
* Can be used to query the index of the selected label after receiving \c selectedElementChanged
* signal with any label type. Selection is valid until the next \c selectedElementChanged signal.
*
@@ -545,6 +562,8 @@ QAbstract3DGraph::ElementType QAbstract3DGraph::selectedElement() const
* \since QtDataVisualization 1.1
*
* \return rendered image.
+ *
+ * \note OpenGL ES2 does not support anitialiasing, so \a msaaSamples is always forced to \c{0}.
*/
QImage QAbstract3DGraph::renderToImage(int msaaSamples, const QSize &imageSize)
{
@@ -591,8 +610,15 @@ qreal QAbstract3DGraph::currentFps() const
* \property QAbstract3DGraph::orthoProjection
* \since QtDataVisualization 1.1
*
- * If \c {true}, orthographic projection will be used for displaying the graph. Defaults to \c{false}.
+ * If \c {true}, orthographic projection will be used for displaying the graph.
+ * \note Orthographic projection can be used to create 2D graphs by replacing the default input
+ * handler with one that doesn't allow rotating the graph and setting the camera to view the graph
+ * directly from the side or from the top. Also, axis labels typically need to be rotated when
+ * viewing the graph from the sides.
+ * Defaults to \c{false}.
* \note Shadows will be disabled when set to \c{true}.
+ *
+ * \sa QAbstract3DAxis::labelAutoRotation, Q3DCamera::cameraPreset
*/
void QAbstract3DGraph::setOrthoProjection(bool enable)
{
@@ -608,14 +634,16 @@ bool QAbstract3DGraph::isOrthoProjection() const
* \property QAbstract3DGraph::aspectRatio
* \since QtDataVisualization 1.1
*
- * Aspect ratio of the graph data. This is the ratio of data scaling between horizontal and
- * vertical axes. Defaults to \c{2.0}.
+ * The aspect ratio is the ratio of the graph scaling between the longest axis on the horizontal
+ * plane and the Y-axis. Defaults to \c{2.0}.
*
* \note Has no effect on Q3DBars.
+ *
+ * \sa horizontalAspectRatio
*/
void QAbstract3DGraph::setAspectRatio(qreal ratio)
{
- d_ptr->m_visualController->setAspectRatio(float(ratio));
+ d_ptr->m_visualController->setAspectRatio(ratio);
}
qreal QAbstract3DGraph::aspectRatio() const
@@ -626,14 +654,20 @@ qreal QAbstract3DGraph::aspectRatio() const
/*!
* \property QAbstract3DGraph::optimizationHints
*
- * Defines if the rendering optimization is default or static. Default mode provides the full feature set at
- * reasonable performance. Static is a beta level feature and currently supports only a subset of the
- * features on the Scatter graph. Missing features are object gradient for mesh objects, both gradients
- * for points, and diffuse and specular color on rotations. At this point static is intended just for
- * introducing a new feature. It optimizes graph rendering and is ideal for large non-changing data
- * sets. It is slower with dynamic data changes and item rotations. Selection is not optimized, so using it
- * with massive data sets is not advisable.
+ * Defines if the rendering optimization is default or static. Default mode provides the full
+ * feature set at reasonable performance. Static mode optimizes graph rendering and is ideal for
+ * large non-changing data sets. It is slower with dynamic data changes and item rotations.
+ * Selection is not optimized, so using it with massive data sets is not advisable.
+ * Static works only on the Scatter graph.
* Defaults to \c{OptimizationDefault}.
+ *
+ * \note On some environments, large graphs using static optimization may not render, because
+ * all of the items are rendered using a single draw call, and different graphics drivers have
+ * different maximum vertice counts per call that they support.
+ * This is mostly an issue on 32bit and/or OpenGL ES2 platforms.
+ * To work around this issue, choose an item mesh with low vertex count or use the point mesh.
+ *
+ * \sa QAbstract3DSeries::mesh
*/
void QAbstract3DGraph::setOptimizationHints(OptimizationHints hints)
{
@@ -646,6 +680,195 @@ QAbstract3DGraph::OptimizationHints QAbstract3DGraph::optimizationHints() const
}
/*!
+ * \property QAbstract3DGraph::polar
+ * \since QtDataVisualization 1.2
+ *
+ * If \c {true}, the horizontal axes are changed into polar axes. The X axis becomes the
+ * angular axis and the Z axis becomes the radial axis.
+ * Polar mode is not available for bar graphs.
+ *
+ * Defaults to \c{false}.
+ *
+ * \sa orthoProjection, radialLabelOffset
+ */
+void QAbstract3DGraph::setPolar(bool enable)
+{
+ d_ptr->m_visualController->setPolar(enable);
+}
+
+bool QAbstract3DGraph::isPolar() const
+{
+ return d_ptr->m_visualController->isPolar();
+}
+
+/*!
+ * \property QAbstract3DGraph::radialLabelOffset
+ * \since QtDataVisualization 1.2
+ *
+ * This property specifies the normalized horizontal offset for the axis labels of the radial
+ * polar axis. The value 0.0 indicates the labels should be drawn next to the 0-angle angular
+ * axis grid line. The value 1.0 indicates the labels are drawn on their normal place at the edge
+ * of the graph background.
+ * This property is ignored if polar property value is \c{false}. Defaults to 1.0.
+ *
+ * \sa polar
+ */
+void QAbstract3DGraph::setRadialLabelOffset(float offset)
+{
+ d_ptr->m_visualController->setRadialLabelOffset(offset);
+}
+
+float QAbstract3DGraph::radialLabelOffset() const
+{
+ return d_ptr->m_visualController->radialLabelOffset();
+}
+
+/*!
+ * \property QAbstract3DGraph::horizontalAspectRatio
+ * \since QtDataVisualization 1.2
+ *
+ * The horizontal aspect ratio is the ratio of the graph scaling between the X and Z axes.
+ * Value of 0.0 indicates automatic scaling according to axis ranges.
+ * Defaults to \c{0.0}.
+ *
+ * \note Has no effect on Q3DBars, which handles scaling on the horizontal plane via
+ * \l{Q3DBars::barThickness}{barThickness} and \l{Q3DBars::barSpacing}{barSpacing} properties.
+ * Polar graphs also ignore this property.
+ *
+ * \sa aspectRatio, polar, Q3DBars::barThickness, Q3DBars::barSpacing
+ */
+void QAbstract3DGraph::setHorizontalAspectRatio(qreal ratio)
+{
+ d_ptr->m_visualController->setHorizontalAspectRatio(ratio);
+}
+
+qreal QAbstract3DGraph::horizontalAspectRatio() const
+{
+ return d_ptr->m_visualController->horizontalAspectRatio();
+}
+
+/*!
+ * \property QAbstract3DGraph::reflection
+ * \since QtDataVisualization 1.2
+ *
+ * Sets floor reflections on or off. Defaults to \c{false}.
+ *
+ * \note Affects only Q3DBars.
+ *
+ * \note In Q3DBars graphs holding both positive and negative values, reflections are not supported
+ * for custom items that intersect the floor plane. In that case, reflections should be turned off
+ * to avoid incorrect rendering.
+ *
+ * \note If using custom surface format, stencil buffer needs to be defined
+ * (QSurfaceFormat::setStencilBufferSize()) for reflections to work.
+ *
+ * \sa reflectivity
+ */
+void QAbstract3DGraph::setReflection(bool enable)
+{
+ d_ptr->m_visualController->setReflection(enable);
+}
+
+bool QAbstract3DGraph::isReflection() const
+{
+ return d_ptr->m_visualController->reflection();
+}
+
+/*!
+ * \property QAbstract3DGraph::reflectivity
+ * \since QtDataVisualization 1.2
+ *
+ * Adjusts floor reflectivity, larger number being more reflective. Valid range is \c{[0...1]}.
+ * Defaults to \c{0.5}.
+ *
+ * \note Affects only Q3DBars.
+ *
+ * \sa reflection
+ */
+void QAbstract3DGraph::setReflectivity(qreal reflectivity)
+{
+ d_ptr->m_visualController->setReflectivity(reflectivity);
+}
+
+qreal QAbstract3DGraph::reflectivity() const
+{
+ return d_ptr->m_visualController->reflectivity();
+}
+
+/*!
+ * \property QAbstract3DGraph::locale
+ * \since QtDataVisualization 1.2
+ *
+ * Sets the locale used for formatting various numeric labels.
+ * Defaults to \c{"C"} locale.
+ *
+ * \sa QValue3DAxis::labelFormat
+ */
+void QAbstract3DGraph::setLocale(const QLocale &locale)
+{
+ d_ptr->m_visualController->setLocale(locale);
+}
+
+QLocale QAbstract3DGraph::locale() const
+{
+ return d_ptr->m_visualController->locale();
+}
+
+/*!
+ * \property QAbstract3DGraph::queriedGraphPosition
+ * \since QtDataVisualization 1.2
+ *
+ * This read-only property contains the latest graph position values along each axis queried using
+ * Q3DScene::graphPositionQuery. The values are normalized to range \c{[-1, 1]}.
+ * If the queried position was outside the graph bounds, the values
+ * will not reflect the real position, but will instead be some undefined position outside
+ * the range \c{[-1, 1]}. The value will be undefined before any queries are made.
+ *
+ * There isn't a single correct 3D coordinate to match to each specific screen position, so to be
+ * consistent, the queries are always done against the inner sides of an invisible box surrounding
+ * the graph.
+ *
+ * \note Bar graphs only allow querying graph position at the graph floor level,
+ * so the Y-value is always zero for bar graphs and the valid queries can be only made at
+ * screen positions that contain the floor of the graph.
+ *
+ * \sa Q3DScene::graphPositionQuery
+ */
+QVector3D QAbstract3DGraph::queriedGraphPosition() const
+{
+ return d_ptr->m_visualController->queriedGraphPosition();
+}
+
+/*!
+ * \property QAbstract3DGraph::margin
+ * \since QtDataVisualization 1.2
+ *
+ * This property contains the absolute value used for graph margin. The graph margin is the space
+ * left between the edge of the plottable graph area and the edge of the graph background.
+ * If the margin value is negative, the margins are determined automatically and can vary according
+ * to size of the items in the series and the type of the graph.
+ * The value is interpreted as a fraction of Y-axis range, provided the graph aspect ratios have
+ * not beed changed from the defaults.
+ * Defaults to \c{-1.0}.
+ *
+ * \note Having smaller than the automatically determined margin on scatter graph can cause
+ * the scatter items at the edges of the graph to overlap with the graph background.
+ *
+ * \note On scatter and surface graphs, if the margin is comparatively small to the axis label
+ * size, the positions of the edge labels of the axes are adjusted to avoid overlap with
+ * the edge labels of the neighboring axes.
+ */
+void QAbstract3DGraph::setMargin(qreal margin)
+{
+ d_ptr->m_visualController->setMargin(margin);
+}
+
+qreal QAbstract3DGraph::margin() const
+{
+ return d_ptr->m_visualController->margin();
+}
+
+/*!
* \internal
*/
bool QAbstract3DGraph::event(QEvent *event)
@@ -795,6 +1018,23 @@ void QAbstract3DGraphPrivate::setVisualController(Abstract3DController *controll
QObject::connect(m_visualController, &Abstract3DController::aspectRatioChanged, q_ptr,
&QAbstract3DGraph::aspectRatioChanged);
+ QObject::connect(m_visualController, &Abstract3DController::polarChanged, q_ptr,
+ &QAbstract3DGraph::polarChanged);
+ QObject::connect(m_visualController, &Abstract3DController::radialLabelOffsetChanged, q_ptr,
+ &QAbstract3DGraph::radialLabelOffsetChanged);
+ QObject::connect(m_visualController, &Abstract3DController::horizontalAspectRatioChanged, q_ptr,
+ &QAbstract3DGraph::horizontalAspectRatioChanged);
+
+ QObject::connect(m_visualController, &Abstract3DController::reflectionChanged, q_ptr,
+ &QAbstract3DGraph::reflectionChanged);
+ QObject::connect(m_visualController, &Abstract3DController::reflectivityChanged, q_ptr,
+ &QAbstract3DGraph::reflectivityChanged);
+ QObject::connect(m_visualController, &Abstract3DController::localeChanged, q_ptr,
+ &QAbstract3DGraph::localeChanged);
+ QObject::connect(m_visualController, &Abstract3DController::queriedGraphPositionChanged, q_ptr,
+ &QAbstract3DGraph::queriedGraphPositionChanged);
+ QObject::connect(m_visualController, &Abstract3DController::marginChanged, q_ptr,
+ &QAbstract3DGraph::marginChanged);
}
void QAbstract3DGraphPrivate::handleDevicePixelRatioChange()
@@ -849,8 +1089,10 @@ QImage QAbstract3DGraphPrivate::renderToImage(int msaaSamples, const QSize &imag
// Render the wanted frame offscreen
m_context->makeCurrent(m_offscreenSurface);
fboFormat.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
- fboFormat.setInternalTextureFormat(GL_RGB);
- fboFormat.setSamples(msaaSamples);
+ if (!Utils::isOpenGLES()) {
+ fboFormat.setInternalTextureFormat(GL_RGB);
+ fboFormat.setSamples(msaaSamples);
+ }
fbo = new QOpenGLFramebufferObject(imageSize, fboFormat);
if (fbo->isValid()) {
QRect originalViewport = m_visualController->m_scene->viewport();
diff --git a/src/datavisualization/engine/qabstract3dgraph.h b/src/datavisualization/engine/qabstract3dgraph.h
index 59f61aae..a0c26a42 100644
--- a/src/datavisualization/engine/qabstract3dgraph.h
+++ b/src/datavisualization/engine/qabstract3dgraph.h
@@ -25,6 +25,7 @@
#include <QtDataVisualization/qabstract3dinputhandler.h>
#include <QtGui/QWindow>
#include <QtGui/QOpenGLFunctions>
+#include <QtCore/QLocale>
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
@@ -50,6 +51,14 @@ class QT_DATAVISUALIZATION_EXPORT QAbstract3DGraph : public QWindow, protected Q
Q_PROPERTY(ElementType selectedElement READ selectedElement NOTIFY selectedElementChanged)
Q_PROPERTY(qreal aspectRatio READ aspectRatio WRITE setAspectRatio NOTIFY aspectRatioChanged)
Q_PROPERTY(OptimizationHints optimizationHints READ optimizationHints WRITE setOptimizationHints NOTIFY optimizationHintsChanged)
+ Q_PROPERTY(bool polar READ isPolar WRITE setPolar NOTIFY polarChanged)
+ Q_PROPERTY(float radialLabelOffset READ radialLabelOffset WRITE setRadialLabelOffset NOTIFY radialLabelOffsetChanged)
+ Q_PROPERTY(qreal horizontalAspectRatio READ horizontalAspectRatio WRITE setHorizontalAspectRatio NOTIFY horizontalAspectRatioChanged)
+ Q_PROPERTY(bool reflection READ isReflection WRITE setReflection NOTIFY reflectionChanged)
+ Q_PROPERTY(qreal reflectivity READ reflectivity WRITE setReflectivity NOTIFY reflectivityChanged)
+ Q_PROPERTY(QLocale locale READ locale WRITE setLocale NOTIFY localeChanged)
+ Q_PROPERTY(QVector3D queriedGraphPosition READ queriedGraphPosition NOTIFY queriedGraphPositionChanged)
+ Q_PROPERTY(qreal margin READ margin WRITE setMargin NOTIFY marginChanged)
protected:
explicit QAbstract3DGraph(QAbstract3DGraphPrivate *d, const QSurfaceFormat *format,
@@ -126,6 +135,7 @@ public:
void removeCustomItem(QCustom3DItem *item);
void removeCustomItemAt(const QVector3D &position);
void releaseCustomItem(QCustom3DItem *item);
+ QList<QCustom3DItem *> customItems() const;
int selectedLabelIndex() const;
QAbstract3DAxis *selectedAxis() const;
@@ -150,6 +160,29 @@ public:
void setOptimizationHints(OptimizationHints hints);
OptimizationHints optimizationHints() const;
+ void setPolar(bool enable);
+ bool isPolar() const;
+
+ void setRadialLabelOffset(float offset);
+ float radialLabelOffset() const;
+
+ void setHorizontalAspectRatio(qreal ratio);
+ qreal horizontalAspectRatio() const;
+
+ void setReflection(bool enable);
+ bool isReflection() const;
+
+ void setReflectivity(qreal reflectivity);
+ qreal reflectivity() const;
+
+ void setLocale(const QLocale &locale);
+ QLocale locale() const;
+
+ QVector3D queriedGraphPosition() const;
+
+ void setMargin(qreal margin);
+ qreal margin() const;
+
protected:
bool event(QEvent *event);
void resizeEvent(QResizeEvent *event);
@@ -173,6 +206,14 @@ signals:
void orthoProjectionChanged(bool enabled);
void aspectRatioChanged(qreal ratio);
void optimizationHintsChanged(QAbstract3DGraph::OptimizationHints hints);
+ void polarChanged(bool enabled);
+ void radialLabelOffsetChanged(float offset);
+ void horizontalAspectRatioChanged(qreal ratio);
+ void reflectionChanged(bool enabled);
+ void reflectivityChanged(qreal reflectivity);
+ void localeChanged(const QLocale &locale);
+ void queriedGraphPositionChanged(const QVector3D &data);
+ void marginChanged(qreal margin);
private:
Q_DISABLE_COPY(QAbstract3DGraph)
diff --git a/src/datavisualization/engine/scatter3drenderer.cpp b/src/datavisualization/engine/scatter3drenderer.cpp
index dd50188b..f6367153 100644
--- a/src/datavisualization/engine/scatter3drenderer.cpp
+++ b/src/datavisualization/engine/scatter3drenderer.cpp
@@ -33,12 +33,9 @@
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
-//#define USE_UNIFORM_SCALING // Scale x and z uniformly, or based on autoscaled values
-
const GLfloat defaultMinSize = 0.01f;
const GLfloat defaultMaxSize = 0.1f;
const GLfloat itemScaler = 3.0f;
-const GLfloat gridLineWidth = 0.005f;
Scatter3DRenderer::Scatter3DRenderer(Scatter3DController *controller)
: Abstract3DRenderer(controller),
@@ -46,30 +43,27 @@ Scatter3DRenderer::Scatter3DRenderer(Scatter3DController *controller)
m_updateLabels(false),
m_dotShader(0),
m_dotGradientShader(0),
- #if defined(QT_OPENGL_ES_2)
+ m_staticSelectedItemGradientShader(0),
+ m_staticSelectedItemShader(0),
m_pointShader(0),
- #endif
m_depthShader(0),
m_selectionShader(0),
m_backgroundShader(0),
- m_labelShader(0),
+ m_staticGradientPointShader(0),
m_bgrTexture(0),
- m_depthTexture(0),
m_selectionTexture(0),
m_depthFrameBuffer(0),
m_selectionFrameBuffer(0),
m_selectionDepthBuffer(0),
m_shadowQualityToShader(100.0f),
m_shadowQualityMultiplier(3),
- m_heightNormalizer(1.0f),
- m_scaleFactor(0),
+ m_scaleX(0.0f),
+ m_scaleY(0.0f),
+ m_scaleZ(0.0f),
m_selectedItemIndex(Scatter3DController::invalidSelectionIndex()),
m_selectedSeriesCache(0),
m_oldSelectedSeriesCache(0),
- m_areaSize(QSizeF(0.0, 0.0)),
m_dotSizeScale(1.0f),
- m_hasHeightAdjustmentChanged(true),
- m_backgroundMargin(defaultMaxSize),
m_maxItemSize(0.0f),
m_clickedIndex(Scatter3DController::invalidSelectionIndex()),
m_havePointSeries(false),
@@ -77,15 +71,13 @@ Scatter3DRenderer::Scatter3DRenderer(Scatter3DController *controller)
m_haveUniformColorMeshSeries(false),
m_haveGradientMeshSeries(false)
{
- m_axisCacheY.setScale(2.0f);
- m_axisCacheY.setTranslate(-1.0f);
-
- initializeOpenGLFunctions();
initializeOpenGL();
}
Scatter3DRenderer::~Scatter3DRenderer()
{
+ fixContextBeforeDelete();
+
if (QOpenGLContext::currentContext()) {
m_textureHelper->glDeleteFramebuffers(1, &m_selectionFrameBuffer);
m_textureHelper->glDeleteRenderbuffers(1, &m_selectionDepthBuffer);
@@ -94,11 +86,13 @@ Scatter3DRenderer::~Scatter3DRenderer()
m_textureHelper->deleteTexture(&m_bgrTexture);
}
delete m_dotShader;
+ delete m_staticSelectedItemGradientShader;
+ delete m_staticSelectedItemShader;
delete m_dotGradientShader;
delete m_depthShader;
delete m_selectionShader;
delete m_backgroundShader;
- delete m_labelShader;
+ delete m_staticGradientPointShader;
}
void Scatter3DRenderer::initializeOpenGL()
@@ -106,28 +100,17 @@ void Scatter3DRenderer::initializeOpenGL()
Abstract3DRenderer::initializeOpenGL();
// Initialize shaders
- initLabelShaders(QStringLiteral(":/shaders/vertexLabel"),
- QStringLiteral(":/shaders/fragmentLabel"));
-#if !defined(QT_OPENGL_ES_2)
- // Init depth shader (for shadows). Init in any case, easier to handle shadow activation if done via api.
- initDepthShader();
-#else
- // Init point shader
- initPointShader();
-#endif
+ if (!m_isOpenGLES) {
+ initDepthShader(); // For shadows
+ loadGridLineMesh();
+ } else {
+ initPointShader();
+ }
// Init selection shader
initSelectionShader();
-#if !defined(QT_OPENGL_ES_2)
- // Load grid line mesh
- loadGridLineMesh();
-#endif
-
- // Load label mesh
- loadLabelMesh();
-
// Set view port
glViewport(m_primarySubViewport.x(),
m_primarySubViewport.y(),
@@ -138,6 +121,53 @@ void Scatter3DRenderer::initializeOpenGL()
loadBackgroundMesh();
}
+void Scatter3DRenderer::fixCameraTarget(QVector3D &target)
+{
+ target.setX(target.x() * m_scaleX);
+ target.setY(target.y() * m_scaleY);
+ target.setZ(target.z() * -m_scaleZ);
+}
+
+void Scatter3DRenderer::getVisibleItemBounds(QVector3D &minBounds, QVector3D &maxBounds)
+{
+ // The inputs are the item bounds in OpenGL coordinates.
+ // The outputs limit these bounds to visible ranges, normalized to range [-1, 1]
+ // Volume shader flips the Y and Z axes, so we need to set negatives of actual values to those
+ float itemRangeX = (maxBounds.x() - minBounds.x());
+ float itemRangeY = (maxBounds.y() - minBounds.y());
+ float itemRangeZ = (maxBounds.z() - minBounds.z());
+
+ if (minBounds.x() < -m_scaleX)
+ minBounds.setX(-1.0f + (2.0f * qAbs(minBounds.x() + m_scaleX) / itemRangeX));
+ else
+ minBounds.setX(-1.0f);
+
+ if (minBounds.y() < -m_scaleY)
+ minBounds.setY(-(-1.0f + (2.0f * qAbs(minBounds.y() + m_scaleY) / itemRangeY)));
+ else
+ minBounds.setY(1.0f);
+
+ if (minBounds.z() < -m_scaleZ)
+ minBounds.setZ(-(-1.0f + (2.0f * qAbs(minBounds.z() + m_scaleZ) / itemRangeZ)));
+ else
+ minBounds.setZ(1.0f);
+
+ if (maxBounds.x() > m_scaleX)
+ maxBounds.setX(1.0f - (2.0f * qAbs(maxBounds.x() - m_scaleX) / itemRangeX));
+ else
+ maxBounds.setX(1.0f);
+
+ if (maxBounds.y() > m_scaleY)
+ maxBounds.setY(-(1.0f - (2.0f * qAbs(maxBounds.y() - m_scaleY) / itemRangeY)));
+ else
+ maxBounds.setY(-1.0f);
+
+ if (maxBounds.z() > m_scaleZ)
+ maxBounds.setZ(-(1.0f - (2.0f * qAbs(maxBounds.z() - m_scaleZ) / itemRangeZ)));
+ else
+ maxBounds.setZ(-1.0f);
+}
+
void Scatter3DRenderer::updateData()
{
calculateSceneScalingFactors();
@@ -145,24 +175,31 @@ void Scatter3DRenderer::updateData()
foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
ScatterSeriesRenderCache *cache = static_cast<ScatterSeriesRenderCache *>(baseCache);
- if (cache->isVisible() && cache->dataDirty()) {
+ if (cache->isVisible()) {
const QScatter3DSeries *currentSeries = cache->series();
ScatterRenderItemArray &renderArray = cache->renderArray();
QScatterDataProxy *dataProxy = currentSeries->dataProxy();
const QScatterDataArray &dataArray = *dataProxy->array();
int dataSize = dataArray.size();
totalDataSize += dataSize;
- if (dataSize != renderArray.size())
- renderArray.resize(dataSize);
+ if (cache->dataDirty()) {
+ if (dataSize != renderArray.size())
+ renderArray.resize(dataSize);
+
+ for (int i = 0; i < dataSize; i++)
+ updateRenderItem(dataArray.at(i), renderArray[i]);
- for (int i = 0; i < dataSize; i++)
- updateRenderItem(dataArray.at(i), renderArray[i]);
- cache->setDataDirty(false);
+ if (m_cachedOptimizationHint.testFlag(QAbstract3DGraph::OptimizationStatic))
+ cache->setStaticBufferDirty(true);
+
+ cache->setDataDirty(false);
+ }
}
}
if (totalDataSize) {
- m_dotSizeScale = GLfloat(qBound(defaultMinSize, 2.0f / float(qSqrt(qreal(totalDataSize))),
+ m_dotSizeScale = GLfloat(qBound(defaultMinSize,
+ 2.0f / float(qSqrt(qreal(totalDataSize))),
defaultMaxSize));
}
@@ -179,6 +216,7 @@ void Scatter3DRenderer::updateData()
points = new ScatterPointBufferHelper();
cache->setBufferPoints(points);
}
+ points->setScaleY(m_scaleY);
points->load(cache);
} else {
ScatterObjectBufferHelper *object = cache->bufferObject();
@@ -187,7 +225,9 @@ void Scatter3DRenderer::updateData()
cache->setBufferObject(object);
}
if (renderArraySize != cache->oldArraySize()
- || cache->object()->objectFile() != cache->oldMeshFileName()) {
+ || cache->object()->objectFile() != cache->oldMeshFileName()
+ || cache->staticBufferDirty()) {
+ object->setScaleY(m_scaleY);
object->fullLoad(cache, m_dotSizeScale);
cache->setOldArraySize(renderArraySize);
cache->setOldMeshFileName(cache->object()->objectFile());
@@ -195,6 +235,8 @@ void Scatter3DRenderer::updateData()
object->update(cache, m_dotSizeScale);
}
}
+
+ cache->setStaticBufferDirty(false);
}
}
}
@@ -205,9 +247,28 @@ void Scatter3DRenderer::updateData()
void Scatter3DRenderer::updateSeries(const QList<QAbstract3DSeries *> &seriesList)
{
+ int seriesCount = seriesList.size();
+
+ // Check OptimizationStatic specific issues before populate marks changeTracker done
+ if (m_cachedOptimizationHint.testFlag(QAbstract3DGraph::OptimizationStatic)) {
+ for (int i = 0; i < seriesCount; i++) {
+ QScatter3DSeries *scatterSeries = static_cast<QScatter3DSeries *>(seriesList[i]);
+ if (scatterSeries->isVisible()) {
+ QAbstract3DSeriesChangeBitField &changeTracker = scatterSeries->d_ptr->m_changeTracker;
+ ScatterSeriesRenderCache *cache =
+ static_cast<ScatterSeriesRenderCache *>(m_renderCacheList.value(scatterSeries));
+ if (cache) {
+ if (changeTracker.baseGradientChanged || changeTracker.colorStyleChanged)
+ cache->setStaticObjectUVDirty(true);
+ if (cache->itemSize() != scatterSeries->itemSize())
+ cache->setStaticBufferDirty(true);
+ }
+ }
+ }
+ }
+
Abstract3DRenderer::updateSeries(seriesList);
- int seriesCount = seriesList.size();
float maxItemSize = 0.0f;
float itemSize = 0.0f;
bool noSelection = true;
@@ -243,13 +304,28 @@ void Scatter3DRenderer::updateSeries(const QList<QAbstract3DSeries *> &seriesLis
else
m_haveGradientMeshSeries = true;
}
+
+ if (cache->staticBufferDirty()) {
+ if (cache->mesh() != QAbstract3DSeries::MeshPoint) {
+ ScatterObjectBufferHelper *object = cache->bufferObject();
+ object->update(cache, m_dotSizeScale);
+ }
+ cache->setStaticBufferDirty(false);
+ }
+ if (cache->staticObjectUVDirty()) {
+ if (cache->mesh() == QAbstract3DSeries::MeshPoint) {
+ ScatterPointBufferHelper *object = cache->bufferPoints();
+ object->updateUVs(cache);
+ } else {
+ ScatterObjectBufferHelper *object = cache->bufferObject();
+ object->updateUVs(cache);
+ }
+ cache->setStaticObjectUVDirty(false);
+ }
}
}
m_maxItemSize = maxItemSize;
- if (maxItemSize > defaultMaxSize)
- m_backgroundMargin = maxItemSize / itemScaler;
- else
- m_backgroundMargin = defaultMaxSize;
+ calculateSceneScalingFactors();
if (noSelection) {
if (!selectionLabel().isEmpty())
@@ -268,6 +344,8 @@ void Scatter3DRenderer::updateItems(const QVector<Scatter3DController::ChangeIte
ScatterSeriesRenderCache *cache = 0;
const QScatter3DSeries *prevSeries = 0;
const QScatterDataArray *dataArray = 0;
+ const bool optimizationStatic = m_cachedOptimizationHint.testFlag(
+ QAbstract3DGraph::OptimizationStatic);
foreach (Scatter3DController::ChangeItem item, items) {
QScatter3DSeries *currentSeries = item.series;
@@ -282,7 +360,43 @@ void Scatter3DRenderer::updateItems(const QVector<Scatter3DController::ChangeIte
}
if (cache->isVisible()) {
const int index = item.index;
- updateRenderItem(dataArray->at(index), cache->renderArray()[index]);
+ if (index >= cache->renderArray().size())
+ continue; // Items removed from array for same render
+ bool oldVisibility;
+ ScatterRenderItem &item = cache->renderArray()[index];
+ if (optimizationStatic)
+ oldVisibility = item.isVisible();
+ updateRenderItem(dataArray->at(index), item);
+ if (optimizationStatic) {
+ if (!cache->visibilityChanged() && oldVisibility != item.isVisible())
+ cache->setVisibilityChanged(true);
+ cache->updateIndices().append(index);
+ }
+ }
+ }
+ if (optimizationStatic) {
+ foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
+ ScatterSeriesRenderCache *cache = static_cast<ScatterSeriesRenderCache *>(baseCache);
+ if (cache->isVisible() && cache->updateIndices().size()) {
+ if (cache->mesh() == QAbstract3DSeries::MeshPoint) {
+ cache->bufferPoints()->update(cache);
+ if (cache->colorStyle() == Q3DTheme::ColorStyleRangeGradient)
+ cache->bufferPoints()->updateUVs(cache);
+ } else {
+ if (cache->visibilityChanged()) {
+ // If any change changes item visibility, full load is needed to
+ // resize the buffers.
+ cache->updateIndices().clear();
+ cache->bufferObject()->fullLoad(cache, m_dotSizeScale);
+ } else {
+ cache->bufferObject()->update(cache, m_dotSizeScale);
+ if (cache->colorStyle() == Q3DTheme::ColorStyleRangeGradient)
+ cache->bufferObject()->updateUVs(cache);
+ }
+ }
+ cache->updateIndices().clear();
+ }
+ cache->setVisibilityChanged(false);
}
}
}
@@ -291,14 +405,46 @@ void Scatter3DRenderer::updateScene(Q3DScene *scene)
{
scene->activeCamera()->d_ptr->setMinYRotation(-90.0f);
- if (m_hasHeightAdjustmentChanged) {
- // Set initial camera position. Also update if height adjustment has changed.
- scene->activeCamera()->d_ptr->setBaseOrientation(cameraDistanceVector, zeroVector,
- upVector);
- m_hasHeightAdjustmentChanged = false;
+ Abstract3DRenderer::updateScene(scene);
+}
+
+void Scatter3DRenderer::updateAxisLabels(QAbstract3DAxis::AxisOrientation orientation,
+ const QStringList &labels)
+{
+ Abstract3DRenderer::updateAxisLabels(orientation, labels);
+
+ // Angular axis label dimensions affect the chart dimensions
+ if (m_polarGraph && orientation == QAbstract3DAxis::AxisOrientationX)
+ calculateSceneScalingFactors();
+}
+
+void Scatter3DRenderer::updateAxisTitleVisibility(QAbstract3DAxis::AxisOrientation orientation,
+ bool visible)
+{
+ Abstract3DRenderer::updateAxisTitleVisibility(orientation, visible);
+
+ // Angular axis title existence affects the chart dimensions
+ if (m_polarGraph && orientation == QAbstract3DAxis::AxisOrientationX)
+ calculateSceneScalingFactors();
+}
+
+void Scatter3DRenderer::updateOptimizationHint(QAbstract3DGraph::OptimizationHints hint)
+{
+ Abstract3DRenderer::updateOptimizationHint(hint);
+
+ Abstract3DRenderer::reInitShaders();
+
+ if (m_isOpenGLES && hint.testFlag(QAbstract3DGraph::OptimizationStatic)
+ && !m_staticGradientPointShader) {
+ initStaticPointShaders(QStringLiteral(":/shaders/vertexPointES2_UV"),
+ QStringLiteral(":/shaders/fragmentLabel"));
}
+}
- Abstract3DRenderer::updateScene(scene);
+void Scatter3DRenderer::updateMargin(float margin)
+{
+ Abstract3DRenderer::updateMargin(margin);
+ calculateSceneScalingFactors();
}
void Scatter3DRenderer::resetClickedStatus()
@@ -374,6 +520,7 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
m_yFlipped = true;
else
m_yFlipped = false;
+ m_yFlippedForGrid = m_yFlipped; // Polar axis grid drawing in abstract needs this
// Calculate background rotation
if (!m_zFlipped && !m_xFlipped)
@@ -389,169 +536,182 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
QVector3D lightPos = m_cachedScene->activeLight()->position();
// Introduce regardless of shadow quality to simplify logic
- QMatrix4x4 depthViewMatrix;
- QMatrix4x4 depthProjectionMatrix;
QMatrix4x4 depthProjectionViewMatrix;
+ ShaderHelper *pointSelectionShader;
+ if (!m_isOpenGLES) {
#if !defined(QT_OPENGL_ES_2)
- if (m_havePointSeries) {
- glEnable(GL_POINT_SMOOTH);
- glEnable(GL_PROGRAM_POINT_SIZE);
- }
-
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
- // Render scene into a depth texture for using with shadow mapping
- // Bind depth shader
- m_depthShader->bind();
-
- // Set viewport for depth map rendering. Must match texture size. Larger values give smoother shadows.
- glViewport(0, 0,
- m_primarySubViewport.width() * m_shadowQualityMultiplier,
- m_primarySubViewport.height() * m_shadowQualityMultiplier);
-
- // Enable drawing to framebuffer
- glBindFramebuffer(GL_FRAMEBUFFER, m_depthFrameBuffer);
- glClear(GL_DEPTH_BUFFER_BIT);
-
- // Set front face culling to reduce self-shadowing issues
- glCullFace(GL_FRONT);
-
- // Get the depth view matrix
- // It may be possible to hack lightPos here if we want to make some tweaks to shadow
- QVector3D depthLightPos = activeCamera->d_ptr->calculatePositionRelativeToCamera(
- zeroVector, 0.0f, 2.5f / m_autoScaleAdjustment);
- depthViewMatrix.lookAt(depthLightPos, zeroVector, upVector);
- // Set the depth projection matrix
- depthProjectionMatrix.perspective(15.0f, viewPortRatio, 3.0f, 100.0f);
- depthProjectionViewMatrix = depthProjectionMatrix * depthViewMatrix;
-
- // Draw dots to depth buffer
- foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
- if (baseCache->isVisible()) {
- ScatterSeriesRenderCache *cache =
- static_cast<ScatterSeriesRenderCache *>(baseCache);
- ObjectHelper *dotObj = cache->object();
- QQuaternion seriesRotation(cache->meshRotation());
- const ScatterRenderItemArray &renderArray = cache->renderArray();
- const int renderArraySize = renderArray.size();
- bool drawingPoints = (cache->mesh() == QAbstract3DSeries::MeshPoint);
- float itemSize = cache->itemSize() / itemScaler;
- if (itemSize == 0.0f)
- itemSize = m_dotSizeScale;
- if (drawingPoints) {
- // Scale points based on shadow quality for shadows, not by zoom level
- glPointSize(itemSize * 100.0f * m_shadowQualityMultiplier);
- }
- QVector3D modelScaler(itemSize, itemSize, itemSize);
+ if (m_havePointSeries) {
+ glEnable(GL_POINT_SMOOTH);
+ glEnable(GL_PROGRAM_POINT_SIZE);
+ }
- if (!optimizationDefault
- && ((drawingPoints && cache->bufferPoints()->indexCount() == 0)
- || (!drawingPoints && cache->bufferObject()->indexCount() == 0))) {
- continue;
- }
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ // Render scene into a depth texture for using with shadow mapping
+ // Bind depth shader
+ m_depthShader->bind();
+
+ // Set viewport for depth map rendering. Must match texture size. Larger values give smoother shadows.
+ glViewport(0, 0,
+ m_primarySubViewport.width() * m_shadowQualityMultiplier,
+ m_primarySubViewport.height() * m_shadowQualityMultiplier);
+
+ // Enable drawing to framebuffer
+ glBindFramebuffer(GL_FRAMEBUFFER, m_depthFrameBuffer);
+ glClear(GL_DEPTH_BUFFER_BIT);
+
+ // Set front face culling to reduce self-shadowing issues
+ glCullFace(GL_FRONT);
+
+ QMatrix4x4 depthViewMatrix;
+ QMatrix4x4 depthProjectionMatrix;
+
+ // Get the depth view matrix
+ // It may be possible to hack lightPos here if we want to make some tweaks to shadow
+ QVector3D depthLightPos = activeCamera->d_ptr->calculatePositionRelativeToCamera(
+ zeroVector, 0.0f, 2.5f / m_autoScaleAdjustment);
+ depthViewMatrix.lookAt(depthLightPos, zeroVector, upVector);
+ // Set the depth projection matrix
+ depthProjectionMatrix.perspective(15.0f, viewPortRatio, 3.0f, 100.0f);
+ depthProjectionViewMatrix = depthProjectionMatrix * depthViewMatrix;
+
+ // Draw dots to depth buffer
+ foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
+ if (baseCache->isVisible()) {
+ ScatterSeriesRenderCache *cache =
+ static_cast<ScatterSeriesRenderCache *>(baseCache);
+ ObjectHelper *dotObj = cache->object();
+ QQuaternion seriesRotation(cache->meshRotation());
+ const ScatterRenderItemArray &renderArray = cache->renderArray();
+ const int renderArraySize = renderArray.size();
+ bool drawingPoints = (cache->mesh() == QAbstract3DSeries::MeshPoint);
+ float itemSize = cache->itemSize() / itemScaler;
+ if (itemSize == 0.0f)
+ itemSize = m_dotSizeScale;
+ if (drawingPoints) {
+ // Scale points based on shadow quality for shadows, not by zoom level
+ m_funcs_2_1->glPointSize(itemSize * 100.0f * m_shadowQualityMultiplier);
+ }
+ QVector3D modelScaler(itemSize, itemSize, itemSize);
- int loopCount = 1;
- if (optimizationDefault)
- loopCount = renderArraySize;
- for (int dot = 0; dot < loopCount; dot++) {
- const ScatterRenderItem &item = renderArray.at(dot);
- if (!item.isVisible() && optimizationDefault)
+ if (!optimizationDefault
+ && ((drawingPoints && cache->bufferPoints()->indexCount() == 0)
+ || (!drawingPoints && cache->bufferObject()->indexCount() == 0))) {
continue;
-
- QMatrix4x4 modelMatrix;
- QMatrix4x4 MVPMatrix;
-
- if (optimizationDefault) {
- modelMatrix.translate(item.translation());
- if (!drawingPoints) {
- if (!seriesRotation.isIdentity() || !item.rotation().isIdentity())
- modelMatrix.rotate(seriesRotation * item.rotation());
- modelMatrix.scale(modelScaler);
- }
}
- MVPMatrix = depthProjectionViewMatrix * modelMatrix;
+ int loopCount = 1;
+ if (optimizationDefault)
+ loopCount = renderArraySize;
+ for (int dot = 0; dot < loopCount; dot++) {
+ const ScatterRenderItem &item = renderArray.at(dot);
+ if (!item.isVisible() && optimizationDefault)
+ continue;
- m_depthShader->setUniformValue(m_depthShader->MVP(), MVPMatrix);
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 MVPMatrix;
- if (drawingPoints) {
- if (optimizationDefault)
- m_drawer->drawPoint(m_depthShader);
- else
- m_drawer->drawPoints(m_depthShader, cache->bufferPoints());
- } else {
if (optimizationDefault) {
- // 1st attribute buffer : vertices
- glEnableVertexAttribArray(m_depthShader->posAtt());
- glBindBuffer(GL_ARRAY_BUFFER, dotObj->vertexBuf());
- glVertexAttribPointer(m_depthShader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0,
- (void *)0);
-
- // Index buffer
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dotObj->elementBuf());
+ modelMatrix.translate(item.translation());
+ if (!drawingPoints) {
+ if (!seriesRotation.isIdentity() || !item.rotation().isIdentity())
+ modelMatrix.rotate(seriesRotation * item.rotation());
+ modelMatrix.scale(modelScaler);
+ }
+ }
- // Draw the triangles
- glDrawElements(GL_TRIANGLES, dotObj->indexCount(), GL_UNSIGNED_SHORT,
- (void *)0);
+ MVPMatrix = depthProjectionViewMatrix * modelMatrix;
- // Free buffers
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
- glBindBuffer(GL_ARRAY_BUFFER, 0);
+ m_depthShader->setUniformValue(m_depthShader->MVP(), MVPMatrix);
- glDisableVertexAttribArray(m_depthShader->posAtt());
+ if (drawingPoints) {
+ if (optimizationDefault)
+ m_drawer->drawPoint(m_depthShader);
+ else
+ m_drawer->drawPoints(m_depthShader, cache->bufferPoints(), 0);
} else {
- ScatterObjectBufferHelper *object = cache->bufferObject();
- // 1st attribute buffer : vertices
- glEnableVertexAttribArray(m_depthShader->posAtt());
- glBindBuffer(GL_ARRAY_BUFFER, object->vertexBuf());
- glVertexAttribPointer(m_depthShader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0,
- (void *)0);
-
- // Index buffer
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, object->elementBuf());
-
- // Draw the triangles
- glDrawElements(GL_TRIANGLES, object->indexCount(),
- object->indicesType(), (void *)0);
-
- // Free buffers
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
- glBindBuffer(GL_ARRAY_BUFFER, 0);
-
- glDisableVertexAttribArray(m_depthShader->posAtt());
+ if (optimizationDefault) {
+ // 1st attribute buffer : vertices
+ glEnableVertexAttribArray(m_depthShader->posAtt());
+ glBindBuffer(GL_ARRAY_BUFFER, dotObj->vertexBuf());
+ glVertexAttribPointer(m_depthShader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0,
+ (void *)0);
+
+ // Index buffer
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dotObj->elementBuf());
+
+ // Draw the triangles
+ glDrawElements(GL_TRIANGLES, dotObj->indexCount(), GL_UNSIGNED_SHORT,
+ (void *)0);
+
+ // Free buffers
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+ glDisableVertexAttribArray(m_depthShader->posAtt());
+ } else {
+ ScatterObjectBufferHelper *object = cache->bufferObject();
+ // 1st attribute buffer : vertices
+ glEnableVertexAttribArray(m_depthShader->posAtt());
+ glBindBuffer(GL_ARRAY_BUFFER, object->vertexBuf());
+ glVertexAttribPointer(m_depthShader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0,
+ (void *)0);
+
+ // Index buffer
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, object->elementBuf());
+
+ // Draw the triangles
+ glDrawElements(GL_TRIANGLES, object->indexCount(),
+ object->indicesType(), (void *)0);
+
+ // Free buffers
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+ glDisableVertexAttribArray(m_depthShader->posAtt());
+ }
}
}
}
}
- }
- Abstract3DRenderer::drawCustomItems(RenderingDepth, m_depthShader, viewMatrix,
- projectionViewMatrix, depthProjectionViewMatrix,
- m_depthTexture, m_shadowQualityToShader);
+ Abstract3DRenderer::drawCustomItems(RenderingDepth, m_depthShader, viewMatrix,
+ projectionViewMatrix,
+ depthProjectionViewMatrix, m_depthTexture,
+ m_shadowQualityToShader);
- // Disable drawing to framebuffer (= enable drawing to screen)
- glBindFramebuffer(GL_FRAMEBUFFER, defaultFboHandle);
+ // Disable drawing to framebuffer (= enable drawing to screen)
+ glBindFramebuffer(GL_FRAMEBUFFER, defaultFboHandle);
- // Reset culling to normal
- glCullFace(GL_BACK);
+ // Reset culling to normal
+ glCullFace(GL_BACK);
- // Revert to original viewport
- glViewport(m_primarySubViewport.x(),
- m_primarySubViewport.y(),
- m_primarySubViewport.width(),
- m_primarySubViewport.height());
+ // Revert to original viewport
+ glViewport(m_primarySubViewport.x(),
+ m_primarySubViewport.y(),
+ m_primarySubViewport.width(),
+ m_primarySubViewport.height());
+ }
+#endif
+ pointSelectionShader = m_selectionShader;
+ } else {
+ pointSelectionShader = m_pointShader;
}
- ShaderHelper *pointSelectionShader = m_selectionShader;
-#else
- ShaderHelper *pointSelectionShader = m_pointShader;
-#endif
ShaderHelper *selectionShader = m_selectionShader;
+ // Do position mapping when necessary
+ if (m_graphPositionQueryPending) {
+ QVector3D graphDimensions(m_scaleX, m_scaleY, m_scaleZ);
+ queriedGraphPosition(projectionViewMatrix, graphDimensions, defaultFboHandle);
+ emit needRender();
+ }
+
// Skip selection mode drawing if we have no selection mode
if (m_cachedSelectionMode > QAbstract3DGraph::SelectionNone
&& SelectOnScene == m_selectionState
- && (m_visibleSeriesCount > 0 || !m_customRenderCache.isEmpty())) {
+ && (m_visibleSeriesCount > 0 || !m_customRenderCache.isEmpty())
+ && m_selectionTexture) {
// Draw dots to selection buffer
glBindFramebuffer(GL_FRAMEBUFFER, m_selectionFrameBuffer);
glViewport(0, 0,
@@ -578,8 +738,8 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
if (itemSize == 0.0f)
itemSize = m_dotSizeScale;
#if !defined(QT_OPENGL_ES_2)
- if (drawingPoints)
- glPointSize(itemSize * activeCamera->zoomLevel()); // Scale points based on zoom
+ if (drawingPoints && !m_isOpenGLES)
+ m_funcs_2_1->glPointSize(itemSize * activeCamera->zoomLevel());
#endif
QVector3D modelScaler(itemSize, itemSize, itemSize);
@@ -627,9 +787,10 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
}
}
- Abstract3DRenderer::drawCustomItems(RenderingSelection, m_selectionShader, viewMatrix,
- projectionViewMatrix, depthProjectionViewMatrix,
- m_depthTexture, m_shadowQualityToShader);
+ Abstract3DRenderer::drawCustomItems(RenderingSelection, m_selectionShader,
+ viewMatrix, projectionViewMatrix,
+ depthProjectionViewMatrix, m_depthTexture,
+ m_shadowQualityToShader);
drawLabels(true, activeCamera, viewMatrix, projectionMatrix);
@@ -639,6 +800,7 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
QVector4D clickedColor = Utils::getSelection(m_inputPosition,
m_viewport.height());
selectionColorToSeriesAndIndex(clickedColor, m_clickedIndex, m_clickedSeries);
+ m_clickResolved = true;
emit needRender();
@@ -683,13 +845,12 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
previousMeshColorStyle = Q3DTheme::ColorStyleRangeGradient;
m_dotGradientShader->setUniformValue(m_dotGradientShader->gradientHeight(), 0.0f);
}
- glEnable(GL_TEXTURE_2D);
} else {
dotShader = pointSelectionShader;
- previousDrawingPoints = true;
- dotShader->bind();
}
+ float rangeGradientYScaler = 0.5f / m_scaleY;
+
foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
if (baseCache->isVisible()) {
ScatterSeriesRenderCache *cache =
@@ -710,14 +871,16 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
if (itemSize == 0.0f)
itemSize = m_dotSizeScale;
#if !defined(QT_OPENGL_ES_2)
- if (drawingPoints)
- glPointSize(itemSize * activeCamera->zoomLevel()); // Scale points based on zoom
+ if (drawingPoints && !m_isOpenGLES)
+ m_funcs_2_1->glPointSize(itemSize * activeCamera->zoomLevel());
#endif
QVector3D modelScaler(itemSize, itemSize, itemSize);
+ int gradientImageHeight = cache->gradientImage().height();
+ int maxGradientPositition = gradientImageHeight - 1;
if (!optimizationDefault
&& ((drawingPoints && cache->bufferPoints()->indexCount() == 0)
- || (!drawingPoints && cache->bufferObject()->indexCount() == 0))) {
+ || (!drawingPoints && cache->bufferObject()->indexCount() == 0))) {
continue;
}
@@ -725,10 +888,18 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
if (drawingPoints != previousDrawingPoints
|| (!drawingPoints &&
(colorStyleIsUniform != (previousMeshColorStyle
- == Q3DTheme::ColorStyleUniform)))) {
+ == Q3DTheme::ColorStyleUniform)))
+ || (!optimizationDefault && drawingPoints)) {
previousDrawingPoints = drawingPoints;
if (drawingPoints) {
- dotShader = pointSelectionShader;
+ if (!optimizationDefault && rangeGradientPoints) {
+ if (m_isOpenGLES)
+ dotShader = m_staticGradientPointShader;
+ else
+ dotShader = m_labelShader;
+ } else {
+ dotShader = pointSelectionShader;
+ }
} else {
if (colorStyleIsUniform)
dotShader = m_dotShader;
@@ -740,13 +911,13 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
if (!drawingPoints && !colorStyleIsUniform && previousMeshColorStyle != colorStyle) {
if (colorStyle == Q3DTheme::ColorStyleObjectGradient) {
- m_dotGradientShader->setUniformValue(m_dotGradientShader->gradientMin(), 0.0f);
- m_dotGradientShader->setUniformValue(m_dotGradientShader->gradientHeight(),
- 0.5f);
+ dotShader->setUniformValue(dotShader->gradientMin(), 0.0f);
+ dotShader->setUniformValue(dotShader->gradientHeight(),
+ 0.5f);
} else {
// Each dot is of uniform color according to its Y-coordinate
- m_dotGradientShader->setUniformValue(m_dotGradientShader->gradientHeight(),
- 0.0f);
+ dotShader->setUniformValue(dotShader->gradientHeight(),
+ 0.0f);
}
}
@@ -760,6 +931,7 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
int loopCount = 1;
if (optimizationDefault)
loopCount = renderArraySize;
+
for (int i = 0; i < loopCount; i++) {
ScatterRenderItem &item = renderArray[i];
if (!item.isVisible() && optimizationDefault)
@@ -791,7 +963,8 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
if (rangeGradientPoints) {
// Drawing points with range gradient
// Get color from gradient based on items y position converted to percent
- int position = int(item.translation().y() * 50.0f) + 50;
+ int position = ((item.translation().y() + m_scaleY) * rangeGradientYScaler) * gradientImageHeight;
+ position = qMin(maxGradientPositition, position); // clamp to edge
dotColor = Utils::vectorFromColor(
cache->gradientImage().pixel(0, position));
} else {
@@ -801,6 +974,9 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
gradientTexture = cache->baseGradientTexture();
}
+ if (!optimizationDefault && rangeGradientPoints)
+ gradientTexture = cache->baseGradientTexture();
+
GLfloat lightStrength = m_cachedTheme->lightStrength();
if (optimizationDefault && selectedSeries && (m_selectedItemIndex == i)) {
if (useColor)
@@ -808,13 +984,12 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
else
gradientTexture = cache->singleHighlightGradientTexture();
lightStrength = m_cachedTheme->highlightLightStrength();
- // Insert data to ScatterRenderItem
- // We don't have ownership, so don't delete the previous one
+ // Save the reference to the item to be used in label drawing
selectedItem = &item;
dotSelectionFound = true;
// Save selected item size (adjusted with font size) for selection label
// positioning
- selectedItemSize = itemSize + (m_cachedTheme->font().pointSizeF() / 500.0f);
+ selectedItemSize = itemSize + m_drawer->scaledFontSize() - 0.05f;
}
if (!drawingPoints) {
@@ -829,10 +1004,10 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
dotShader->setUniformValue(dotShader->color(), dotColor);
} else if (colorStyle == Q3DTheme::ColorStyleRangeGradient) {
dotShader->setUniformValue(dotShader->gradientMin(),
- (item.translation().y() + 1.0f) / 2.0f);
+ (item.translation().y() + m_scaleY)
+ * rangeGradientYScaler);
}
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) {
if (!drawingPoints) {
// Set shadow shader bindings
QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
@@ -853,11 +1028,9 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
if (optimizationDefault)
m_drawer->drawPoint(dotShader);
else
- m_drawer->drawPoints(dotShader, cache->bufferPoints());
+ m_drawer->drawPoints(dotShader, cache->bufferPoints(), gradientTexture);
}
- } else
-#endif
- {
+ } else {
if (!drawingPoints) {
// Set shadowless shader bindings
dotShader->setUniformValue(dotShader->lightS(), lightStrength);
@@ -871,100 +1044,139 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
if (optimizationDefault)
m_drawer->drawPoint(dotShader);
else
- m_drawer->drawPoints(dotShader, cache->bufferPoints());
+ m_drawer->drawPoints(dotShader, cache->bufferPoints(), gradientTexture);
}
}
}
+
// Draw the selected item on static optimization
if (!optimizationDefault && selectedSeries
&& m_selectedItemIndex != Scatter3DController::invalidSelectionIndex()) {
ScatterRenderItem &item = renderArray[m_selectedItemIndex];
- ObjectHelper *dotObj = cache->object();
+ if (item.isVisible()) {
+ ShaderHelper *selectionShader;
+ if (drawingPoints) {
+ selectionShader = pointSelectionShader;
+ } else {
+ if (colorStyleIsUniform)
+ selectionShader = m_staticSelectedItemShader;
+ else
+ selectionShader = m_staticSelectedItemGradientShader;
+ }
+ selectionShader->bind();
- QMatrix4x4 modelMatrix;
- QMatrix4x4 itModelMatrix;
+ ObjectHelper *dotObj = cache->object();
- modelMatrix.translate(item.translation());
- if (!drawingPoints) {
- if (!seriesRotation.isIdentity() || !item.rotation().isIdentity()) {
- QQuaternion totalRotation = seriesRotation * item.rotation();
- modelMatrix.rotate(totalRotation);
- itModelMatrix.rotate(totalRotation);
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 itModelMatrix;
+
+ modelMatrix.translate(item.translation());
+ if (!drawingPoints) {
+ if (!seriesRotation.isIdentity() || !item.rotation().isIdentity()) {
+ QQuaternion totalRotation = seriesRotation * item.rotation();
+ modelMatrix.rotate(totalRotation);
+ itModelMatrix.rotate(totalRotation);
+ }
+ modelMatrix.scale(modelScaler);
+ itModelMatrix.scale(modelScaler);
+
+ selectionShader->setUniformValue(selectionShader->lightP(),
+ lightPos);
+ selectionShader->setUniformValue(selectionShader->view(),
+ viewMatrix);
+ selectionShader->setUniformValue(selectionShader->ambientS(),
+ m_cachedTheme->ambientLightStrength());
+ selectionShader->setUniformValue(selectionShader->lightColor(),
+ lightColor);
}
- modelMatrix.scale(modelScaler);
- itModelMatrix.scale(modelScaler);
- }
- QMatrix4x4 MVPMatrix;
+ QMatrix4x4 MVPMatrix;
#ifdef SHOW_DEPTH_TEXTURE_SCENE
- MVPMatrix = depthProjectionViewMatrix * modelMatrix;
+ MVPMatrix = depthProjectionViewMatrix * modelMatrix;
#else
- MVPMatrix = projectionViewMatrix * modelMatrix;
+ MVPMatrix = projectionViewMatrix * modelMatrix;
#endif
- if (useColor)
- dotColor = cache->singleHighlightColor();
- else
- gradientTexture = cache->singleHighlightGradientTexture();
- GLfloat lightStrength = m_cachedTheme->highlightLightStrength();
- // Save the reference to the item to be used on label drawing
- selectedItem = &item;
- dotSelectionFound = true;
- // Save selected item size (adjusted with font size) for selection label
- // positioning
- selectedItemSize = itemSize + (m_cachedTheme->font().pointSizeF() / 500.0f);
-
- if (!drawingPoints) {
- // Set shader bindings
- dotShader->setUniformValue(dotShader->model(), modelMatrix);
- dotShader->setUniformValue(dotShader->nModel(),
- itModelMatrix.inverted().transposed());
- }
+ if (useColor)
+ dotColor = cache->singleHighlightColor();
+ else
+ gradientTexture = cache->singleHighlightGradientTexture();
+ GLfloat lightStrength = m_cachedTheme->highlightLightStrength();
+ // Save the reference to the item to be used in label drawing
+ selectedItem = &item;
+ dotSelectionFound = true;
+ // Save selected item size (adjusted with font size) for selection label
+ // positioning
+ selectedItemSize = itemSize + m_drawer->scaledFontSize() - 0.05f;
- dotShader->setUniformValue(dotShader->MVP(), MVPMatrix);
- if (useColor) {
- dotShader->setUniformValue(dotShader->color(), dotColor);
- } else if (colorStyle == Q3DTheme::ColorStyleRangeGradient) {
- dotShader->setUniformValue(dotShader->gradientMin(),
- (item.translation().y() + 1.0f) / 2.0f);
- }
+ if (!drawingPoints) {
+ // Set shader bindings
+ selectionShader->setUniformValue(selectionShader->model(), modelMatrix);
+ selectionShader->setUniformValue(selectionShader->nModel(),
+ itModelMatrix.inverted().transposed());
+ if (!colorStyleIsUniform) {
+ if (colorStyle == Q3DTheme::ColorStyleObjectGradient) {
+ selectionShader->setUniformValue(selectionShader->gradientMin(),
+ 0.0f);
+ selectionShader->setUniformValue(selectionShader->gradientHeight(),
+ 0.5f);
+ } else {
+ // Each dot is of uniform color according to its Y-coordinate
+ selectionShader->setUniformValue(selectionShader->gradientHeight(),
+ 0.0f);
+ selectionShader->setUniformValue(selectionShader->gradientMin(),
+ (item.translation().y() + m_scaleY)
+ * rangeGradientYScaler);
+ }
+ }
+ }
- if (!drawingPoints) {
- glEnable(GL_POLYGON_OFFSET_FILL);
- glPolygonOffset(-1.0f, 1.0f);
- }
+ selectionShader->setUniformValue(selectionShader->MVP(), MVPMatrix);
+ if (useColor)
+ selectionShader->setUniformValue(selectionShader->color(), dotColor);
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
if (!drawingPoints) {
- // Set shadow shader bindings
- QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
- dotShader->setUniformValue(dotShader->depth(), depthMVPMatrix);
- dotShader->setUniformValue(dotShader->lightS(), lightStrength / 10.0f);
-
- // Draw the object
- m_drawer->drawObject(dotShader, dotObj, gradientTexture, m_depthTexture);
- } else {
- // Draw the object
- m_drawer->drawPoint(dotShader);
+ glEnable(GL_POLYGON_OFFSET_FILL);
+ glPolygonOffset(-1.0f, 1.0f);
}
- } else
-#endif
- {
- if (!drawingPoints) {
- // Set shadowless shader bindings
- dotShader->setUniformValue(dotShader->lightS(), lightStrength);
- // Draw the object
- m_drawer->drawObject(dotShader, dotObj, gradientTexture);
+
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone
+ && !m_isOpenGLES) {
+ if (!drawingPoints) {
+ // Set shadow shader bindings
+ QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
+ selectionShader->setUniformValue(selectionShader->shadowQ(),
+ m_shadowQualityToShader);
+ selectionShader->setUniformValue(selectionShader->depth(),
+ depthMVPMatrix);
+ selectionShader->setUniformValue(selectionShader->lightS(),
+ lightStrength / 10.0f);
+
+ // Draw the object
+ m_drawer->drawObject(selectionShader, dotObj, gradientTexture,
+ m_depthTexture);
+ } else {
+ // Draw the object
+ m_drawer->drawPoint(selectionShader);
+ }
} else {
- // Draw the object
- m_drawer->drawPoint(dotShader);
+ if (!drawingPoints) {
+ // Set shadowless shader bindings
+ selectionShader->setUniformValue(selectionShader->lightS(),
+ lightStrength);
+ // Draw the object
+ m_drawer->drawObject(selectionShader, dotObj, gradientTexture);
+ } else {
+ // Draw the object
+ m_drawer->drawPoint(selectionShader);
+ }
}
- }
- if (!drawingPoints)
- glDisable(GL_POLYGON_OFFSET_FILL);
+ if (!drawingPoints)
+ glDisable(GL_POLYGON_OFFSET_FILL);
+ }
+ dotShader->bind();
}
}
}
@@ -987,25 +1199,12 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
QMatrix4x4 MVPMatrix;
QMatrix4x4 itModelMatrix;
-#ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z
- GLfloat xScale = (m_graphAspectRatio * m_areaSize.width()) / m_scaleFactor
- + m_backgroundMargin;
- GLfloat zScale = (m_graphAspectRatio * m_areaSize.height()) / m_scaleFactor
- + m_backgroundMargin;
- if (m_maxItemSize > xScale)
- xScale = m_maxItemSize;
- if (m_maxItemSize > zScale)
- zScale = m_maxItemSize;
- QVector3D bgScale(xScale, 1.0f + m_backgroundMargin, zScale);
-#else // ..and this if we want uniform scaling based on largest dimension
- QVector3D bgScale((m_graphAspectRatio + m_backgroundMargin),
- 1.0f + m_backgroundMargin,
- (m_graphAspectRatio + m_backgroundMargin));
-#endif
+ QVector3D bgScale(m_scaleXWithBackground, m_scaleYWithBackground,
+ m_scaleZWithBackground);
modelMatrix.scale(bgScale);
// If we're viewing from below, background object must be flipped
if (m_yFlipped) {
- modelMatrix.rotate(180.0f, 1.0, 0.0, 0.0);
+ modelMatrix.rotate(m_xFlipRotation);
modelMatrix.rotate(270.0f - backgroundRotation, 0.0f, 1.0f, 0.0f);
} else {
modelMatrix.rotate(backgroundRotation, 0.0f, 1.0f, 0.0f);
@@ -1031,8 +1230,7 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
m_cachedTheme->ambientLightStrength() * 2.0f);
m_backgroundShader->setUniformValue(m_backgroundShader->lightColor(), lightColor);
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) {
// Set shadow shader bindings
QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
m_backgroundShader->setUniformValue(m_backgroundShader->shadowQ(),
@@ -1043,9 +1241,7 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
// Draw the object
m_drawer->drawObject(m_backgroundShader, m_backgroundObj, 0, m_depthTexture);
- } else
-#endif
- {
+ } else {
// Set shadowless shader bindings
m_backgroundShader->setUniformValue(m_backgroundShader->lightS(),
m_cachedTheme->lightStrength());
@@ -1055,16 +1251,17 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
}
}
- // Disable textures
- glDisable(GL_TEXTURE_2D);
-
// Draw grid lines
+ QVector3D gridLineScaleX(m_scaleXWithBackground, gridLineWidth, gridLineWidth);
+ QVector3D gridLineScaleZ(gridLineWidth, gridLineWidth, m_scaleZWithBackground);
+ QVector3D gridLineScaleY(gridLineWidth, m_scaleYWithBackground, gridLineWidth);
+
if (m_cachedTheme->isGridEnabled()) {
-#if !(defined QT_OPENGL_ES_2)
- ShaderHelper *lineShader = m_backgroundShader;
-#else
- ShaderHelper *lineShader = m_selectionShader; // Plain color shader for GL_LINES
-#endif
+ ShaderHelper *lineShader;
+ if (m_isOpenGLES)
+ lineShader = m_selectionShader; // Plain color shader for GL_LINES
+ else
+ lineShader = m_backgroundShader;
// Bind line shader
lineShader->bind();
@@ -1076,15 +1273,12 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
lineShader->setUniformValue(lineShader->color(), lineColor);
lineShader->setUniformValue(lineShader->ambientS(), m_cachedTheme->ambientLightStrength());
lineShader->setUniformValue(lineShader->lightColor(), lightColor);
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) {
// Set shadowed shader bindings
lineShader->setUniformValue(lineShader->shadowQ(), m_shadowQualityToShader);
lineShader->setUniformValue(lineShader->lightS(),
m_cachedTheme->lightStrength() / 20.0f);
- } else
-#endif
- {
+ } else {
// Set shadowless shader bindings
lineShader->setUniformValue(lineShader->lightS(),
m_cachedTheme->lightStrength() / 2.5f);
@@ -1094,242 +1288,213 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
QQuaternion lineXRotation;
if (m_xFlipped)
- lineYRotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, -90.0f);
+ lineYRotation = m_yRightAngleRotationNeg;
else
- lineYRotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, 90.0f);
+ lineYRotation = m_yRightAngleRotation;
- if (m_yFlipped)
- lineXRotation = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, 90.0f);
+ if (m_yFlippedForGrid)
+ lineXRotation = m_xRightAngleRotation;
else
- lineXRotation = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, -90.0f);
+ lineXRotation = m_xRightAngleRotationNeg;
- GLfloat yFloorLinePosition = -1.0f - m_backgroundMargin + gridLineOffset;
- if (m_yFlipped)
+ GLfloat yFloorLinePosition = -m_scaleYWithBackground + gridLineOffset;
+ if (m_yFlippedForGrid)
yFloorLinePosition = -yFloorLinePosition;
// Rows (= Z)
if (m_axisCacheZ.segmentCount() > 0) {
// Floor lines
int gridLineCount = m_axisCacheZ.gridLineCount();
+ if (m_polarGraph) {
+ drawRadialGrid(lineShader, yFloorLinePosition, projectionViewMatrix,
+ depthProjectionViewMatrix);
+ } else {
+ for (int line = 0; line < gridLineCount; line++) {
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 MVPMatrix;
+ QMatrix4x4 itModelMatrix;
-#ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z
- GLfloat xScale = (m_graphAspectRatio * m_areaSize.width()) / m_scaleFactor
- + m_backgroundMargin;
- if (m_maxItemSize > xScale)
- xScale = m_maxItemSize;
- QVector3D gridLineScaler(xScale, gridLineWidth, gridLineWidth);
-#else // ..and this if we want uniform scaling based on largest dimension
- QVector3D gridLineScaler((m_graphAspectRatio + m_backgroundMargin),
- gridLineWidth, gridLineWidth);
-#endif
+ modelMatrix.translate(0.0f, yFloorLinePosition,
+ m_axisCacheZ.gridLinePosition(line));
- for (int line = 0; line < gridLineCount; line++) {
- QMatrix4x4 modelMatrix;
- QMatrix4x4 MVPMatrix;
- QMatrix4x4 itModelMatrix;
-
- modelMatrix.translate(0.0f, yFloorLinePosition,
- m_axisCacheZ.gridLinePosition(line));
+ modelMatrix.scale(gridLineScaleX);
+ itModelMatrix.scale(gridLineScaleX);
- modelMatrix.scale(gridLineScaler);
- itModelMatrix.scale(gridLineScaler);
+ modelMatrix.rotate(lineXRotation);
+ itModelMatrix.rotate(lineXRotation);
- modelMatrix.rotate(lineXRotation);
- itModelMatrix.rotate(lineXRotation);
+ MVPMatrix = projectionViewMatrix * modelMatrix;
- MVPMatrix = projectionViewMatrix * modelMatrix;
+ // Set the rest of the shader bindings
+ lineShader->setUniformValue(lineShader->model(), modelMatrix);
+ lineShader->setUniformValue(lineShader->nModel(),
+ itModelMatrix.inverted().transposed());
+ lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
- // Set the rest of the shader bindings
- lineShader->setUniformValue(lineShader->model(), modelMatrix);
- lineShader->setUniformValue(lineShader->nModel(),
- itModelMatrix.inverted().transposed());
- lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
-
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
- QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
- // Set shadow shader bindings
- lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
- } else {
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj);
+ if (m_isOpenGLES) {
+ m_drawer->drawLine(lineShader);
+ } else {
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
+ // Set shadow shader bindings
+ lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ } else {
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj);
+ }
+ }
}
-#else
- m_drawer->drawLine(lineShader);
-#endif
- }
- // Side wall lines
- gridLineScaler = QVector3D(gridLineWidth, 1.0f + m_backgroundMargin, gridLineWidth);
-#ifndef USE_UNIFORM_SCALING
- GLfloat lineXTrans = (m_graphAspectRatio * m_areaSize.width())
- / m_scaleFactor - gridLineOffset + m_backgroundMargin;
- if (m_maxItemSize > lineXTrans)
- lineXTrans = m_maxItemSize - gridLineOffset;
-#else
- GLfloat lineXTrans = m_graphAspectRatio + m_backgroundMargin - gridLineOffset;
-#endif
- if (!m_xFlipped)
- lineXTrans = -lineXTrans;
+ // Side wall lines
+ GLfloat lineXTrans = m_scaleXWithBackground - gridLineOffset;
- for (int line = 0; line < gridLineCount; line++) {
- QMatrix4x4 modelMatrix;
- QMatrix4x4 MVPMatrix;
- QMatrix4x4 itModelMatrix;
+ if (!m_xFlipped)
+ lineXTrans = -lineXTrans;
- modelMatrix.translate(lineXTrans, 0.0f, m_axisCacheZ.gridLinePosition(line));
+ for (int line = 0; line < gridLineCount; line++) {
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 MVPMatrix;
+ QMatrix4x4 itModelMatrix;
- modelMatrix.scale(gridLineScaler);
- itModelMatrix.scale(gridLineScaler);
+ modelMatrix.translate(lineXTrans, 0.0f, m_axisCacheZ.gridLinePosition(line));
-#if !defined(QT_OPENGL_ES_2)
- modelMatrix.rotate(lineYRotation);
- itModelMatrix.rotate(lineYRotation);
-#else
- modelMatrix.rotate(90.0f, 0.0f, 0.0f, 1.0f);
- itModelMatrix.rotate(90.0f, 0.0f, 0.0f, 1.0f);
-#endif
+ modelMatrix.scale(gridLineScaleY);
+ itModelMatrix.scale(gridLineScaleY);
- MVPMatrix = projectionViewMatrix * modelMatrix;
+ if (m_isOpenGLES) {
+ modelMatrix.rotate(m_zRightAngleRotation);
+ itModelMatrix.rotate(m_zRightAngleRotation);
+ } else {
+ modelMatrix.rotate(lineYRotation);
+ itModelMatrix.rotate(lineYRotation);
+ }
- // Set the rest of the shader bindings
- lineShader->setUniformValue(lineShader->model(), modelMatrix);
- lineShader->setUniformValue(lineShader->nModel(),
- itModelMatrix.inverted().transposed());
- lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
+ MVPMatrix = projectionViewMatrix * modelMatrix;
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
- // Set shadow shader bindings
- QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
- lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
- } else {
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj);
+ // Set the rest of the shader bindings
+ lineShader->setUniformValue(lineShader->model(), modelMatrix);
+ lineShader->setUniformValue(lineShader->nModel(),
+ itModelMatrix.inverted().transposed());
+ lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
+
+ if (!m_isOpenGLES) {
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ // Set shadow shader bindings
+ QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
+ lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ } else {
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj);
+ }
+ } else {
+ m_drawer->drawLine(lineShader);
+ }
}
-#else
- m_drawer->drawLine(lineShader);
-#endif
}
}
// Columns (= X)
if (m_axisCacheX.segmentCount() > 0) {
-#if defined(QT_OPENGL_ES_2)
- lineXRotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, 90.0f);
-#endif
+ if (m_isOpenGLES)
+ lineXRotation = m_yRightAngleRotation;
// Floor lines
int gridLineCount = m_axisCacheX.gridLineCount();
-#ifndef USE_UNIFORM_SCALING
- GLfloat zScale = (m_graphAspectRatio * m_areaSize.height()) / m_scaleFactor
- + m_backgroundMargin;
- if (m_maxItemSize > zScale)
- zScale = m_maxItemSize;
- QVector3D gridLineScaler(gridLineWidth, gridLineWidth, zScale);
-#else
- QVector3D gridLineScaler(gridLineWidth, gridLineWidth,
- m_graphAspectRatio + m_backgroundMargin);
-#endif
-
- for (int line = 0; line < gridLineCount; line++) {
- QMatrix4x4 modelMatrix;
- QMatrix4x4 MVPMatrix;
- QMatrix4x4 itModelMatrix;
-
- modelMatrix.translate(m_axisCacheX.gridLinePosition(line), yFloorLinePosition,
- 0.0f);
+ if (m_polarGraph) {
+ drawAngularGrid(lineShader, yFloorLinePosition, projectionViewMatrix,
+ depthProjectionViewMatrix);
+ } else {
+ for (int line = 0; line < gridLineCount; line++) {
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 MVPMatrix;
+ QMatrix4x4 itModelMatrix;
- modelMatrix.scale(gridLineScaler);
- itModelMatrix.scale(gridLineScaler);
+ modelMatrix.translate(m_axisCacheX.gridLinePosition(line), yFloorLinePosition,
+ 0.0f);
- modelMatrix.rotate(lineXRotation);
- itModelMatrix.rotate(lineXRotation);
+ modelMatrix.scale(gridLineScaleZ);
+ itModelMatrix.scale(gridLineScaleZ);
- MVPMatrix = projectionViewMatrix * modelMatrix;
+ modelMatrix.rotate(lineXRotation);
+ itModelMatrix.rotate(lineXRotation);
- // Set the rest of the shader bindings
- lineShader->setUniformValue(lineShader->model(), modelMatrix);
- lineShader->setUniformValue(lineShader->nModel(),
- itModelMatrix.inverted().transposed());
- lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
+ MVPMatrix = projectionViewMatrix * modelMatrix;
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
- // Set shadow shader bindings
- QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
- lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
- } else {
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj);
+ // Set the rest of the shader bindings
+ lineShader->setUniformValue(lineShader->model(), modelMatrix);
+ lineShader->setUniformValue(lineShader->nModel(),
+ itModelMatrix.inverted().transposed());
+ lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
+
+ if (!m_isOpenGLES) {
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ // Set shadow shader bindings
+ QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
+ lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ } else {
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj);
+ }
+ } else {
+ m_drawer->drawLine(lineShader);
+ }
}
-#else
- m_drawer->drawLine(lineShader);
-#endif
- }
-
- // Back wall lines
-#ifndef USE_UNIFORM_SCALING
- GLfloat lineZTrans = (m_graphAspectRatio * m_areaSize.height())
- / m_scaleFactor - gridLineOffset + m_backgroundMargin;
- if (m_maxItemSize > lineZTrans)
- lineZTrans = m_maxItemSize - gridLineOffset;
-#else
- GLfloat lineZTrans = m_graphAspectRatio + m_backgroundMargin - gridLineOffset;
-#endif
- if (!m_zFlipped)
- lineZTrans = -lineZTrans;
- gridLineScaler = QVector3D(gridLineWidth, 1.0f + m_backgroundMargin, gridLineWidth);
+ // Back wall lines
+ GLfloat lineZTrans = m_scaleZWithBackground - gridLineOffset;
- for (int line = 0; line < gridLineCount; line++) {
- QMatrix4x4 modelMatrix;
- QMatrix4x4 MVPMatrix;
- QMatrix4x4 itModelMatrix;
+ if (!m_zFlipped)
+ lineZTrans = -lineZTrans;
- modelMatrix.translate(m_axisCacheX.gridLinePosition(line), 0.0f, lineZTrans);
+ for (int line = 0; line < gridLineCount; line++) {
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 MVPMatrix;
+ QMatrix4x4 itModelMatrix;
- modelMatrix.scale(gridLineScaler);
- itModelMatrix.scale(gridLineScaler);
+ modelMatrix.translate(m_axisCacheX.gridLinePosition(line), 0.0f, lineZTrans);
-#if !defined(QT_OPENGL_ES_2)
- if (m_zFlipped) {
- modelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f);
- itModelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f);
- }
-#else
- modelMatrix.rotate(90.0f, 0.0f, 0.0f, 1.0f);
- itModelMatrix.rotate(90.0f, 0.0f, 0.0f, 1.0f);
-#endif
+ modelMatrix.scale(gridLineScaleY);
+ itModelMatrix.scale(gridLineScaleY);
- MVPMatrix = projectionViewMatrix * modelMatrix;
+ if (m_isOpenGLES) {
+ modelMatrix.rotate(m_zRightAngleRotation);
+ itModelMatrix.rotate(m_zRightAngleRotation);
+ } else {
+ if (m_zFlipped) {
+ modelMatrix.rotate(m_xFlipRotation);
+ itModelMatrix.rotate(m_xFlipRotation);
+ }
+ }
- // Set the rest of the shader bindings
- lineShader->setUniformValue(lineShader->model(), modelMatrix);
- lineShader->setUniformValue(lineShader->nModel(),
- itModelMatrix.inverted().transposed());
- lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
+ MVPMatrix = projectionViewMatrix * modelMatrix;
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
- // Set shadow shader bindings
- QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
- lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
- } else {
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj);
+ // Set the rest of the shader bindings
+ lineShader->setUniformValue(lineShader->model(), modelMatrix);
+ lineShader->setUniformValue(lineShader->nModel(),
+ itModelMatrix.inverted().transposed());
+ lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
+
+ if (!m_isOpenGLES) {
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ // Set shadow shader bindings
+ QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
+ lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ } else {
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj);
+ }
+ } else {
+ m_drawer->drawLine(lineShader);
+ }
}
-#else
- m_drawer->drawLine(lineShader);
-#endif
}
}
@@ -1338,21 +1503,8 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
// Back wall
int gridLineCount = m_axisCacheY.gridLineCount();
-#ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z
- GLfloat lineZTrans = (m_graphAspectRatio * m_areaSize.height())
- / m_scaleFactor - gridLineOffset + m_backgroundMargin;
- if (m_maxItemSize > lineZTrans)
- lineZTrans = m_maxItemSize - gridLineOffset;
- GLfloat xScale = (m_graphAspectRatio * m_areaSize.width()) / m_scaleFactor
- + m_backgroundMargin;
- if (m_maxItemSize > xScale)
- xScale = m_maxItemSize;
- QVector3D gridLineScaler(xScale, gridLineWidth, gridLineWidth);
-#else // ..and this if we want uniform scaling based on largest dimension
- GLfloat lineZTrans = m_graphAspectRatio + m_backgroundMargin - gridLineOffset;
- QVector3D gridLineScaler((m_graphAspectRatio + m_backgroundMargin),
- gridLineWidth, gridLineWidth);
-#endif
+ GLfloat lineZTrans = m_scaleZWithBackground - gridLineOffset;
+
if (!m_zFlipped)
lineZTrans = -lineZTrans;
@@ -1363,12 +1515,12 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
modelMatrix.translate(0.0f, m_axisCacheY.gridLinePosition(line), lineZTrans);
- modelMatrix.scale(gridLineScaler);
- itModelMatrix.scale(gridLineScaler);
+ modelMatrix.scale(gridLineScaleX);
+ itModelMatrix.scale(gridLineScaleX);
if (m_zFlipped) {
- modelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f);
- itModelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f);
+ modelMatrix.rotate(m_xFlipRotation);
+ itModelMatrix.rotate(m_xFlipRotation);
}
MVPMatrix = projectionViewMatrix * modelMatrix;
@@ -1379,38 +1531,25 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
itModelMatrix.inverted().transposed());
lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
- // Set shadow shader bindings
- QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
- lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ if (!m_isOpenGLES) {
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ // Set shadow shader bindings
+ QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
+ lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ } else {
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj);
+ }
} else {
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj);
+ m_drawer->drawLine(lineShader);
}
-#else
- m_drawer->drawLine(lineShader);
-#endif
}
// Side wall
-#ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z
- GLfloat lineXTrans = (m_graphAspectRatio * m_areaSize.width())
- / m_scaleFactor - gridLineOffset + m_backgroundMargin;
- if (m_maxItemSize > lineXTrans)
- lineXTrans = m_maxItemSize - gridLineOffset;
- GLfloat zScale = (m_graphAspectRatio * m_areaSize.height())
- / m_scaleFactor + m_backgroundMargin;
- if (m_maxItemSize > zScale)
- zScale = m_maxItemSize;
- gridLineScaler = QVector3D(gridLineWidth, gridLineWidth, zScale);
-#else // ..and this if we want uniform scaling based on largest dimension
- GLfloat lineXTrans = m_graphAspectRatio + m_backgroundMargin - gridLineOffset;
- gridLineScaler = QVector3D(gridLineWidth, gridLineWidth,
- m_graphAspectRatio + m_backgroundMargin);
-#endif
+ GLfloat lineXTrans = m_scaleXWithBackground - gridLineOffset;
+
if (!m_xFlipped)
lineXTrans = -lineXTrans;
@@ -1421,8 +1560,8 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
modelMatrix.translate(lineXTrans, m_axisCacheY.gridLinePosition(line), 0.0f);
- modelMatrix.scale(gridLineScaler);
- itModelMatrix.scale(gridLineScaler);
+ modelMatrix.scale(gridLineScaleZ);
+ itModelMatrix.scale(gridLineScaleZ);
modelMatrix.rotate(lineYRotation);
itModelMatrix.rotate(lineYRotation);
@@ -1435,20 +1574,20 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
itModelMatrix.inverted().transposed());
lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
- // Set shadow shader bindings
- QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
- lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ if (!m_isOpenGLES) {
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ // Set shadow shader bindings
+ QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
+ lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ } else {
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj);
+ }
} else {
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj);
+ m_drawer->drawLine(lineShader);
}
-#else
- m_drawer->drawLine(lineShader);
-#endif
}
}
}
@@ -1489,7 +1628,6 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
glEnable(GL_DEPTH_TEST);
}
- glDisable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
// Release shader
@@ -1512,7 +1650,6 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
shader = m_labelShader;
shader->bind();
- glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
@@ -1532,15 +1669,14 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
// Z Labels
if (m_axisCacheZ.segmentCount() > 0) {
int labelCount = m_axisCacheZ.labelCount();
-#ifndef USE_UNIFORM_SCALING
- GLfloat labelXTrans = (m_graphAspectRatio * m_areaSize.width())
- / m_scaleFactor + labelMargin + m_backgroundMargin;
- if (m_maxItemSize > labelXTrans)
- labelXTrans = m_maxItemSize + labelMargin;
-#else
- GLfloat labelXTrans = m_graphAspectRatio + m_backgroundMargin + labelMargin;
-#endif
- GLfloat labelYTrans = -1.0f - m_backgroundMargin;
+ float labelXTrans = m_scaleXWithBackground + labelMargin;
+ float labelYTrans = -m_scaleYWithBackground;
+ if (m_polarGraph) {
+ labelXTrans *= m_radialLabelOffset;
+ // YTrans up only if over background
+ if (m_radialLabelOffset < 1.0f)
+ labelYTrans += gridLineOffset + gridLineWidth;
+ }
Qt::AlignmentFlag alignment = (m_xFlipped == m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight;
QVector3D labelRotation;
if (m_xFlipped)
@@ -1550,7 +1686,7 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
if (labelAutoAngle == 0.0f) {
if (m_zFlipped)
labelRotation.setY(180.0f);
- if (m_yFlipped) {
+ if (m_yFlippedForGrid) {
if (m_zFlipped)
labelRotation.setY(180.0f);
else
@@ -1562,7 +1698,7 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
} else {
if (m_zFlipped)
labelRotation.setY(180.0f);
- if (m_yFlipped) {
+ if (m_yFlippedForGrid) {
if (m_zFlipped) {
if (m_xFlipped) {
labelRotation.setX(90.0f - (labelAutoAngle - fractionCamX)
@@ -1621,13 +1757,35 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
}
float offsetValue = 0.0f;
for (int label = startIndex; label != endIndex; label = label + indexStep) {
- labelTrans.setZ(m_axisCacheZ.labelPosition(label));
-
glPolygonOffset(offsetValue++ / -10.0f, 1.0f);
-
+ const LabelItem &axisLabelItem = *m_axisCacheZ.labelItems().at(label);
// Draw the label here
+ if (m_polarGraph) {
+ float direction = m_zFlipped ? -1.0f : 1.0f;
+ labelTrans.setZ((m_axisCacheZ.formatter()->labelPositions().at(label)
+ * -m_polarRadius
+ + m_drawer->scaledFontSize() + gridLineWidth) * direction);
+ } else {
+ labelTrans.setZ(m_axisCacheZ.labelPosition(label));
+ }
+ if (label == 0 || label == (labelCount - 1)) {
+ // If the margin is small, adjust the position of the edge labels to avoid overlapping
+ // with labels of the other axes.
+ float scaleFactor = m_drawer->scaledFontSize() / axisLabelItem.size().height();
+ float labelOverlap = qAbs(labelTrans.z())
+ + (scaleFactor * axisLabelItem.size().height() / 2.0f)
+ - m_scaleZWithBackground + labelMargin;
+ // No need to adjust quite as much on the front edges
+ if (label != startIndex)
+ labelOverlap /= 2.0f;
+ if (labelOverlap > 0.0f) {
+ if (label == 0)
+ labelTrans.setZ(labelTrans.z() - labelOverlap);
+ else
+ labelTrans.setZ(labelTrans.z() + labelOverlap);
+ }
+ }
m_dummyRenderItem.setTranslation(labelTrans);
- const LabelItem &axisLabelItem = *m_axisCacheZ.labelItems().at(label);
if (drawSelection) {
QVector4D labelColor = QVector4D(label / 255.0f, 0.0f, 0.0f,
@@ -1642,7 +1800,14 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
labelsMaxWidth = qMax(labelsMaxWidth, float(axisLabelItem.size().width()));
}
if (!drawSelection && m_axisCacheZ.isTitleVisible()) {
- labelTrans.setZ(0.0f);
+ if (m_polarGraph) {
+ float titleZ = -m_polarRadius / 2.0f;
+ if (m_zFlipped)
+ titleZ = -titleZ;
+ labelTrans.setZ(titleZ);
+ } else {
+ labelTrans.setZ(0.0f);
+ }
drawAxisTitleZ(labelRotation, labelTrans, totalRotation, m_dummyRenderItem,
activeCamera, labelsMaxWidth, viewMatrix, projectionMatrix, shader);
}
@@ -1656,15 +1821,13 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
fractionCamY = activeCamera->yRotation() * labelAngleFraction;
fractionCamX = activeCamera->xRotation() * labelAngleFraction;
int labelCount = m_axisCacheX.labelCount();
-#ifndef USE_UNIFORM_SCALING
- GLfloat labelZTrans = (m_graphAspectRatio * m_areaSize.height())
- / m_scaleFactor + labelMargin + m_backgroundMargin;
- if (m_maxItemSize > labelZTrans)
- labelZTrans = m_maxItemSize + labelMargin;
-#else
- GLfloat labelZTrans = m_graphAspectRatio + m_backgroundMargin + labelMargin;
-#endif
- GLfloat labelYTrans = -1.0f - m_backgroundMargin;
+ float labelZTrans = 0.0f;
+ float labelYTrans = -m_scaleYWithBackground;
+ if (m_polarGraph)
+ labelYTrans += gridLineOffset + gridLineWidth;
+ else
+ labelZTrans = m_scaleZWithBackground + labelMargin;
+
Qt::AlignmentFlag alignment = (m_xFlipped != m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight;
QVector3D labelRotation;
if (m_zFlipped)
@@ -1675,7 +1838,7 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
labelRotation = QVector3D(-90.0f, 90.0f, 0.0f);
if (m_xFlipped)
labelRotation.setY(-90.0f);
- if (m_yFlipped) {
+ if (m_yFlippedForGrid) {
if (m_xFlipped)
labelRotation.setY(-90.0f);
else
@@ -1687,7 +1850,7 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
labelRotation.setY(-90.0f);
else
labelRotation.setY(90.0f);
- if (m_yFlipped) {
+ if (m_yFlippedForGrid) {
if (m_zFlipped) {
if (m_xFlipped) {
labelRotation.setX(90.0f - (2.0f * labelAutoAngle - fractionCamX)
@@ -1735,6 +1898,14 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
}
QQuaternion totalRotation = Utils::calculateRotation(labelRotation);
+ if (m_polarGraph) {
+ if ((!m_yFlippedForGrid && (m_zFlipped != m_xFlipped))
+ || (m_yFlippedForGrid && (m_zFlipped == m_xFlipped))) {
+ totalRotation *= m_zRightAngleRotation;
+ } else {
+ totalRotation *= m_zRightAngleRotationNeg;
+ }
+ }
QVector3D labelTrans = QVector3D(0.0f, labelYTrans, labelZTrans);
if (m_xFlipped) {
startIndex = labelCount - 1;
@@ -1746,14 +1917,64 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
indexStep = 1;
}
float offsetValue = 0.0f;
- for (int label = startIndex; label != endIndex; label = label + indexStep) {
- labelTrans.setX(m_axisCacheX.labelPosition(label));
+ bool showLastLabel = false;
+ QVector<float> &labelPositions = m_axisCacheX.formatter()->labelPositions();
+ int lastLabelPosIndex = labelPositions.size() - 1;
+ if (labelPositions.size()
+ && (labelPositions.at(lastLabelPosIndex) != 1.0f || labelPositions.at(0) != 0.0f)) {
+ // Avoid overlapping first and last label if they would get on same position
+ showLastLabel = true;
+ }
+ for (int label = startIndex; label != endIndex; label = label + indexStep) {
glPolygonOffset(offsetValue++ / -10.0f, 1.0f);
-
// Draw the label here
- m_dummyRenderItem.setTranslation(labelTrans);
+ if (m_polarGraph) {
+ // Calculate angular position
+ if (label == lastLabelPosIndex && !showLastLabel)
+ continue;
+ float labelPosition = labelPositions.at(label);
+ qreal angle = labelPosition * M_PI * 2.0;
+ labelTrans.setX((m_polarRadius + labelMargin) * float(qSin(angle)));
+ labelTrans.setZ(-(m_polarRadius + labelMargin) * float(qCos(angle)));
+ // Alignment depends on label angular position, as well as flips
+ Qt::AlignmentFlag vAlignment = Qt::AlignCenter;
+ Qt::AlignmentFlag hAlignment = Qt::AlignCenter;
+ const float centerMargin = 0.005f;
+ if (labelPosition < 0.25f - centerMargin || labelPosition > 0.75f + centerMargin)
+ vAlignment = m_zFlipped ? Qt::AlignTop : Qt::AlignBottom;
+ else if (labelPosition > 0.25f + centerMargin && labelPosition < 0.75f - centerMargin)
+ vAlignment = m_zFlipped ? Qt::AlignBottom : Qt::AlignTop;
+
+ if (labelPosition < 0.50f - centerMargin && labelPosition > centerMargin)
+ hAlignment = m_zFlipped ? Qt::AlignRight : Qt::AlignLeft;
+ else if (labelPosition < 1.0f - centerMargin && labelPosition > 0.5f + centerMargin)
+ hAlignment = m_zFlipped ? Qt::AlignLeft : Qt::AlignRight;
+ if (m_yFlippedForGrid && vAlignment != Qt::AlignCenter)
+ vAlignment = (vAlignment == Qt::AlignTop) ? Qt::AlignBottom : Qt::AlignTop;
+ alignment = Qt::AlignmentFlag(vAlignment | hAlignment);
+ } else {
+ labelTrans.setX(m_axisCacheX.labelPosition(label));
+ }
const LabelItem &axisLabelItem = *m_axisCacheX.labelItems().at(label);
+ if (label == 0 || label == (labelCount - 1)) {
+ // If the margin is small, adjust the position of the edge labels to avoid overlapping
+ // with labels of the other axes.
+ float scaleFactor = m_drawer->scaledFontSize() / axisLabelItem.size().height();
+ float labelOverlap = qAbs(labelTrans.x())
+ + (scaleFactor * axisLabelItem.size().height() / 2.0f)
+ - m_scaleXWithBackground + labelMargin;
+ // No need to adjust quite as much on the front edges
+ if (label != startIndex)
+ labelOverlap /= 2.0f;
+ if (labelOverlap > 0.0f) {
+ if (label == 0)
+ labelTrans.setX(labelTrans.x() + labelOverlap);
+ else
+ labelTrans.setX(labelTrans.x() - labelOverlap);
+ }
+ }
+ m_dummyRenderItem.setTranslation(labelTrans);
if (drawSelection) {
QVector4D labelColor = QVector4D(0.0f, label / 255.0f, 0.0f,
@@ -1769,8 +1990,20 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
}
if (!drawSelection && m_axisCacheX.isTitleVisible()) {
labelTrans.setX(0.0f);
+ bool radial = false;
+ if (m_polarGraph) {
+ if (m_xFlipped == m_zFlipped)
+ totalRotation *= m_zRightAngleRotation;
+ else
+ totalRotation *= m_zRightAngleRotationNeg;
+ if (m_yFlippedForGrid)
+ totalRotation *= QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, -180.0f);
+ labelTrans.setZ(-m_polarRadius);
+ radial = true;
+ }
drawAxisTitleX(labelRotation, labelTrans, totalRotation, m_dummyRenderItem,
- activeCamera, labelsMaxWidth, viewMatrix, projectionMatrix, shader);
+ activeCamera, labelsMaxWidth, viewMatrix, projectionMatrix, shader,
+ radial);
}
}
@@ -1782,22 +2015,13 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
fractionCamY = activeCamera->yRotation() * labelAngleFraction;
fractionCamX = activeCamera->xRotation() * labelAngleFraction;
int labelCount = m_axisCacheY.labelCount();
-#ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z
- GLfloat labelXTrans = (m_graphAspectRatio* m_areaSize.width())
- / m_scaleFactor + m_backgroundMargin;
- GLfloat labelZTrans = (m_graphAspectRatio * m_areaSize.height())
- / m_scaleFactor + m_backgroundMargin;
- if (m_maxItemSize > labelXTrans)
- labelXTrans = m_maxItemSize;
- if (m_maxItemSize > labelZTrans)
- labelZTrans = m_maxItemSize;
-#else // ..and this if we want uniform scaling based on largest dimension
- GLfloat labelXTrans = m_graphAspectRatio + m_backgroundMargin;
- GLfloat labelZTrans = labelXTrans;
-#endif
+
+ float labelXTrans = m_scaleXWithBackground;
+ float labelZTrans = m_scaleZWithBackground;
+
// Back & side wall
- GLfloat labelMarginXTrans = labelMargin;
- GLfloat labelMarginZTrans = labelMargin;
+ float labelMarginXTrans = labelMargin;
+ float labelMarginZTrans = labelMargin;
QVector3D backLabelRotation(0.0f, -90.0f, 0.0f);
QVector3D sideLabelRotation(0.0f, 0.0f, 0.0f);
Qt::AlignmentFlag backAlignment =
@@ -1854,7 +2078,7 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
float offsetValue = 0.0f;
for (int label = startIndex; label != endIndex; label = label + indexStep) {
const LabelItem &axisLabelItem = *m_axisCacheY.labelItems().at(label);
- const GLfloat labelYTrans = m_axisCacheY.labelPosition(label);
+ float labelYTrans = m_axisCacheY.labelPosition(label);
glPolygonOffset(offsetValue++ / -10.0f, 1.0f);
@@ -1864,6 +2088,21 @@ void Scatter3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
shader->setUniformValue(shader->color(), labelColor);
}
+ if (label == startIndex) {
+ // If the margin is small, adjust the position of the edge label to avoid
+ // overlapping with labels of the other axes.
+ float scaleFactor = m_drawer->scaledFontSize() / axisLabelItem.size().height();
+ float labelOverlap = qAbs(labelYTrans)
+ + (scaleFactor * axisLabelItem.size().height() / 2.0f)
+ - m_scaleYWithBackground + labelMargin;
+ if (labelOverlap > 0.0f) {
+ if (label == 0)
+ labelYTrans += labelOverlap;
+ else
+ labelYTrans -= labelOverlap;
+ }
+ }
+
// Back wall
labelTransBack.setY(labelYTrans);
m_dummyRenderItem.setTranslation(labelTransBack);
@@ -1958,10 +2197,8 @@ void Scatter3DRenderer::updateShadowQuality(QAbstract3DGraph::ShadowQuality qual
handleShadowQualityChange();
-#if !defined(QT_OPENGL_ES_2)
// Re-init depth buffer
updateDepthBuffer();
-#endif
}
void Scatter3DRenderer::loadBackgroundMesh()
@@ -1972,8 +2209,13 @@ void Scatter3DRenderer::loadBackgroundMesh()
void Scatter3DRenderer::updateTextures()
{
+ Abstract3DRenderer::updateTextures();
+
// Drawer has changed; this flag needs to be checked when checking if we need to update labels
m_updateLabels = true;
+
+ if (m_polarGraph)
+ calculateSceneScalingFactors();
}
void Scatter3DRenderer::fixMeshFileName(QString &fileName, QAbstract3DSeries::Mesh mesh)
@@ -1991,35 +2233,83 @@ void Scatter3DRenderer::calculateTranslation(ScatterRenderItem &item)
{
// We need to normalize translations
const QVector3D &pos = item.position();
- float xTrans = m_axisCacheX.positionAt(pos.x());
+ float xTrans;
float yTrans = m_axisCacheY.positionAt(pos.y());
- float zTrans = m_axisCacheZ.positionAt(pos.z());
+ float zTrans;
+ if (m_polarGraph) {
+ calculatePolarXZ(pos, xTrans, zTrans);
+ } else {
+ xTrans = m_axisCacheX.positionAt(pos.x());
+ zTrans = m_axisCacheZ.positionAt(pos.z());
+ }
item.setTranslation(QVector3D(xTrans, yTrans, zTrans));
}
void Scatter3DRenderer::calculateSceneScalingFactors()
{
- m_heightNormalizer = GLfloat(m_axisCacheY.max() - m_axisCacheY.min()) / 2.0f;
- m_areaSize.setHeight((m_axisCacheZ.max() - m_axisCacheZ.min()) / 2.0f);
- m_areaSize.setWidth((m_axisCacheX.max() - m_axisCacheX.min()) / 2.0f);
- m_scaleFactor = qMax(m_areaSize.width(), m_areaSize.height());
-
-#ifndef USE_UNIFORM_SCALING // Use this if we want to use autoscaling for x and z
- float factorScaler = 2.0f * m_graphAspectRatio / m_scaleFactor;
- m_axisCacheX.setScale(factorScaler * m_areaSize.width());
- m_axisCacheZ.setScale(-factorScaler * m_areaSize.height());
-#else // ..and this if we want uniform scaling based on largest dimension
- m_axisCacheX.setScale(2.0f * m_graphAspectRatio);
- m_axisCacheZ.setScale(-m_axisCacheX.scale());
-#endif
- m_axisCacheX.setTranslate(-m_axisCacheX.scale() / 2.0f);
- m_axisCacheZ.setTranslate(-m_axisCacheZ.scale() / 2.0f);
+ if (m_requestedMargin < 0.0f) {
+ if (m_maxItemSize > defaultMaxSize)
+ m_hBackgroundMargin = m_maxItemSize / itemScaler;
+ else
+ m_hBackgroundMargin = defaultMaxSize;
+ m_vBackgroundMargin = m_hBackgroundMargin;
+ } else {
+ m_hBackgroundMargin = m_requestedMargin;
+ m_vBackgroundMargin = m_requestedMargin;
+ }
+ if (m_polarGraph) {
+ float polarMargin = calculatePolarBackgroundMargin();
+ m_hBackgroundMargin = qMax(m_hBackgroundMargin, polarMargin);
+ }
+
+ float horizontalAspectRatio;
+ if (m_polarGraph)
+ horizontalAspectRatio = 1.0f;
+ else
+ horizontalAspectRatio = m_graphHorizontalAspectRatio;
+
+ QSizeF areaSize;
+ if (horizontalAspectRatio == 0.0f) {
+ areaSize.setHeight(m_axisCacheZ.max() - m_axisCacheZ.min());
+ areaSize.setWidth(m_axisCacheX.max() - m_axisCacheX.min());
+ } else {
+ areaSize.setHeight(1.0f);
+ areaSize.setWidth(horizontalAspectRatio);
+ }
+
+ float horizontalMaxDimension;
+ if (m_graphAspectRatio > 2.0f) {
+ horizontalMaxDimension = 2.0f;
+ m_scaleY = 2.0f / m_graphAspectRatio;
+ } else {
+ horizontalMaxDimension = m_graphAspectRatio;
+ m_scaleY = 1.0f;
+ }
+ if (m_polarGraph)
+ m_polarRadius = horizontalMaxDimension;
+
+ float scaleFactor = qMax(areaSize.width(), areaSize.height());
+ m_scaleX = horizontalMaxDimension * areaSize.width() / scaleFactor;
+ m_scaleZ = horizontalMaxDimension * areaSize.height() / scaleFactor;
+
+ m_scaleXWithBackground = m_scaleX + m_hBackgroundMargin;
+ m_scaleYWithBackground = m_scaleY + m_vBackgroundMargin;
+ m_scaleZWithBackground = m_scaleZ + m_hBackgroundMargin;
+
+ m_axisCacheX.setScale(m_scaleX * 2.0f);
+ m_axisCacheY.setScale(m_scaleY * 2.0f);
+ m_axisCacheZ.setScale(-m_scaleZ * 2.0f);
+ m_axisCacheX.setTranslate(-m_scaleX);
+ m_axisCacheY.setTranslate(-m_scaleY);
+ m_axisCacheZ.setTranslate(m_scaleZ);
+
+ updateCameraViewport();
+ updateCustomItemPositions();
}
void Scatter3DRenderer::initShaders(const QString &vertexShader, const QString &fragmentShader)
{
- if (m_dotShader)
- delete m_dotShader;
+ delete m_dotShader;
m_dotShader = new ShaderHelper(this, vertexShader, fragmentShader);
m_dotShader->initialize();
}
@@ -2027,16 +2317,30 @@ void Scatter3DRenderer::initShaders(const QString &vertexShader, const QString &
void Scatter3DRenderer::initGradientShaders(const QString &vertexShader,
const QString &fragmentShader)
{
- if (m_dotGradientShader)
- delete m_dotGradientShader;
+ delete m_dotGradientShader;
m_dotGradientShader = new ShaderHelper(this, vertexShader, fragmentShader);
m_dotGradientShader->initialize();
+
+}
+
+void Scatter3DRenderer::initStaticSelectedItemShaders(const QString &vertexShader,
+ const QString &fragmentShader,
+ const QString &gradientVertexShader,
+ const QString &gradientFragmentShader)
+{
+ delete m_staticSelectedItemShader;
+ m_staticSelectedItemShader = new ShaderHelper(this, vertexShader, fragmentShader);
+ m_staticSelectedItemShader->initialize();
+
+ delete m_staticSelectedItemGradientShader;
+ m_staticSelectedItemGradientShader = new ShaderHelper(this, gradientVertexShader,
+ gradientFragmentShader);
+ m_staticSelectedItemGradientShader->initialize();
}
void Scatter3DRenderer::initSelectionShader()
{
- if (m_selectionShader)
- delete m_selectionShader;
+ delete m_selectionShader;
m_selectionShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexPlainColor"),
QStringLiteral(":/shaders/fragmentPlainColor"));
m_selectionShader->initialize();
@@ -2054,41 +2358,45 @@ void Scatter3DRenderer::initSelectionBuffer()
m_selectionDepthBuffer);
}
-#if !defined(QT_OPENGL_ES_2)
void Scatter3DRenderer::initDepthShader()
{
- if (m_depthShader)
- delete m_depthShader;
- m_depthShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexDepth"),
- QStringLiteral(":/shaders/fragmentDepth"));
- m_depthShader->initialize();
+ if (!m_isOpenGLES) {
+ if (m_depthShader)
+ delete m_depthShader;
+ m_depthShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexDepth"),
+ QStringLiteral(":/shaders/fragmentDepth"));
+ m_depthShader->initialize();
+ }
}
void Scatter3DRenderer::updateDepthBuffer()
{
- m_textureHelper->deleteTexture(&m_depthTexture);
+ if (!m_isOpenGLES) {
+ m_textureHelper->deleteTexture(&m_depthTexture);
- if (m_primarySubViewport.size().isEmpty())
- return;
+ if (m_primarySubViewport.size().isEmpty())
+ return;
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
- m_depthTexture = m_textureHelper->createDepthTextureFrameBuffer(m_primarySubViewport.size(),
- m_depthFrameBuffer,
- m_shadowQualityMultiplier);
- if (!m_depthTexture)
- lowerShadowQuality();
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ m_depthTexture = m_textureHelper->createDepthTextureFrameBuffer(m_primarySubViewport.size(),
+ m_depthFrameBuffer,
+ m_shadowQualityMultiplier);
+ if (!m_depthTexture)
+ lowerShadowQuality();
+ }
}
}
-#else
+
void Scatter3DRenderer::initPointShader()
{
- if (m_pointShader)
- delete m_pointShader;
- m_pointShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexPointES2"),
- QStringLiteral(":/shaders/fragmentPlainColor"));
- m_pointShader->initialize();
+ if (m_isOpenGLES) {
+ if (m_pointShader)
+ delete m_pointShader;
+ m_pointShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexPointES2"),
+ QStringLiteral(":/shaders/fragmentPlainColor"));
+ m_pointShader->initialize();
+ }
}
-#endif
void Scatter3DRenderer::initBackgroundShaders(const QString &vertexShader,
const QString &fragmentShader)
@@ -2099,12 +2407,13 @@ void Scatter3DRenderer::initBackgroundShaders(const QString &vertexShader,
m_backgroundShader->initialize();
}
-void Scatter3DRenderer::initLabelShaders(const QString &vertexShader, const QString &fragmentShader)
+void Scatter3DRenderer::initStaticPointShaders(const QString &vertexShader,
+ const QString &fragmentShader)
{
- if (m_labelShader)
- delete m_labelShader;
- m_labelShader = new ShaderHelper(this, vertexShader, fragmentShader);
- m_labelShader->initialize();
+ if (m_staticGradientPointShader)
+ delete m_staticGradientPointShader;
+ m_staticGradientPointShader = new ShaderHelper(this, vertexShader, fragmentShader);
+ m_staticGradientPointShader->initialize();
}
void Scatter3DRenderer::selectionColorToSeriesAndIndex(const QVector4D &color,
@@ -2190,13 +2499,17 @@ QVector3D Scatter3DRenderer::convertPositionToTranslation(const QVector3D &posit
float yTrans = 0.0f;
float zTrans = 0.0f;
if (!isAbsolute) {
- xTrans = m_axisCacheX.positionAt(position.x());
+ if (m_polarGraph) {
+ calculatePolarXZ(position, xTrans, zTrans);
+ } else {
+ xTrans = m_axisCacheX.positionAt(position.x());
+ zTrans = m_axisCacheZ.positionAt(position.z());
+ }
yTrans = m_axisCacheY.positionAt(position.y());
- zTrans = m_axisCacheZ.positionAt(position.z());
} else {
- xTrans = position.x() * m_axisCacheX.scale() / 2.0f;
- yTrans = position.y();
- zTrans = position.z() * m_axisCacheZ.scale() / 2.0f;
+ xTrans = position.x() * m_scaleX;
+ yTrans = position.y() * m_scaleY;
+ zTrans = position.z() * -m_scaleZ;
}
return QVector3D(xTrans, yTrans, zTrans);
}
diff --git a/src/datavisualization/engine/scatter3drenderer_p.h b/src/datavisualization/engine/scatter3drenderer_p.h
index 7f213179..b45b31a2 100644
--- a/src/datavisualization/engine/scatter3drenderer_p.h
+++ b/src/datavisualization/engine/scatter3drenderer_p.h
@@ -53,31 +53,28 @@ private:
bool m_updateLabels;
ShaderHelper *m_dotShader;
ShaderHelper *m_dotGradientShader;
-#if defined(QT_OPENGL_ES_2)
+ ShaderHelper *m_staticSelectedItemGradientShader;
+ ShaderHelper *m_staticSelectedItemShader;
ShaderHelper *m_pointShader;
-#endif
ShaderHelper *m_depthShader;
ShaderHelper *m_selectionShader;
ShaderHelper *m_backgroundShader;
- ShaderHelper *m_labelShader;
+ ShaderHelper *m_staticGradientPointShader;
GLuint m_bgrTexture;
- GLuint m_depthTexture;
GLuint m_selectionTexture;
GLuint m_depthFrameBuffer;
GLuint m_selectionFrameBuffer;
GLuint m_selectionDepthBuffer;
GLfloat m_shadowQualityToShader;
GLint m_shadowQualityMultiplier;
- GLfloat m_heightNormalizer;
- GLfloat m_scaleFactor;
+ float m_scaleX;
+ float m_scaleY;
+ float m_scaleZ;
int m_selectedItemIndex;
ScatterSeriesRenderCache *m_selectedSeriesCache;
ScatterSeriesRenderCache *m_oldSelectedSeriesCache;
- QSizeF m_areaSize;
GLfloat m_dotSizeScale;
- bool m_hasHeightAdjustmentChanged;
ScatterRenderItem m_dummyRenderItem;
- GLfloat m_backgroundMargin;
GLfloat m_maxItemSize;
int m_clickedIndex;
bool m_havePointSeries;
@@ -94,6 +91,12 @@ public:
SeriesRenderCache *createNewCache(QAbstract3DSeries *series);
void updateItems(const QVector<Scatter3DController::ChangeItem> &items);
void updateScene(Q3DScene *scene);
+ void updateAxisLabels(QAbstract3DAxis::AxisOrientation orientation,
+ const QStringList &labels);
+ void updateAxisTitleVisibility(QAbstract3DAxis::AxisOrientation orientation,
+ bool visible);
+ void updateOptimizationHint(QAbstract3DGraph::OptimizationHints hint);
+ void updateMargin(float margin);
QVector3D convertPositionToTranslation(const QVector3D &position, bool isAbsolute);
@@ -107,10 +110,16 @@ public slots:
protected:
virtual void initializeOpenGL();
+ virtual void fixCameraTarget(QVector3D &target);
+ virtual void getVisibleItemBounds(QVector3D &minBounds, QVector3D &maxBounds);
private:
virtual void initShaders(const QString &vertexShader, const QString &fragmentShader);
virtual void initGradientShaders(const QString &vertexShader, const QString &fragmentShader);
+ virtual void initStaticSelectedItemShaders(const QString &vertexShader,
+ const QString &fragmentShader,
+ const QString &gradientVertexShader,
+ const QString &gradientFragmentShader);
virtual void updateShadowQuality(QAbstract3DGraph::ShadowQuality quality);
virtual void updateTextures();
virtual void fixMeshFileName(QString &fileName, QAbstract3DSeries::Mesh mesh);
@@ -122,14 +131,11 @@ private:
void loadBackgroundMesh();
void initSelectionShader();
void initBackgroundShaders(const QString &vertexShader, const QString &fragmentShader);
- void initLabelShaders(const QString &vertexShader, const QString &fragmentShader);
+ void initStaticPointShaders(const QString &vertexShader, const QString &fragmentShader);
void initSelectionBuffer();
-#if !defined(QT_OPENGL_ES_2)
void initDepthShader();
void updateDepthBuffer();
-#else
void initPointShader();
-#endif
void calculateTranslation(ScatterRenderItem &item);
void calculateSceneScalingFactors();
diff --git a/src/datavisualization/engine/scatterseriesrendercache.cpp b/src/datavisualization/engine/scatterseriesrendercache.cpp
index e8888d19..4930dde1 100644
--- a/src/datavisualization/engine/scatterseriesrendercache.cpp
+++ b/src/datavisualization/engine/scatterseriesrendercache.cpp
@@ -27,10 +27,12 @@ ScatterSeriesRenderCache::ScatterSeriesRenderCache(QAbstract3DSeries *series,
: SeriesRenderCache(series, renderer),
m_itemSize(0.0f),
m_selectionIndexOffset(0),
+ m_staticBufferDirty(false),
m_oldRenderArraySize(0),
m_oldMeshFileName(QString()),
m_scatterBufferObj(0),
- m_scatterBufferPoints(0)
+ m_scatterBufferPoints(0),
+ m_visibilityChanged(false)
{
}
diff --git a/src/datavisualization/engine/scatterseriesrendercache_p.h b/src/datavisualization/engine/scatterseriesrendercache_p.h
index 490e21fb..9c6e8e8f 100644
--- a/src/datavisualization/engine/scatterseriesrendercache_p.h
+++ b/src/datavisualization/engine/scatterseriesrendercache_p.h
@@ -53,6 +53,8 @@ public:
inline float itemSize() const { return m_itemSize; }
inline void setSelectionIndexOffset(int offset) { m_selectionIndexOffset = offset; }
inline int selectionIndexOffset() const { return m_selectionIndexOffset; }
+ inline void setStaticBufferDirty(bool state) { m_staticBufferDirty = state; }
+ inline bool staticBufferDirty() const { return m_staticBufferDirty; }
inline int oldArraySize() const { return m_oldRenderArraySize; }
inline void setOldArraySize(int size) { m_oldRenderArraySize = size; }
inline const QString &oldMeshFileName() const { return m_oldMeshFileName; }
@@ -61,15 +63,23 @@ public:
inline ScatterObjectBufferHelper *bufferObject() const { return m_scatterBufferObj; }
inline void setBufferPoints(ScatterPointBufferHelper *object) { m_scatterBufferPoints = object; }
inline ScatterPointBufferHelper *bufferPoints() const { return m_scatterBufferPoints; }
+ inline QVector<int> &updateIndices() { return m_updateIndices; }
+ inline QVector<int> &bufferIndices() { return m_bufferIndices; }
+ inline void setVisibilityChanged(bool changed) { m_visibilityChanged = changed; }
+ inline bool visibilityChanged() const { return m_visibilityChanged; }
protected:
ScatterRenderItemArray m_renderArray;
float m_itemSize;
int m_selectionIndexOffset; // Temporarily cached value for selection color calculations
+ bool m_staticBufferDirty;
int m_oldRenderArraySize; // Used to detect if full buffer change needed
QString m_oldMeshFileName; // Used to detect if full buffer change needed
ScatterObjectBufferHelper *m_scatterBufferObj;
ScatterPointBufferHelper *m_scatterBufferPoints;
+ QVector<int> m_updateIndices; // Used as temporary cache during item updates
+ QVector<int> m_bufferIndices; // Cache for mapping renderarray to mesh buffer
+ bool m_visibilityChanged; // Used to detect if full buffer change needed
};
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/engine/selectionpointer.cpp b/src/datavisualization/engine/selectionpointer.cpp
index 183d3f8e..b57ec511 100644
--- a/src/datavisualization/engine/selectionpointer.cpp
+++ b/src/datavisualization/engine/selectionpointer.cpp
@@ -122,9 +122,6 @@ void SelectionPointer::render(GLuint defaultFboHandle, bool useOrtho)
MVPMatrix = projectionMatrix * viewMatrix * modelMatrix;
- // Enable texturing
- glEnable(GL_TEXTURE_2D);
-
QVector3D lightPos = m_cachedScene->activeLight()->position();
//
@@ -186,9 +183,6 @@ void SelectionPointer::render(GLuint defaultFboHandle, bool useOrtho)
// Release shader
glUseProgram(0);
- // Disable textures
- glDisable(GL_TEXTURE_2D);
-
// Disable transparency
glDisable(GL_BLEND);
@@ -217,10 +211,12 @@ void SelectionPointer::setRotation(const QQuaternion &rotation)
m_rotation = rotation;
}
-void SelectionPointer::setLabel(const QString &label)
+void SelectionPointer::setLabel(const QString &label, bool themeChange)
{
- m_label = label;
- m_drawer->generateLabelItem(m_labelItem, m_label);
+ if (themeChange || m_label != label) {
+ m_label = label;
+ m_drawer->generateLabelItem(m_labelItem, m_label);
+ }
}
void SelectionPointer::setPointerObject(ObjectHelper *object)
@@ -236,7 +232,7 @@ void SelectionPointer::setLabelObject(ObjectHelper *object)
void SelectionPointer::handleDrawerChange()
{
m_cachedTheme = m_drawer->theme();
- setLabel(m_label);
+ setLabel(m_label, true);
}
void SelectionPointer::updateBoundingRect(const QRect &rect)
@@ -256,15 +252,16 @@ void SelectionPointer::initShaders()
// The shader for the small point ball
if (m_pointShader)
delete m_pointShader;
-#if !defined(QT_OPENGL_ES_2)
- m_pointShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"),
- QStringLiteral(":/shaders/fragment"));
-#else
- m_pointShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"),
- QStringLiteral(":/shaders/fragmentES2"));
-#endif
- m_pointShader->initialize();
+ if (Utils::isOpenGLES()) {
+ m_pointShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"),
+ QStringLiteral(":/shaders/fragmentES2"));
+ } else {
+ m_pointShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"),
+ QStringLiteral(":/shaders/fragment"));
+ }
+
+ m_pointShader->initialize();
}
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/engine/selectionpointer_p.h b/src/datavisualization/engine/selectionpointer_p.h
index 7dc28024..08382b9f 100644
--- a/src/datavisualization/engine/selectionpointer_p.h
+++ b/src/datavisualization/engine/selectionpointer_p.h
@@ -49,7 +49,7 @@ public:
void render(GLuint defaultFboHandle = 0, bool useOrtho = false);
void setPosition(const QVector3D &position);
- void setLabel(const QString &label);
+ void setLabel(const QString &label, bool themeChange = false);
void setPointerObject(ObjectHelper *object);
void setLabelObject(ObjectHelper *object);
void handleDrawerChange();
diff --git a/src/datavisualization/engine/seriesrendercache.cpp b/src/datavisualization/engine/seriesrendercache.cpp
index dc4b9db3..77af31c7 100644
--- a/src/datavisualization/engine/seriesrendercache.cpp
+++ b/src/datavisualization/engine/seriesrendercache.cpp
@@ -37,7 +37,8 @@ SeriesRenderCache::SeriesRenderCache(QAbstract3DSeries *series, Abstract3DRender
m_valid(false),
m_visible(false),
m_renderer(renderer),
- m_objectDirty(true)
+ m_objectDirty(true),
+ m_staticObjectUVDirty(false)
{
}
@@ -91,9 +92,8 @@ void SeriesRenderCache::populate(bool newSeries)
meshFileName = QStringLiteral(":/defaultMeshes/arrow");
break;
case QAbstract3DSeries::MeshPoint:
-#if defined(QT_OPENGL_ES_2)
- qWarning("QAbstract3DSeries::MeshPoint is not fully supported on OpenGL ES2");
-#endif
+ if (Utils::isOpenGLES())
+ qWarning("QAbstract3DSeries::MeshPoint is not fully supported on OpenGL ES2");
break;
default:
// Default to cube
diff --git a/src/datavisualization/engine/seriesrendercache_p.h b/src/datavisualization/engine/seriesrendercache_p.h
index 96b61b87..5047d671 100644
--- a/src/datavisualization/engine/seriesrendercache_p.h
+++ b/src/datavisualization/engine/seriesrendercache_p.h
@@ -71,6 +71,8 @@ public:
inline bool isVisible() const { return m_visible; }
inline void setDataDirty(bool state) { m_objectDirty = state; }
inline bool dataDirty() const { return m_objectDirty; }
+ inline void setStaticObjectUVDirty(bool state) { m_staticObjectUVDirty = state; }
+ inline bool staticObjectUVDirty() { return m_staticObjectUVDirty; }
protected:
QAbstract3DSeries *m_series;
@@ -94,6 +96,7 @@ protected:
bool m_visible;
Abstract3DRenderer *m_renderer;
bool m_objectDirty;
+ bool m_staticObjectUVDirty;
};
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/engine/shaders/3dsliceframes.frag b/src/datavisualization/engine/shaders/3dsliceframes.frag
new file mode 100644
index 00000000..44c080dc
--- /dev/null
+++ b/src/datavisualization/engine/shaders/3dsliceframes.frag
@@ -0,0 +1,15 @@
+#version 120
+
+uniform highp vec4 color_mdl;
+uniform highp vec2 sliceFrameWidth;
+
+varying highp vec3 pos;
+
+void main() {
+ highp vec2 absPos = min(vec2(1.0, 1.0), abs(pos.xy));
+ if (absPos.x > sliceFrameWidth.x || absPos.y > sliceFrameWidth.y)
+ gl_FragColor = color_mdl;
+ else
+ discard;
+}
+
diff --git a/src/datavisualization/engine/shaders/default.frag b/src/datavisualization/engine/shaders/default.frag
index d16055a3..0276ed95 100644
--- a/src/datavisualization/engine/shaders/default.frag
+++ b/src/datavisualization/engine/shaders/default.frag
@@ -32,6 +32,7 @@ void main() {
materialAmbientColor +
materialDiffuseColor * lightStrength * pow(cosTheta, 2) / distance +
materialSpecularColor * lightStrength * pow(cosAlpha, 5) / distance;
- gl_FragColor.a = 1.0;
+ gl_FragColor.a = color_mdl.a;
+ gl_FragColor = clamp(gl_FragColor, 0.0, 1.0);
}
diff --git a/src/datavisualization/engine/shaders/defaultNoMatrices.vert b/src/datavisualization/engine/shaders/defaultNoMatrices.vert
new file mode 100644
index 00000000..cef10c19
--- /dev/null
+++ b/src/datavisualization/engine/shaders/defaultNoMatrices.vert
@@ -0,0 +1,26 @@
+attribute highp vec3 vertexPosition_mdl;
+attribute highp vec2 vertexUV;
+attribute highp vec3 vertexNormal_mdl;
+
+uniform highp mat4 MVP;
+uniform highp mat4 V;
+uniform highp vec3 lightPosition_wrld;
+
+varying highp vec3 lightPosition_wrld_frag;
+varying highp vec3 position_wrld;
+varying highp vec3 normal_cmr;
+varying highp vec3 eyeDirection_cmr;
+varying highp vec3 lightDirection_cmr;
+varying highp vec2 coords_mdl;
+
+void main() {
+ gl_Position = MVP * vec4(vertexPosition_mdl, 1.0);
+ coords_mdl = vertexPosition_mdl.xy;
+ position_wrld = vertexPosition_mdl;
+ vec3 vertexPosition_cmr = vec4(V * vec4(vertexPosition_mdl, 1.0)).xyz;
+ eyeDirection_cmr = vec3(0.0, 0.0, 0.0) - vertexPosition_cmr;
+ vec3 lightPosition_cmr = vec4(V * vec4(lightPosition_wrld, 1.0)).xyz;
+ lightDirection_cmr = lightPosition_cmr + eyeDirection_cmr;
+ normal_cmr = vec4(V * vec4(vertexNormal_mdl, 0.0)).xyz;
+ lightPosition_wrld_frag = lightPosition_wrld;
+}
diff --git a/src/datavisualization/engine/shaders/default_ES2.frag b/src/datavisualization/engine/shaders/default_ES2.frag
index 73d66d5b..60fa3c43 100644
--- a/src/datavisualization/engine/shaders/default_ES2.frag
+++ b/src/datavisualization/engine/shaders/default_ES2.frag
@@ -34,6 +34,6 @@ void main() {
materialAmbientColor +
materialDiffuseColor * lightStrength * (cosTheta * cosTheta) / distance +
materialSpecularColor * lightStrength * (cosAlpha * cosAlpha * cosAlpha * cosAlpha * cosAlpha) / distance;
- gl_FragColor.a = 1.0;
+ gl_FragColor.a = color_mdl.a;
}
diff --git a/src/datavisualization/engine/shaders/point_ES2_UV.vert b/src/datavisualization/engine/shaders/point_ES2_UV.vert
new file mode 100644
index 00000000..f181db4f
--- /dev/null
+++ b/src/datavisualization/engine/shaders/point_ES2_UV.vert
@@ -0,0 +1,12 @@
+uniform highp mat4 MVP;
+
+attribute highp vec3 vertexPosition_mdl;
+attribute highp vec2 vertexUV;
+
+varying highp vec2 UV;
+
+void main() {
+ gl_PointSize = 5.0;
+ gl_Position = MVP * vec4(vertexPosition_mdl, 1.0);
+ UV = vertexUV;
+}
diff --git a/src/datavisualization/engine/shaders/position.vert b/src/datavisualization/engine/shaders/position.vert
new file mode 100644
index 00000000..34849eae
--- /dev/null
+++ b/src/datavisualization/engine/shaders/position.vert
@@ -0,0 +1,10 @@
+uniform highp mat4 MVP;
+
+attribute highp vec3 vertexPosition_mdl;
+
+varying highp vec3 pos;
+
+void main() {
+ gl_Position = MVP * vec4(vertexPosition_mdl, 1.0);
+ pos = vertexPosition_mdl;
+}
diff --git a/src/datavisualization/engine/shaders/positionmap.frag b/src/datavisualization/engine/shaders/positionmap.frag
new file mode 100644
index 00000000..9a277ab8
--- /dev/null
+++ b/src/datavisualization/engine/shaders/positionmap.frag
@@ -0,0 +1,11 @@
+varying highp vec3 pos;
+
+void main() {
+ // This shader encodes the axis position into the vertex color, assuming the object
+ // is a cube filling the entire data area of the graph
+ gl_FragColor = vec4((pos.x + 1.0) / 2.0,
+ (pos.y + 1.0) / 2.0,
+ (-pos.z + 1.0) / 2.0,
+ 0.0);
+}
+
diff --git a/src/datavisualization/engine/shaders/shadowNoMatrices.vert b/src/datavisualization/engine/shaders/shadowNoMatrices.vert
new file mode 100644
index 00000000..31748503
--- /dev/null
+++ b/src/datavisualization/engine/shaders/shadowNoMatrices.vert
@@ -0,0 +1,36 @@
+#version 120
+
+uniform highp mat4 MVP;
+uniform highp mat4 V;
+uniform highp mat4 M;
+uniform highp mat4 depthMVP;
+uniform highp vec3 lightPosition_wrld;
+
+attribute highp vec3 vertexPosition_mdl;
+attribute highp vec3 vertexNormal_mdl;
+attribute highp vec2 vertexUV;
+
+varying highp vec2 UV;
+varying highp vec3 position_wrld;
+varying highp vec3 normal_cmr;
+varying highp vec3 eyeDirection_cmr;
+varying highp vec3 lightDirection_cmr;
+varying highp vec4 shadowCoord;
+varying highp vec2 coords_mdl;
+
+const highp mat4 bias = mat4(0.5, 0.0, 0.0, 0.0,
+ 0.0, 0.5, 0.0, 0.0,
+ 0.0, 0.0, 0.5, 0.0,
+ 0.5, 0.5, 0.5, 1.0);
+
+void main() {
+ gl_Position = MVP * vec4(vertexPosition_mdl, 1.0);
+ coords_mdl = vertexPosition_mdl.xy;
+ shadowCoord = bias * depthMVP * vec4(vertexPosition_mdl, 1.0);
+ position_wrld = vertexPosition_mdl;
+ vec3 vertexPosition_cmr = vec4(V * vec4(vertexPosition_mdl, 1.0)).xyz;
+ eyeDirection_cmr = vec3(0.0, 0.0, 0.0) - vertexPosition_cmr;
+ lightDirection_cmr = vec4(V * vec4(lightPosition_wrld, 0.0)).xyz;
+ normal_cmr = vec4(V * vec4(vertexNormal_mdl, 0.0)).xyz;
+ UV = vertexUV;
+}
diff --git a/src/datavisualization/engine/shaders/shadowNoTex.frag b/src/datavisualization/engine/shaders/shadowNoTex.frag
index b2e7adfc..84e2f209 100644
--- a/src/datavisualization/engine/shaders/shadowNoTex.frag
+++ b/src/datavisualization/engine/shaders/shadowNoTex.frag
@@ -61,6 +61,6 @@ void main() {
(materialAmbientColor +
materialDiffuseColor * lightStrength * cosTheta +
materialSpecularColor * lightStrength * pow(cosAlpha, 10));
- gl_FragColor.a = 1.0;
+ gl_FragColor.a = color_mdl.a;
gl_FragColor.rgb = visibility * clamp(gl_FragColor.rgb, 0.0, 1.0);
}
diff --git a/src/datavisualization/engine/shaders/surface.frag b/src/datavisualization/engine/shaders/surface.frag
index f17dd73e..238e5fed 100644
--- a/src/datavisualization/engine/shaders/surface.frag
+++ b/src/datavisualization/engine/shaders/surface.frag
@@ -11,9 +11,11 @@ uniform highp vec3 lightPosition_wrld;
uniform highp float lightStrength;
uniform highp float ambientStrength;
uniform highp vec4 lightColor;
+uniform highp float gradMin;
+uniform highp float gradHeight;
void main() {
- highp vec2 gradientUV = vec2(0.0, (coords_mdl.y + 1.0) / 2.0);
+ highp vec2 gradientUV = vec2(0.0, gradMin + coords_mdl.y * gradHeight);
highp vec3 materialDiffuseColor = texture2D(textureSampler, gradientUV).xyz;
highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor;
highp vec3 materialSpecularColor = lightColor.rgb;
diff --git a/src/datavisualization/engine/shaders/surfaceFlat.frag b/src/datavisualization/engine/shaders/surfaceFlat.frag
index 748fb3dd..1a0bbdeb 100644
--- a/src/datavisualization/engine/shaders/surfaceFlat.frag
+++ b/src/datavisualization/engine/shaders/surfaceFlat.frag
@@ -13,9 +13,11 @@ uniform highp vec3 lightPosition_wrld;
uniform highp float lightStrength;
uniform highp float ambientStrength;
uniform highp vec4 lightColor;
+uniform highp float gradMin;
+uniform highp float gradHeight;
void main() {
- highp vec2 gradientUV = vec2(0.0, (coords_mdl.y + 1.0) / 2.0);
+ highp vec2 gradientUV = vec2(0.0, gradMin + coords_mdl.y * gradHeight);
highp vec3 materialDiffuseColor = texture2D(textureSampler, gradientUV).xyz;
highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor;
highp vec3 materialSpecularColor = lightColor.rgb;
diff --git a/src/datavisualization/engine/shaders/surfaceFlat.vert b/src/datavisualization/engine/shaders/surfaceFlat.vert
index 102bea78..0f953f4c 100644
--- a/src/datavisualization/engine/shaders/surfaceFlat.vert
+++ b/src/datavisualization/engine/shaders/surfaceFlat.vert
@@ -4,6 +4,7 @@
attribute highp vec3 vertexPosition_mdl;
attribute highp vec3 vertexNormal_mdl;
+attribute highp vec2 vertexUV;
uniform highp mat4 MVP;
uniform highp mat4 V;
@@ -11,6 +12,7 @@ uniform highp mat4 M;
uniform highp mat4 itM;
uniform highp vec3 lightPosition_wrld;
+varying highp vec2 UV;
varying highp vec3 position_wrld;
flat varying highp vec3 normal_cmr;
varying highp vec3 eyeDirection_cmr;
@@ -26,4 +28,5 @@ void main() {
vec3 lightPosition_cmr = vec4(V * vec4(lightPosition_wrld, 1.0)).xyz;
lightDirection_cmr = lightPosition_cmr + eyeDirection_cmr;
normal_cmr = vec4(V * itM * vec4(vertexNormal_mdl, 0.0)).xyz;
+ UV = vertexUV;
}
diff --git a/src/datavisualization/engine/shaders/surfaceShadowFlat.frag b/src/datavisualization/engine/shaders/surfaceShadowFlat.frag
index 0613a40c..7eaba7f5 100644
--- a/src/datavisualization/engine/shaders/surfaceShadowFlat.frag
+++ b/src/datavisualization/engine/shaders/surfaceShadowFlat.frag
@@ -2,20 +2,22 @@
#extension GL_EXT_gpu_shader4 : require
-varying highp vec3 coords_mdl;
+varying highp vec2 coords_mdl;
varying highp vec3 position_wrld;
flat varying highp vec3 normal_cmr;
varying highp vec3 eyeDirection_cmr;
varying highp vec3 lightDirection_cmr;
+varying highp vec4 shadowCoord;
uniform highp sampler2DShadow shadowMap;
uniform sampler2D textureSampler;
-varying highp vec4 shadowCoord;
uniform highp vec3 lightPosition_wrld;
uniform highp float lightStrength;
uniform highp float ambientStrength;
uniform highp float shadowQuality;
uniform highp vec4 lightColor;
+uniform highp float gradMin;
+uniform highp float gradHeight;
highp vec2 poissonDisk[16] = vec2[16](vec2(-0.94201624, -0.39906216),
vec2(0.94558609, -0.76890725),
@@ -35,7 +37,7 @@ highp vec2 poissonDisk[16] = vec2[16](vec2(-0.94201624, -0.39906216),
vec2(0.14383161, -0.14100790));
void main() {
- highp vec2 gradientUV = vec2(0.0, (coords_mdl.y + 1.0) / 2.0);
+ highp vec2 gradientUV = vec2(0.0, gradMin + coords_mdl.y * gradHeight);
highp vec3 materialDiffuseColor = texture2D(textureSampler, gradientUV).xyz;
highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor;
highp vec3 materialSpecularColor = lightColor.rgb;
@@ -49,7 +51,7 @@ void main() {
highp float cosAlpha = clamp(dot(E, R), 0.0, 1.0);
highp float bias = 0.005 * tan(acos(cosTheta));
- bias = clamp(bias, 0.0, 0.01);
+ bias = clamp(bias, 0.001, 0.01);
vec4 shadCoords = shadowCoord;
shadCoords.z -= bias;
diff --git a/src/datavisualization/engine/shaders/surfaceShadowFlat.vert b/src/datavisualization/engine/shaders/surfaceShadowFlat.vert
index 8da7b196..98fdde3f 100644
--- a/src/datavisualization/engine/shaders/surfaceShadowFlat.vert
+++ b/src/datavisualization/engine/shaders/surfaceShadowFlat.vert
@@ -2,9 +2,6 @@
#extension GL_EXT_gpu_shader4 : require
-attribute highp vec3 vertexPosition_mdl;
-attribute highp vec3 vertexNormal_mdl;
-
uniform highp mat4 MVP;
uniform highp mat4 V;
uniform highp mat4 M;
@@ -12,12 +9,17 @@ uniform highp mat4 itM;
uniform highp mat4 depthMVP;
uniform highp vec3 lightPosition_wrld;
+attribute highp vec3 vertexPosition_mdl;
+attribute highp vec3 vertexNormal_mdl;
+attribute highp vec2 vertexUV;
+
+varying highp vec2 UV;
varying highp vec3 position_wrld;
flat varying highp vec3 normal_cmr;
varying highp vec3 eyeDirection_cmr;
varying highp vec3 lightDirection_cmr;
varying highp vec4 shadowCoord;
-varying highp vec3 coords_mdl;
+varying highp vec2 coords_mdl;
const highp mat4 bias = mat4(0.5, 0.0, 0.0, 0.0,
0.0, 0.5, 0.0, 0.0,
@@ -26,12 +28,12 @@ const highp mat4 bias = mat4(0.5, 0.0, 0.0, 0.0,
void main() {
gl_Position = MVP * vec4(vertexPosition_mdl, 1.0);
- coords_mdl = vertexPosition_mdl;
+ coords_mdl = vertexPosition_mdl.xy;
shadowCoord = bias * depthMVP * vec4(vertexPosition_mdl, 1.0);
position_wrld = vec4(M * vec4(vertexPosition_mdl, 1.0)).xyz;
vec3 vertexPosition_cmr = vec4(V * M * vec4(vertexPosition_mdl, 1.0)).xyz;
eyeDirection_cmr = vec3(0.0, 0.0, 0.0) - vertexPosition_cmr;
- vec3 lightPosition_cmr = vec4(V * vec4(lightPosition_wrld, 1.0)).xyz;
- lightDirection_cmr = lightPosition_cmr + eyeDirection_cmr;
+ lightDirection_cmr = vec4(V * vec4(lightPosition_wrld, 0.0)).xyz;
normal_cmr = vec4(V * itM * vec4(vertexNormal_mdl, 0.0)).xyz;
+ UV = vertexUV;
}
diff --git a/src/datavisualization/engine/shaders/surfaceShadowNoTex.frag b/src/datavisualization/engine/shaders/surfaceShadowNoTex.frag
index 1acf8f69..985214be 100644
--- a/src/datavisualization/engine/shaders/surfaceShadowNoTex.frag
+++ b/src/datavisualization/engine/shaders/surfaceShadowNoTex.frag
@@ -5,15 +5,17 @@ varying highp vec3 position_wrld;
varying highp vec3 normal_cmr;
varying highp vec3 eyeDirection_cmr;
varying highp vec3 lightDirection_cmr;
+varying highp vec4 shadowCoord;
uniform highp sampler2DShadow shadowMap;
uniform sampler2D textureSampler;
-varying highp vec4 shadowCoord;
uniform highp vec3 lightPosition_wrld;
uniform highp float lightStrength;
uniform highp float ambientStrength;
uniform highp float shadowQuality;
uniform highp vec4 lightColor;
+uniform highp float gradMin;
+uniform highp float gradHeight;
highp vec2 poissonDisk[16] = vec2[16](vec2(-0.94201624, -0.39906216),
vec2(0.94558609, -0.76890725),
@@ -33,7 +35,7 @@ highp vec2 poissonDisk[16] = vec2[16](vec2(-0.94201624, -0.39906216),
vec2(0.14383161, -0.14100790));
void main() {
- highp vec2 gradientUV = vec2(0.0, (coords_mdl.y + 1.0) / 2.0);
+ highp vec2 gradientUV = vec2(0.0, gradMin + coords_mdl.y * gradHeight);
highp vec3 materialDiffuseColor = texture2D(textureSampler, gradientUV).xyz;
highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor;
highp vec3 materialSpecularColor = lightColor.rgb;
@@ -47,7 +49,7 @@ void main() {
highp float cosAlpha = clamp(dot(E, R), 0.0, 1.0);
highp float bias = 0.005 * tan(acos(cosTheta));
- bias = clamp(bias, 0.0, 0.01);
+ bias = clamp(bias, 0.001, 0.01);
vec4 shadCoords = shadowCoord;
shadCoords.z -= bias;
diff --git a/src/datavisualization/engine/shaders/surfaceTexturedFlat.frag b/src/datavisualization/engine/shaders/surfaceTexturedFlat.frag
new file mode 100644
index 00000000..7c654e0c
--- /dev/null
+++ b/src/datavisualization/engine/shaders/surfaceTexturedFlat.frag
@@ -0,0 +1,39 @@
+#version 120
+
+#extension GL_EXT_gpu_shader4 : require
+
+varying highp vec3 coords_mdl;
+varying highp vec3 position_wrld;
+flat varying highp vec3 normal_cmr;
+varying highp vec3 eyeDirection_cmr;
+varying highp vec3 lightDirection_cmr;
+varying highp vec2 UV;
+
+uniform sampler2D textureSampler;
+uniform highp vec3 lightPosition_wrld;
+uniform highp float lightStrength;
+uniform highp float ambientStrength;
+uniform highp vec4 lightColor;
+
+void main() {
+ highp vec3 materialDiffuseColor = texture2D(textureSampler, UV).xyz;
+ highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor;
+ highp vec3 materialSpecularColor = lightColor.rgb;
+
+ highp float distance = length(lightPosition_wrld - position_wrld);
+
+ highp vec3 n = normalize(normal_cmr);
+ highp vec3 l = normalize(lightDirection_cmr);
+ highp float cosTheta = clamp(dot(n, l), 0.0, 1.0);
+
+ highp vec3 E = normalize(eyeDirection_cmr);
+ highp vec3 R = reflect(-l, n);
+ highp float cosAlpha = clamp(dot(E, R), 0.0, 1.0);
+
+ gl_FragColor.rgb =
+ materialAmbientColor +
+ materialDiffuseColor * lightStrength * pow(cosTheta, 2) / distance +
+ materialSpecularColor * lightStrength * pow(cosAlpha, 10) / distance;
+ gl_FragColor.a = 1.0;
+}
+
diff --git a/src/datavisualization/engine/shaders/surfaceTexturedShadow.frag b/src/datavisualization/engine/shaders/surfaceTexturedShadow.frag
new file mode 100644
index 00000000..a4259565
--- /dev/null
+++ b/src/datavisualization/engine/shaders/surfaceTexturedShadow.frag
@@ -0,0 +1,67 @@
+#version 120
+
+uniform highp float lightStrength;
+uniform highp float ambientStrength;
+uniform highp float shadowQuality;
+uniform highp sampler2D textureSampler;
+uniform highp sampler2DShadow shadowMap;
+uniform highp vec4 lightColor;
+
+varying highp vec4 shadowCoord;
+varying highp vec2 UV;
+varying highp vec3 position_wrld;
+varying highp vec3 normal_cmr;
+varying highp vec3 eyeDirection_cmr;
+varying highp vec3 lightDirection_cmr;
+
+highp vec2 poissonDisk[16] = vec2[16](vec2(-0.94201624, -0.39906216),
+ vec2(0.94558609, -0.76890725),
+ vec2(-0.094184101, -0.92938870),
+ vec2(0.34495938, 0.29387760),
+ vec2(-0.91588581, 0.45771432),
+ vec2(-0.81544232, -0.87912464),
+ vec2(-0.38277543, 0.27676845),
+ vec2(0.97484398, 0.75648379),
+ vec2(0.44323325, -0.97511554),
+ vec2(0.53742981, -0.47373420),
+ vec2(-0.26496911, -0.41893023),
+ vec2(0.79197514, 0.19090188),
+ vec2(-0.24188840, 0.99706507),
+ vec2(-0.81409955, 0.91437590),
+ vec2(0.19984126, 0.78641367),
+ vec2(0.14383161, -0.14100790));
+
+void main() {
+ highp vec3 materialDiffuseColor = texture2D(textureSampler, UV).rgb;
+ highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor;
+ highp vec3 materialSpecularColor = lightColor.rgb * 0.2;
+
+ highp vec3 n = normalize(normal_cmr);
+ highp vec3 l = normalize(lightDirection_cmr);
+ highp float cosTheta = clamp(dot(n, l), 0.0, 1.0);
+
+ highp vec3 E = normalize(eyeDirection_cmr);
+ highp vec3 R = reflect(-l, n);
+ highp float cosAlpha = clamp(dot(E, R), 0.0, 1.0);
+
+ highp float bias = 0.005 * tan(acos(cosTheta));
+ bias = clamp(bias, 0.001, 0.01);
+
+ vec4 shadCoords = shadowCoord;
+ shadCoords.z -= bias;
+
+ highp float visibility = 0.6;
+ for (int i = 0; i < 15; i++) {
+ vec4 shadCoordsPD = shadCoords;
+ shadCoordsPD.x += cos(poissonDisk[i].x) / shadowQuality;
+ shadCoordsPD.y += sin(poissonDisk[i].y) / shadowQuality;
+ visibility += 0.025 * shadow2DProj(shadowMap, shadCoordsPD).r;
+ }
+
+ gl_FragColor.rgb =
+ (materialAmbientColor +
+ materialDiffuseColor * lightStrength * cosTheta +
+ materialSpecularColor * lightStrength * pow(cosAlpha, 10));
+ gl_FragColor.a = texture2D(textureSampler, UV).a;
+ gl_FragColor.rgb = visibility * clamp(gl_FragColor.rgb, 0.0, 1.0);
+}
diff --git a/src/datavisualization/engine/shaders/surfaceTexturedShadowFlat.frag b/src/datavisualization/engine/shaders/surfaceTexturedShadowFlat.frag
new file mode 100644
index 00000000..496f4777
--- /dev/null
+++ b/src/datavisualization/engine/shaders/surfaceTexturedShadowFlat.frag
@@ -0,0 +1,72 @@
+#version 120
+
+#extension GL_EXT_gpu_shader4 : require
+
+varying highp vec3 coords_mdl;
+varying highp vec3 position_wrld;
+flat varying highp vec3 normal_cmr;
+varying highp vec3 eyeDirection_cmr;
+varying highp vec3 lightDirection_cmr;
+varying highp vec2 UV;
+
+uniform highp sampler2DShadow shadowMap;
+uniform sampler2D textureSampler;
+varying highp vec4 shadowCoord;
+uniform highp vec3 lightPosition_wrld;
+uniform highp float lightStrength;
+uniform highp float ambientStrength;
+uniform highp float shadowQuality;
+uniform highp vec4 lightColor;
+
+highp vec2 poissonDisk[16] = vec2[16](vec2(-0.94201624, -0.39906216),
+ vec2(0.94558609, -0.76890725),
+ vec2(-0.094184101, -0.92938870),
+ vec2(0.34495938, 0.29387760),
+ vec2(-0.91588581, 0.45771432),
+ vec2(-0.81544232, -0.87912464),
+ vec2(-0.38277543, 0.27676845),
+ vec2(0.97484398, 0.75648379),
+ vec2(0.44323325, -0.97511554),
+ vec2(0.53742981, -0.47373420),
+ vec2(-0.26496911, -0.41893023),
+ vec2(0.79197514, 0.19090188),
+ vec2(-0.24188840, 0.99706507),
+ vec2(-0.81409955, 0.91437590),
+ vec2(0.19984126, 0.78641367),
+ vec2(0.14383161, -0.14100790));
+
+void main() {
+ highp vec3 materialDiffuseColor = texture2D(textureSampler, UV).xyz;
+ highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor;
+ highp vec3 materialSpecularColor = lightColor.rgb;
+
+ highp vec3 n = normalize(normal_cmr);
+ highp vec3 l = normalize(lightDirection_cmr);
+ highp float cosTheta = clamp(dot(n, l), 0.0, 1.0);
+
+ highp vec3 E = normalize(eyeDirection_cmr);
+ highp vec3 R = reflect(-l, n);
+ highp float cosAlpha = clamp(dot(E, R), 0.0, 1.0);
+
+ highp float bias = 0.005 * tan(acos(cosTheta));
+ bias = clamp(bias, 0.001, 0.01);
+
+ vec4 shadCoords = shadowCoord;
+ shadCoords.z -= bias;
+
+ highp float visibility = 0.6;
+ for (int i = 0; i < 15; i++) {
+ vec4 shadCoordsPD = shadCoords;
+ shadCoordsPD.x += cos(poissonDisk[i].x) / shadowQuality;
+ shadCoordsPD.y += sin(poissonDisk[i].y) / shadowQuality;
+ visibility += 0.025 * shadow2DProj(shadowMap, shadCoordsPD).r;
+ }
+
+ gl_FragColor.rgb =
+ (materialAmbientColor +
+ materialDiffuseColor * lightStrength * cosTheta +
+ materialSpecularColor * lightStrength * pow(cosAlpha, 10));
+ gl_FragColor.a = 1.0;
+ gl_FragColor.rgb = visibility * clamp(gl_FragColor.rgb, 0.0, 1.0);
+}
+
diff --git a/src/datavisualization/engine/shaders/surface_ES2.frag b/src/datavisualization/engine/shaders/surface_ES2.frag
index 58d13834..1d1bdc3e 100644
--- a/src/datavisualization/engine/shaders/surface_ES2.frag
+++ b/src/datavisualization/engine/shaders/surface_ES2.frag
@@ -10,9 +10,11 @@ uniform sampler2D textureSampler;
uniform highp float lightStrength;
uniform highp float ambientStrength;
uniform highp vec4 lightColor;
+uniform highp float gradMin;
+uniform highp float gradHeight;
void main() {
- highp vec2 gradientUV = vec2(0.0, (coords_mdl.y + 1.0) / 2.0);
+ highp vec2 gradientUV = vec2(0.0, gradMin + coords_mdl.y * gradHeight);
highp vec3 materialDiffuseColor = texture2D(textureSampler, gradientUV).xyz;
highp vec3 materialAmbientColor = lightColor.rgb * ambientStrength * materialDiffuseColor;
highp vec3 materialSpecularColor = lightColor.rgb;
diff --git a/src/datavisualization/engine/shaders/texture3d.frag b/src/datavisualization/engine/shaders/texture3d.frag
new file mode 100644
index 00000000..3f9c42ff
--- /dev/null
+++ b/src/datavisualization/engine/shaders/texture3d.frag
@@ -0,0 +1,155 @@
+#version 120
+
+varying highp vec3 pos;
+varying highp vec3 rayDir;
+
+uniform highp sampler3D textureSampler;
+uniform highp vec4 colorIndex[256];
+uniform highp int color8Bit;
+uniform highp vec3 textureDimensions;
+uniform highp int sampleCount; // This is the maximum sample count
+uniform highp float alphaMultiplier;
+uniform highp int preserveOpacity;
+uniform highp vec3 minBounds;
+uniform highp vec3 maxBounds;
+
+// Ray traveling straight through a single 'alpha thickness' applies 100% of the encountered alpha.
+// Rays traveling shorter distances apply a fraction. This is used to normalize the alpha over
+// entire volume, regardless of texture dimensions
+const highp float alphaThicknesses = 32.0;
+
+void main() {
+ vec3 rayStart = pos;
+
+ highp vec3 startBounds = minBounds;
+ highp vec3 endBounds = maxBounds;
+ if (rayDir.x < 0.0) {
+ startBounds.x = maxBounds.x;
+ endBounds.x = minBounds.x;
+ }
+ if (rayDir.y > 0.0) {
+ startBounds.y = maxBounds.y;
+ endBounds.y = minBounds.y;
+ }
+ if (rayDir.z > 0.0) {
+ startBounds.z = maxBounds.z;
+ endBounds.z = minBounds.z;
+ }
+
+ // Calculate ray intersection endpoint
+ highp vec3 rayStop;
+ highp vec3 invRayDir = 1.0 / rayDir;
+ highp vec3 t = (endBounds - rayStart) * invRayDir;
+ highp float endT = min(t.x, min(t.y, t.z));
+ rayStop = rayStart + endT * rayDir;
+ if (endT <= 0.0)
+ discard;
+
+ // Convert intersections to texture coords
+ rayStart = 0.5 * (rayStart + 1.0);
+ rayStop = 0.5 * (rayStop + 1.0);
+
+ highp vec3 ray = rayStop - rayStart;
+
+ highp vec3 absRay = abs(ray);
+ highp vec3 invAbsRay = 1.0 / absRay;
+ highp float fullDist = length(ray);
+ highp vec3 curPos = rayStart;
+
+ highp vec4 curColor = vec4(0, 0, 0, 0);
+ highp float curAlpha = 0.0;
+ highp float curLen = 0.0;
+ highp vec3 curRgb = vec3(0, 0, 0);
+
+ highp vec4 destColor = vec4(0, 0, 0, 0);
+ highp float totalOpacity = 1.0;
+
+ highp float extraAlphaMultiplier = fullDist * alphaThicknesses * alphaMultiplier;
+
+ // nextEdges vector indicates the next edges of the texel boundaries along each axis that
+ // the ray is about to cross. The first edges are offset by a fraction of a texel to
+ // avoid artifacts from rounding errors later.
+ highp vec3 nextEdges = vec3(floor(curPos.x / textureDimensions.x) * textureDimensions.x,
+ floor(curPos.y / textureDimensions.y) * textureDimensions.y,
+ floor(curPos.z / textureDimensions.z) * textureDimensions.z);
+
+ highp vec3 textureSteps = textureDimensions;
+ highp vec3 textureOffset = textureDimensions * 0.001;
+ if (ray.x > 0) {
+ nextEdges.x += textureDimensions.x + textureOffset.x;
+ } else {
+ nextEdges.x -= textureOffset.x;
+ textureSteps.x = -textureDimensions.x;
+ }
+ if (ray.y > 0) {
+ nextEdges.y += textureDimensions.y + textureOffset.y;
+ } else {
+ nextEdges.y -= textureOffset.y;
+ textureSteps.y = -textureDimensions.y;
+ }
+ if (ray.z > 0) {
+ nextEdges.z += textureDimensions.z + textureOffset.z;
+ } else {
+ nextEdges.z -= textureOffset.z;
+ textureSteps.z = -textureDimensions.z;
+ }
+
+ // Raytrace into volume, need to sample pixels along the eye ray until we hit opacity 1
+ for (int i = 0; i < sampleCount; i++) {
+ curColor = texture3D(textureSampler, curPos);
+ if (color8Bit != 0)
+ curColor = colorIndex[int(curColor.r * 255.0)];
+
+ // Find which dimension has least to go to figure out the next step distance
+ highp vec3 delta = abs(nextEdges - curPos);
+ highp vec3 modDelta = delta * invAbsRay;
+ highp float minDelta = min(modDelta.x, min(modDelta.y, modDelta.z));
+ highp float stepSize;
+ if (minDelta == modDelta.x) {
+ nextEdges.x += textureSteps.x;
+ stepSize = delta.x * invAbsRay.x;
+ }
+ if (minDelta == modDelta.y) {
+ nextEdges.y += textureSteps.y;
+ stepSize = delta.y * invAbsRay.y;
+ }
+ if (minDelta == modDelta.z) {
+ nextEdges.z += textureSteps.z;
+ stepSize = delta.z * invAbsRay.z;
+ }
+
+ curPos += stepSize * ray;
+ curLen += stepSize;
+
+ if (curColor.a >= 0.0) {
+ if (curColor.a == 1.0 && (preserveOpacity == 1 || alphaMultiplier >= 1.0))
+ curAlpha = 1.0;
+ else
+ curAlpha = curColor.a * extraAlphaMultiplier * stepSize;
+ highp float nextOpacity = totalOpacity - curAlpha;
+ // If opacity goes beyond full opacity, we need to adjust current alpha according
+ // to the fraction of the distance the material is visible, so that we don't get
+ // box artifacts around texels.
+ if (nextOpacity < 0.0) {
+ curAlpha *= totalOpacity / curAlpha;
+ nextOpacity = 0.0;
+ }
+ curRgb = curColor.rgb * curAlpha * (totalOpacity + nextOpacity);
+ totalOpacity = nextOpacity;
+ destColor.rgb += curRgb;
+ }
+
+ if (curLen >= 1.0 || totalOpacity <= 0.0)
+ break;
+ }
+
+ if (totalOpacity == 1.0)
+ discard;
+
+ // Brighten up the final color if there is some transparency left
+ if (totalOpacity >= 0.0 && totalOpacity < 1.0)
+ destColor *= (1.0 - (totalOpacity * 0.5)) / (1.0 - totalOpacity);
+
+ destColor.a = (1.0 - totalOpacity);
+ gl_FragColor = clamp(destColor, 0.0, 1.0);
+}
diff --git a/src/datavisualization/engine/shaders/texture3d.vert b/src/datavisualization/engine/shaders/texture3d.vert
new file mode 100644
index 00000000..ef3f1b25
--- /dev/null
+++ b/src/datavisualization/engine/shaders/texture3d.vert
@@ -0,0 +1,36 @@
+uniform highp mat4 MVP;
+uniform highp vec3 minBounds;
+uniform highp vec3 maxBounds;
+uniform highp vec3 cameraPositionRelativeToModel;
+
+attribute highp vec3 vertexPosition_mdl;
+attribute highp vec2 vertexUV;
+attribute highp vec3 vertexNormal_mdl;
+
+varying highp vec3 pos;
+varying highp vec3 rayDir;
+
+void main() {
+ gl_Position = MVP * vec4(vertexPosition_mdl, 1.0);
+
+ highp vec3 minBoundsNorm = minBounds;
+ highp vec3 maxBoundsNorm = maxBounds;
+
+ // Y and Z are flipped in bounds to be directly usable in texture calculations,
+ // so flip them back to normal for position calculations
+ minBoundsNorm.yz = -minBoundsNorm.yz;
+ maxBoundsNorm.yz = -maxBoundsNorm.yz;
+
+ minBoundsNorm = 0.5 * (minBoundsNorm + 1.0);
+ maxBoundsNorm = 0.5 * (maxBoundsNorm + 1.0);
+
+ pos = vertexPosition_mdl
+ + ((1.0 - vertexPosition_mdl) * minBoundsNorm)
+ - ((1.0 + vertexPosition_mdl) * (1.0 - maxBoundsNorm));
+
+ rayDir = -(cameraPositionRelativeToModel - pos);
+
+ // Flip Y and Z so QImage bits work directly for texture and first image is in the front
+ rayDir.yz = -rayDir.yz;
+ pos.yz = -pos.yz;
+}
diff --git a/src/datavisualization/engine/shaders/texture3dlowdef.frag b/src/datavisualization/engine/shaders/texture3dlowdef.frag
new file mode 100644
index 00000000..ed0d41ce
--- /dev/null
+++ b/src/datavisualization/engine/shaders/texture3dlowdef.frag
@@ -0,0 +1,109 @@
+#version 120
+
+varying highp vec3 pos;
+varying highp vec3 rayDir;
+
+uniform highp sampler3D textureSampler;
+uniform highp vec4 colorIndex[256];
+uniform highp int color8Bit;
+uniform highp vec3 textureDimensions;
+uniform highp int sampleCount; // This is the maximum sample count
+uniform highp float alphaMultiplier;
+uniform highp int preserveOpacity;
+uniform highp vec3 minBounds;
+uniform highp vec3 maxBounds;
+
+// Ray traveling straight through a single 'alpha thickness' applies 100% of the encountered alpha.
+// Rays traveling shorter distances apply a fraction. This is used to normalize the alpha over
+// entire volume, regardless of texture dimensions
+const highp float alphaThicknesses = 32.0;
+const highp float SQRT3 = 1.73205081;
+
+void main() {
+ vec3 rayStart = pos;
+ highp vec3 startBounds = minBounds;
+ highp vec3 endBounds = maxBounds;
+ if (rayDir.x < 0.0) {
+ startBounds.x = maxBounds.x;
+ endBounds.x = minBounds.x;
+ }
+ if (rayDir.y > 0.0) {
+ startBounds.y = maxBounds.y;
+ endBounds.y = minBounds.y;
+ }
+ if (rayDir.z > 0.0) {
+ startBounds.z = maxBounds.z;
+ endBounds.z = minBounds.z;
+ }
+
+ // Calculate ray intersection endpoint
+ highp vec3 rayStop;
+ highp vec3 invRayDir = 1.0 / rayDir;
+ highp vec3 t = (endBounds - rayStart) * invRayDir;
+ highp float endT = min(t.x, min(t.y, t.z));
+ if (endT <= 0.0)
+ discard;
+ rayStop = rayStart + endT * rayDir;
+
+ // Convert intersections to texture coords
+ rayStart = 0.5 * (rayStart + 1.0);
+ rayStop = 0.5 * (rayStop + 1.0);
+
+ highp vec3 ray = rayStop - rayStart;
+
+ highp float fullDist = length(ray);
+ highp float stepSize = SQRT3 / sampleCount;
+ highp vec3 step = (SQRT3 * normalize(ray)) / sampleCount;
+
+ rayStart += (step * 0.001);
+
+ highp vec3 curPos = rayStart;
+ highp float curLen = 0.0;
+ highp vec4 curColor = vec4(0, 0, 0, 0);
+ highp float curAlpha = 0.0;
+ highp vec3 curRgb = vec3(0, 0, 0);
+ highp vec4 destColor = vec4(0, 0, 0, 0);
+ highp float totalOpacity = 1.0;
+
+ highp float extraAlphaMultiplier = stepSize * alphaThicknesses * alphaMultiplier;
+
+ // Raytrace into volume, need to sample pixels along the eye ray until we hit opacity 1
+ for (int i = 0; i < sampleCount; i++) {
+ curColor = texture3D(textureSampler, curPos);
+ if (color8Bit != 0)
+ curColor = colorIndex[int(curColor.r * 255.0)];
+
+ if (curColor.a >= 0.0) {
+ if (curColor.a == 1.0 && (preserveOpacity == 1 || alphaMultiplier >= 1.0))
+ curAlpha = 1.0;
+ else
+ curAlpha = curColor.a * extraAlphaMultiplier;
+ highp float nextOpacity = totalOpacity - curAlpha;
+ // If opacity goes beyond full opacity, we need to adjust current alpha according
+ // to the fraction of the distance the material is visible, so that we don't get
+ // box artifacts around texels.
+ if (nextOpacity < 0.0) {
+ curAlpha *= totalOpacity / curAlpha;
+ nextOpacity = 0.0;
+ }
+
+ curRgb = curColor.rgb * curAlpha * (totalOpacity + nextOpacity);
+ destColor.rgb += curRgb;
+ totalOpacity = nextOpacity;
+ }
+ curPos += step;
+ curLen += stepSize;
+ if (curLen >= fullDist || totalOpacity <= 0.0)
+ break;
+ }
+
+ if (totalOpacity == 1.0)
+ discard;
+
+ // Brighten up the final color if there is some transparency left
+ if (totalOpacity >= 0.0 && totalOpacity < 1.0)
+ destColor *= (1.0 - (totalOpacity * 0.5)) / (1.0 - totalOpacity);
+
+ destColor.a = (1.0 - totalOpacity);
+ gl_FragColor = clamp(destColor, 0.0, 1.0);
+}
diff --git a/src/datavisualization/engine/shaders/texture3dslice.frag b/src/datavisualization/engine/shaders/texture3dslice.frag
new file mode 100644
index 00000000..c555af98
--- /dev/null
+++ b/src/datavisualization/engine/shaders/texture3dslice.frag
@@ -0,0 +1,155 @@
+#version 120
+
+varying highp vec3 pos;
+varying highp vec3 rayDir;
+
+uniform highp sampler3D textureSampler;
+uniform highp vec3 volumeSliceIndices;
+uniform highp vec4 colorIndex[256];
+uniform highp int color8Bit;
+uniform highp float alphaMultiplier;
+uniform highp int preserveOpacity;
+uniform highp vec3 minBounds;
+uniform highp vec3 maxBounds;
+
+const highp vec3 xPlaneNormal = vec3(1.0, 0, 0);
+const highp vec3 yPlaneNormal = vec3(0, 1.0, 0);
+const highp vec3 zPlaneNormal = vec3(0, 0, 1.0);
+
+void main() {
+ // Find out where ray intersects the slice planes
+ vec3 normRayDir = normalize(rayDir);
+ highp vec3 rayStart = pos;
+ highp float minT = 2.0f;
+ if (normRayDir.x != 0.0 && normRayDir.y != 0.0 && normRayDir.z != 0.0) {
+ highp vec3 boxBounds = vec3(1.0, 1.0, 1.0);
+ highp vec3 invRayDir = 1.0 / normRayDir;
+ if (normRayDir.x < 0)
+ boxBounds.x = -1.0;
+ if (normRayDir.y < 0)
+ boxBounds.y = -1.0;
+ if (normRayDir.z < 0)
+ boxBounds.z = -1.0;
+ highp vec3 t = (boxBounds - rayStart) * invRayDir;
+ minT = min(t.x, min(t.y, t.z));
+ }
+
+ highp vec3 xPoint = vec3(volumeSliceIndices.x, 0, 0);
+ highp vec3 yPoint = vec3(0, volumeSliceIndices.y, 0);
+ highp vec3 zPoint = vec3(0, 0, volumeSliceIndices.z);
+ highp float firstD = minT + 1.0;
+ highp float secondD = firstD;
+ highp float thirdD = firstD;
+ if (volumeSliceIndices.x >= -1.0) {
+ highp float dx = dot(xPoint - rayStart, xPlaneNormal) / dot(normRayDir, xPlaneNormal);
+ if (dx >= 0.0 && dx <= minT)
+ firstD = min(dx, firstD);
+ }
+ if (volumeSliceIndices.y >= -1.0) {
+ highp float dy = dot(yPoint - rayStart, yPlaneNormal) / dot(normRayDir, yPlaneNormal);
+ if (dy >= 0.0 && dy <= minT) {
+ if (dy < firstD) {
+ secondD = firstD;
+ firstD = dy;
+ } else {
+ secondD = dy;
+ }
+ }
+ }
+ if (volumeSliceIndices.z >= -1.0) {
+ highp float dz = dot(zPoint - rayStart, zPlaneNormal) / dot(normRayDir, zPlaneNormal);
+ if (dz >= 0.0) {
+ if (dz < firstD && dz <= minT) {
+ thirdD = secondD;
+ secondD = firstD;
+ firstD = dz;
+ } else if (dz < secondD){
+ thirdD = secondD;
+ secondD = dz;
+ } else {
+ thirdD = dz;
+ }
+ }
+ }
+
+ highp vec4 destColor = vec4(0.0, 0.0, 0.0, 0.0);
+ highp vec4 curColor = vec4(0.0, 0.0, 0.0, 0.0);
+ highp float totalAlpha = 0.0;
+ highp vec3 curRgb = vec3(0, 0, 0);
+ highp float curAlpha = 0.0;
+
+ // Convert intersection to texture coords
+
+ if (firstD <= minT) {
+ highp vec3 texelVec = rayStart + normRayDir * firstD;
+ if (clamp(texelVec.x, minBounds.x, maxBounds.x) == texelVec.x
+ && clamp(texelVec.y, maxBounds.y, minBounds.y) == texelVec.y
+ && clamp(texelVec.z, maxBounds.z, minBounds.z) == texelVec.z) {
+ texelVec = 0.5 * (texelVec + 1.0);
+ curColor = texture3D(textureSampler, texelVec);
+ if (color8Bit != 0)
+ curColor = colorIndex[int(curColor.r * 255.0)];
+
+ if (curColor.a > 0.0) {
+ curAlpha = curColor.a;
+ if (curColor.a == 1.0 && preserveOpacity != 0)
+ curAlpha = 1.0;
+ else
+ curAlpha = clamp(curColor.a * alphaMultiplier, 0.0, 1.0);
+ destColor.rgb = curColor.rgb * curAlpha;
+ totalAlpha = curAlpha;
+ }
+ }
+ if (secondD <= minT && totalAlpha < 1.0) {
+ texelVec = rayStart + normRayDir * secondD;
+ if (clamp(texelVec.x, minBounds.x, maxBounds.x) == texelVec.x
+ && clamp(texelVec.y, maxBounds.y, minBounds.y) == texelVec.y
+ && clamp(texelVec.z, maxBounds.z, minBounds.z) == texelVec.z) {
+ texelVec = 0.5 * (texelVec + 1.0);
+ curColor = texture3D(textureSampler, texelVec);
+ if (color8Bit != 0)
+ curColor = colorIndex[int(curColor.r * 255.0)];
+ if (curColor.a > 0.0) {
+ if (curColor.a == 1.0 && preserveOpacity != 0)
+ curAlpha = 1.0;
+ else
+ curAlpha = clamp(curColor.a * alphaMultiplier, 0.0, 1.0);
+ curRgb = curColor.rgb * curAlpha * (1.0 - totalAlpha);
+ destColor.rgb += curRgb;
+ totalAlpha += curAlpha;
+ }
+ }
+ if (thirdD <= minT && totalAlpha < 1.0) {
+ texelVec = rayStart + normRayDir * thirdD;
+ if (clamp(texelVec.x, minBounds.x, maxBounds.x) == texelVec.x
+ && clamp(texelVec.y, maxBounds.y, minBounds.y) == texelVec.y
+ && clamp(texelVec.z, maxBounds.z, minBounds.z) == texelVec.z) {
+ texelVec = 0.5 * (texelVec + 1.0);
+ curColor = texture3D(textureSampler, texelVec);
+ if (curColor.a > 0.0) {
+ if (color8Bit != 0)
+ curColor = colorIndex[int(curColor.r * 255.0)];
+ if (curColor.a == 1.0 && preserveOpacity != 0)
+ curAlpha = 1.0;
+ else
+ curAlpha = clamp(curColor.a * alphaMultiplier, 0.0, 1.0);
+ curRgb = curColor.rgb * curAlpha * (1.0 - totalAlpha);
+ destColor.rgb += curRgb;
+ totalAlpha += curAlpha;
+ }
+ }
+ }
+ }
+ }
+
+ if (totalAlpha == 0.0)
+ discard;
+
+ // Brighten up the final color if there is some transparency left
+ if (totalAlpha > 0.0 && totalAlpha < 1.0)
+ destColor *= 1.0 / totalAlpha;
+
+ destColor.a = totalAlpha;
+ gl_FragColor = clamp(destColor, 0.0, 1.0);
+}
+
diff --git a/src/datavisualization/engine/surface3dcontroller.cpp b/src/datavisualization/engine/surface3dcontroller.cpp
index c03bafd8..a6c086da 100644
--- a/src/datavisualization/engine/surface3dcontroller.cpp
+++ b/src/datavisualization/engine/surface3dcontroller.cpp
@@ -29,7 +29,8 @@ Surface3DController::Surface3DController(QRect rect, Q3DScene *scene)
m_renderer(0),
m_selectedPoint(invalidSelectionPosition()),
m_selectedSeries(0),
- m_flatShadingSupported(true)
+ m_flatShadingSupported(true),
+ m_flipHorizontalGrid(false)
{
// Setting a null axis creates a new default axis according to orientation and graph type.
// Note: these cannot be set in the Abstract3DController constructor, as they will call virtual
@@ -79,6 +80,17 @@ void Surface3DController::synchDataToRenderer()
m_renderer->updateSelectedPoint(m_selectedPoint, m_selectedSeries);
m_changeTracker.selectedPointChanged = false;
}
+
+ if (m_changeTracker.flipHorizontalGridChanged) {
+ m_renderer->updateFlipHorizontalGrid(m_flipHorizontalGrid);
+ m_changeTracker.flipHorizontalGridChanged = false;
+ }
+
+ if (m_changeTracker.surfaceTextureChanged) {
+ m_renderer->updateSurfaceTextures(m_changedTextures);
+ m_changeTracker.surfaceTextureChanged = false;
+ m_changedTextures.clear();
+ }
}
void Surface3DController::handleAxisAutoAdjustRangeChangedInOrientation(
@@ -140,6 +152,9 @@ void Surface3DController::addSeries(QAbstract3DSeries *series)
QSurface3DSeries *surfaceSeries = static_cast<QSurface3DSeries *>(series);
if (surfaceSeries->selectedPoint() != invalidSelectionPosition())
setSelectedPoint(surfaceSeries->selectedPoint(), surfaceSeries, false);
+
+ if (!surfaceSeries->texture().isNull())
+ updateSurfaceTexture(surfaceSeries);
}
void Surface3DController::removeSeries(QAbstract3DSeries *series)
@@ -168,6 +183,21 @@ QList<QSurface3DSeries *> Surface3DController::surfaceSeriesList()
return surfaceSeriesList;
}
+void Surface3DController::setFlipHorizontalGrid(bool flip)
+{
+ if (m_flipHorizontalGrid != flip) {
+ m_flipHorizontalGrid = flip;
+ m_changeTracker.flipHorizontalGridChanged = true;
+ emit flipHorizontalGridChanged(flip);
+ emitNeedRender();
+ }
+}
+
+bool Surface3DController::flipHorizontalGrid() const
+{
+ return m_flipHorizontalGrid;
+}
+
void Surface3DController::setSelectionMode(QAbstract3DGraph::SelectionFlags mode)
{
// Currently surface only supports row and column modes when also slicing
@@ -429,6 +459,16 @@ void Surface3DController::handleRowsRemoved(int startIndex, int count)
emitNeedRender();
}
+void Surface3DController::updateSurfaceTexture(QSurface3DSeries *series)
+{
+ m_changeTracker.surfaceTextureChanged = true;
+
+ if (!m_changedTextures.contains(series))
+ m_changedTextures.append(series);
+
+ emitNeedRender();
+}
+
void Surface3DController::adjustAxisRanges()
{
QValue3DAxis *valueAxisX = static_cast<QValue3DAxis *>(m_axisX);
diff --git a/src/datavisualization/engine/surface3dcontroller_p.h b/src/datavisualization/engine/surface3dcontroller_p.h
index 2be74f35..8bcdea91 100644
--- a/src/datavisualization/engine/surface3dcontroller_p.h
+++ b/src/datavisualization/engine/surface3dcontroller_p.h
@@ -38,14 +38,18 @@ class Surface3DRenderer;
class QSurface3DSeries;
struct Surface3DChangeBitField {
- bool selectedPointChanged : 1;
- bool rowsChanged : 1;
- bool itemChanged : 1;
+ bool selectedPointChanged : 1;
+ bool rowsChanged : 1;
+ bool itemChanged : 1;
+ bool flipHorizontalGridChanged : 1;
+ bool surfaceTextureChanged : 1;
Surface3DChangeBitField() :
selectedPointChanged(true),
rowsChanged(false),
- itemChanged(false)
+ itemChanged(false),
+ flipHorizontalGridChanged(true),
+ surfaceTextureChanged(true)
{
}
};
@@ -73,6 +77,8 @@ private:
bool m_flatShadingSupported;
QVector<ChangeItem> m_changedItems;
QVector<ChangeRow> m_changedRows;
+ bool m_flipHorizontalGrid;
+ QVector<QSurface3DSeries *> m_changedTextures;
public:
explicit Surface3DController(QRect rect, Q3DScene *scene = 0);
@@ -101,6 +107,11 @@ public:
virtual void removeSeries(QAbstract3DSeries *series);
virtual QList<QSurface3DSeries *> surfaceSeriesList();
+ void setFlipHorizontalGrid(bool flip);
+ bool flipHorizontalGrid() const;
+
+ void updateSurfaceTexture(QSurface3DSeries *series);
+
public slots:
void handleArrayReset();
void handleRowsAdded(int startIndex, int count);
@@ -113,6 +124,7 @@ public slots:
signals:
void selectedSeriesChanged(QSurface3DSeries *series);
+ void flipHorizontalGridChanged(bool flip);
private:
Q_DISABLE_COPY(Surface3DController)
diff --git a/src/datavisualization/engine/surface3drenderer.cpp b/src/datavisualization/engine/surface3drenderer.cpp
index 38c8d8fe..37d6b463 100644
--- a/src/datavisualization/engine/surface3drenderer.cpp
+++ b/src/datavisualization/engine/surface3drenderer.cpp
@@ -30,10 +30,6 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
//#define SHOW_DEPTH_TEXTURE_SCENE
-// Margin for background (1.10 make it 10% larger to avoid
-// selection ball being drawn inside background)
-const GLfloat backgroundMargin = 1.1f;
-const GLfloat gridLineWidth = 0.005f;
const GLfloat sliceZScale = 0.1f;
const GLfloat sliceUnits = 2.5f;
const uint greenMultiplier = 256;
@@ -47,19 +43,16 @@ Surface3DRenderer::Surface3DRenderer(Surface3DController *controller)
m_backgroundShader(0),
m_surfaceFlatShader(0),
m_surfaceSmoothShader(0),
+ m_surfaceTexturedSmoothShader(0),
+ m_surfaceTexturedFlatShader(0),
m_surfaceGridShader(0),
m_surfaceSliceFlatShader(0),
m_surfaceSliceSmoothShader(0),
m_selectionShader(0),
- m_labelShader(0),
m_heightNormalizer(0.0f),
- m_scaleFactor(0.0f),
m_scaleX(0.0f),
+ m_scaleY(0.0f),
m_scaleZ(0.0f),
- m_scaleXWithBackground(0.0f),
- m_scaleZWithBackground(0.0f),
- m_depthTexture(0),
- m_depthModelTexture(0),
m_depthFrameBuffer(0),
m_selectionFrameBuffer(0),
m_selectionDepthBuffer(0),
@@ -68,16 +61,12 @@ Surface3DRenderer::Surface3DRenderer(Surface3DController *controller)
m_flatSupported(true),
m_selectionActive(false),
m_shadowQualityMultiplier(3),
- m_hasHeightAdjustmentChanged(true),
m_selectedPoint(Surface3DController::invalidSelectionPosition()),
m_selectedSeries(0),
m_clickedPosition(Surface3DController::invalidSelectionPosition()),
m_selectionTexturesDirty(false),
m_noShadowTexture(0)
{
- m_axisCacheY.setScale(2.0f);
- m_axisCacheY.setTranslate(-1.0f);
-
// Check if flat feature is supported
ShaderHelper tester(this, QStringLiteral(":/shaders/vertexSurfaceFlat"),
QStringLiteral(":/shaders/fragmentSurfaceFlat"));
@@ -90,12 +79,13 @@ Surface3DRenderer::Surface3DRenderer(Surface3DController *controller)
" Requires at least GLSL version 1.2 with GL_EXT_gpu_shader4 extension.";
}
- initializeOpenGLFunctions();
initializeOpenGL();
}
Surface3DRenderer::~Surface3DRenderer()
{
+ fixContextBeforeDelete();
+
if (QOpenGLContext::currentContext()) {
m_textureHelper->glDeleteFramebuffers(1, &m_depthFrameBuffer);
m_textureHelper->glDeleteRenderbuffers(1, &m_selectionDepthBuffer);
@@ -103,7 +93,6 @@ Surface3DRenderer::~Surface3DRenderer()
m_textureHelper->deleteTexture(&m_noShadowTexture);
m_textureHelper->deleteTexture(&m_depthTexture);
- m_textureHelper->deleteTexture(&m_depthModelTexture);
m_textureHelper->deleteTexture(&m_selectionResultTexture);
}
delete m_depthShader;
@@ -111,10 +100,11 @@ Surface3DRenderer::~Surface3DRenderer()
delete m_selectionShader;
delete m_surfaceFlatShader;
delete m_surfaceSmoothShader;
+ delete m_surfaceTexturedSmoothShader;
+ delete m_surfaceTexturedFlatShader;
delete m_surfaceGridShader;
delete m_surfaceSliceFlatShader;
delete m_surfaceSliceSmoothShader;
- delete m_labelShader;
}
void Surface3DRenderer::initializeOpenGL()
@@ -123,25 +113,15 @@ void Surface3DRenderer::initializeOpenGL()
// Initialize shaders
initSurfaceShaders();
- initLabelShaders(QStringLiteral(":/shaders/vertexLabel"),
- QStringLiteral(":/shaders/fragmentLabel"));
-#if !defined(QT_OPENGL_ES_2)
- // Init depth shader (for shadows). Init in any case, easier to handle shadow activation if done via api.
- initDepthShader();
-#endif
+ if (!m_isOpenGLES) {
+ initDepthShader(); // For shadows
+ loadGridLineMesh();
+ }
// Init selection shader
initSelectionShaders();
-#if !(defined QT_OPENGL_ES_2)
- // Load grid line mesh
- loadGridLineMesh();
-#endif
-
- // Load label mesh
- loadLabelMesh();
-
// Resize in case we've missed resize events
// Resize calls initSelectionBuffer and initDepthBuffer, so they don't need to be called here
handleResize();
@@ -155,6 +135,53 @@ void Surface3DRenderer::initializeOpenGL()
m_noShadowTexture = m_textureHelper->create2DTexture(image, false, true, false, true);
}
+void Surface3DRenderer::fixCameraTarget(QVector3D &target)
+{
+ target.setX(target.x() * m_scaleX);
+ target.setY(target.y() * m_scaleY);
+ target.setZ(target.z() * -m_scaleZ);
+}
+
+void Surface3DRenderer::getVisibleItemBounds(QVector3D &minBounds, QVector3D &maxBounds)
+{
+ // The inputs are the item bounds in OpenGL coordinates.
+ // The outputs limit these bounds to visible ranges, normalized to range [-1, 1]
+ // Volume shader flips the Y and Z axes, so we need to set negatives of actual values to those
+ float itemRangeX = (maxBounds.x() - minBounds.x());
+ float itemRangeY = (maxBounds.y() - minBounds.y());
+ float itemRangeZ = (maxBounds.z() - minBounds.z());
+
+ if (minBounds.x() < -m_scaleX)
+ minBounds.setX(-1.0f + (2.0f * qAbs(minBounds.x() + m_scaleX) / itemRangeX));
+ else
+ minBounds.setX(-1.0f);
+
+ if (minBounds.y() < -m_scaleY)
+ minBounds.setY(-(-1.0f + (2.0f * qAbs(minBounds.y() + m_scaleY) / itemRangeY)));
+ else
+ minBounds.setY(1.0f);
+
+ if (minBounds.z() < -m_scaleZ)
+ minBounds.setZ(-(-1.0f + (2.0f * qAbs(minBounds.z() + m_scaleZ) / itemRangeZ)));
+ else
+ minBounds.setZ(1.0f);
+
+ if (maxBounds.x() > m_scaleX)
+ maxBounds.setX(1.0f - (2.0f * qAbs(maxBounds.x() - m_scaleX) / itemRangeX));
+ else
+ maxBounds.setX(1.0f);
+
+ if (maxBounds.y() > m_scaleY)
+ maxBounds.setY(-(1.0f - (2.0f * qAbs(maxBounds.y() - m_scaleY) / itemRangeY)));
+ else
+ maxBounds.setY(-1.0f);
+
+ if (maxBounds.z() > m_scaleZ)
+ maxBounds.setZ(-(1.0f - (2.0f * qAbs(maxBounds.z() - m_scaleZ) / itemRangeZ)));
+ else
+ maxBounds.setZ(-1.0f);
+}
+
void Surface3DRenderer::updateData()
{
calculateSceneScalingFactors();
@@ -264,6 +291,33 @@ void Surface3DRenderer::updateSeries(const QList<QAbstract3DSeries *> &seriesLis
}
}
+void Surface3DRenderer::updateSurfaceTextures(QVector<QSurface3DSeries *> seriesList)
+{
+ foreach (QSurface3DSeries *series, seriesList) {
+ SurfaceSeriesRenderCache *cache =
+ static_cast<SurfaceSeriesRenderCache *>(m_renderCacheList.value(series));
+ if (cache) {
+ GLuint oldTexture = cache->surfaceTexture();
+ m_textureHelper->deleteTexture(&oldTexture);
+ cache->setSurfaceTexture(0);
+
+ const QSurface3DSeries *currentSeries = cache->series();
+ QSurfaceDataProxy *dataProxy = currentSeries->dataProxy();
+ const QSurfaceDataArray &array = *dataProxy->array();
+
+ if (!series->texture().isNull()) {
+ cache->setSurfaceTexture(m_textureHelper->create2DTexture(
+ series->texture(), true, true, true));
+
+ if (cache->isFlatShadingEnabled())
+ cache->surfaceObject()->coarseUVs(array, cache->dataArray());
+ else
+ cache->surfaceObject()->smoothUVs(array, cache->dataArray());
+ }
+ }
+ }
+}
+
SeriesRenderCache *Surface3DRenderer::createNewCache(QAbstract3DSeries *series)
{
m_selectionTexturesDirty = true;
@@ -301,10 +355,13 @@ void Surface3DRenderer::updateRows(const QVector<Surface3DController::ChangeRow>
srcArray->at(row)->at(j + sampleSpace.x());
}
- if (cache->isFlatShadingEnabled())
- cache->surfaceObject()->updateCoarseRow(dstArray, row - sampleSpace.y());
- else
- cache->surfaceObject()->updateSmoothRow(dstArray, row - sampleSpace.y());
+ if (cache->isFlatShadingEnabled()) {
+ cache->surfaceObject()->updateCoarseRow(dstArray, row - sampleSpace.y(),
+ m_polarGraph);
+ } else {
+ cache->surfaceObject()->updateSmoothRow(dstArray, row - sampleSpace.y(),
+ m_polarGraph);
+ }
}
if (updateBuffers)
cache->surfaceObject()->uploadBuffers();
@@ -343,9 +400,9 @@ void Surface3DRenderer::updateItems(const QVector<Surface3DController::ChangeIte
(*(dstArray.at(y)))[x] = srcArray->at(point.x())->at(point.y());
if (cache->isFlatShadingEnabled())
- cache->surfaceObject()->updateCoarseItem(dstArray, y, x);
+ cache->surfaceObject()->updateCoarseItem(dstArray, y, x, m_polarGraph);
else
- cache->surfaceObject()->updateSmoothItem(dstArray, y, x);
+ cache->surfaceObject()->updateSmoothItem(dstArray, y, x, m_polarGraph);
}
if (updateBuffers)
cache->surfaceObject()->uploadBuffers();
@@ -538,10 +595,12 @@ void Surface3DRenderer::updateSliceObject(SurfaceSeriesRenderCache *cache, const
QRect sliceRect(0, 0, sliceRow->size(), 2);
if (sliceRow->size() > 0) {
- if (cache->isFlatShadingEnabled())
- cache->sliceSurfaceObject()->setUpData(sliceDataArray, sliceRect, true, flipZX);
- else
- cache->sliceSurfaceObject()->setUpSmoothData(sliceDataArray, sliceRect, true, flipZX);
+ if (cache->isFlatShadingEnabled()) {
+ cache->sliceSurfaceObject()->setUpData(sliceDataArray, sliceRect, true, false, flipZX);
+ } else {
+ cache->sliceSurfaceObject()->setUpSmoothData(sliceDataArray, sliceRect, true, false,
+ flipZX);
+ }
}
}
@@ -664,15 +723,6 @@ QRect Surface3DRenderer::calculateSampleRect(const QSurfaceDataArray &array)
void Surface3DRenderer::updateScene(Q3DScene *scene)
{
- // Set initial camera position
- // X must be 0 for rotation to work - we can use "setCameraRotation" for setting it later
- if (m_hasHeightAdjustmentChanged) {
- scene->activeCamera()->d_ptr->setBaseOrientation(cameraDistanceVector, zeroVector,
- upVector);
- // For now this is used just to make things once. Proper use will come
- m_hasHeightAdjustmentChanged = false;
- }
-
Abstract3DRenderer::updateScene(scene);
if (m_selectionActive
@@ -716,6 +766,14 @@ void Surface3DRenderer::render(GLuint defaultFboHandle)
void Surface3DRenderer::drawSlicedScene()
{
+ if (m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionRow)
+ == m_cachedSelectionMode.testFlag(QAbstract3DGraph::SelectionColumn)) {
+ qWarning("Invalid selection mode. Either QAbstract3DGraph::SelectionRow or"
+ " QAbstract3DGraph::SelectionColumn must be set before calling"
+ " setSlicingActive(true).");
+ return;
+ }
+
QVector3D lightPos;
QVector4D lightColor = Utils::vectorFromColor(m_cachedTheme->lightColor());
@@ -751,6 +809,19 @@ void Surface3DRenderer::drawSlicedScene()
AxisRenderCache &sliceCache = rowMode ? m_axisCacheX : m_axisCacheZ;
GLfloat scaleXBackground = 0.0f;
+ if (rowMode) {
+ // Don't use the regular margin for polar, as the graph is not going to be to scale anyway,
+ // and polar graphs often have quite a bit of margin, resulting in ugly slices.
+ if (m_polarGraph)
+ scaleXBackground = m_scaleX + 0.1f;
+ else
+ scaleXBackground = m_scaleXWithBackground;
+ } else {
+ if (m_polarGraph)
+ scaleXBackground = m_scaleZ + 0.1f;
+ else
+ scaleXBackground = m_scaleZWithBackground;
+ }
// Disable culling to avoid ugly conditionals with reversed axes and data
glDisable(GL_CULL_FACE);
@@ -767,11 +838,6 @@ void Surface3DRenderer::drawSlicedScene()
drawGrid = true;
}
- if (rowMode)
- scaleXBackground = m_scaleXWithBackground;
- else
- scaleXBackground = m_scaleZWithBackground;
-
QMatrix4x4 MVPMatrix;
QMatrix4x4 modelMatrix;
QMatrix4x4 itModelMatrix;
@@ -790,9 +856,27 @@ void Surface3DRenderer::drawSlicedScene()
surfaceShader->bind();
- GLuint colorTexture = cache->baseUniformTexture();;
- if (cache->colorStyle() != Q3DTheme::ColorStyleUniform)
+ GLuint colorTexture = cache->baseUniformTexture();
+ if (cache->colorStyle() == Q3DTheme::ColorStyleUniform) {
+ colorTexture = cache->baseUniformTexture();
+ surfaceShader->setUniformValue(surfaceShader->gradientMin(), 0.0f);
+ surfaceShader->setUniformValue(surfaceShader->gradientHeight(), 0.0f);
+ } else {
colorTexture = cache->baseGradientTexture();
+ if (cache->colorStyle() == Q3DTheme::ColorStyleObjectGradient) {
+ float objMin = cache->surfaceObject()->minYValue();
+ float objMax = cache->surfaceObject()->maxYValue();
+ float objRange = objMax - objMin;
+ surfaceShader->setUniformValue(surfaceShader->gradientMin(),
+ -(objMin / objRange));
+ surfaceShader->setUniformValue(surfaceShader->gradientHeight(),
+ 1.0f / objRange);
+ } else {
+ surfaceShader->setUniformValue(surfaceShader->gradientMin(), 0.5f);
+ surfaceShader->setUniformValue(surfaceShader->gradientHeight(),
+ 1.0f / (m_scaleY * 2.0f));
+ }
+ }
// Set shader bindings
surfaceShader->setUniformValue(surfaceShader->lightP(), lightPos);
@@ -801,9 +885,10 @@ void Surface3DRenderer::drawSlicedScene()
surfaceShader->setUniformValue(surfaceShader->nModel(),
itModelMatrix.inverted().transposed());
surfaceShader->setUniformValue(surfaceShader->MVP(), MVPMatrix);
- surfaceShader->setUniformValue(surfaceShader->lightS(), 0.15f);
+ surfaceShader->setUniformValue(surfaceShader->lightS(), 0.0f);
surfaceShader->setUniformValue(surfaceShader->ambientS(),
- m_cachedTheme->ambientLightStrength() * 2.3f);
+ m_cachedTheme->ambientLightStrength()
+ + m_cachedTheme->lightStrength() / 10.0f);
surfaceShader->setUniformValue(surfaceShader->lightColor(), lightColor);
m_drawer->drawObject(surfaceShader, cache->sliceSurfaceObject(), colorTexture);
@@ -830,19 +915,16 @@ void Surface3DRenderer::drawSlicedScene()
}
}
- // Disable textures
- glDisable(GL_TEXTURE_2D);
-
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
// Grid lines
- if (m_cachedTheme->isGridEnabled() && m_heightNormalizer) {
-#if !(defined QT_OPENGL_ES_2)
- ShaderHelper *lineShader = m_backgroundShader;
-#else
- ShaderHelper *lineShader = m_selectionShader; // Plain color shader for GL_LINES
-#endif
+ if (m_cachedTheme->isGridEnabled()) {
+ ShaderHelper *lineShader;
+ if (m_isOpenGLES)
+ lineShader = m_selectionShader; // Plain color shader for GL_LINES
+ else
+ lineShader = m_backgroundShader;
// Bind line shader
lineShader->bind();
@@ -853,7 +935,8 @@ void Surface3DRenderer::drawSlicedScene()
lineShader->setUniformValue(lineShader->view(), viewMatrix);
lineShader->setUniformValue(lineShader->color(), lineColor);
lineShader->setUniformValue(lineShader->ambientS(),
- m_cachedTheme->ambientLightStrength() * 2.3f);
+ m_cachedTheme->ambientLightStrength()
+ + m_cachedTheme->lightStrength() / 10.0f);
lineShader->setUniformValue(lineShader->lightS(), 0.0f);
lineShader->setUniformValue(lineShader->lightColor(), lightColor);
@@ -881,16 +964,15 @@ void Surface3DRenderer::drawSlicedScene()
lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
// Draw the object
-#if !(defined QT_OPENGL_ES_2)
- m_drawer->drawObject(lineShader, m_gridLineObj);
-#else
- m_drawer->drawLine(lineShader);
-#endif
+ if (m_isOpenGLES)
+ m_drawer->drawLine(lineShader);
+ else
+ m_drawer->drawObject(lineShader, m_gridLineObj);
}
}
// Vertical lines
- QVector3D gridLineScaleY(gridLineWidth, backgroundMargin, gridLineWidth);
+ QVector3D gridLineScaleY(gridLineWidth, m_scaleYWithBackground, gridLineWidth);
gridLineCount = sliceCache.gridLineCount();
for (int line = 0; line < gridLineCount; line++) {
@@ -901,10 +983,11 @@ void Surface3DRenderer::drawSlicedScene()
modelMatrix.translate(sliceCache.gridLinePosition(line), 0.0f, -1.0f);
modelMatrix.scale(gridLineScaleY);
itModelMatrix.scale(gridLineScaleY);
-#if (defined QT_OPENGL_ES_2)
- modelMatrix.rotate(90.0f, 0.0f, 0.0f, 1.0f);
- itModelMatrix.rotate(90.0f, 0.0f, 0.0f, 1.0f);
-#endif
+
+ if (m_isOpenGLES) {
+ modelMatrix.rotate(m_zRightAngleRotation);
+ itModelMatrix.rotate(m_zRightAngleRotation);
+ }
MVPMatrix = projectionViewMatrix * modelMatrix;
@@ -915,17 +998,15 @@ void Surface3DRenderer::drawSlicedScene()
lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
// Draw the object
-#if !(defined QT_OPENGL_ES_2)
- m_drawer->drawObject(lineShader, m_gridLineObj);
-#else
- m_drawer->drawLine(lineShader);
-#endif
+ if (m_isOpenGLES)
+ m_drawer->drawLine(lineShader);
+ else
+ m_drawer->drawObject(lineShader, m_gridLineObj);
}
}
// Draw labels
m_labelShader->bind();
- glEnable(GL_TEXTURE_2D);
glDisable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
@@ -959,12 +1040,15 @@ void Surface3DRenderer::drawSlicedScene()
labelNbr = 0;
positionComp.setY(-0.1f);
- labelTrans.setY(-backgroundMargin);
+ labelTrans.setY(-m_scaleYWithBackground);
labelCount = sliceCache.labelCount();
for (int label = 0; label < labelCount; label++) {
if (countLabelItems > labelNbr) {
// Draw the label here
- labelTrans.setX(sliceCache.labelPosition(label));
+ if (rowMode)
+ labelTrans.setX(sliceCache.labelPosition(label));
+ else
+ labelTrans.setX(-sliceCache.labelPosition(label));
m_dummyRenderItem.setTranslation(labelTrans);
@@ -998,7 +1082,6 @@ void Surface3DRenderer::drawSlicedScene()
m_cachedSelectionMode, m_labelShader, m_labelObj, activeCamera,
false, false, Drawer::LabelMid, Qt::AlignBottom);
- glDisable(GL_TEXTURE_2D);
glEnable(GL_DEPTH_TEST);
glDisable(GL_BLEND);
@@ -1048,6 +1131,21 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
else
m_xFlipped = true;
+ m_yFlippedForGrid = m_yFlipped;
+ if (m_flipHorizontalGrid) {
+ if (!m_useOrthoProjection) {
+ // Need to determine if camera is below graph top
+ float distanceToCenter = activeCamera->position().length()
+ / activeCamera->zoomLevel() / m_autoScaleAdjustment * 100.0f;
+ qreal cameraAngle = qreal(activeCamera->yRotation()) / 180.0 * M_PI;
+ float cameraYPos = float(qSin(cameraAngle)) * distanceToCenter;
+ m_yFlippedForGrid = cameraYPos < (m_scaleYWithBackground - m_oldCameraTarget.y());
+ } else if (m_useOrthoProjection && activeCamera->yRotation() == 0.0f) {
+ // With ortho we only need to flip at angle zero, to fix label autorotation angles
+ m_yFlippedForGrid = !m_yFlipped;
+ }
+ }
+
// calculate background rotation based on view matrix rotation
if (viewMatrix.row(0).x() > 0 && viewMatrix.row(0).z() <= 0)
backgroundRotation = 270.0f;
@@ -1065,9 +1163,8 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
QMatrix4x4 depthProjectionViewMatrix;
// Draw depth buffer
-#if !defined(QT_OPENGL_ES_2)
GLfloat adjustedLightStrength = m_cachedTheme->lightStrength() / 10.0f;
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone &&
+ if (!m_isOpenGLES && m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone &&
(!m_renderCacheList.isEmpty() || !m_customRenderCache.isEmpty())) {
// Render scene into a depth texture for using with shadow mapping
// Enable drawing to depth framebuffer
@@ -1097,6 +1194,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
/ (GLfloat)m_primarySubViewport.height(), 3.0f, 100.0f);
depthProjectionViewMatrix = depthProjectionMatrix * depthViewMatrix;
+ // Surface is not closed, so don't cull anything
glDisable(GL_CULL_FACE);
foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
@@ -1104,45 +1202,9 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
SurfaceObject *object = cache->surfaceObject();
if (object->indexCount() && cache->surfaceVisible() && cache->isVisible()
&& cache->sampleSpace().width() >= 2 && cache->sampleSpace().height() >= 2) {
- QMatrix4x4 modelMatrix;
- QMatrix4x4 MVPMatrix;
-
- MVPMatrix = depthProjectionViewMatrix * modelMatrix;
- cache->setMVPMatrix(MVPMatrix);
- m_depthShader->setUniformValue(m_depthShader->MVP(), MVPMatrix);
-
- // 1st attribute buffer : vertices
- glEnableVertexAttribArray(m_depthShader->posAtt());
- glBindBuffer(GL_ARRAY_BUFFER, object->vertexBuf());
- glVertexAttribPointer(m_depthShader->posAtt(), 3, GL_FLOAT, GL_FALSE, 0,
- (void *)0);
-
- // Index buffer
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, object->elementBuf());
-
- // Draw the triangles
- glDrawElements(GL_TRIANGLES, object->indexCount(),
- object->indicesType(), (void *)0);
- }
- }
-
- glEnable(GL_CULL_FACE);
- glCullFace(GL_FRONT);
-
- Abstract3DRenderer::drawCustomItems(RenderingDepth, m_depthShader, viewMatrix,
- projectionViewMatrix, depthProjectionViewMatrix,
- m_depthTexture, m_shadowQualityToShader);
-
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D,
- m_depthModelTexture, 0);
- glClear(GL_DEPTH_BUFFER_BIT);
-
- foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
- SurfaceSeriesRenderCache *cache = static_cast<SurfaceSeriesRenderCache *>(baseCache);
- SurfaceObject *object = cache->surfaceObject();
- if (object->indexCount() && cache->surfaceVisible() && cache->isVisible()
- && cache->sampleSpace().width() >= 2 && cache->sampleSpace().height() >= 2) {
- m_depthShader->setUniformValue(m_depthShader->MVP(), cache->MVPMatrix());
+ // No translation nor scaling for surfaces, therefore no modelMatrix
+ // Use directly projectionViewMatrix
+ m_depthShader->setUniformValue(m_depthShader->MVP(), depthProjectionViewMatrix);
// 1st attribute buffer : vertices
glEnableVertexAttribArray(m_depthShader->posAtt());
@@ -1165,9 +1227,13 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
glDisableVertexAttribArray(m_depthShader->posAtt());
+ glEnable(GL_CULL_FACE);
+ glCullFace(GL_FRONT);
+
Abstract3DRenderer::drawCustomItems(RenderingDepth, m_depthShader, viewMatrix,
- projectionViewMatrix, depthProjectionViewMatrix,
- m_depthTexture, m_shadowQualityToShader);
+ projectionViewMatrix,
+ depthProjectionViewMatrix, m_depthTexture,
+ m_shadowQualityToShader);
// Disable drawing to depth framebuffer (= enable drawing to screen)
glBindFramebuffer(GL_FRAMEBUFFER, defaultFboHandle);
@@ -1182,15 +1248,20 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
}
-#endif
- // Enable texturing
- glEnable(GL_TEXTURE_2D);
+
+ // Do position mapping when necessary
+ if (m_graphPositionQueryPending) {
+ QVector3D graphDimensions(m_scaleX, m_scaleY, m_scaleZ);
+ queriedGraphPosition(projectionViewMatrix, graphDimensions, defaultFboHandle);
+ emit needRender();
+ }
// Draw selection buffer
if (!m_cachedIsSlicingActivated && (!m_renderCacheList.isEmpty()
|| !m_customRenderCache.isEmpty())
&& m_selectionState == SelectOnScene
- && m_cachedSelectionMode > QAbstract3DGraph::SelectionNone) {
+ && m_cachedSelectionMode > QAbstract3DGraph::SelectionNone
+ && m_selectionResultTexture) {
m_selectionShader->bind();
glBindFramebuffer(GL_FRAMEBUFFER, m_selectionFrameBuffer);
glViewport(0,
@@ -1208,18 +1279,17 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
foreach (SeriesRenderCache *baseCache, m_renderCacheList) {
SurfaceSeriesRenderCache *cache = static_cast<SurfaceSeriesRenderCache *>(baseCache);
if (cache->surfaceObject()->indexCount() && cache->renderable()) {
- QMatrix4x4 modelMatrix;
- QMatrix4x4 MVPMatrix;
+ m_selectionShader->setUniformValue(m_selectionShader->MVP(), projectionViewMatrix);
- MVPMatrix = projectionViewMatrix * modelMatrix;
- m_selectionShader->setUniformValue(m_selectionShader->MVP(), MVPMatrix);
+ cache->surfaceObject()->activateSurfaceTexture(false);
m_drawer->drawObject(m_selectionShader, cache->surfaceObject(),
cache->selectionTexture());
}
}
m_surfaceGridShader->bind();
- Abstract3DRenderer::drawCustomItems(RenderingSelection, m_surfaceGridShader, viewMatrix,
+ Abstract3DRenderer::drawCustomItems(RenderingSelection, m_surfaceGridShader,
+ viewMatrix,
projectionViewMatrix, depthProjectionViewMatrix,
m_depthTexture, m_shadowQualityToShader);
drawLabels(true, activeCamera, viewMatrix, projectionMatrix);
@@ -1237,6 +1307,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
+ uint(clickedColor.w()) * alphaMultiplier;
m_clickedPosition = selectionIdToSurfacePoint(selectionId);
+ m_clickResolved = true;
emit needRender();
@@ -1249,7 +1320,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
// Draw the surface
if (!m_renderCacheList.isEmpty()) {
- // For surface we can see climpses from underneath
+ // For surface we can see glimpses from underneath
glDisable(GL_CULL_FACE);
bool drawGrid = false;
@@ -1261,9 +1332,9 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
QMatrix4x4 itModelMatrix;
#ifdef SHOW_DEPTH_TEXTURE_SCENE
- MVPMatrix = depthProjectionViewMatrix * modelMatrix;
+ MVPMatrix = depthProjectionViewMatrix;
#else
- MVPMatrix = projectionViewMatrix * modelMatrix;
+ MVPMatrix = projectionViewMatrix;
#endif
cache->setMVPMatrix(MVPMatrix);
@@ -1279,8 +1350,13 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
if (cache->surfaceVisible()) {
ShaderHelper *shader = m_surfaceFlatShader;
- if (!cache->isFlatShadingEnabled())
+ if (cache->surfaceTexture())
+ shader = m_surfaceTexturedFlatShader;
+ if (!cache->isFlatShadingEnabled()) {
shader = m_surfaceSmoothShader;
+ if (cache->surfaceTexture())
+ shader = m_surfaceTexturedSmoothShader;
+ }
shader->bind();
// Set shader bindings
@@ -1294,14 +1370,35 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
m_cachedTheme->ambientLightStrength());
shader->setUniformValue(shader->lightColor(), lightColor);
- GLuint gradientTexture;
- if (cache->colorStyle() == Q3DTheme::ColorStyleUniform)
- gradientTexture = cache->baseUniformTexture();
- else
- gradientTexture = cache->baseGradientTexture();
+ // Set the surface texturing
+ cache->surfaceObject()->activateSurfaceTexture(false);
+ GLuint texture;
+ if (cache->surfaceTexture()) {
+ texture = cache->surfaceTexture();
+ cache->surfaceObject()->activateSurfaceTexture(true);
+ } else {
+ if (cache->colorStyle() == Q3DTheme::ColorStyleUniform) {
+ texture = cache->baseUniformTexture();
+ shader->setUniformValue(shader->gradientMin(), 0.0f);
+ shader->setUniformValue(shader->gradientHeight(), 0.0f);
+ } else {
+ texture = cache->baseGradientTexture();
+ if (cache->colorStyle() == Q3DTheme::ColorStyleObjectGradient) {
+ float objMin = cache->surfaceObject()->minYValue();
+ float objMax = cache->surfaceObject()->maxYValue();
+ float objRange = objMax - objMin;
+ shader->setUniformValue(shader->gradientMin(), -(objMin / objRange));
+ shader->setUniformValue(shader->gradientHeight(), 1.0f / objRange);
+ } else {
+ shader->setUniformValue(shader->gradientMin(), 0.5f);
+ shader->setUniformValue(shader->gradientHeight(),
+ 1.0f / (m_scaleY * 2.0f));
+ }
+ }
+ }
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ if (!m_isOpenGLES &&
+ m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
// Set shadow shader bindings
QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
shader->setUniformValue(shader->shadowQ(), m_shadowQualityToShader);
@@ -1309,17 +1406,13 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
shader->setUniformValue(shader->lightS(), adjustedLightStrength);
// Draw the objects
- m_drawer->drawObject(shader, cache->surfaceObject(), gradientTexture,
- m_depthModelTexture);
- } else
-#endif
- {
+ m_drawer->drawObject(shader, cache->surfaceObject(), texture,
+ m_depthTexture);
+ } else {
// Set shadowless shader bindings
- shader->setUniformValue(shader->lightS(),
- m_cachedTheme->lightStrength());
-
+ shader->setUniformValue(shader->lightS(), m_cachedTheme->lightStrength());
// Draw the objects
- m_drawer->drawObject(shader, cache->surfaceObject(), gradientTexture);
+ m_drawer->drawObject(shader, cache->surfaceObject(), texture);
}
}
}
@@ -1359,12 +1452,12 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
QMatrix4x4 MVPMatrix;
QMatrix4x4 itModelMatrix;
- QVector3D bgScale(m_scaleXWithBackground, backgroundMargin, m_scaleZWithBackground);
+ QVector3D bgScale(m_scaleXWithBackground, m_scaleYWithBackground, m_scaleZWithBackground);
modelMatrix.scale(bgScale);
// If we're viewing from below, background object must be flipped
if (m_yFlipped) {
- modelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f);
+ modelMatrix.rotate(m_xFlipRotation);
modelMatrix.rotate(270.0f - backgroundRotation, 0.0f, 1.0f, 0.0f);
} else {
modelMatrix.rotate(backgroundRotation, 0.0f, 1.0f, 0.0f);
@@ -1392,8 +1485,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
m_cachedTheme->ambientLightStrength() * 2.0f);
m_backgroundShader->setUniformValue(m_backgroundShader->lightColor(), lightColor);
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) {
// Set shadow shader bindings
QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
m_backgroundShader->setUniformValue(m_backgroundShader->shadowQ(),
@@ -1406,11 +1498,7 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
m_drawer->drawObject(m_backgroundShader, m_backgroundObj, 0, m_noShadowTexture);
else
m_drawer->drawObject(m_backgroundShader, m_backgroundObj, 0, m_depthTexture);
- } else
-#else
- Q_UNUSED(noShadows);
-#endif
- {
+ } else {
// Set shadowless shader bindings
m_backgroundShader->setUniformValue(m_backgroundShader->lightS(),
m_cachedTheme->lightStrength());
@@ -1423,14 +1511,14 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
// Draw grid lines
QVector3D gridLineScaleX(m_scaleXWithBackground, gridLineWidth, gridLineWidth);
QVector3D gridLineScaleZ(gridLineWidth, gridLineWidth, m_scaleZWithBackground);
- QVector3D gridLineScaleY(gridLineWidth, backgroundMargin, gridLineWidth);
+ QVector3D gridLineScaleY(gridLineWidth, m_scaleYWithBackground, gridLineWidth);
- if (m_cachedTheme->isGridEnabled() && m_heightNormalizer) {
-#if !(defined QT_OPENGL_ES_2)
- ShaderHelper *lineShader = m_backgroundShader;
-#else
- ShaderHelper *lineShader = m_selectionShader; // Plain color shader for GL_LINES
-#endif
+ if (m_cachedTheme->isGridEnabled()) {
+ ShaderHelper *lineShader;
+ if (m_isOpenGLES)
+ lineShader = m_surfaceGridShader; // Plain color shader for GL_LINES
+ else
+ lineShader = m_backgroundShader;
// Bind line shader
lineShader->bind();
@@ -1442,15 +1530,12 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
lineShader->setUniformValue(lineShader->color(), lineColor);
lineShader->setUniformValue(lineShader->ambientS(), m_cachedTheme->ambientLightStrength());
lineShader->setUniformValue(lineShader->lightColor(), lightColor);
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !m_isOpenGLES) {
// Set shadowed shader bindings
lineShader->setUniformValue(lineShader->shadowQ(), m_shadowQualityToShader);
lineShader->setUniformValue(lineShader->lightS(),
m_cachedTheme->lightStrength() / 20.0f);
- } else
-#endif
- {
+ } else {
// Set shadowless shader bindings
lineShader->setUniformValue(lineShader->lightS(),
m_cachedTheme->lightStrength() / 2.5f);
@@ -1460,205 +1545,211 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
QQuaternion lineXRotation;
if (m_xFlipped)
- lineYRotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, -90.0f);
+ lineYRotation = m_yRightAngleRotationNeg;
else
- lineYRotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, 90.0f);
+ lineYRotation = m_yRightAngleRotation;
- if (m_yFlipped)
- lineXRotation = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, 90.0f);
+ if (m_yFlippedForGrid)
+ lineXRotation = m_xRightAngleRotation;
else
- lineXRotation = QQuaternion::fromAxisAndAngle(1.0f, 0.0f, 0.0f, -90.0f);
+ lineXRotation = m_xRightAngleRotationNeg;
- GLfloat yFloorLinePosition = -backgroundMargin + gridLineOffset;
- if (m_yFlipped)
+ float yFloorLinePosition = -m_scaleYWithBackground + gridLineOffset;
+ if (m_yFlipped != m_flipHorizontalGrid)
yFloorLinePosition = -yFloorLinePosition;
// Rows (= Z)
if (m_axisCacheZ.segmentCount() > 0) {
- // Floor lines
int gridLineCount = m_axisCacheZ.gridLineCount();
-
- for (int line = 0; line < gridLineCount; line++) {
- QMatrix4x4 modelMatrix;
- QMatrix4x4 MVPMatrix;
- QMatrix4x4 itModelMatrix;
-
- modelMatrix.translate(0.0f, yFloorLinePosition,
- m_axisCacheZ.gridLinePosition(line));
-
- modelMatrix.scale(gridLineScaleX);
- itModelMatrix.scale(gridLineScaleX);
-
- modelMatrix.rotate(lineXRotation);
- itModelMatrix.rotate(lineXRotation);
-
- MVPMatrix = projectionViewMatrix * modelMatrix;
-
- // Set the rest of the shader bindings
- lineShader->setUniformValue(lineShader->model(), modelMatrix);
- lineShader->setUniformValue(lineShader->nModel(),
- itModelMatrix.inverted().transposed());
- lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
-
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
- // Set shadow shader bindings
- QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
- lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
- } else {
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj);
+ // Floor lines
+ if (m_polarGraph) {
+ drawRadialGrid(lineShader, yFloorLinePosition, projectionViewMatrix,
+ depthProjectionViewMatrix);
+ } else {
+ for (int line = 0; line < gridLineCount; line++) {
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 MVPMatrix;
+ QMatrix4x4 itModelMatrix;
+
+ modelMatrix.translate(0.0f, yFloorLinePosition,
+ m_axisCacheZ.gridLinePosition(line));
+
+ modelMatrix.scale(gridLineScaleX);
+ itModelMatrix.scale(gridLineScaleX);
+
+ modelMatrix.rotate(lineXRotation);
+ itModelMatrix.rotate(lineXRotation);
+
+ MVPMatrix = projectionViewMatrix * modelMatrix;
+
+ // Set the rest of the shader bindings
+ lineShader->setUniformValue(lineShader->model(), modelMatrix);
+ lineShader->setUniformValue(lineShader->nModel(),
+ itModelMatrix.inverted().transposed());
+ lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
+
+ if (!m_isOpenGLES) {
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ // Set shadow shader bindings
+ QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
+ lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ } else {
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj);
+ }
+ } else {
+ m_drawer->drawLine(lineShader);
+ }
}
-#else
- m_drawer->drawLine(lineShader);
-#endif
- }
+ // Side wall lines
+ GLfloat lineXTrans = m_scaleXWithBackground - gridLineOffset;
- // Side wall lines
- GLfloat lineXTrans = m_scaleXWithBackground - gridLineOffset;
+ if (!m_xFlipped)
+ lineXTrans = -lineXTrans;
- if (!m_xFlipped)
- lineXTrans = -lineXTrans;
+ for (int line = 0; line < gridLineCount; line++) {
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 MVPMatrix;
+ QMatrix4x4 itModelMatrix;
- for (int line = 0; line < gridLineCount; line++) {
- QMatrix4x4 modelMatrix;
- QMatrix4x4 MVPMatrix;
- QMatrix4x4 itModelMatrix;
-
- modelMatrix.translate(lineXTrans, 0.0f, m_axisCacheZ.gridLinePosition(line));
+ modelMatrix.translate(lineXTrans, 0.0f, m_axisCacheZ.gridLinePosition(line));
- modelMatrix.scale(gridLineScaleY);
- itModelMatrix.scale(gridLineScaleY);
+ modelMatrix.scale(gridLineScaleY);
+ itModelMatrix.scale(gridLineScaleY);
-#if !defined(QT_OPENGL_ES_2)
- modelMatrix.rotate(lineYRotation);
- itModelMatrix.rotate(lineYRotation);
-#else
- modelMatrix.rotate(90.0f, 0.0f, 0.0f, 1.0f);
- itModelMatrix.rotate(90.0f, 0.0f, 0.0f, 1.0f);
-#endif
-
- MVPMatrix = projectionViewMatrix * modelMatrix;
-
- // Set the rest of the shader bindings
- lineShader->setUniformValue(lineShader->model(), modelMatrix);
- lineShader->setUniformValue(lineShader->nModel(),
- itModelMatrix.inverted().transposed());
- lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
+ if (m_isOpenGLES) {
+ modelMatrix.rotate(m_zRightAngleRotation);
+ itModelMatrix.rotate(m_zRightAngleRotation);
+ } else {
+ modelMatrix.rotate(lineYRotation);
+ itModelMatrix.rotate(lineYRotation);
+ }
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
- // Set shadow shader bindings
- QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
- lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
- } else {
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj);
+ MVPMatrix = projectionViewMatrix * modelMatrix;
+
+ // Set the rest of the shader bindings
+ lineShader->setUniformValue(lineShader->model(), modelMatrix);
+ lineShader->setUniformValue(lineShader->nModel(),
+ itModelMatrix.inverted().transposed());
+ lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
+
+ if (!m_isOpenGLES) {
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ // Set shadow shader bindings
+ QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
+ lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ } else {
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj);
+ }
+ } else {
+ m_drawer->drawLine(lineShader);
+ }
}
-#else
- m_drawer->drawLine(lineShader);
-#endif
}
}
// Columns (= X)
if (m_axisCacheX.segmentCount() > 0) {
-#if defined(QT_OPENGL_ES_2)
- lineXRotation = QQuaternion::fromAxisAndAngle(0.0f, 1.0f, 0.0f, 90.0f);
-#endif
+ if (m_isOpenGLES)
+ lineXRotation = m_yRightAngleRotation;
+
// Floor lines
int gridLineCount = m_axisCacheX.gridLineCount();
- for (int line = 0; line < gridLineCount; line++) {
- QMatrix4x4 modelMatrix;
- QMatrix4x4 MVPMatrix;
- QMatrix4x4 itModelMatrix;
-
- modelMatrix.translate(m_axisCacheX.gridLinePosition(line), yFloorLinePosition,
- 0.0f);
-
- modelMatrix.scale(gridLineScaleZ);
- itModelMatrix.scale(gridLineScaleZ);
-
- modelMatrix.rotate(lineXRotation);
- itModelMatrix.rotate(lineXRotation);
-
- MVPMatrix = projectionViewMatrix * modelMatrix;
-
- // Set the rest of the shader bindings
- lineShader->setUniformValue(lineShader->model(), modelMatrix);
- lineShader->setUniformValue(lineShader->nModel(),
- itModelMatrix.inverted().transposed());
- lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
-
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
- // Set shadow shader bindings
- QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
- lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
- } else {
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj);
+ if (m_polarGraph) {
+ drawAngularGrid(lineShader, yFloorLinePosition, projectionViewMatrix,
+ depthProjectionViewMatrix);
+ } else {
+ for (int line = 0; line < gridLineCount; line++) {
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 MVPMatrix;
+ QMatrix4x4 itModelMatrix;
+
+ modelMatrix.translate(m_axisCacheX.gridLinePosition(line), yFloorLinePosition,
+ 0.0f);
+
+ modelMatrix.scale(gridLineScaleZ);
+ itModelMatrix.scale(gridLineScaleZ);
+
+ modelMatrix.rotate(lineXRotation);
+ itModelMatrix.rotate(lineXRotation);
+
+ MVPMatrix = projectionViewMatrix * modelMatrix;
+
+ // Set the rest of the shader bindings
+ lineShader->setUniformValue(lineShader->model(), modelMatrix);
+ lineShader->setUniformValue(lineShader->nModel(),
+ itModelMatrix.inverted().transposed());
+ lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
+
+ if (!m_isOpenGLES) {
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ // Set shadow shader bindings
+ QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
+ lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ } else {
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj);
+ }
+ } else {
+ m_drawer->drawLine(lineShader);
+ }
}
-#else
- m_drawer->drawLine(lineShader);
-#endif
- }
- // Back wall lines
- GLfloat lineZTrans = m_scaleZWithBackground - gridLineOffset;
+ // Back wall lines
+ GLfloat lineZTrans = m_scaleZWithBackground - gridLineOffset;
- if (!m_zFlipped)
- lineZTrans = -lineZTrans;
-
- for (int line = 0; line < gridLineCount; line++) {
- QMatrix4x4 modelMatrix;
- QMatrix4x4 MVPMatrix;
- QMatrix4x4 itModelMatrix;
-
- modelMatrix.translate(m_axisCacheX.gridLinePosition(line), 0.0f, lineZTrans);
+ if (!m_zFlipped)
+ lineZTrans = -lineZTrans;
- modelMatrix.scale(gridLineScaleY);
- itModelMatrix.scale(gridLineScaleY);
+ for (int line = 0; line < gridLineCount; line++) {
+ QMatrix4x4 modelMatrix;
+ QMatrix4x4 MVPMatrix;
+ QMatrix4x4 itModelMatrix;
-#if !defined(QT_OPENGL_ES_2)
- if (m_zFlipped) {
- modelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f);
- itModelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f);
- }
-#else
- modelMatrix.rotate(90.0f, 0.0f, 0.0f, 1.0f);
- itModelMatrix.rotate(90.0f, 0.0f, 0.0f, 1.0f);
-#endif
+ modelMatrix.translate(m_axisCacheX.gridLinePosition(line), 0.0f, lineZTrans);
- MVPMatrix = projectionViewMatrix * modelMatrix;
+ modelMatrix.scale(gridLineScaleY);
+ itModelMatrix.scale(gridLineScaleY);
- // Set the rest of the shader bindings
- lineShader->setUniformValue(lineShader->model(), modelMatrix);
- lineShader->setUniformValue(lineShader->nModel(),
- itModelMatrix.inverted().transposed());
- lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
+ if (m_isOpenGLES) {
+ modelMatrix.rotate(m_zRightAngleRotation);
+ itModelMatrix.rotate(m_zRightAngleRotation);
+ } else if (m_zFlipped) {
+ modelMatrix.rotate(m_xFlipRotation);
+ itModelMatrix.rotate(m_xFlipRotation);
+ }
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
- // Set shadow shader bindings
- QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
- lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
- } else {
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj);
+ MVPMatrix = projectionViewMatrix * modelMatrix;
+
+ // Set the rest of the shader bindings
+ lineShader->setUniformValue(lineShader->model(), modelMatrix);
+ lineShader->setUniformValue(lineShader->nModel(),
+ itModelMatrix.inverted().transposed());
+ lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
+
+ if (!m_isOpenGLES) {
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ // Set shadow shader bindings
+ QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
+ lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ } else {
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj);
+ }
+ } else {
+ m_drawer->drawLine(lineShader);
+ }
}
-#else
- m_drawer->drawLine(lineShader);
-#endif
}
}
@@ -1683,8 +1774,8 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
itModelMatrix.scale(gridLineScaleX);
if (m_zFlipped) {
- modelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f);
- itModelMatrix.rotate(180.0f, 1.0f, 0.0f, 0.0f);
+ modelMatrix.rotate(m_xFlipRotation);
+ itModelMatrix.rotate(m_xFlipRotation);
}
MVPMatrix = projectionViewMatrix * modelMatrix;
@@ -1695,20 +1786,20 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
itModelMatrix.inverted().transposed());
lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
- // Set shadow shader bindings
- QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
- lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ if (!m_isOpenGLES) {
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ // Set shadow shader bindings
+ QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
+ lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ } else {
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj);
+ }
} else {
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj);
+ m_drawer->drawLine(lineShader);
}
-#else
- m_drawer->drawLine(lineShader);
-#endif
}
// Side wall
@@ -1738,20 +1829,20 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
itModelMatrix.inverted().transposed());
lineShader->setUniformValue(lineShader->MVP(), MVPMatrix);
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
- // Set shadow shader bindings
- QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
- lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ if (!m_isOpenGLES) {
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ // Set shadow shader bindings
+ QMatrix4x4 depthMVPMatrix = depthProjectionViewMatrix * modelMatrix;
+ lineShader->setUniformValue(lineShader->depth(), depthMVPMatrix);
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj, 0, m_depthTexture);
+ } else {
+ // Draw the object
+ m_drawer->drawObject(lineShader, m_gridLineObj);
+ }
} else {
- // Draw the object
- m_drawer->drawObject(lineShader, m_gridLineObj);
+ m_drawer->drawLine(lineShader);
}
-#else
- m_drawer->drawLine(lineShader);
-#endif
}
}
}
@@ -1811,7 +1902,7 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
} else {
shader = m_labelShader;
shader->bind();
- glEnable(GL_TEXTURE_2D);
+
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
@@ -1832,8 +1923,14 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
QVector3D positionZComp(0.0f, 0.0f, 0.0f);
if (m_axisCacheZ.segmentCount() > 0) {
int labelCount = m_axisCacheZ.labelCount();
- GLfloat labelXTrans = m_scaleXWithBackground + labelMargin;
- GLfloat labelYTrans = -backgroundMargin;
+ float labelXTrans = m_scaleXWithBackground + labelMargin;
+ float labelYTrans = m_flipHorizontalGrid ? m_scaleYWithBackground : -m_scaleYWithBackground;
+ if (m_polarGraph) {
+ labelXTrans *= m_radialLabelOffset;
+ // YTrans up only if over background
+ if (m_radialLabelOffset < 1.0f)
+ labelYTrans += gridLineOffset + gridLineWidth;
+ }
Qt::AlignmentFlag alignment = (m_xFlipped == m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight;
QVector3D labelRotation;
if (m_xFlipped)
@@ -1843,7 +1940,7 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
if (labelAutoAngle == 0.0f) {
if (m_zFlipped)
labelRotation.setY(180.0f);
- if (m_yFlipped) {
+ if (m_yFlippedForGrid) {
if (m_zFlipped)
labelRotation.setY(180.0f);
else
@@ -1855,7 +1952,7 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
} else {
if (m_zFlipped)
labelRotation.setY(180.0f);
- if (m_yFlipped) {
+ if (m_yFlippedForGrid) {
if (m_zFlipped) {
if (m_xFlipped) {
labelRotation.setX(90.0f - (labelAutoAngle - fractionCamX)
@@ -1920,10 +2017,34 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
float offsetValue = 0.0f;
for (int label = startIndex; label != endIndex; label = label + indexStep) {
glPolygonOffset(offsetValue++ / -10.0f, 1.0f);
+ const LabelItem &axisLabelItem = *m_axisCacheZ.labelItems().at(label);
// Draw the label here
- labelTrans.setZ(m_axisCacheZ.labelPosition(label));
+ if (m_polarGraph) {
+ float direction = m_zFlipped ? -1.0f : 1.0f;
+ labelTrans.setZ((m_axisCacheZ.formatter()->labelPositions().at(label)
+ * -m_polarRadius
+ + m_drawer->scaledFontSize() + gridLineWidth) * direction);
+ } else {
+ labelTrans.setZ(m_axisCacheZ.labelPosition(label));
+ }
+ if (label == 0 || label == (labelCount - 1)) {
+ // If the margin is small, adjust the position of the edge labels to avoid overlapping
+ // with labels of the other axes.
+ float scaleFactor = m_drawer->scaledFontSize() / axisLabelItem.size().height();
+ float labelOverlap = qAbs(labelTrans.z())
+ + (scaleFactor * axisLabelItem.size().height() / 2.0f)
+ - m_scaleZWithBackground + labelMargin;
+ // No need to adjust quite as much on the front edges
+ if (label != startIndex)
+ labelOverlap /= 2.0f;
+ if (labelOverlap > 0.0f) {
+ if (label == 0)
+ labelTrans.setZ(labelTrans.z() - labelOverlap);
+ else
+ labelTrans.setZ(labelTrans.z() + labelOverlap);
+ }
+ }
m_dummyRenderItem.setTranslation(labelTrans);
- const LabelItem &axisLabelItem = *m_axisCacheZ.labelItems().at(label);
if (drawSelection) {
QVector4D labelColor = QVector4D(label / 255.0f, 0.0f, 0.0f,
@@ -1938,7 +2059,14 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
labelsMaxWidth = qMax(labelsMaxWidth, float(axisLabelItem.size().width()));
}
if (!drawSelection && m_axisCacheZ.isTitleVisible()) {
- labelTrans.setZ(0.0f);
+ if (m_polarGraph) {
+ float titleZ = -m_polarRadius / 2.0f;
+ if (m_zFlipped)
+ titleZ = -titleZ;
+ labelTrans.setZ(titleZ);
+ } else {
+ labelTrans.setZ(0.0f);
+ }
drawAxisTitleZ(labelRotation, labelTrans, totalRotation, m_dummyRenderItem,
activeCamera, labelsMaxWidth, viewMatrix, projectionMatrix, shader);
}
@@ -1952,8 +2080,13 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
fractionCamX = activeCamera->xRotation() * labelAngleFraction;
int labelCount = m_axisCacheX.labelCount();
- GLfloat labelZTrans = m_scaleZWithBackground + labelMargin;
- GLfloat labelYTrans = -backgroundMargin;
+ float labelZTrans = 0.0f;
+ float labelYTrans = m_flipHorizontalGrid ? m_scaleYWithBackground : -m_scaleYWithBackground;
+ if (m_polarGraph)
+ labelYTrans += gridLineOffset + gridLineWidth;
+ else
+ labelZTrans = m_scaleZWithBackground + labelMargin;
+
Qt::AlignmentFlag alignment = (m_xFlipped != m_zFlipped) ? Qt::AlignLeft : Qt::AlignRight;
QVector3D labelRotation;
if (m_zFlipped)
@@ -1964,7 +2097,7 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
labelRotation = QVector3D(-90.0f, 90.0f, 0.0f);
if (m_xFlipped)
labelRotation.setY(-90.0f);
- if (m_yFlipped) {
+ if (m_yFlippedForGrid) {
if (m_xFlipped)
labelRotation.setY(-90.0f);
else
@@ -1976,7 +2109,7 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
labelRotation.setY(-90.0f);
else
labelRotation.setY(90.0f);
- if (m_yFlipped) {
+ if (m_yFlippedForGrid) {
if (m_zFlipped) {
if (m_xFlipped) {
labelRotation.setX(90.0f - (2.0f * labelAutoAngle - fractionCamX)
@@ -2022,7 +2155,16 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
}
}
}
+
QQuaternion totalRotation = Utils::calculateRotation(labelRotation);
+ if (m_polarGraph) {
+ if ((!m_yFlippedForGrid && (m_zFlipped != m_xFlipped))
+ || (m_yFlippedForGrid && (m_zFlipped == m_xFlipped))) {
+ totalRotation *= m_zRightAngleRotation;
+ } else {
+ totalRotation *= m_zRightAngleRotationNeg;
+ }
+ }
QVector3D labelTrans = QVector3D(0.0f,
labelYTrans,
@@ -2038,12 +2180,64 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
indexStep = 1;
}
float offsetValue = 0.0f;
+ bool showLastLabel = false;
+ QVector<float> &labelPositions = m_axisCacheX.formatter()->labelPositions();
+ int lastLabelPosIndex = labelPositions.size() - 1;
+ if (labelPositions.size()
+ && (labelPositions.at(lastLabelPosIndex) != 1.0f || labelPositions.at(0) != 0.0f)) {
+ // Avoid overlapping first and last label if they would get on same position
+ showLastLabel = true;
+ }
+
for (int label = startIndex; label != endIndex; label = label + indexStep) {
glPolygonOffset(offsetValue++ / -10.0f, 1.0f);
// Draw the label here
- labelTrans.setX(m_axisCacheX.labelPosition(label));
- m_dummyRenderItem.setTranslation(labelTrans);
+ if (m_polarGraph) {
+ // Calculate angular position
+ if (label == lastLabelPosIndex && !showLastLabel)
+ continue;
+ float labelPosition = labelPositions.at(label);
+ qreal angle = labelPosition * M_PI * 2.0;
+ labelTrans.setX((m_polarRadius + labelMargin) * float(qSin(angle)));
+ labelTrans.setZ(-(m_polarRadius + labelMargin) * float(qCos(angle)));
+ // Alignment depends on label angular position, as well as flips
+ Qt::AlignmentFlag vAlignment = Qt::AlignCenter;
+ Qt::AlignmentFlag hAlignment = Qt::AlignCenter;
+ const float centerMargin = 0.005f;
+ if (labelPosition < 0.25f - centerMargin || labelPosition > 0.75f + centerMargin)
+ vAlignment = m_zFlipped ? Qt::AlignTop : Qt::AlignBottom;
+ else if (labelPosition > 0.25f + centerMargin && labelPosition < 0.75f - centerMargin)
+ vAlignment = m_zFlipped ? Qt::AlignBottom : Qt::AlignTop;
+
+ if (labelPosition < 0.50f - centerMargin && labelPosition > centerMargin)
+ hAlignment = m_zFlipped ? Qt::AlignRight : Qt::AlignLeft;
+ else if (labelPosition < 1.0f - centerMargin && labelPosition > 0.5f + centerMargin)
+ hAlignment = m_zFlipped ? Qt::AlignLeft : Qt::AlignRight;
+ if (m_yFlippedForGrid && vAlignment != Qt::AlignCenter)
+ vAlignment = (vAlignment == Qt::AlignTop) ? Qt::AlignBottom : Qt::AlignTop;
+ alignment = Qt::AlignmentFlag(vAlignment | hAlignment);
+ } else {
+ labelTrans.setX(m_axisCacheX.labelPosition(label));
+ }
const LabelItem &axisLabelItem = *m_axisCacheX.labelItems().at(label);
+ if (label == 0 || label == (labelCount - 1)) {
+ // If the margin is small, adjust the position of the edge labels to avoid overlapping
+ // with labels of the other axes.
+ float scaleFactor = m_drawer->scaledFontSize() / axisLabelItem.size().height();
+ float labelOverlap = qAbs(labelTrans.x())
+ + (scaleFactor * axisLabelItem.size().height() / 2.0f)
+ - m_scaleXWithBackground + labelMargin;
+ // No need to adjust quite as much on the front edges
+ if (label != startIndex)
+ labelOverlap /= 2.0f;
+ if (labelOverlap > 0.0f) {
+ if (label == 0)
+ labelTrans.setX(labelTrans.x() + labelOverlap);
+ else
+ labelTrans.setX(labelTrans.x() - labelOverlap);
+ }
+ }
+ m_dummyRenderItem.setTranslation(labelTrans);
if (drawSelection) {
QVector4D labelColor = QVector4D(0.0f, label / 255.0f, 0.0f,
@@ -2059,8 +2253,20 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
}
if (!drawSelection && m_axisCacheX.isTitleVisible()) {
labelTrans.setX(0.0f);
+ bool radial = false;
+ if (m_polarGraph) {
+ if (m_xFlipped == m_zFlipped)
+ totalRotation *= m_zRightAngleRotation;
+ else
+ totalRotation *= m_zRightAngleRotationNeg;
+ if (m_yFlippedForGrid)
+ totalRotation *= QQuaternion::fromAxisAndAngle(0.0f, 0.0f, 1.0f, -180.0f);
+ labelTrans.setZ(-m_polarRadius);
+ radial = true;
+ }
drawAxisTitleX(labelRotation, labelTrans, totalRotation, m_dummyRenderItem,
- activeCamera, labelsMaxWidth, viewMatrix, projectionMatrix, shader);
+ activeCamera, labelsMaxWidth, viewMatrix, projectionMatrix, shader,
+ radial);
}
}
// Y Labels
@@ -2072,12 +2278,12 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
fractionCamX = activeCamera->xRotation() * labelAngleFraction;
int labelCount = m_axisCacheY.labelCount();
- GLfloat labelXTrans = m_scaleXWithBackground;
- GLfloat labelZTrans = m_scaleZWithBackground;
+ float labelXTrans = m_scaleXWithBackground;
+ float labelZTrans = m_scaleZWithBackground;
// Back & side wall
- GLfloat labelMarginXTrans = labelMargin;
- GLfloat labelMarginZTrans = labelMargin;
+ float labelMarginXTrans = labelMargin;
+ float labelMarginZTrans = labelMargin;
QVector3D backLabelRotation(0.0f, -90.0f, 0.0f);
QVector3D sideLabelRotation(0.0f, 0.0f, 0.0f);
Qt::AlignmentFlag backAlignment =
@@ -2134,7 +2340,7 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
float offsetValue = 0.0f;
for (int label = startIndex; label != endIndex; label = label + indexStep) {
const LabelItem &axisLabelItem = *m_axisCacheY.labelItems().at(label);
- const GLfloat labelYTrans = m_axisCacheY.labelPosition(label);
+ float labelYTrans = m_axisCacheY.labelPosition(label);
glPolygonOffset(offsetValue++ / -10.0f, 1.0f);
@@ -2144,6 +2350,21 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
shader->setUniformValue(shader->color(), labelColor);
}
+ if (label == startIndex) {
+ // If the margin is small, adjust the position of the edge label to avoid
+ // overlapping with labels of the other axes.
+ float scaleFactor = m_drawer->scaledFontSize() / axisLabelItem.size().height();
+ float labelOverlap = qAbs(labelYTrans)
+ + (scaleFactor * axisLabelItem.size().height() / 2.0f)
+ - m_scaleYWithBackground + labelMargin;
+ if (labelOverlap > 0.0f) {
+ if (label == 0)
+ labelYTrans += labelOverlap;
+ else
+ labelYTrans -= labelOverlap;
+ }
+ }
+
// Back wall
labelTransBack.setY(labelYTrans);
m_dummyRenderItem.setTranslation(labelTransBack);
@@ -2174,10 +2395,8 @@ void Surface3DRenderer::drawLabels(bool drawSelection, const Q3DCamera *activeCa
}
glDisable(GL_POLYGON_OFFSET_FILL);
- if (!drawSelection) {
- glDisable(GL_TEXTURE_2D);
+ if (!drawSelection)
glDisable(GL_BLEND);
- }
}
void Surface3DRenderer::updateSelectionMode(QAbstract3DGraph::SelectionFlags mode)
@@ -2205,11 +2424,12 @@ void Surface3DRenderer::updateSelectionTextures()
void Surface3DRenderer::createSelectionTexture(SurfaceSeriesRenderCache *cache,
uint &lastSelectionId)
{
- // Create the selection ID image. Each grid corner gets 2x2 pixel area of
- // ID color so that each vertex (data point) has 4x4 pixel area of ID color
+ // Create the selection ID image. Each grid corner gets 1 pixel area of
+ // ID color so that each vertex (data point) has 2x2 pixel area of ID color,
+ // except the vertices on the edges.
const QRect &sampleSpace = cache->sampleSpace();
- int idImageWidth = (sampleSpace.width() - 1) * 4;
- int idImageHeight = (sampleSpace.height() - 1) * 4;
+ int idImageWidth = (sampleSpace.width() - 1) * 2;
+ int idImageHeight = (sampleSpace.height() - 1) * 2;
if (idImageHeight <= 0 || idImageWidth <= 0) {
cache->setSelectionIdRange(-1, -1);
@@ -2221,21 +2441,21 @@ void Surface3DRenderer::createSelectionTexture(SurfaceSeriesRenderCache *cache,
uint idStart = lastSelectionId;
uchar *bits = new uchar[idImageWidth * idImageHeight * 4 * sizeof(uchar)];
- for (int i = 0; i < idImageHeight; i += 4) {
- for (int j = 0; j < idImageWidth; j += 4) {
+ for (int i = 0; i < idImageHeight; i += 2) {
+ for (int j = 0; j < idImageWidth; j += 2) {
int p = (i * idImageWidth + j) * 4;
uchar r, g, b, a;
idToRGBA(lastSelectionId, &r, &g, &b, &a);
- fillIdCorner(&bits[p], r, g, b, a, stride);
+ fillIdCorner(&bits[p], r, g, b, a);
idToRGBA(lastSelectionId + 1, &r, &g, &b, &a);
- fillIdCorner(&bits[p + 8], r, g, b, a, stride);
+ fillIdCorner(&bits[p + 4], r, g, b, a);
idToRGBA(lastSelectionId + sampleSpace.width(), &r, &g, &b, &a);
- fillIdCorner(&bits[p + 2 * stride], r, g, b, a, stride);
+ fillIdCorner(&bits[p + stride], r, g, b, a);
idToRGBA(lastSelectionId + sampleSpace.width() + 1, &r, &g, &b, &a);
- fillIdCorner(&bits[p + 2 * stride + 8], r, g, b, a, stride);
+ fillIdCorner(&bits[p + stride + 4], r, g, b, a);
lastSelectionId++;
}
@@ -2263,24 +2483,12 @@ void Surface3DRenderer::initSelectionBuffer()
m_selectionDepthBuffer);
}
-void Surface3DRenderer::fillIdCorner(uchar *p, uchar r, uchar g, uchar b, uchar a, int stride)
+void Surface3DRenderer::fillIdCorner(uchar *p, uchar r, uchar g, uchar b, uchar a)
{
p[0] = r;
p[1] = g;
p[2] = b;
p[3] = a;
- p[4] = r;
- p[5] = g;
- p[6] = b;
- p[7] = a;
- p[stride + 0] = r;
- p[stride + 1] = g;
- p[stride + 2] = b;
- p[stride + 3] = a;
- p[stride + 4] = r;
- p[stride + 5] = g;
- p[stride + 6] = b;
- p[stride + 7] = a;
}
void Surface3DRenderer::idToRGBA(uint id, uchar *r, uchar *g, uchar *b, uchar *a)
@@ -2293,21 +2501,66 @@ void Surface3DRenderer::idToRGBA(uint id, uchar *r, uchar *g, uchar *b, uchar *a
void Surface3DRenderer::calculateSceneScalingFactors()
{
+ // Margin for background (the default 0.10 makes it 10% larger to avoid
+ // selection ball being drawn inside background)
+ if (m_requestedMargin < 0.0f) {
+ m_hBackgroundMargin = 0.1f;
+ m_vBackgroundMargin = 0.1f;
+ } else {
+ m_hBackgroundMargin = m_requestedMargin;
+ m_vBackgroundMargin = m_requestedMargin;
+ }
+ if (m_polarGraph) {
+ float polarMargin = calculatePolarBackgroundMargin();
+ m_hBackgroundMargin = qMax(m_hBackgroundMargin, polarMargin);
+ }
+
// Calculate scene scaling and translation factors
m_heightNormalizer = GLfloat(m_axisCacheY.max() - m_axisCacheY.min());
- m_areaSize.setHeight(m_axisCacheZ.max() - m_axisCacheZ.min());
- m_areaSize.setWidth(m_axisCacheX.max() - m_axisCacheX.min());
- m_scaleFactor = qMax(m_areaSize.width(), m_areaSize.height());
- m_scaleX = m_graphAspectRatio * m_areaSize.width() / m_scaleFactor;
- m_scaleZ = m_graphAspectRatio * m_areaSize.height() / m_scaleFactor;
- m_scaleXWithBackground = m_scaleX + backgroundMargin - 1.0f;
- m_scaleZWithBackground = m_scaleZ + backgroundMargin - 1.0f;
-
- float factorScaler = 2.0f * m_graphAspectRatio / m_scaleFactor;
- m_axisCacheX.setScale(factorScaler * m_areaSize.width());
- m_axisCacheZ.setScale(-factorScaler * m_areaSize.height());
- m_axisCacheX.setTranslate(-m_axisCacheX.scale() / 2.0f);
- m_axisCacheZ.setTranslate(-m_axisCacheZ.scale() / 2.0f);
+
+ float horizontalAspectRatio;
+ if (m_polarGraph)
+ horizontalAspectRatio = 1.0f;
+ else
+ horizontalAspectRatio = m_graphHorizontalAspectRatio;
+
+ QSizeF areaSize;
+ if (horizontalAspectRatio == 0.0f) {
+ areaSize.setHeight(m_axisCacheZ.max() - m_axisCacheZ.min());
+ areaSize.setWidth(m_axisCacheX.max() - m_axisCacheX.min());
+ } else {
+ areaSize.setHeight(1.0f);
+ areaSize.setWidth(horizontalAspectRatio);
+ }
+
+ float horizontalMaxDimension;
+ if (m_graphAspectRatio > 2.0f) {
+ horizontalMaxDimension = 2.0f;
+ m_scaleY = 2.0f / m_graphAspectRatio;
+ } else {
+ horizontalMaxDimension = m_graphAspectRatio;
+ m_scaleY = 1.0f;
+ }
+ if (m_polarGraph)
+ m_polarRadius = horizontalMaxDimension;
+
+ float scaleFactor = qMax(areaSize.width(), areaSize.height());
+ m_scaleX = horizontalMaxDimension * areaSize.width() / scaleFactor;
+ m_scaleZ = horizontalMaxDimension * areaSize.height() / scaleFactor;
+
+ m_scaleXWithBackground = m_scaleX + m_hBackgroundMargin;
+ m_scaleYWithBackground = m_scaleY + m_vBackgroundMargin;
+ m_scaleZWithBackground = m_scaleZ + m_hBackgroundMargin;
+
+ m_axisCacheX.setScale(m_scaleX * 2.0f);
+ m_axisCacheY.setScale(m_scaleY * 2.0f);
+ m_axisCacheZ.setScale(-m_scaleZ * 2.0f);
+ m_axisCacheX.setTranslate(-m_scaleX);
+ m_axisCacheY.setTranslate(-m_scaleY);
+ m_axisCacheZ.setTranslate(m_scaleZ);
+
+ updateCameraViewport();
+ updateCustomItemPositions();
}
void Surface3DRenderer::checkFlatSupport(SurfaceSeriesRenderCache *cache)
@@ -2326,10 +2579,20 @@ void Surface3DRenderer::updateObjects(SurfaceSeriesRenderCache *cache, bool dime
QSurfaceDataArray &dataArray = cache->dataArray();
const QRect &sampleSpace = cache->sampleSpace();
- if (cache->isFlatShadingEnabled())
- cache->surfaceObject()->setUpData(dataArray, sampleSpace, dimensionChanged);
- else
- cache->surfaceObject()->setUpSmoothData(dataArray, sampleSpace, dimensionChanged);
+ const QSurface3DSeries *currentSeries = cache->series();
+ QSurfaceDataProxy *dataProxy = currentSeries->dataProxy();
+ const QSurfaceDataArray &array = *dataProxy->array();
+
+ if (cache->isFlatShadingEnabled()) {
+ cache->surfaceObject()->setUpData(dataArray, sampleSpace, dimensionChanged, m_polarGraph);
+ if (cache->surfaceTexture())
+ cache->surfaceObject()->coarseUVs(array, dataArray);
+ } else {
+ cache->surfaceObject()->setUpSmoothData(dataArray, sampleSpace, dimensionChanged,
+ m_polarGraph);
+ if (cache->surfaceTexture())
+ cache->surfaceObject()->smoothUVs(array, dataArray);
+ }
}
void Surface3DRenderer::updateSelectedPoint(const QPoint &position, QSurface3DSeries *series)
@@ -2339,6 +2602,11 @@ void Surface3DRenderer::updateSelectedPoint(const QPoint &position, QSurface3DSe
m_selectionDirty = true;
}
+void Surface3DRenderer::updateFlipHorizontalGrid(bool flip)
+{
+ m_flipHorizontalGrid = flip;
+}
+
void Surface3DRenderer::resetClickedStatus()
{
m_clickedPosition = Surface3DController::invalidSelectionPosition();
@@ -2539,14 +2807,15 @@ void Surface3DRenderer::updateShadowQuality(QAbstract3DGraph::ShadowQuality qual
handleShadowQualityChange();
-#if !defined(QT_OPENGL_ES_2)
updateDepthBuffer();
-#endif
}
void Surface3DRenderer::updateTextures()
{
- // Do nothing, but required as it is pure virtual on parent
+ Abstract3DRenderer::updateTextures();
+
+ if (m_polarGraph)
+ calculateSceneScalingFactors();
}
void Surface3DRenderer::updateSlicingActive(bool isSlicing)
@@ -2556,12 +2825,13 @@ void Surface3DRenderer::updateSlicingActive(bool isSlicing)
m_cachedIsSlicingActivated = isSlicing;
- if (!m_cachedIsSlicingActivated)
- initSelectionBuffer(); // We need to re-init selection buffer in case there has been a resize
+ if (!m_cachedIsSlicingActivated) {
+ // We need to re-init selection buffer in case there has been a resize
+ initSelectionBuffer();
+ initCursorPositionBuffer();
+ }
-#if !defined(QT_OPENGL_ES_2)
updateDepthBuffer(); // Re-init depth buffer as well
-#endif
m_selectionDirty = true;
@@ -2577,55 +2847,68 @@ void Surface3DRenderer::initShaders(const QString &vertexShader, const QString &
Q_UNUSED(vertexShader);
Q_UNUSED(fragmentShader);
- // draw the shader for the surface according to smooth status, shadow and uniform color
- if (m_surfaceFlatShader)
- delete m_surfaceFlatShader;
- if (m_surfaceSmoothShader)
- delete m_surfaceSmoothShader;
- if (m_surfaceSliceFlatShader)
- delete m_surfaceSliceFlatShader;
- if (m_surfaceSliceSmoothShader)
- delete m_surfaceSliceSmoothShader;
-
-#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
- m_surfaceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexShadow"),
- QStringLiteral(":/shaders/fragmentSurfaceShadowNoTex"));
- } else {
- m_surfaceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"),
- QStringLiteral(":/shaders/fragmentSurface"));
- }
- m_surfaceSliceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"),
- QStringLiteral(":/shaders/fragmentSurface"));
- if (m_flatSupported) {
+ delete m_surfaceFlatShader;
+ delete m_surfaceSmoothShader;
+ delete m_surfaceTexturedSmoothShader;
+ delete m_surfaceTexturedFlatShader;
+ delete m_surfaceSliceFlatShader;
+ delete m_surfaceSliceSmoothShader;
+
+ if (!m_isOpenGLES) {
if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
- m_surfaceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceShadowFlat"),
- QStringLiteral(":/shaders/fragmentSurfaceShadowFlat"));
+ m_surfaceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexShadow"),
+ QStringLiteral(":/shaders/fragmentSurfaceShadowNoTex"));
+ m_surfaceTexturedSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexShadow"),
+ QStringLiteral(":/shaders/fragmentTexturedSurfaceShadow"));
+ } else {
+ m_surfaceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"),
+ QStringLiteral(":/shaders/fragmentSurface"));
+ m_surfaceTexturedSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexTexture"),
+ QStringLiteral(":/shaders/fragmentTexture"));
+ }
+ m_surfaceSliceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"),
+ QStringLiteral(":/shaders/fragmentSurface"));
+ if (m_flatSupported) {
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ m_surfaceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceShadowFlat"),
+ QStringLiteral(":/shaders/fragmentSurfaceShadowFlat"));
+ m_surfaceTexturedFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceShadowFlat"),
+ QStringLiteral(":/shaders/fragmentTexturedSurfaceShadowFlat"));
+ } else {
+ m_surfaceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceFlat"),
+ QStringLiteral(":/shaders/fragmentSurfaceFlat"));
+ m_surfaceTexturedFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceFlat"),
+ QStringLiteral(":/shaders/fragmentSurfaceTexturedFlat"));
+ }
+ m_surfaceSliceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceFlat"),
+ QStringLiteral(":/shaders/fragmentSurfaceFlat"));
} else {
- m_surfaceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceFlat"),
- QStringLiteral(":/shaders/fragmentSurfaceFlat"));
+ m_surfaceFlatShader = 0;
+ m_surfaceSliceFlatShader = 0;
+ m_surfaceTexturedFlatShader = 0;
}
- m_surfaceSliceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexSurfaceFlat"),
- QStringLiteral(":/shaders/fragmentSurfaceFlat"));
} else {
- m_surfaceFlatShader = 0;
- m_surfaceSliceFlatShader = 0;
+ m_surfaceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"),
+ QStringLiteral(":/shaders/fragmentSurfaceES2"));
+ m_surfaceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"),
+ QStringLiteral(":/shaders/fragmentSurfaceES2"));
+ m_surfaceTexturedSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexTexture"),
+ QStringLiteral(":/shaders/fragmentTextureES2"));
+ m_surfaceTexturedFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexTexture"),
+ QStringLiteral(":/shaders/fragmentTextureES2"));
+ m_surfaceSliceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"),
+ QStringLiteral(":/shaders/fragmentSurfaceES2"));
+ m_surfaceSliceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"),
+ QStringLiteral(":/shaders/fragmentSurfaceES2"));
}
-#else
- m_surfaceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"),
- QStringLiteral(":/shaders/fragmentSurfaceES2"));
- m_surfaceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"),
- QStringLiteral(":/shaders/fragmentSurfaceES2"));
- m_surfaceSliceSmoothShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"),
- QStringLiteral(":/shaders/fragmentSurfaceES2"));
- m_surfaceSliceFlatShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertex"),
- QStringLiteral(":/shaders/fragmentSurfaceES2"));
-#endif
+
m_surfaceSmoothShader->initialize();
m_surfaceSliceSmoothShader->initialize();
+ m_surfaceTexturedSmoothShader->initialize();
if (m_flatSupported) {
m_surfaceFlatShader->initialize();
m_surfaceSliceFlatShader->initialize();
+ m_surfaceTexturedFlatShader->initialize();
}
}
@@ -2660,45 +2943,33 @@ void Surface3DRenderer::initSurfaceShaders()
handleShadowQualityChange();
}
-void Surface3DRenderer::initLabelShaders(const QString &vertexShader, const QString &fragmentShader)
-{
- if (m_labelShader)
- delete m_labelShader;
- m_labelShader = new ShaderHelper(this, vertexShader, fragmentShader);
- m_labelShader->initialize();
-}
-
-#if !defined(QT_OPENGL_ES_2)
void Surface3DRenderer::initDepthShader()
{
- if (m_depthShader)
+ if (!m_isOpenGLES) {
delete m_depthShader;
- m_depthShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexDepth"),
- QStringLiteral(":/shaders/fragmentDepth"));
- m_depthShader->initialize();
+ m_depthShader = new ShaderHelper(this, QStringLiteral(":/shaders/vertexDepth"),
+ QStringLiteral(":/shaders/fragmentDepth"));
+ m_depthShader->initialize();
+ }
}
void Surface3DRenderer::updateDepthBuffer()
{
- m_textureHelper->deleteTexture(&m_depthTexture);
- m_textureHelper->deleteTexture(&m_depthModelTexture);
+ if (!m_isOpenGLES) {
+ m_textureHelper->deleteTexture(&m_depthTexture);
- if (m_primarySubViewport.size().isEmpty())
- return;
+ if (m_primarySubViewport.size().isEmpty())
+ return;
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
- m_depthTexture = m_textureHelper->createDepthTextureFrameBuffer(m_primarySubViewport.size(),
- m_depthFrameBuffer,
- m_shadowQualityMultiplier);
- m_textureHelper->fillDepthTexture(m_depthTexture, m_primarySubViewport.size(),
- m_shadowQualityMultiplier, 1.0f);
- m_depthModelTexture = m_textureHelper->createDepthTexture(m_primarySubViewport.size(),
- m_shadowQualityMultiplier);
- if (!m_depthTexture || !m_depthModelTexture)
- lowerShadowQuality();
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ m_depthTexture = m_textureHelper->createDepthTextureFrameBuffer(m_primarySubViewport.size(),
+ m_depthFrameBuffer,
+ m_shadowQualityMultiplier);
+ if (!m_depthTexture)
+ lowerShadowQuality();
+ }
}
}
-#endif
QVector3D Surface3DRenderer::convertPositionToTranslation(const QVector3D &position,
bool isAbsolute)
@@ -2707,15 +2978,44 @@ QVector3D Surface3DRenderer::convertPositionToTranslation(const QVector3D &posit
float yTrans = 0.0f;
float zTrans = 0.0f;
if (!isAbsolute) {
- xTrans = m_axisCacheX.positionAt(position.x());
+ if (m_polarGraph) {
+ calculatePolarXZ(position, xTrans, zTrans);
+ } else {
+ xTrans = m_axisCacheX.positionAt(position.x());
+ zTrans = m_axisCacheZ.positionAt(position.z());
+ }
yTrans = m_axisCacheY.positionAt(position.y());
- zTrans = m_axisCacheZ.positionAt(position.z());
} else {
xTrans = position.x() * m_scaleX;
- yTrans = position.y();
- zTrans = position.z() * m_scaleZ;
+ yTrans = position.y() * m_scaleY;
+ zTrans = position.z() * -m_scaleZ;
}
return QVector3D(xTrans, yTrans, zTrans);
}
+void Surface3DRenderer::updateAxisLabels(QAbstract3DAxis::AxisOrientation orientation,
+ const QStringList &labels)
+{
+ Abstract3DRenderer::updateAxisLabels(orientation, labels);
+
+ // Angular axis label dimensions affect the chart dimensions
+ if (m_polarGraph && orientation == QAbstract3DAxis::AxisOrientationX)
+ calculateSceneScalingFactors();
+}
+
+void Surface3DRenderer::updateAxisTitleVisibility(QAbstract3DAxis::AxisOrientation orientation, bool visible)
+{
+ Abstract3DRenderer::updateAxisTitleVisibility(orientation, visible);
+
+ // Angular axis title existence affects the chart dimensions
+ if (m_polarGraph && orientation == QAbstract3DAxis::AxisOrientationX)
+ calculateSceneScalingFactors();
+}
+
+void Surface3DRenderer::updateMargin(float margin)
+{
+ Abstract3DRenderer::updateMargin(margin);
+ calculateSceneScalingFactors();
+}
+
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/engine/surface3drenderer_p.h b/src/datavisualization/engine/surface3drenderer_p.h
index efa8ff7e..57b6f9e6 100644
--- a/src/datavisualization/engine/surface3drenderer_p.h
+++ b/src/datavisualization/engine/surface3drenderer_p.h
@@ -51,19 +51,16 @@ private:
ShaderHelper *m_backgroundShader;
ShaderHelper *m_surfaceFlatShader;
ShaderHelper *m_surfaceSmoothShader;
+ ShaderHelper *m_surfaceTexturedSmoothShader;
+ ShaderHelper *m_surfaceTexturedFlatShader;
ShaderHelper *m_surfaceGridShader;
ShaderHelper *m_surfaceSliceFlatShader;
ShaderHelper *m_surfaceSliceSmoothShader;
ShaderHelper *m_selectionShader;
- ShaderHelper *m_labelShader;
- GLfloat m_heightNormalizer;
- GLfloat m_scaleFactor;
- GLfloat m_scaleX;
- GLfloat m_scaleZ;
- GLfloat m_scaleXWithBackground;
- GLfloat m_scaleZWithBackground;
- GLuint m_depthTexture;
- GLuint m_depthModelTexture;
+ float m_heightNormalizer;
+ float m_scaleX;
+ float m_scaleY;
+ float m_scaleZ;
GLuint m_depthFrameBuffer;
GLuint m_selectionFrameBuffer;
GLuint m_selectionDepthBuffer;
@@ -73,13 +70,12 @@ private:
bool m_selectionActive;
AbstractRenderItem m_dummyRenderItem;
GLint m_shadowQualityMultiplier;
- QSizeF m_areaSize;
- bool m_hasHeightAdjustmentChanged;
QPoint m_selectedPoint;
QSurface3DSeries *m_selectedSeries;
QPoint m_clickedPosition;
bool m_selectionTexturesDirty;
GLuint m_noShadowTexture;
+ bool m_flipHorizontalGrid;
public:
explicit Surface3DRenderer(Surface3DController *controller);
@@ -87,6 +83,7 @@ public:
void updateData();
void updateSeries(const QList<QAbstract3DSeries *> &seriesList);
+ void updateSurfaceTextures(QVector<QSurface3DSeries *> seriesList);
SeriesRenderCache *createNewCache(QAbstract3DSeries *series);
void cleanCache(SeriesRenderCache *cache);
void updateSelectionMode(QAbstract3DGraph::SelectionFlags mode);
@@ -95,14 +92,22 @@ public:
void updateScene(Q3DScene *scene);
void updateSlicingActive(bool isSlicing);
void updateSelectedPoint(const QPoint &position, QSurface3DSeries *series);
+ void updateFlipHorizontalGrid(bool flip);
inline QPoint clickedPosition() const { return m_clickedPosition; }
void resetClickedStatus();
QVector3D convertPositionToTranslation(const QVector3D &position, bool isAbsolute);
+ void updateAxisLabels(QAbstract3DAxis::AxisOrientation orientation,
+ const QStringList &labels);
+ void updateAxisTitleVisibility(QAbstract3DAxis::AxisOrientation orientation,
+ bool visible);
+ void updateMargin(float margin);
void render(GLuint defaultFboHandle = 0);
protected:
void initializeOpenGL();
+ virtual void fixCameraTarget(QVector3D &target);
+ virtual void getVisibleItemBounds(QVector3D &minBounds, QVector3D &maxBounds);
signals:
void flatShadingSupportedChanged(bool supported);
@@ -128,7 +133,6 @@ private:
void calculateSceneScalingFactors();
void initBackgroundShaders(const QString &vertexShader, const QString &fragmentShader);
- void initLabelShaders(const QString &vertexShader, const QString &fragmentShader);
void initSelectionShaders();
void initSurfaceShaders();
void initSelectionBuffer();
@@ -136,13 +140,11 @@ private:
void updateSelectionTextures();
void createSelectionTexture(SurfaceSeriesRenderCache *cache, uint &lastSelectionId);
void idToRGBA(uint id, uchar *r, uchar *g, uchar *b, uchar *a);
- void fillIdCorner(uchar *p, uchar r, uchar g, uchar b, uchar a, int stride);
+ void fillIdCorner(uchar *p, uchar r, uchar g, uchar b, uchar a);
void surfacePointSelected(const QPoint &point);
void updateSelectionPoint(SurfaceSeriesRenderCache *cache, const QPoint &point, bool label);
QPoint selectionIdToSurfacePoint(uint id);
-#if !defined(QT_OPENGL_ES_2)
void updateDepthBuffer();
-#endif
void emitSelectedPointChanged(QPoint position);
Q_DISABLE_COPY(Surface3DRenderer)
diff --git a/src/datavisualization/engine/surfaceseriesrendercache.cpp b/src/datavisualization/engine/surfaceseriesrendercache.cpp
index 1cce5288..f1ad752a 100644
--- a/src/datavisualization/engine/surfaceseriesrendercache.cpp
+++ b/src/datavisualization/engine/surfaceseriesrendercache.cpp
@@ -39,7 +39,8 @@ SurfaceSeriesRenderCache::SurfaceSeriesRenderCache(QAbstract3DSeries *series,
m_sliceSelectionPointer(0),
m_mainSelectionPointer(0),
m_slicePointerActive(false),
- m_mainPointerActive(false)
+ m_mainPointerActive(false),
+ m_surfaceTexture(0)
{
}
@@ -62,8 +63,10 @@ void SurfaceSeriesRenderCache::populate(bool newSeries)
void SurfaceSeriesRenderCache::cleanup(TextureHelper *texHelper)
{
- if (QOpenGLContext::currentContext())
+ if (QOpenGLContext::currentContext()) {
texHelper->deleteTexture(&m_selectionTexture);
+ texHelper->deleteTexture(&m_surfaceTexture);
+ }
delete m_surfaceObj;
delete m_sliceSurfaceObj;
diff --git a/src/datavisualization/engine/surfaceseriesrendercache_p.h b/src/datavisualization/engine/surfaceseriesrendercache_p.h
index b6254a75..4d04149f 100644
--- a/src/datavisualization/engine/surfaceseriesrendercache_p.h
+++ b/src/datavisualization/engine/surfaceseriesrendercache_p.h
@@ -85,6 +85,8 @@ public:
inline bool slicePointerActive() const { return m_slicePointerActive; }
inline void setMainPointerActivity(bool activity) { m_mainPointerActive = activity; }
inline bool mainPointerActive() const { return m_mainPointerActive; }
+ inline void setSurfaceTexture(GLuint texture) { m_surfaceTexture = texture; }
+ inline GLuint surfaceTexture() const { return m_surfaceTexture; }
protected:
bool m_surfaceVisible;
@@ -105,6 +107,7 @@ protected:
SelectionPointer *m_mainSelectionPointer;
bool m_slicePointerActive;
bool m_mainPointerActive;
+ GLuint m_surfaceTexture;
};
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/global/datavisualizationglobal_p.h b/src/datavisualization/global/datavisualizationglobal_p.h
index abdac998..16bf7c6d 100644
--- a/src/datavisualization/global/datavisualizationglobal_p.h
+++ b/src/datavisualization/global/datavisualizationglobal_p.h
@@ -52,6 +52,7 @@ static const float gridLineOffset = 0.0035f; // Offset for lifting grid lines of
// y position is added to the minimum height (or can be thought to be that much above or below the camera)
static const QVector3D defaultLightPos = QVector3D(0.0f, 0.5f, 0.0f);
static const QVector3D zeroVector = QVector3D(0.0f, 0.0f, 0.0f);
+static const QVector3D oneVector = QVector3D(1.0f, 1.0f, 1.0f);
static const QVector3D upVector = QVector3D(0.0f, 1.0f, 0.0f);
static const QVector3D cameraDistanceVector = QVector3D(0.0f, 0.0f, cameraDistance);
static const QQuaternion identityQuaternion;
@@ -69,6 +70,7 @@ static const GLfloat gradientTextureWidth = 2.0f;
static const GLfloat uniformTextureHeight = 64.0f;
static const GLfloat uniformTextureWidth = 2.0f;
static const GLfloat labelMargin = 0.05f;
+static const GLfloat gridLineWidth = 0.005f;
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/global/global.pri b/src/datavisualization/global/global.pri
index 00a3eb65..a43ffb20 100644
--- a/src/datavisualization/global/global.pri
+++ b/src/datavisualization/global/global.pri
@@ -1,3 +1,5 @@
HEADERS += \
$$PWD/qdatavisualizationglobal.h \
$$PWD/datavisualizationglobal_p.h
+
+INCLUDEPATH += $$PWD
diff --git a/src/datavisualization/global/qdatavisualizationglobal.h b/src/datavisualization/global/qdatavisualizationglobal.h
index c637f408..a7f96361 100644
--- a/src/datavisualization/global/qdatavisualizationglobal.h
+++ b/src/datavisualization/global/qdatavisualizationglobal.h
@@ -21,11 +21,11 @@
#include <QtCore/qglobal.h>
-#define QT_DATAVISUALIZATION_VERSION_STR "1.1.1"
+#define QT_DATAVISUALIZATION_VERSION_STR "1.2.0"
/*
QT_DATAVISUALIZATION_VERSION is (major << 16) + (minor << 8) + patch.
*/
-#define QT_DATAVISUALIZATION_VERSION 0x010101
+#define QT_DATAVISUALIZATION_VERSION 0x010200
/*
can be used like #if (QT_DATAVISUALIZATION_VERSION >= QT_DATAVISUALIZATION_VERSION_CHECK(1, 0, 0))
*/
diff --git a/src/datavisualization/input/input.pri b/src/datavisualization/input/input.pri
index 5a4c4a76..f6b9fd6a 100644
--- a/src/datavisualization/input/input.pri
+++ b/src/datavisualization/input/input.pri
@@ -10,3 +10,5 @@ SOURCES += \
$$PWD/qabstract3dinputhandler.cpp \
$$PWD/q3dinputhandler.cpp \
$$PWD/qtouch3dinputhandler.cpp
+
+INCLUDEPATH += $$PWD
diff --git a/src/datavisualization/input/q3dinputhandler.cpp b/src/datavisualization/input/q3dinputhandler.cpp
index f0044096..43cee84e 100644
--- a/src/datavisualization/input/q3dinputhandler.cpp
+++ b/src/datavisualization/input/q3dinputhandler.cpp
@@ -18,19 +18,20 @@
#include "datavisualizationglobal_p.h"
#include "q3dinputhandler_p.h"
+#include "abstract3dcontroller_p.h"
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
-const int minZoomLevel = 10;
-const int halfSizeZoomLevel = 50;
-const int oneToOneZoomLevel = 100;
-const int maxZoomLevel = 500;
+static const int halfSizeZoomLevel = 50;
+static const int oneToOneZoomLevel = 100;
+static const int driftTowardCenterLevel = 175;
+static const float wheelZoomDrift = 0.1f;
-const int nearZoomRangeDivider = 12;
-const int midZoomRangeDivider = 60;
-const int farZoomRangeDivider = 120;
+static const int nearZoomRangeDivider = 12;
+static const int midZoomRangeDivider = 60;
+static const int farZoomRangeDivider = 120;
-const float rotationSpeed = 100.0f;
+static const float rotationSpeed = 100.0f;
/*!
* \class Q3DInputHandler
@@ -55,12 +56,61 @@ const float rotationSpeed = 100.0f;
* \l {QAbstract3DGraph::selectionMode}{selection mode}.
* \row
* \li Mouse wheel
- * \li Zoom in/out within default range (10...500%).
+ * \li Zoom in/out within the allowable zoom range set for Q3DCamera.
* \row
* \li Left click on the primary view when the secondary view is visible
* \li Closes the secondary view.
* \note Secondary view is available only for Q3DBars and Q3DSurface graphs.
* \endtable
+ *
+ * Rotation, zoom, and selection can each be individually disabled using
+ * corresponding properties of this class.
+ */
+
+/*!
+ * \qmltype InputHandler3D
+ * \inqmlmodule QtDataVisualization
+ * \since QtDataVisualization 1.2
+ * \ingroup datavisualization_qml
+ * \instantiates Q3DInputHandler
+ * \brief Basic wheel mouse based input handler.
+ *
+ * InputHandler3D is the basic input handler for wheel mouse type of input devices.
+ *
+ * See Q3DInputHandler documentation for more details.
+ */
+
+/*!
+ * \qmlproperty bool InputHandler3D::rotationEnabled
+ * \since QtDataVisualization 1.2
+ *
+ * This property specifies if this input handler allows graph rotation.
+ * Defaults to \c{true}.
+ */
+
+/*!
+ * \qmlproperty bool InputHandler3D::zoomEnabled
+ * \since QtDataVisualization 1.2
+ *
+ * This property specifies if this input handler allows graph zooming.
+ * Defaults to \c{true}.
+ */
+
+/*!
+ * \qmlproperty bool InputHandler3D::selectionEnabled
+ * \since QtDataVisualization 1.2
+ *
+ * This property specifies if this input handler allows selection from the graph.
+ * Defaults to \c{true}.
+ */
+
+/*!
+ * \qmlproperty bool InputHandler3D::zoomAtTargetEnabled
+ * \since QtDataVisualization 1.2
+ *
+ * This property specifies if zooming changes the camera target to the position of the input
+ * at the time of the zoom.
+ * Defaults to \c{true}.
*/
/*!
@@ -92,29 +142,35 @@ void Q3DInputHandler::mousePressEvent(QMouseEvent *event, const QPoint &mousePos
Q_UNUSED(mousePos);
#else
if (Qt::LeftButton == event->button()) {
- if (scene()->isSlicingActive()) {
- if (scene()->isPointInPrimarySubView(mousePos))
+ if (isSelectionEnabled()) {
+ if (scene()->isSlicingActive()) {
+ if (scene()->isPointInPrimarySubView(mousePos))
+ setInputView(InputViewOnPrimary);
+ else if (scene()->isPointInSecondarySubView(mousePos))
+ setInputView(InputViewOnSecondary);
+ else
+ setInputView(InputViewNone);
+ } else {
+ // update mouse positions to prevent jumping when releasing or repressing a button
+ setInputPosition(mousePos);
+ scene()->setSelectionQueryPosition(mousePos);
setInputView(InputViewOnPrimary);
- else if (scene()->isPointInSecondarySubView(mousePos))
- setInputView(InputViewOnSecondary);
- else
- setInputView(InputViewNone);
- } else {
- // update mouse positions to prevent jumping when releasing or repressing a button
- setInputPosition(mousePos);
- scene()->setSelectionQueryPosition(mousePos);
- setInputView(InputViewOnPrimary);
- d_ptr->m_inputState = QAbstract3DInputHandlerPrivate::InputStateSelecting;
+ d_ptr->m_inputState = QAbstract3DInputHandlerPrivate::InputStateSelecting;
+ }
}
} else if (Qt::MiddleButton == event->button()) {
- // reset rotations
- setInputPosition(QPoint(0, 0));
+ if (isRotationEnabled()) {
+ // reset rotations
+ setInputPosition(QPoint(0, 0));
+ }
} else if (Qt::RightButton == event->button()) {
- // disable rotating when in slice view
- if (!scene()->isSlicingActive())
- d_ptr->m_inputState = QAbstract3DInputHandlerPrivate::InputStateRotating;
- // update mouse positions to prevent jumping when releasing or repressing a button
- setInputPosition(mousePos);
+ if (isRotationEnabled()) {
+ // disable rotating when in slice view
+ if (!scene()->isSlicingActive())
+ d_ptr->m_inputState = QAbstract3DInputHandlerPrivate::InputStateRotating;
+ // update mouse positions to prevent jumping when releasing or repressing a button
+ setInputPosition(mousePos);
+ }
}
#endif
}
@@ -148,7 +204,8 @@ void Q3DInputHandler::mouseMoveEvent(QMouseEvent *event, const QPoint &mousePos)
#if defined(Q_OS_IOS)
Q_UNUSED(mousePos);
#else
- if (QAbstract3DInputHandlerPrivate::InputStateRotating == d_ptr->m_inputState) {
+ if (QAbstract3DInputHandlerPrivate::InputStateRotating == d_ptr->m_inputState
+ && isRotationEnabled()) {
// Calculate mouse movement since last frame
float xRotation = scene()->activeCamera()->xRotation();
float yRotation = scene()->activeCamera()->yRotation();
@@ -174,34 +231,191 @@ void Q3DInputHandler::mouseMoveEvent(QMouseEvent *event, const QPoint &mousePos)
*/
void Q3DInputHandler::wheelEvent(QWheelEvent *event)
{
- // disable zooming if in slice view
- if (scene()->isSlicingActive())
- return;
+ if (isZoomEnabled()) {
+ // disable zooming if in slice view
+ if (scene()->isSlicingActive())
+ return;
- // Adjust zoom level based on what zoom range we're in.
- int zoomLevel = scene()->activeCamera()->zoomLevel();
- if (zoomLevel > oneToOneZoomLevel)
- zoomLevel += event->angleDelta().y() / nearZoomRangeDivider;
- else if (zoomLevel > halfSizeZoomLevel)
- zoomLevel += event->angleDelta().y() / midZoomRangeDivider;
- else
- zoomLevel += event->angleDelta().y() / farZoomRangeDivider;
- if (zoomLevel > maxZoomLevel)
- zoomLevel = maxZoomLevel;
- else if (zoomLevel < minZoomLevel)
- zoomLevel = minZoomLevel;
+ // Adjust zoom level based on what zoom range we're in.
+ Q3DCamera *camera = scene()->activeCamera();
+ int zoomLevel = int(camera->zoomLevel());
+ const int minZoomLevel = int(camera->minZoomLevel());
+ const int maxZoomLevel = int(camera->maxZoomLevel());
+ if (zoomLevel > oneToOneZoomLevel)
+ zoomLevel += event->angleDelta().y() / nearZoomRangeDivider;
+ else if (zoomLevel > halfSizeZoomLevel)
+ zoomLevel += event->angleDelta().y() / midZoomRangeDivider;
+ else
+ zoomLevel += event->angleDelta().y() / farZoomRangeDivider;
+ zoomLevel = qBound(minZoomLevel, zoomLevel, maxZoomLevel);
- scene()->activeCamera()->setZoomLevel(zoomLevel);
+ if (isZoomAtTargetEnabled()) {
+ scene()->setGraphPositionQuery(event->pos());
+ d_ptr->m_zoomAtTargetPending = true;
+ // If zoom at target is enabled, we don't want to zoom yet, as that causes
+ // jitter. Instead, we zoom next frame, when we apply the camera position.
+ d_ptr->m_requestedZoomLevel = zoomLevel;
+ d_ptr->m_driftMultiplier = wheelZoomDrift;
+ } else {
+ camera->setZoomLevel(zoomLevel);
+ }
+ }
+}
+
+/*!
+ * \property Q3DInputHandler::rotationEnabled
+ * \since QtDataVisualization 1.2
+ *
+ * This property specifies if this input handler allows graph rotation.
+ * Defaults to \c{true}.
+ */
+void Q3DInputHandler::setRotationEnabled(bool enable)
+{
+ if (d_ptr->m_rotationEnabled != enable) {
+ d_ptr->m_rotationEnabled = enable;
+ emit rotationEnabledChanged(enable);
+ }
+}
+
+bool Q3DInputHandler::isRotationEnabled() const
+{
+ return d_ptr->m_rotationEnabled;
+}
+
+/*!
+ * \property Q3DInputHandler::zoomEnabled
+ * \since QtDataVisualization 1.2
+ *
+ * This property specifies if this input handler allows graph zooming.
+ * Defaults to \c{true}.
+ */
+void Q3DInputHandler::setZoomEnabled(bool enable)
+{
+ if (d_ptr->m_zoomEnabled != enable) {
+ d_ptr->m_zoomEnabled = enable;
+ emit zoomEnabledChanged(enable);
+ }
+}
+
+bool Q3DInputHandler::isZoomEnabled() const
+{
+ return d_ptr->m_zoomEnabled;
+}
+
+/*!
+ * \property Q3DInputHandler::selectionEnabled
+ * \since QtDataVisualization 1.2
+ *
+ * This property specifies if this input handler allows selection from the graph.
+ * Defaults to \c{true}.
+ */
+void Q3DInputHandler::setSelectionEnabled(bool enable)
+{
+ if (d_ptr->m_selectionEnabled != enable) {
+ d_ptr->m_selectionEnabled = enable;
+ emit selectionEnabledChanged(enable);
+ }
+}
+
+bool Q3DInputHandler::isSelectionEnabled() const
+{
+ return d_ptr->m_selectionEnabled;
+}
+
+/*!
+ * \property Q3DInputHandler::zoomAtTargetEnabled
+ * \since QtDataVisualization 1.2
+ *
+ * This property specifies if zooming should change the camera target so that the zoomed point
+ * of the graph stays at the same location after the zoom.
+ * Defaults to \c{true}.
+ */
+void Q3DInputHandler::setZoomAtTargetEnabled(bool enable)
+{
+ if (d_ptr->m_zoomAtTargetEnabled != enable) {
+ d_ptr->m_zoomAtTargetEnabled = enable;
+ emit zoomAtTargetEnabledChanged(enable);
+ }
+}
+
+bool Q3DInputHandler::isZoomAtTargetEnabled() const
+{
+ return d_ptr->m_zoomAtTargetEnabled;
}
Q3DInputHandlerPrivate::Q3DInputHandlerPrivate(Q3DInputHandler *q)
: q_ptr(q),
- m_inputState(QAbstract3DInputHandlerPrivate::InputStateNone)
+ m_inputState(QAbstract3DInputHandlerPrivate::InputStateNone),
+ m_rotationEnabled(true),
+ m_zoomEnabled(true),
+ m_selectionEnabled(true),
+ m_zoomAtTargetEnabled(true),
+ m_zoomAtTargetPending(false),
+ m_controller(0),
+ m_requestedZoomLevel(0.0f),
+ m_driftMultiplier(0.0f)
{
+ QObject::connect(q, &QAbstract3DInputHandler::sceneChanged,
+ this, &Q3DInputHandlerPrivate::handleSceneChange);
}
Q3DInputHandlerPrivate::~Q3DInputHandlerPrivate()
{
}
+void Q3DInputHandlerPrivate::handleSceneChange(Q3DScene *scene)
+{
+ if (scene) {
+ if (m_controller) {
+ QObject::disconnect(m_controller, &Abstract3DController::queriedGraphPositionChanged,
+ this, &Q3DInputHandlerPrivate::handleQueriedGraphPositionChange);
+ }
+
+ m_controller = qobject_cast<Abstract3DController *>(scene->parent());
+
+ if (m_controller) {
+ QObject::connect(m_controller, &Abstract3DController::queriedGraphPositionChanged,
+ this, &Q3DInputHandlerPrivate::handleQueriedGraphPositionChange);
+ }
+ }
+}
+
+void Q3DInputHandlerPrivate::handleQueriedGraphPositionChange()
+{
+ if (m_zoomAtTargetPending) {
+ // Check if the zoom point is on graph
+ QVector3D newTarget = m_controller->queriedGraphPosition();
+ float currentZoom = m_requestedZoomLevel;
+ float previousZoom = q_ptr->scene()->activeCamera()->zoomLevel();
+ q_ptr->scene()->activeCamera()->setZoomLevel(currentZoom);
+ float diffAdj = 0.0f;
+
+ // If zooming in/out outside the graph, or zooming out after certain point,
+ // move towards the center.
+ if ((qAbs(newTarget.x()) > 1.0f
+ || qAbs(newTarget.y()) > 1.0f
+ || qAbs(newTarget.z()) > 1.0f)
+ || (previousZoom > currentZoom && currentZoom <= driftTowardCenterLevel)) {
+ newTarget = zeroVector;
+ // Add some extra correction so that we actually reach the center eventually
+ diffAdj = m_driftMultiplier;
+ if (previousZoom > currentZoom)
+ diffAdj *= 2.0f; // Correct towards center little more when zooming out
+ }
+
+ float zoomFraction = 1.0f - (previousZoom / currentZoom);
+
+ // Adjust camera towards the zoom point, attempting to keep the cursor at same graph point
+ QVector3D oldTarget = q_ptr->scene()->activeCamera()->target();
+ QVector3D origDiff = newTarget - oldTarget;
+ QVector3D diff = origDiff * zoomFraction + (origDiff.normalized() * diffAdj);
+ if (diff.length() > origDiff.length())
+ diff = origDiff;
+ q_ptr->scene()->activeCamera()->setTarget(oldTarget + diff);
+
+ if (q_ptr->scene()->selectionQueryPosition() == Q3DScene::invalidSelectionPoint())
+ m_zoomAtTargetPending = false;
+ }
+}
+
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/input/q3dinputhandler.h b/src/datavisualization/input/q3dinputhandler.h
index 118bd829..5423b4ea 100644
--- a/src/datavisualization/input/q3dinputhandler.h
+++ b/src/datavisualization/input/q3dinputhandler.h
@@ -28,17 +28,36 @@ class Q3DInputHandlerPrivate;
class QT_DATAVISUALIZATION_EXPORT Q3DInputHandler : public QAbstract3DInputHandler
{
Q_OBJECT
+ Q_PROPERTY(bool rotationEnabled READ isRotationEnabled WRITE setRotationEnabled NOTIFY rotationEnabledChanged)
+ Q_PROPERTY(bool zoomEnabled READ isZoomEnabled WRITE setZoomEnabled NOTIFY zoomEnabledChanged)
+ Q_PROPERTY(bool selectionEnabled READ isSelectionEnabled WRITE setSelectionEnabled NOTIFY selectionEnabledChanged)
+ Q_PROPERTY(bool zoomAtTargetEnabled READ isZoomAtTargetEnabled WRITE setZoomAtTargetEnabled NOTIFY zoomAtTargetEnabledChanged)
public:
explicit Q3DInputHandler(QObject *parent = 0);
virtual ~Q3DInputHandler();
+ void setRotationEnabled(bool enable);
+ bool isRotationEnabled() const;
+ void setZoomEnabled(bool enable);
+ bool isZoomEnabled() const;
+ void setSelectionEnabled(bool enable);
+ bool isSelectionEnabled() const;
+ void setZoomAtTargetEnabled(bool enable);
+ bool isZoomAtTargetEnabled() const;
+
// Input event listeners
virtual void mousePressEvent(QMouseEvent *event, const QPoint &mousePos);
virtual void mouseReleaseEvent(QMouseEvent *event, const QPoint &mousePos);
virtual void mouseMoveEvent(QMouseEvent *event, const QPoint &mousePos);
virtual void wheelEvent(QWheelEvent *event);
+signals:
+ void rotationEnabledChanged(bool enable);
+ void zoomEnabledChanged(bool enable);
+ void selectionEnabledChanged(bool enable);
+ void zoomAtTargetEnabledChanged(bool enable);
+
private:
Q_DISABLE_COPY(Q3DInputHandler)
diff --git a/src/datavisualization/input/q3dinputhandler_p.h b/src/datavisualization/input/q3dinputhandler_p.h
index a9b27307..79b1c8dd 100644
--- a/src/datavisualization/input/q3dinputhandler_p.h
+++ b/src/datavisualization/input/q3dinputhandler_p.h
@@ -34,15 +34,36 @@
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
-class Q3DInputHandlerPrivate
+class Abstract3DController;
+
+class Q3DInputHandlerPrivate : public QObject
{
+ Q_OBJECT
public:
Q3DInputHandlerPrivate(Q3DInputHandler *q);
~Q3DInputHandlerPrivate();
-public:
+public slots:
+ void handleSceneChange(Q3DScene *scene);
+ void handleQueriedGraphPositionChange();
+
+private:
Q3DInputHandler *q_ptr;
+protected:
QAbstract3DInputHandlerPrivate::InputState m_inputState;
+
+ bool m_rotationEnabled;
+ bool m_zoomEnabled;
+ bool m_selectionEnabled;
+ bool m_zoomAtTargetEnabled;
+ bool m_zoomAtTargetPending;
+
+ Abstract3DController *m_controller; // Not owned
+
+ float m_requestedZoomLevel;
+ float m_driftMultiplier;
+
+ friend class Q3DInputHandler;
};
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/input/qtouch3dinputhandler.cpp b/src/datavisualization/input/qtouch3dinputhandler.cpp
index 30f31d4a..357a6f3e 100644
--- a/src/datavisualization/input/qtouch3dinputhandler.cpp
+++ b/src/datavisualization/input/qtouch3dinputhandler.cpp
@@ -22,17 +22,16 @@
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
-const float maxTapAndHoldJitter = 20.0f;
-const int maxPinchJitter = 10;
+static const float maxTapAndHoldJitter = 20.0f;
+static const int maxPinchJitter = 10;
#if defined (Q_OS_ANDROID) || defined(Q_OS_IOS)
-const int maxSelectionJitter = 10;
+static const int maxSelectionJitter = 10;
#else
-const int maxSelectionJitter = 5;
+static const int maxSelectionJitter = 5;
#endif
-const int tapAndHoldTime = 250;
-const float rotationSpeed = 200.0f;
-const int minZoomLevel = 10;
-const int maxZoomLevel = 500;
+static const int tapAndHoldTime = 250;
+static const float rotationSpeed = 200.0f;
+static const float touchZoomDrift = 0.02f;
/*!
* \class QTouch3DInputHandler
@@ -60,12 +59,29 @@ const int maxZoomLevel = 500;
* \li Same as tap.
* \row
* \li Pinch
- * \li Zoom in/out within default range (10...500%).
+ * \li Zoom in/out within the allowable zoom range set for Q3DCamera.
* \row
* \li Tap on the primary view when the secondary view is visible
* \li Closes the secondary view.
* \note Secondary view is available only for Q3DBars and Q3DSurface graphs.
* \endtable
+ *
+ * Rotation, zoom, and selection can each be individually disabled using
+ * corresponding Q3DInputHandler properties.
+ */
+
+/*!
+ * \qmltype TouchInputHandler3D
+ * \inqmlmodule QtDataVisualization
+ * \since QtDataVisualization 1.2
+ * \ingroup datavisualization_qml
+ * \instantiates QTouch3DInputHandler
+ * \inherits InputHandler3D
+ * \brief Basic touch display based input handler.
+ *
+ * TouchInputHandler3D is the basic input handler for touch screen devices.
+ *
+ * See QTouch3DInputHandler documentation for more details.
*/
/*!
@@ -97,36 +113,46 @@ void QTouch3DInputHandler::touchEvent(QTouchEvent *event)
if (!scene()->isSlicingActive() && points.count() == 2) {
d_ptr->m_holdTimer->stop();
QPointF distance = points.at(0).pos() - points.at(1).pos();
- d_ptr->handlePinchZoom(distance.manhattanLength());
+ QPoint midPoint = ((points.at(0).pos() + points.at(1).pos()) / 2.0).toPoint();
+ d_ptr->handlePinchZoom(distance.manhattanLength(), midPoint);
} else if (points.count() == 1) {
QPointF pointerPos = points.at(0).pos();
if (event->type() == QEvent::TouchBegin) {
// Flush input state
d_ptr->m_inputState = QAbstract3DInputHandlerPrivate::InputStateNone;
if (scene()->isSlicingActive()) {
- if (scene()->isPointInPrimarySubView(pointerPos.toPoint()))
- setInputView(InputViewOnPrimary);
- else if (scene()->isPointInSecondarySubView(pointerPos.toPoint()))
- setInputView(InputViewOnSecondary);
- else
- setInputView(InputViewNone);
+ if (isSelectionEnabled()) {
+ if (scene()->isPointInPrimarySubView(pointerPos.toPoint()))
+ setInputView(InputViewOnPrimary);
+ else if (scene()->isPointInSecondarySubView(pointerPos.toPoint()))
+ setInputView(InputViewOnSecondary);
+ else
+ setInputView(InputViewNone);
+ }
} else {
// Handle possible tap-and-hold selection
- d_ptr->m_startHoldPos = pointerPos;
- d_ptr->m_touchHoldPos = d_ptr->m_startHoldPos;
- d_ptr->m_holdTimer->start();
- setInputView(InputViewOnPrimary);
+ if (isSelectionEnabled()) {
+ d_ptr->m_startHoldPos = pointerPos;
+ d_ptr->m_touchHoldPos = d_ptr->m_startHoldPos;
+ d_ptr->m_holdTimer->start();
+ setInputView(InputViewOnPrimary);
+ }
// Start rotating
- d_ptr->m_inputState = QAbstract3DInputHandlerPrivate::InputStateRotating;
- setInputPosition(pointerPos.toPoint());
+ if (isRotationEnabled()) {
+ d_ptr->m_inputState = QAbstract3DInputHandlerPrivate::InputStateRotating;
+ setInputPosition(pointerPos.toPoint());
+ setInputView(InputViewOnPrimary);
+ }
}
} else if (event->type() == QEvent::TouchEnd) {
setInputView(InputViewNone);
d_ptr->m_holdTimer->stop();
// Handle possible selection
if (!scene()->isSlicingActive()
- && QAbstract3DInputHandlerPrivate::InputStatePinching != d_ptr->m_inputState)
+ && QAbstract3DInputHandlerPrivate::InputStatePinching
+ != d_ptr->m_inputState) {
d_ptr->handleSelection(pointerPos);
+ }
} else if (event->type() == QEvent::TouchUpdate) {
if (!scene()->isSlicingActive()) {
d_ptr->m_touchHoldPos = pointerPos;
@@ -140,7 +166,8 @@ void QTouch3DInputHandler::touchEvent(QTouchEvent *event)
}
QTouch3DInputHandlerPrivate::QTouch3DInputHandlerPrivate(QTouch3DInputHandler *q)
- : q_ptr(q),
+ : Q3DInputHandlerPrivate(q),
+ q_ptr(q),
m_holdTimer(0),
m_inputState(QAbstract3DInputHandlerPrivate::InputStateNone)
{
@@ -156,54 +183,71 @@ QTouch3DInputHandlerPrivate::~QTouch3DInputHandlerPrivate()
delete m_holdTimer;
}
-void QTouch3DInputHandlerPrivate::handlePinchZoom(float distance)
+void QTouch3DInputHandlerPrivate::handlePinchZoom(float distance, const QPoint &pos)
{
- int newDistance = distance;
- int prevDist = q_ptr->prevDistance();
- if (prevDist > 0 && qAbs(prevDist - newDistance) < maxPinchJitter)
- return;
- m_inputState = QAbstract3DInputHandlerPrivate::InputStatePinching;
- Q3DCamera *camera = q_ptr->scene()->activeCamera();
- int zoomLevel = camera->zoomLevel();
- float zoomRate = qSqrt(qSqrt(zoomLevel));
- if (newDistance > prevDist)
- zoomLevel += zoomRate;
- else
- zoomLevel -= zoomRate;
- if (zoomLevel > maxZoomLevel)
- zoomLevel = maxZoomLevel;
- else if (zoomLevel < minZoomLevel)
- zoomLevel = minZoomLevel;
- camera->setZoomLevel(zoomLevel);
- q_ptr->setPrevDistance(newDistance);
+ if (q_ptr->isZoomEnabled()) {
+ int newDistance = distance;
+ int prevDist = q_ptr->prevDistance();
+ if (prevDist > 0 && qAbs(prevDist - newDistance) < maxPinchJitter)
+ return;
+ m_inputState = QAbstract3DInputHandlerPrivate::InputStatePinching;
+ Q3DCamera *camera = q_ptr->scene()->activeCamera();
+ int zoomLevel = int(camera->zoomLevel());
+ const int minZoomLevel = int(camera->minZoomLevel());
+ const int maxZoomLevel = int(camera->maxZoomLevel());
+ float zoomRate = qSqrt(qSqrt(zoomLevel));
+ if (newDistance > prevDist)
+ zoomLevel += zoomRate;
+ else
+ zoomLevel -= zoomRate;
+ zoomLevel = qBound(minZoomLevel, zoomLevel, maxZoomLevel);
+
+ if (q_ptr->isZoomAtTargetEnabled()) {
+ q_ptr->scene()->setGraphPositionQuery(pos);
+ m_zoomAtTargetPending = true;
+ // If zoom at target is enabled, we don't want to zoom yet, as that causes
+ // jitter. Instead, we zoom next frame, when we apply the camera position.
+ m_requestedZoomLevel = zoomLevel;
+ m_driftMultiplier = touchZoomDrift;
+ } else {
+ camera->setZoomLevel(zoomLevel);
+ }
+
+ q_ptr->setPrevDistance(newDistance);
+ }
}
void QTouch3DInputHandlerPrivate::handleTapAndHold()
{
- QPointF distance = m_startHoldPos - m_touchHoldPos;
- if (distance.manhattanLength() < maxTapAndHoldJitter) {
- q_ptr->setInputPosition(m_touchHoldPos.toPoint());
- q_ptr->scene()->setSelectionQueryPosition(m_touchHoldPos.toPoint());
- m_inputState = QAbstract3DInputHandlerPrivate::InputStateSelecting;
+ if (q_ptr->isSelectionEnabled()) {
+ QPointF distance = m_startHoldPos - m_touchHoldPos;
+ if (distance.manhattanLength() < maxTapAndHoldJitter) {
+ q_ptr->setInputPosition(m_touchHoldPos.toPoint());
+ q_ptr->scene()->setSelectionQueryPosition(m_touchHoldPos.toPoint());
+ m_inputState = QAbstract3DInputHandlerPrivate::InputStateSelecting;
+ }
}
}
void QTouch3DInputHandlerPrivate::handleSelection(const QPointF &position)
{
- QPointF distance = m_startHoldPos - position;
- if (distance.manhattanLength() < maxSelectionJitter) {
- m_inputState = QAbstract3DInputHandlerPrivate::InputStateSelecting;
- q_ptr->scene()->setSelectionQueryPosition(position.toPoint());
- } else {
- m_inputState = QAbstract3DInputHandlerPrivate::InputStateNone;
- q_ptr->setInputView(QAbstract3DInputHandler::InputViewNone);
+ if (q_ptr->isSelectionEnabled()) {
+ QPointF distance = m_startHoldPos - position;
+ if (distance.manhattanLength() < maxSelectionJitter) {
+ m_inputState = QAbstract3DInputHandlerPrivate::InputStateSelecting;
+ q_ptr->scene()->setSelectionQueryPosition(position.toPoint());
+ } else {
+ m_inputState = QAbstract3DInputHandlerPrivate::InputStateNone;
+ q_ptr->setInputView(QAbstract3DInputHandler::InputViewNone);
+ }
+ q_ptr->setPreviousInputPos(position.toPoint());
}
- q_ptr->setPreviousInputPos(position.toPoint());
}
void QTouch3DInputHandlerPrivate::handleRotation(const QPointF &position)
{
- if (QAbstract3DInputHandlerPrivate::InputStateRotating == m_inputState) {
+ if (q_ptr->isRotationEnabled()
+ && QAbstract3DInputHandlerPrivate::InputStateRotating == m_inputState) {
Q3DScene *scene = q_ptr->scene();
Q3DCamera *camera = scene->activeCamera();
float xRotation = camera->xRotation();
diff --git a/src/datavisualization/input/qtouch3dinputhandler_p.h b/src/datavisualization/input/qtouch3dinputhandler_p.h
index 613b5f28..b01904ca 100644
--- a/src/datavisualization/input/qtouch3dinputhandler_p.h
+++ b/src/datavisualization/input/qtouch3dinputhandler_p.h
@@ -19,7 +19,7 @@
#ifndef QTOUCH3DINPUTHANDLER_P_H
#define QTOUCH3DINPUTHANDLER_P_H
-#include "qabstract3dinputhandler_p.h"
+#include "q3dinputhandler_p.h"
#include "qtouch3dinputhandler.h"
class QTimer;
@@ -28,7 +28,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
class QAbstract3DInputHandler;
-class QTouch3DInputHandlerPrivate : public QObject
+class QTouch3DInputHandlerPrivate : public Q3DInputHandlerPrivate
{
Q_OBJECT
@@ -36,13 +36,14 @@ public:
QTouch3DInputHandlerPrivate(QTouch3DInputHandler *q);
~QTouch3DInputHandlerPrivate();
- void handlePinchZoom(float distance);
+ void handlePinchZoom(float distance, const QPoint &pos);
void handleTapAndHold();
void handleSelection(const QPointF &position);
void handleRotation(const QPointF &position);
-public:
+private:
QTouch3DInputHandler *q_ptr;
+public:
QTimer *m_holdTimer;
QAbstract3DInputHandlerPrivate::InputState m_inputState;
QPointF m_startHoldPos;
diff --git a/src/datavisualization/theme/q3dtheme.cpp b/src/datavisualization/theme/q3dtheme.cpp
index 97ff8f81..a1dfe8b3 100644
--- a/src/datavisualization/theme/q3dtheme.cpp
+++ b/src/datavisualization/theme/q3dtheme.cpp
@@ -323,7 +323,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
/*!
* \qmlproperty real Theme3D::lightStrength
*
- * Specular light strength for the whole graph. Value must be between 0.0 and 1.0.
+ * Specular light strength for the whole graph. Value must be between 0.0 and 10.0.
*/
/*!
@@ -335,7 +335,7 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
/*!
* \qmlproperty real Theme3D::highlightLightStrength
*
- * Specular light strength for highlighted objects. Value must be between 0.0 and 1.0.
+ * Specular light strength for highlighted objects. Value must be between 0.0 and 10.0.
*/
/*!
@@ -679,7 +679,7 @@ QLinearGradient Q3DTheme::multiHighlightGradient() const
/*!
* \property Q3DTheme::lightStrength
*
- * Specular light strength for the whole graph. Value must be 0.0f and 10.0f.
+ * Specular light strength for the whole graph. Value must be between 0.0f and 10.0f.
*/
void Q3DTheme::setLightStrength(float strength)
{
diff --git a/src/datavisualization/theme/theme.pri b/src/datavisualization/theme/theme.pri
index 1de3035a..cf7b434c 100644
--- a/src/datavisualization/theme/theme.pri
+++ b/src/datavisualization/theme/theme.pri
@@ -6,3 +6,5 @@ HEADERS += \
SOURCES += \
$$PWD/q3dtheme.cpp \
$$PWD/thememanager.cpp
+
+INCLUDEPATH += $$PWD
diff --git a/src/datavisualization/utils/abstractobjecthelper.cpp b/src/datavisualization/utils/abstractobjecthelper.cpp
index c350d096..40b3a45e 100644
--- a/src/datavisualization/utils/abstractobjecthelper.cpp
+++ b/src/datavisualization/utils/abstractobjecthelper.cpp
@@ -28,6 +28,7 @@ AbstractObjectHelper::AbstractObjectHelper()
m_indexCount(0),
m_meshDataLoaded(false)
{
+ initializeOpenGLFunctions();
}
AbstractObjectHelper::~AbstractObjectHelper()
diff --git a/src/datavisualization/utils/abstractobjecthelper_p.h b/src/datavisualization/utils/abstractobjecthelper_p.h
index c1618909..99f85dab 100644
--- a/src/datavisualization/utils/abstractobjecthelper_p.h
+++ b/src/datavisualization/utils/abstractobjecthelper_p.h
@@ -38,11 +38,11 @@ class AbstractObjectHelper: protected QOpenGLFunctions
protected:
AbstractObjectHelper();
public:
- ~AbstractObjectHelper();
+ virtual ~AbstractObjectHelper();
GLuint vertexBuf();
GLuint normalBuf();
- GLuint uvBuf();
+ virtual GLuint uvBuf();
GLuint elementBuf();
GLuint indexCount();
GLuint indicesType();
diff --git a/src/datavisualization/utils/objecthelper.cpp b/src/datavisualization/utils/objecthelper.cpp
index a66e0f7e..4240d6f5 100644
--- a/src/datavisualization/utils/objecthelper.cpp
+++ b/src/datavisualization/utils/objecthelper.cpp
@@ -111,7 +111,6 @@ ObjectHelper *ObjectHelper::getObjectHelper(const Abstract3DRenderer *cacheId,
void ObjectHelper::load()
{
- initializeOpenGLFunctions();
if (m_meshDataLoaded) {
// Delete old data
glDeleteBuffers(1, &m_vertexbuffer);
@@ -122,6 +121,10 @@ void ObjectHelper::load()
m_indexedVertices.clear();
m_indexedUVs.clear();
m_indexedNormals.clear();
+ m_vertexbuffer = 0;
+ m_uvbuffer = 0;
+ m_normalbuffer = 0;
+ m_elementbuffer = 0;
}
QVector<QVector3D> vertices;
QVector<QVector2D> uvs;
diff --git a/src/datavisualization/utils/objecthelper_p.h b/src/datavisualization/utils/objecthelper_p.h
index c84f53bd..b00e5d8d 100644
--- a/src/datavisualization/utils/objecthelper_p.h
+++ b/src/datavisualization/utils/objecthelper_p.h
@@ -41,7 +41,7 @@ class ObjectHelper : public AbstractObjectHelper
private:
ObjectHelper(const QString &objectFile);
public:
- ~ObjectHelper();
+ virtual ~ObjectHelper();
static void resetObjectHelper(const Abstract3DRenderer *cacheId, ObjectHelper *&obj,
const QString &meshFile);
diff --git a/src/datavisualization/utils/qutils.h b/src/datavisualization/utils/qutils.h
index 43375a9c..d4acfc99 100644
--- a/src/datavisualization/utils/qutils.h
+++ b/src/datavisualization/utils/qutils.h
@@ -28,17 +28,21 @@ inline static QSurfaceFormat qDefaultSurfaceFormat(bool antialias = true)
QSurfaceFormat surfaceFormat;
surfaceFormat.setDepthBufferSize(24);
+ surfaceFormat.setStencilBufferSize(8);
surfaceFormat.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
-#if !defined(QT_OPENGL_ES_2)
- surfaceFormat.setRenderableType(QSurfaceFormat::OpenGL);
-#else
+ surfaceFormat.setRenderableType(QSurfaceFormat::DefaultRenderableType);
+#if defined(QT_OPENGL_ES_2)
// Antialias not supported for ES
antialias = false;
- surfaceFormat.setRenderableType(QSurfaceFormat::OpenGLES);
+ surfaceFormat.setRedBufferSize(8);
+ surfaceFormat.setBlueBufferSize(8);
+ surfaceFormat.setGreenBufferSize(8);
#endif
if (antialias)
surfaceFormat.setSamples(8);
+ else
+ surfaceFormat.setSamples(0);
return surfaceFormat;
}
diff --git a/src/datavisualization/utils/scatterobjectbufferhelper.cpp b/src/datavisualization/utils/scatterobjectbufferhelper.cpp
index d68b9df4..44c84ae0 100644
--- a/src/datavisualization/utils/scatterobjectbufferhelper.cpp
+++ b/src/datavisualization/utils/scatterobjectbufferhelper.cpp
@@ -20,12 +20,14 @@
#include "objecthelper_p.h"
#include <QtGui/QVector2D>
#include <QtGui/QMatrix4x4>
+#include <QtCore/qmath.h>
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
const GLfloat itemScaler = 3.0f;
ScatterObjectBufferHelper::ScatterObjectBufferHelper()
+ : m_scaleY(0.0f)
{
m_indicesType = GL_UNSIGNED_INT;
}
@@ -36,34 +38,40 @@ ScatterObjectBufferHelper::~ScatterObjectBufferHelper()
void ScatterObjectBufferHelper::fullLoad(ScatterSeriesRenderCache *cache, qreal dotScale)
{
- initializeOpenGLFunctions();
-
m_meshDataLoaded = false;
m_indexCount = 0;
ObjectHelper *dotObj = cache->object();
- ScatterRenderItemArray &renderArray = cache->renderArray();
+ const ScatterRenderItemArray &renderArray = cache->renderArray();
const uint renderArraySize = renderArray.size();
+
if (renderArraySize == 0)
return; // No use to go forward
- uint itemCount = renderArraySize;
+
+ uint itemCount = 0;
QQuaternion seriesRotation(cache->meshRotation());
+
if (m_meshDataLoaded) {
// Delete old data
glDeleteBuffers(1, &m_vertexbuffer);
glDeleteBuffers(1, &m_uvbuffer);
glDeleteBuffers(1, &m_normalbuffer);
glDeleteBuffers(1, &m_elementbuffer);
+ m_vertexbuffer = 0;
+ m_uvbuffer = 0;
+ m_normalbuffer = 0;
+ m_elementbuffer = 0;
}
+
// Index vertices
const QVector<unsigned short> indices = dotObj->indices();
const QVector<QVector3D> indexed_vertices = dotObj->indexedvertices();
const QVector<QVector2D> indexed_uvs = dotObj->indexedUVs();
const QVector<QVector3D> indexed_normals = dotObj->indexedNormals();
- int indicesCount = indices.count();
- int verticeCount = indexed_vertices.count();
- int uvsCount = indexed_uvs.count();
- int normalsCount = indexed_normals.count();
+ const int indicesCount = indices.count();
+ const int verticeCount = indexed_vertices.count();
+ const int uvsCount = indexed_uvs.count();
+ const int normalsCount = indexed_normals.count();
float itemSize = cache->itemSize() / itemScaler;
if (itemSize == 0.0f)
@@ -89,47 +97,58 @@ void ScatterObjectBufferHelper::fullLoad(ScatterSeriesRenderCache *cache, qreal
buffered_indices.resize(indicesCount * renderArraySize);
buffered_vertices.resize(verticeCount * renderArraySize);
- buffered_uvs.resize(uvsCount * renderArraySize);
buffered_normals.resize(normalsCount * renderArraySize);
- uint pos = 0;
+ buffered_uvs.resize(uvsCount * renderArraySize);
+
+ if (cache->colorStyle() == Q3DTheme::ColorStyleRangeGradient)
+ createRangeGradientUVs(cache, buffered_uvs);
+ else if (cache->colorStyle() == Q3DTheme::ColorStyleObjectGradient)
+ createObjectGradientUVs(cache, buffered_uvs, indexed_vertices);
+
+ QVector2D dummyUV(0.0f, 0.0f);
+
+ cache->bufferIndices().resize(renderArraySize);
for (uint i = 0; i < renderArraySize; i++) {
- ScatterRenderItem &item = renderArray[i];
- if (!item.isVisible()) {
- itemCount--;
+ const ScatterRenderItem &item = renderArray.at(i);
+ if (!item.isVisible())
continue;
- }
+ else
+ cache->bufferIndices()[i] = itemCount;
- int offset = pos * verticeCount;
+ int offset = itemCount * verticeCount;
if (item.rotation().isIdentity()) {
- for (int j = 0; j < verticeCount; j++)
+ for (int j = 0; j < verticeCount; j++) {
buffered_vertices[j + offset] = scaled_vertices[j] + item.translation();
+ buffered_normals[j + offset] = indexed_normals[j];
+ }
} else {
QMatrix4x4 matrix;
- matrix.rotate(seriesRotation * item.rotation());
- modelMatrix = matrix.transposed();
- modelMatrix.scale(modelScaler);
+ QQuaternion totalRotation = seriesRotation * item.rotation();
+ matrix.rotate(totalRotation);
+ matrix.scale(modelScaler);
+ QMatrix4x4 itModelMatrix = matrix.inverted();
+ modelMatrix = matrix.transposed(); // Because of row-column major difference
- for (int j = 0; j < verticeCount; j++)
+ for (int j = 0; j < verticeCount; j++) {
buffered_vertices[j + offset] = indexed_vertices[j] * modelMatrix
+ item.translation();
+ buffered_normals[j + offset] = indexed_normals[j] * itModelMatrix;
+ }
}
- offset = pos * normalsCount;
- for (int j = 0; j < normalsCount; j++)
- buffered_normals[j + offset] = indexed_normals[j];
-
- offset = pos * uvsCount;
- for (int j = 0; j < uvsCount; j++)
- buffered_uvs[j + offset] = indexed_uvs[j];
+ if (cache->colorStyle() == Q3DTheme::ColorStyleUniform) {
+ offset = itemCount * uvsCount;
+ for (int j = 0; j < uvsCount; j++)
+ buffered_uvs[j + offset] = dummyUV;
+ }
- int offsetVertice = i * verticeCount;
- offset = pos * indicesCount;
- for (int j = 0; j < indicesCount; j++) {
+ int offsetVertice = itemCount * verticeCount;
+ offset = itemCount * indicesCount;
+ for (int j = 0; j < indicesCount; j++)
buffered_indices[j + offset] = GLuint(indices[j] + offsetVertice);
- }
- pos++;
+ itemCount++;
}
m_indexCount = indicesCount * itemCount;
@@ -164,15 +183,128 @@ void ScatterObjectBufferHelper::fullLoad(ScatterSeriesRenderCache *cache, qreal
}
}
-void ScatterObjectBufferHelper::update(ScatterSeriesRenderCache *cache, qreal dotScale)
+void ScatterObjectBufferHelper::updateUVs(ScatterSeriesRenderCache *cache)
+{
+ ObjectHelper *dotObj = cache->object();
+ const int uvsCount = dotObj->indexedUVs().count();
+ const ScatterRenderItemArray &renderArray = cache->renderArray();
+ const bool updateAll = (cache->updateIndices().size() == 0);
+ const int updateSize = updateAll ? renderArray.size() : cache->updateIndices().size();
+
+ if (!updateSize)
+ return;
+
+ QVector<QVector2D> buffered_uvs;
+ buffered_uvs.resize(uvsCount * updateSize);
+
+ uint itemCount = 0;
+ if (cache->colorStyle() == Q3DTheme::ColorStyleRangeGradient) {
+ itemCount = createRangeGradientUVs(cache, buffered_uvs);
+ } else if (cache->colorStyle() == Q3DTheme::ColorStyleObjectGradient) {
+ const QVector<QVector3D> indexed_vertices = dotObj->indexedvertices();
+ itemCount = createObjectGradientUVs(cache, buffered_uvs, indexed_vertices);
+ }
+
+ glBindBuffer(GL_ARRAY_BUFFER, m_uvbuffer);
+ int itemSize = uvsCount * sizeof(QVector2D);
+ if (cache->updateIndices().size()) {
+ int pos = 0;
+ for (int i = 0; i < updateSize; i++) {
+ int index = cache->updateIndices().at(i);
+ if (renderArray.at(index).isVisible()) {
+ int dataPos = cache->bufferIndices().at(index);
+ glBufferSubData(GL_ARRAY_BUFFER, itemSize * dataPos, itemSize,
+ &buffered_uvs.at(uvsCount * pos++));
+ }
+ }
+ } else {
+ glBufferData(GL_ARRAY_BUFFER, itemSize * itemCount, &buffered_uvs.at(0), GL_STATIC_DRAW);
+ }
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+}
+
+uint ScatterObjectBufferHelper::createRangeGradientUVs(ScatterSeriesRenderCache *cache,
+ QVector<QVector2D> &buffered_uvs)
{
- initializeOpenGLFunctions();
+ ObjectHelper *dotObj = cache->object();
+ const int uvsCount = dotObj->indexedUVs().count();
+ const ScatterRenderItemArray &renderArray = cache->renderArray();
+ const bool updateAll = (cache->updateIndices().size() == 0);
+ const int updateSize = updateAll ? renderArray.size() : cache->updateIndices().size();
+ const float yAdjustment = 0.1f;
+ const float flippedYAdjustment = 0.9f;
+
+ QVector2D uv;
+ uv.setX(0.0f);
+ uint pos = 0;
+ for (int i = 0; i < updateSize; i++) {
+ int index = updateAll ? i : cache->updateIndices().at(i);
+ const ScatterRenderItem &item = renderArray.at(index);
+ if (!item.isVisible())
+ continue;
+
+ float y = ((item.translation().y() + m_scaleY) * 0.5f) / m_scaleY;
+ // Avoid values near gradient texel boundary, as this causes artifacts
+ // with some graphics cards.
+ const float floorY = float(qFloor(y * gradientTextureHeight));
+ const float diff = (y * gradientTextureHeight) - floorY;
+ if (diff < yAdjustment)
+ y += yAdjustment / gradientTextureHeight;
+ else if (diff > flippedYAdjustment)
+ y -= yAdjustment / gradientTextureHeight;
+ uv.setY(y);
+
+ int offset = pos * uvsCount;
+ for (int j = 0; j < uvsCount; j++)
+ buffered_uvs[j + offset] = uv;
+
+ pos++;
+ }
+
+ return pos;
+}
+
+uint ScatterObjectBufferHelper::createObjectGradientUVs(ScatterSeriesRenderCache *cache,
+ QVector<QVector2D> &buffered_uvs,
+ const QVector<QVector3D> &indexed_vertices)
+{
ObjectHelper *dotObj = cache->object();
- ScatterRenderItemArray &renderArray = cache->renderArray();
- const int renderArraySize = renderArray.size();
+ const int uvsCount = dotObj->indexedUVs().count();
+ const ScatterRenderItemArray &renderArray = cache->renderArray();
+ const uint renderArraySize = renderArray.size();
+
+ QVector2D uv;
+ uv.setX(0.0f);
+ uint pos = 0;
+ for (uint i = 0; i < renderArraySize; i++) {
+ const ScatterRenderItem &item = renderArray.at(i);
+ if (!item.isVisible())
+ continue;
+
+ int offset = pos * uvsCount;
+ for (int j = 0; j < uvsCount; j++) {
+ uv.setY((indexed_vertices.at(j).y() + 1.0f) / 2.0f);
+ buffered_uvs[j + offset] = uv;
+ }
+
+ pos++;
+ }
+
+ return pos;
+}
+
+void ScatterObjectBufferHelper::update(ScatterSeriesRenderCache *cache, qreal dotScale)
+{
+ ObjectHelper *dotObj = cache->object();
+ const ScatterRenderItemArray &renderArray = cache->renderArray();
+ const bool updateAll = (cache->updateIndices().size() == 0);
+ const int updateSize = updateAll ? renderArray.size() : cache->updateIndices().size();
QQuaternion seriesRotation(cache->meshRotation());
+ if (!updateSize)
+ return;
+
// Index vertices
const QVector<QVector3D> indexed_vertices = dotObj->indexedvertices();
int verticeCount = indexed_vertices.count();
@@ -195,14 +327,16 @@ void ScatterObjectBufferHelper::update(ScatterSeriesRenderCache *cache, qreal do
scaled_vertices[i] = indexed_vertices[i] * modelMatrix;
QVector<QVector3D> buffered_vertices;
+ buffered_vertices.resize(verticeCount * updateSize);
- buffered_vertices.resize(verticeCount * renderArraySize);
- for (int i = 0; i < renderArraySize; i++) {
- ScatterRenderItem &item = renderArray[i];
+ int itemCount = 0;
+ for (int i = 0; i < updateSize; i++) {
+ int index = updateAll ? i : cache->updateIndices().at(i);
+ const ScatterRenderItem &item = renderArray.at(index);
if (!item.isVisible())
continue;
- const int offset = i * verticeCount;
+ const int offset = itemCount * verticeCount;
if (item.rotation().isIdentity()) {
for (int j = 0; j < verticeCount; j++)
buffered_vertices[j + offset] = scaled_vertices[j] + item.translation();
@@ -216,15 +350,28 @@ void ScatterObjectBufferHelper::update(ScatterSeriesRenderCache *cache, qreal do
buffered_vertices[j + offset] = indexed_vertices[j] * modelMatrix
+ item.translation();
}
+ itemCount++;
}
glBindBuffer(GL_ARRAY_BUFFER, m_vertexbuffer);
- glBufferData(GL_ARRAY_BUFFER, buffered_vertices.size() * sizeof(QVector3D),
- &buffered_vertices.at(0),
- GL_DYNAMIC_DRAW);
-
+ int sizeOfItem = verticeCount * sizeof(QVector3D);
+ if (updateAll) {
+ if (itemCount) {
+ glBufferData(GL_ARRAY_BUFFER, itemCount * sizeOfItem,
+ &buffered_vertices.at(0), GL_STATIC_DRAW);
+ }
+ } else {
+ itemCount = 0;
+ for (int i = 0; i < updateSize; i++) {
+ int index = updateAll ? i : cache->updateIndices().at(i);
+ if (renderArray.at(index).isVisible()) {
+ glBufferSubData(GL_ARRAY_BUFFER, cache->bufferIndices().at(index) * sizeOfItem,
+ sizeOfItem, &buffered_vertices.at(itemCount * verticeCount));
+ itemCount++;
+ }
+ }
+ }
glBindBuffer(GL_ARRAY_BUFFER, 0);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
m_meshDataLoaded = true;
}
diff --git a/src/datavisualization/utils/scatterobjectbufferhelper_p.h b/src/datavisualization/utils/scatterobjectbufferhelper_p.h
index 952c3d7d..08a42900 100644
--- a/src/datavisualization/utils/scatterobjectbufferhelper_p.h
+++ b/src/datavisualization/utils/scatterobjectbufferhelper_p.h
@@ -39,10 +39,21 @@ class ScatterObjectBufferHelper : public AbstractObjectHelper
{
public:
ScatterObjectBufferHelper();
- ~ScatterObjectBufferHelper();
+ virtual ~ScatterObjectBufferHelper();
void fullLoad(ScatterSeriesRenderCache *cache, qreal dotScale);
void update(ScatterSeriesRenderCache *cache, qreal dotScale);
+ void updateUVs(ScatterSeriesRenderCache *cache);
+ void setScaleY(float scale) { m_scaleY = scale; }
+
+private:
+ uint createRangeGradientUVs(ScatterSeriesRenderCache *cache,
+ QVector<QVector2D> &buffered_uvs);
+ uint createObjectGradientUVs(ScatterSeriesRenderCache *cache,
+ QVector<QVector2D> &buffered_uvs,
+ const QVector<QVector3D> &indexed_vertices);
+
+ float m_scaleY;
};
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/utils/scatterpointbufferhelper.cpp b/src/datavisualization/utils/scatterpointbufferhelper.cpp
index b14d84ad..22e76f92 100644
--- a/src/datavisualization/utils/scatterpointbufferhelper.cpp
+++ b/src/datavisualization/utils/scatterpointbufferhelper.cpp
@@ -17,6 +17,7 @@
****************************************************************************/
#include "scatterpointbufferhelper_p.h"
+#include <QtGui/QVector2D>
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
@@ -24,8 +25,7 @@ const QVector3D hiddenPos(-1000.0f, -1000.0f, -1000.0f);
ScatterPointBufferHelper::ScatterPointBufferHelper()
: m_pointbuffer(0),
- m_oldRemoveIndex(0),
- m_oldRemove(false)
+ m_oldRemoveIndex(-1)
{
m_indicesType = GL_UNSIGNED_INT;
}
@@ -47,7 +47,8 @@ void ScatterPointBufferHelper::pushPoint(uint pointIndex)
{
glBindBuffer(GL_ARRAY_BUFFER, m_pointbuffer);
- if (m_oldRemove && m_oldRemoveIndex < pointIndex) {
+ // Pop the previous point if it is still pushed
+ if (m_oldRemoveIndex >= 0) {
glBufferSubData(GL_ARRAY_BUFFER, m_oldRemoveIndex * sizeof(QVector3D),
sizeof(QVector3D), &m_bufferedPoints.at(m_oldRemoveIndex));
}
@@ -59,26 +60,22 @@ void ScatterPointBufferHelper::pushPoint(uint pointIndex)
glBindBuffer(GL_ARRAY_BUFFER, 0);
m_oldRemoveIndex = pointIndex;
- m_oldRemove = true;
}
void ScatterPointBufferHelper::popPoint()
{
- if (m_oldRemove) {
+ if (m_oldRemoveIndex >= 0) {
glBindBuffer(GL_ARRAY_BUFFER, m_pointbuffer);
glBufferSubData(GL_ARRAY_BUFFER, m_oldRemoveIndex * sizeof(QVector3D),
sizeof(QVector3D), &m_bufferedPoints.at(m_oldRemoveIndex));
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
- m_oldRemoveIndex = 0;
- m_oldRemove = false;
+ m_oldRemoveIndex = -1;
}
void ScatterPointBufferHelper::load(ScatterSeriesRenderCache *cache)
{
- initializeOpenGLFunctions();
-
ScatterRenderItemArray &renderArray = cache->renderArray();
const int renderArraySize = renderArray.size();
m_indexCount = 0;
@@ -86,13 +83,16 @@ void ScatterPointBufferHelper::load(ScatterSeriesRenderCache *cache)
if (m_meshDataLoaded) {
// Delete old data
glDeleteBuffers(1, &m_pointbuffer);
+ glDeleteBuffers(1, &m_uvbuffer);
m_bufferedPoints.clear();
+ m_pointbuffer = 0;
+ m_uvbuffer = 0;
}
bool itemsVisible = false;
m_bufferedPoints.resize(renderArraySize);
for (int i = 0; i < renderArraySize; i++) {
- ScatterRenderItem &item = renderArray[i];
+ const ScatterRenderItem &item = renderArray.at(i);
if (!item.isVisible()) {
m_bufferedPoints[i] = hiddenPos;
} else {
@@ -101,19 +101,108 @@ void ScatterPointBufferHelper::load(ScatterSeriesRenderCache *cache)
}
}
+ QVector<QVector2D> buffered_uvs;
if (itemsVisible)
m_indexCount = renderArraySize;
if (m_indexCount > 0) {
+ if (cache->colorStyle() == Q3DTheme::ColorStyleRangeGradient)
+ createRangeGradientUVs(cache, buffered_uvs);
+
glGenBuffers(1, &m_pointbuffer);
glBindBuffer(GL_ARRAY_BUFFER, m_pointbuffer);
glBufferData(GL_ARRAY_BUFFER, m_bufferedPoints.size() * sizeof(QVector3D),
&m_bufferedPoints.at(0),
GL_DYNAMIC_DRAW);
+
+ if (buffered_uvs.size()) {
+ glGenBuffers(1, &m_uvbuffer);
+ glBindBuffer(GL_ARRAY_BUFFER, m_uvbuffer);
+ glBufferData(GL_ARRAY_BUFFER, buffered_uvs.size() * sizeof(QVector2D),
+ &buffered_uvs.at(0), GL_STATIC_DRAW);
+ }
+
glBindBuffer(GL_ARRAY_BUFFER, 0);
m_meshDataLoaded = true;
}
}
+void ScatterPointBufferHelper::update(ScatterSeriesRenderCache *cache)
+{
+ // It may be that the buffer hasn't yet been initialized, in case the entire series was
+ // hidden items. No need to update in that case.
+ if (m_indexCount > 0) {
+ const ScatterRenderItemArray &renderArray = cache->renderArray();
+ const int updateSize = cache->updateIndices().size();
+
+ glBindBuffer(GL_ARRAY_BUFFER, m_pointbuffer);
+ for (int i = 0; i < updateSize; i++) {
+ int index = cache->updateIndices().at(i);
+ const ScatterRenderItem &item = renderArray.at(index);
+ if (!item.isVisible())
+ m_bufferedPoints[index] = hiddenPos;
+ else
+ m_bufferedPoints[index] = item.translation();
+
+ if (index != m_oldRemoveIndex) {
+ glBufferSubData(GL_ARRAY_BUFFER, index * sizeof(QVector3D),
+ sizeof(QVector3D), &m_bufferedPoints.at(index));
+ }
+ }
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ }
+}
+
+void ScatterPointBufferHelper::updateUVs(ScatterSeriesRenderCache *cache)
+{
+ // It may be that the buffer hasn't yet been initialized, in case the entire series was
+ // hidden items. No need to update in that case.
+ if (m_indexCount > 0) {
+ QVector<QVector2D> buffered_uvs;
+ createRangeGradientUVs(cache, buffered_uvs);
+
+ if (buffered_uvs.size()) {
+ if (!m_uvbuffer)
+ glGenBuffers(1, &m_uvbuffer);
+
+ int updateSize = cache->updateIndices().size();
+ glBindBuffer(GL_ARRAY_BUFFER, m_uvbuffer);
+ if (updateSize) {
+ for (int i = 0; i < updateSize; i++) {
+ int index = cache->updateIndices().at(i);
+ glBufferSubData(GL_ARRAY_BUFFER, index * sizeof(QVector2D),
+ sizeof(QVector2D), &buffered_uvs.at(i));
+
+ }
+ } else {
+ glBufferData(GL_ARRAY_BUFFER, buffered_uvs.size() * sizeof(QVector2D),
+ &buffered_uvs.at(0), GL_STATIC_DRAW);
+ }
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ }
+ }
+}
+
+void ScatterPointBufferHelper::createRangeGradientUVs(ScatterSeriesRenderCache *cache,
+ QVector<QVector2D> &buffered_uvs)
+{
+ const ScatterRenderItemArray &renderArray = cache->renderArray();
+ const bool updateAll = (cache->updateIndices().size() == 0);
+ const int updateSize = updateAll ? renderArray.size() : cache->updateIndices().size();
+ buffered_uvs.resize(updateSize);
+
+ QVector2D uv;
+ uv.setX(0.0f);
+ for (int i = 0; i < updateSize; i++) {
+ int index = updateAll ? i : cache->updateIndices().at(i);
+ const ScatterRenderItem &item = renderArray.at(index);
+
+ float y = ((item.translation().y() + m_scaleY) * 0.5f) / m_scaleY;
+ uv.setY(y);
+ buffered_uvs[i] = uv;
+ }
+}
+
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/utils/scatterpointbufferhelper_p.h b/src/datavisualization/utils/scatterpointbufferhelper_p.h
index b3adcfa8..8b34542d 100644
--- a/src/datavisualization/utils/scatterpointbufferhelper_p.h
+++ b/src/datavisualization/utils/scatterpointbufferhelper_p.h
@@ -39,21 +39,28 @@ class ScatterPointBufferHelper : public AbstractObjectHelper
{
public:
ScatterPointBufferHelper();
- ~ScatterPointBufferHelper();
+ virtual ~ScatterPointBufferHelper();
GLuint pointBuf();
void pushPoint(uint pointIndex);
void popPoint();
void load(ScatterSeriesRenderCache *cache);
+ void update(ScatterSeriesRenderCache *cache);
+ void setScaleY(float scale) { m_scaleY = scale; }
+ void updateUVs(ScatterSeriesRenderCache *cache);
public:
GLuint m_pointbuffer;
private:
+ void createRangeGradientUVs(ScatterSeriesRenderCache *cache,
+ QVector<QVector2D> &buffered_uvs);
+
+private:
QVector<QVector3D> m_bufferedPoints;
- uint m_oldRemoveIndex;
- bool m_oldRemove;
+ int m_oldRemoveIndex;
+ float m_scaleY;
};
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/utils/shaderhelper.cpp b/src/datavisualization/utils/shaderhelper.cpp
index 7fb237c6..bbd7fe0e 100644
--- a/src/datavisualization/utils/shaderhelper.cpp
+++ b/src/datavisualization/utils/shaderhelper.cpp
@@ -40,7 +40,37 @@ ShaderHelper::ShaderHelper(QObject *parent,
m_vertexShaderFile(vertexShader),
m_fragmentShaderFile(fragmentShader),
m_textureFile(texture),
- m_depthTextureFile(depthTexture)
+ m_depthTextureFile(depthTexture),
+ m_positionAttr(0),
+ m_uvAttr(0),
+ m_normalAttr(0),
+ m_colorUniform(0),
+ m_viewMatrixUniform(0),
+ m_modelMatrixUniform(0),
+ m_invTransModelMatrixUniform(0),
+ m_depthMatrixUniform(0),
+ m_mvpMatrixUniform(0),
+ m_lightPositionUniform(0),
+ m_lightStrengthUniform(0),
+ m_ambientStrengthUniform(0),
+ m_shadowQualityUniform(0),
+ m_textureUniform(0),
+ m_shadowUniform(0),
+ m_gradientMinUniform(0),
+ m_gradientHeightUniform(0),
+ m_lightColorUniform(0),
+ m_volumeSliceIndicesUniform(0),
+ m_colorIndexUniform(0),
+ m_cameraPositionRelativeToModelUniform(0),
+ m_color8BitUniform(0),
+ m_textureDimensionsUniform(0),
+ m_sampleCountUniform(0),
+ m_alphaMultiplierUniform(0),
+ m_preserveOpacityUniform(0),
+ m_minBoundsUniform(0),
+ m_maxBoundsUniform(0),
+ m_sliceFrameWidthUniform(0),
+ m_initialized(false)
{
}
@@ -93,6 +123,17 @@ void ShaderHelper::initialize()
m_gradientMinUniform = m_program->uniformLocation("gradMin");
m_gradientHeightUniform = m_program->uniformLocation("gradHeight");
m_lightColorUniform = m_program->uniformLocation("lightColor");
+ m_volumeSliceIndicesUniform = m_program->uniformLocation("volumeSliceIndices");
+ m_colorIndexUniform = m_program->uniformLocation("colorIndex");
+ m_cameraPositionRelativeToModelUniform = m_program->uniformLocation("cameraPositionRelativeToModel");
+ m_color8BitUniform = m_program->uniformLocation("color8Bit");
+ m_textureDimensionsUniform = m_program->uniformLocation("textureDimensions");
+ m_sampleCountUniform = m_program->uniformLocation("sampleCount");
+ m_alphaMultiplierUniform = m_program->uniformLocation("alphaMultiplier");
+ m_preserveOpacityUniform = m_program->uniformLocation("preserveOpacity");
+ m_minBoundsUniform = m_program->uniformLocation("minBounds");
+ m_maxBoundsUniform = m_program->uniformLocation("maxBounds");
+ m_sliceFrameWidthUniform = m_program->uniformLocation("sliceFrameWidth");
m_initialized = true;
}
@@ -125,151 +166,239 @@ void ShaderHelper::release()
m_program->release();
}
-void ShaderHelper::setUniformValue(GLuint uniform, const QVector3D &value)
+void ShaderHelper::setUniformValue(GLint uniform, const QVector2D &value)
{
m_program->setUniformValue(uniform, value);
}
-void ShaderHelper::setUniformValue(GLuint uniform, const QVector4D &value)
+void ShaderHelper::setUniformValue(GLint uniform, const QVector3D &value)
{
m_program->setUniformValue(uniform, value);
}
-void ShaderHelper::setUniformValue(GLuint uniform, const QMatrix4x4 &value)
+void ShaderHelper::setUniformValue(GLint uniform, const QVector4D &value)
{
m_program->setUniformValue(uniform, value);
}
-void ShaderHelper::setUniformValue(GLuint uniform, GLfloat value)
+void ShaderHelper::setUniformValue(GLint uniform, const QMatrix4x4 &value)
{
m_program->setUniformValue(uniform, value);
}
-void ShaderHelper::setUniformValue(GLuint uniform, GLint value)
+void ShaderHelper::setUniformValue(GLint uniform, GLfloat value)
{
m_program->setUniformValue(uniform, value);
}
-GLuint ShaderHelper::MVP()
+void ShaderHelper::setUniformValue(GLint uniform, GLint value)
+{
+ m_program->setUniformValue(uniform, value);
+}
+
+void ShaderHelper::setUniformValueArray(GLint uniform, const QVector4D *values, int count)
+{
+ m_program->setUniformValueArray(uniform, values, count);
+}
+
+GLint ShaderHelper::MVP()
{
if (!m_initialized)
qFatal("Shader not initialized");
return m_mvpMatrixUniform;
}
-GLuint ShaderHelper::view()
+GLint ShaderHelper::view()
{
if (!m_initialized)
qFatal("Shader not initialized");
return m_viewMatrixUniform;
}
-GLuint ShaderHelper::model()
+GLint ShaderHelper::model()
{
if (!m_initialized)
qFatal("Shader not initialized");
return m_modelMatrixUniform;
}
-GLuint ShaderHelper::nModel()
+GLint ShaderHelper::nModel()
{
if (!m_initialized)
qFatal("Shader not initialized");
return m_invTransModelMatrixUniform;
}
-GLuint ShaderHelper::depth()
+GLint ShaderHelper::depth()
{
if (!m_initialized)
qFatal("Shader not initialized");
return m_depthMatrixUniform;
}
-GLuint ShaderHelper::lightP()
+GLint ShaderHelper::lightP()
{
if (!m_initialized)
qFatal("Shader not initialized");
return m_lightPositionUniform;
}
-GLuint ShaderHelper::lightS()
+GLint ShaderHelper::lightS()
{
if (!m_initialized)
qFatal("Shader not initialized");
return m_lightStrengthUniform;
}
-GLuint ShaderHelper::ambientS()
+GLint ShaderHelper::ambientS()
{
if (!m_initialized)
qFatal("Shader not initialized");
return m_ambientStrengthUniform;
}
-GLuint ShaderHelper::shadowQ()
+GLint ShaderHelper::shadowQ()
{
if (!m_initialized)
qFatal("Shader not initialized");
return m_shadowQualityUniform;
}
-GLuint ShaderHelper::color()
+GLint ShaderHelper::color()
{
if (!m_initialized)
qFatal("Shader not initialized");
return m_colorUniform;
}
-GLuint ShaderHelper::texture()
+GLint ShaderHelper::texture()
{
if (!m_initialized)
qFatal("Shader not initialized");
return m_textureUniform;
}
-GLuint ShaderHelper::shadow()
+GLint ShaderHelper::shadow()
{
if (!m_initialized)
qFatal("Shader not initialized");
return m_shadowUniform;
}
-GLuint ShaderHelper::gradientMin()
+GLint ShaderHelper::gradientMin()
{
if (!m_initialized)
qFatal("Shader not initialized");
return m_gradientMinUniform;
}
-GLuint ShaderHelper::gradientHeight()
+GLint ShaderHelper::gradientHeight()
{
if (!m_initialized)
qFatal("Shader not initialized");
return m_gradientHeightUniform;
}
-GLuint ShaderHelper::lightColor()
+GLint ShaderHelper::lightColor()
{
if (!m_initialized)
qFatal("Shader not initialized");
return m_lightColorUniform;
}
-GLuint ShaderHelper::posAtt()
+GLint ShaderHelper::volumeSliceIndices()
+{
+ if (!m_initialized)
+ qFatal("Shader not initialized");
+ return m_volumeSliceIndicesUniform;
+}
+
+GLint ShaderHelper::colorIndex()
+{
+ if (!m_initialized)
+ qFatal("Shader not initialized");
+ return m_colorIndexUniform;
+}
+
+GLint ShaderHelper::cameraPositionRelativeToModel()
+{
+ if (!m_initialized)
+ qFatal("Shader not initialized");
+ return m_cameraPositionRelativeToModelUniform;
+}
+
+GLint ShaderHelper::color8Bit()
+{
+ if (!m_initialized)
+ qFatal("Shader not initialized");
+ return m_color8BitUniform;
+}
+
+GLint ShaderHelper::textureDimensions()
+{
+ if (!m_initialized)
+ qFatal("Shader not initialized");
+ return m_textureDimensionsUniform;
+}
+
+GLint ShaderHelper::sampleCount()
+{
+ if (!m_initialized)
+ qFatal("Shader not initialized");
+ return m_sampleCountUniform;
+}
+
+GLint ShaderHelper::alphaMultiplier()
+{
+ if (!m_initialized)
+ qFatal("Shader not initialized");
+ return m_alphaMultiplierUniform;
+}
+
+GLint ShaderHelper::preserveOpacity()
+{
+ if (!m_initialized)
+ qFatal("Shader not initialized");
+ return m_preserveOpacityUniform;
+}
+
+GLint ShaderHelper::maxBounds()
+{
+ if (!m_initialized)
+ qFatal("Shader not initialized");
+ return m_maxBoundsUniform;
+}
+
+GLint ShaderHelper::minBounds()
+{
+ if (!m_initialized)
+ qFatal("Shader not initialized");
+ return m_minBoundsUniform;
+}
+
+GLint ShaderHelper::sliceFrameWidth()
+{
+
+ if (!m_initialized)
+ qFatal("Shader not initialized");
+ return m_sliceFrameWidthUniform;
+}
+
+GLint ShaderHelper::posAtt()
{
if (!m_initialized)
qFatal("Shader not initialized");
return m_positionAttr;
}
-GLuint ShaderHelper::uvAtt()
+GLint ShaderHelper::uvAtt()
{
if (!m_initialized)
qFatal("Shader not initialized");
return m_uvAttr;
}
-GLuint ShaderHelper::normalAtt()
+GLint ShaderHelper::normalAtt()
{
if (!m_initialized)
qFatal("Shader not initialized");
diff --git a/src/datavisualization/utils/shaderhelper_p.h b/src/datavisualization/utils/shaderhelper_p.h
index fdef0dff..812cba18 100644
--- a/src/datavisualization/utils/shaderhelper_p.h
+++ b/src/datavisualization/utils/shaderhelper_p.h
@@ -52,31 +52,44 @@ class ShaderHelper
bool testCompile();
void bind();
void release();
- void setUniformValue(GLuint uniform, const QVector3D &value);
- void setUniformValue(GLuint uniform, const QVector4D &value);
- void setUniformValue(GLuint uniform, const QMatrix4x4 &value);
- void setUniformValue(GLuint uniform, GLfloat value);
- void setUniformValue(GLuint uniform, GLint value);
-
- GLuint MVP();
- GLuint view();
- GLuint model();
- GLuint nModel();
- GLuint depth();
- GLuint lightP();
- GLuint lightS();
- GLuint ambientS();
- GLuint shadowQ();
- GLuint color();
- GLuint texture();
- GLuint shadow();
- GLuint gradientMin();
- GLuint gradientHeight();
- GLuint lightColor();
-
- GLuint posAtt();
- GLuint uvAtt();
- GLuint normalAtt();
+ void setUniformValue(GLint uniform, const QVector2D &value);
+ void setUniformValue(GLint uniform, const QVector3D &value);
+ void setUniformValue(GLint uniform, const QVector4D &value);
+ void setUniformValue(GLint uniform, const QMatrix4x4 &value);
+ void setUniformValue(GLint uniform, GLfloat value);
+ void setUniformValue(GLint uniform, GLint value);
+ void setUniformValueArray(GLint uniform, const QVector4D *values, int count);
+
+ GLint MVP();
+ GLint view();
+ GLint model();
+ GLint nModel();
+ GLint depth();
+ GLint lightP();
+ GLint lightS();
+ GLint ambientS();
+ GLint shadowQ();
+ GLint color();
+ GLint texture();
+ GLint shadow();
+ GLint gradientMin();
+ GLint gradientHeight();
+ GLint lightColor();
+ GLint volumeSliceIndices();
+ GLint colorIndex();
+ GLint cameraPositionRelativeToModel();
+ GLint color8Bit();
+ GLint textureDimensions();
+ GLint sampleCount();
+ GLint alphaMultiplier();
+ GLint preserveOpacity();
+ GLint maxBounds();
+ GLint minBounds();
+ GLint sliceFrameWidth();
+
+ GLint posAtt();
+ GLint uvAtt();
+ GLint normalAtt();
private:
QObject *m_caller;
@@ -88,25 +101,36 @@ class ShaderHelper
QString m_textureFile;
QString m_depthTextureFile;
- GLuint m_positionAttr;
- GLuint m_uvAttr;
- GLuint m_normalAttr;
-
- GLuint m_colorUniform;
- GLuint m_viewMatrixUniform;
- GLuint m_modelMatrixUniform;
- GLuint m_invTransModelMatrixUniform;
- GLuint m_depthMatrixUniform;
- GLuint m_mvpMatrixUniform;
- GLuint m_lightPositionUniform;
- GLuint m_lightStrengthUniform;
- GLuint m_ambientStrengthUniform;
- GLuint m_shadowQualityUniform;
- GLuint m_textureUniform;
- GLuint m_shadowUniform;
- GLuint m_gradientMinUniform;
- GLuint m_gradientHeightUniform;
- GLuint m_lightColorUniform;
+ GLint m_positionAttr;
+ GLint m_uvAttr;
+ GLint m_normalAttr;
+
+ GLint m_colorUniform;
+ GLint m_viewMatrixUniform;
+ GLint m_modelMatrixUniform;
+ GLint m_invTransModelMatrixUniform;
+ GLint m_depthMatrixUniform;
+ GLint m_mvpMatrixUniform;
+ GLint m_lightPositionUniform;
+ GLint m_lightStrengthUniform;
+ GLint m_ambientStrengthUniform;
+ GLint m_shadowQualityUniform;
+ GLint m_textureUniform;
+ GLint m_shadowUniform;
+ GLint m_gradientMinUniform;
+ GLint m_gradientHeightUniform;
+ GLint m_lightColorUniform;
+ GLint m_volumeSliceIndicesUniform;
+ GLint m_colorIndexUniform;
+ GLint m_cameraPositionRelativeToModelUniform;
+ GLint m_color8BitUniform;
+ GLint m_textureDimensionsUniform;
+ GLint m_sampleCountUniform;
+ GLint m_alphaMultiplierUniform;
+ GLint m_preserveOpacityUniform;
+ GLint m_minBoundsUniform;
+ GLint m_maxBoundsUniform;
+ GLint m_sliceFrameWidthUniform;
GLboolean m_initialized;
};
diff --git a/src/datavisualization/utils/surfaceobject.cpp b/src/datavisualization/utils/surfaceobject.cpp
index d999ba90..b93030b1 100644
--- a/src/datavisualization/utils/surfaceobject.cpp
+++ b/src/datavisualization/utils/surfaceobject.cpp
@@ -30,26 +30,31 @@ SurfaceObject::SurfaceObject(Surface3DRenderer *renderer)
m_gridIndexCount(0),
m_axisCacheX(renderer->m_axisCacheX),
m_axisCacheY(renderer->m_axisCacheY),
- m_axisCacheZ(renderer->m_axisCacheZ)
-
+ m_axisCacheZ(renderer->m_axisCacheZ),
+ m_renderer(renderer),
+ m_returnTextureBuffer(false),
+ m_dataDimension(0),
+ m_oldDataDimension(-1)
{
m_indicesType = GL_UNSIGNED_INT;
- initializeOpenGLFunctions();
glGenBuffers(1, &m_vertexbuffer);
glGenBuffers(1, &m_normalbuffer);
glGenBuffers(1, &m_uvbuffer);
glGenBuffers(1, &m_elementbuffer);
glGenBuffers(1, &m_gridElementbuffer);
+ glGenBuffers(1, &m_uvTextureBuffer);
}
SurfaceObject::~SurfaceObject()
{
- if (QOpenGLContext::currentContext())
+ if (QOpenGLContext::currentContext()) {
glDeleteBuffers(1, &m_gridElementbuffer);
+ glDeleteBuffers(1, &m_uvTextureBuffer);
+ }
}
void SurfaceObject::setUpSmoothData(const QSurfaceDataArray &dataArray, const QRect &space,
- bool changeGeometry, bool flipXZ)
+ bool changeGeometry, bool polar, bool flipXZ)
{
m_columns = space.width();
m_rows = space.height();
@@ -59,6 +64,12 @@ void SurfaceObject::setUpSmoothData(const QSurfaceDataArray &dataArray, const QR
m_surfaceType = SurfaceSmooth;
+ checkDirections(dataArray);
+ bool indicesDirty = false;
+ if (m_dataDimension != m_oldDataDimension)
+ indicesDirty = true;
+ m_oldDataDimension = m_dataDimension;
+
// Create/populate vertix table
if (changeGeometry)
m_vertices.resize(totalSize);
@@ -68,17 +79,14 @@ void SurfaceObject::setUpSmoothData(const QSurfaceDataArray &dataArray, const QR
uvs.resize(totalSize);
int totalIndex = 0;
- AxisRenderCache &xCache = flipXZ ? m_axisCacheZ : m_axisCacheX;
- AxisRenderCache &zCache = flipXZ ? m_axisCacheX : m_axisCacheZ;
+ // Init min and max to ridiculous values
+ m_minY = 10000000.0;
+ m_maxY = -10000000.0f;
for (int i = 0; i < m_rows; i++) {
const QSurfaceDataRow &p = *dataArray.at(i);
for (int j = 0; j < m_columns; j++) {
- const QSurfaceDataItem &data = p.at(j);
- float normalizedX = xCache.positionAt(data.x());
- float normalizedY = m_axisCacheY.positionAt(data.y());
- float normalizedZ = zCache.positionAt(data.z());
- m_vertices[totalIndex] = QVector3D(normalizedX, normalizedY, normalizedZ);
+ getNormalizedVertex(p.at(j), m_vertices[totalIndex], polar, flipXZ);
if (changeGeometry)
uvs[totalIndex] = QVector2D(GLfloat(j) * uvX, GLfloat(i) * uvY);
totalIndex++;
@@ -95,161 +103,305 @@ void SurfaceObject::setUpSmoothData(const QSurfaceDataArray &dataArray, const QR
// Create normals
int rowLimit = m_rows - 1;
int colLimit = m_columns - 1;
- int rowColLimit = rowLimit * m_columns;
- int totalLimit = totalSize - 1;
if (changeGeometry)
m_normals.resize(totalSize);
totalIndex = 0;
- const bool flipNormal = checkFlipNormal(dataArray);
- for (int row = 0; row < rowColLimit; row += m_columns) {
- for (int j = 0; j < colLimit; j++) {
- m_normals[totalIndex++] = normal(m_vertices.at(row + j),
- m_vertices.at(row + j + 1),
- m_vertices.at(row + m_columns + j),
- flipNormal);
- }
- int p = row + colLimit;
- m_normals[totalIndex++] = normal(m_vertices.at(p),
- m_vertices.at(p + m_columns),
- m_vertices.at(p - 1),
- flipNormal);
- }
- for (int j = rowColLimit; j < totalLimit; j++) {
- m_normals[totalIndex++] = normal(m_vertices.at(j),
- m_vertices.at(j - m_columns),
- m_vertices.at(j + 1),
- flipNormal);
+
+ if ((m_dataDimension == BothAscending) || (m_dataDimension == XDescending)) {
+ for (int row = 0; row < rowLimit; row++)
+ createSmoothNormalBodyLine(totalIndex, row * m_columns);
+ createSmoothNormalUpperLine(totalIndex);
+ } else { // BothDescending || ZDescending
+ createSmoothNormalUpperLine(totalIndex);
+ for (int row = 1; row < m_rows; row++)
+ createSmoothNormalBodyLine(totalIndex, row * m_columns);
}
- m_normals[totalIndex++] = normal(m_vertices.at(totalLimit),
- m_vertices.at(totalLimit - 1),
- m_vertices.at(totalLimit - m_columns),
- flipNormal);
// Create indices table
- if (changeGeometry)
+ if (changeGeometry || indicesDirty)
createSmoothIndices(0, 0, colLimit, rowLimit);
// Create line element indices
if (changeGeometry)
createSmoothGridlineIndices(0, 0, colLimit, rowLimit);
- createBuffers(m_vertices, uvs, m_normals, 0, changeGeometry);
+ createBuffers(m_vertices, uvs, m_normals, 0);
}
-void SurfaceObject::updateSmoothRow(const QSurfaceDataArray &dataArray, int rowIndex)
+void SurfaceObject::createSmoothNormalBodyLine(int &totalIndex, int column)
+{
+ int colLimit = m_columns - 1;
+
+ if (m_dataDimension == BothAscending) {
+ int end = colLimit + column;
+ for (int j = column; j < end; j++) {
+ m_normals[totalIndex++] = normal(m_vertices.at(j),
+ m_vertices.at(j + 1),
+ m_vertices.at(j + m_columns));
+ }
+ m_normals[totalIndex++] = normal(m_vertices.at(end),
+ m_vertices.at(end + m_columns),
+ m_vertices.at(end - 1));
+ } else if (m_dataDimension == XDescending) {
+ m_normals[totalIndex++] = normal(m_vertices.at(column),
+ m_vertices.at(column + m_columns),
+ m_vertices.at(column + 1));
+ int end = column + m_columns;
+ for (int j = column + 1; j < end; j++) {
+ m_normals[totalIndex++] = normal(m_vertices.at(j),
+ m_vertices.at(j - 1),
+ m_vertices.at(j + m_columns));
+ }
+ } else if (m_dataDimension == ZDescending) {
+ int end = colLimit + column;
+ for (int j = column; j < end; j++) {
+ m_normals[totalIndex++] = normal(m_vertices.at(j),
+ m_vertices.at(j + 1),
+ m_vertices.at(j - m_columns));
+ }
+ m_normals[totalIndex++] = normal(m_vertices.at(end),
+ m_vertices.at(end - m_columns),
+ m_vertices.at(end - 1));
+ } else { // BothDescending
+ m_normals[totalIndex++] = normal(m_vertices.at(column),
+ m_vertices.at(column - m_columns),
+ m_vertices.at(column + 1));
+ int end = column + m_columns;
+ for (int j = column + 1; j < end; j++) {
+ m_normals[totalIndex++] = normal(m_vertices.at(j),
+ m_vertices.at(j - 1),
+ m_vertices.at(j - m_columns));
+ }
+ }
+}
+
+void SurfaceObject::createSmoothNormalUpperLine(int &totalIndex)
+{
+ if (m_dataDimension == BothAscending) {
+ int lineEnd = m_rows * m_columns - 1;
+ for (int j = (m_rows - 1) * m_columns; j < lineEnd; j++) {
+ m_normals[totalIndex++] = normal(m_vertices.at(j),
+ m_vertices.at(j - m_columns),
+ m_vertices.at(j + 1));
+ }
+ m_normals[totalIndex++] = normal(m_vertices.at(lineEnd),
+ m_vertices.at(lineEnd - 1),
+ m_vertices.at(lineEnd - m_columns));
+ } else if (m_dataDimension == XDescending) {
+ int lineStart = (m_rows - 1) * m_columns;
+ int lineEnd = m_rows * m_columns;
+ m_normals[totalIndex++] = normal(m_vertices.at(lineStart),
+ m_vertices.at(lineStart + 1),
+ m_vertices.at(lineStart - m_columns));
+ for (int j = lineStart + 1; j < lineEnd; j++) {
+ m_normals[totalIndex++] = normal(m_vertices.at(j),
+ m_vertices.at(j - m_columns),
+ m_vertices.at(j - 1));
+ }
+ } else if (m_dataDimension == ZDescending) {
+ int colLimit = m_columns - 1;
+ for (int j = 0; j < colLimit; j++) {
+ m_normals[totalIndex++] = normal(m_vertices.at(j),
+ m_vertices.at(j + m_columns),
+ m_vertices.at(j + 1));
+ }
+ m_normals[totalIndex++] = normal(m_vertices.at(colLimit),
+ m_vertices.at(colLimit - 1),
+ m_vertices.at(colLimit + m_columns));
+ } else { // BothDescending
+ m_normals[totalIndex++] = normal(m_vertices.at(0),
+ m_vertices.at(1),
+ m_vertices.at(m_columns));
+ for (int j = 1; j < m_columns; j++) {
+ m_normals[totalIndex++] = normal(m_vertices.at(j),
+ m_vertices.at(j + m_columns),
+ m_vertices.at(j - 1));
+ }
+ }
+}
+
+QVector3D SurfaceObject::createSmoothNormalBodyLineItem(int x, int y)
+{
+ int p = y * m_columns + x;
+ if (m_dataDimension == BothAscending) {
+ if (x < m_columns - 1) {
+ return normal(m_vertices.at(p), m_vertices.at(p + 1),
+ m_vertices.at(p + m_columns));
+ } else {
+ return normal(m_vertices.at(p), m_vertices.at(p + m_columns),
+ m_vertices.at(p - 1));
+ }
+ } else if (m_dataDimension == XDescending) {
+ if (x == 0) {
+ return normal(m_vertices.at(p), m_vertices.at(p + m_columns),
+ m_vertices.at(p + 1));
+ } else {
+ return normal(m_vertices.at(p), m_vertices.at(p - 1),
+ m_vertices.at(p + m_columns));
+ }
+ } else if (m_dataDimension == ZDescending) {
+ if (x < m_columns - 1) {
+ return normal(m_vertices.at(p), m_vertices.at(p + 1),
+ m_vertices.at(p - m_columns));
+ } else {
+ return normal(m_vertices.at(p), m_vertices.at(p - m_columns),
+ m_vertices.at(p - 1));
+ }
+ } else { // BothDescending
+ if (x == 0) {
+ return normal(m_vertices.at(p), m_vertices.at(p - m_columns),
+ m_vertices.at(p + 1));
+ } else {
+ return normal(m_vertices.at(p), m_vertices.at(p - 1),
+ m_vertices.at(p - m_columns));
+ }
+ }
+}
+
+QVector3D SurfaceObject::createSmoothNormalUpperLineItem(int x, int y)
+{
+ int p = y * m_columns + x;
+ if (m_dataDimension == BothAscending) {
+ if (x < m_columns - 1) {
+ return normal(m_vertices.at(p), m_vertices.at(p - m_columns),
+ m_vertices.at(p + 1));
+ } else {
+ return normal(m_vertices.at(p), m_vertices.at(p - 1),
+ m_vertices.at(p - m_columns));
+ }
+ } else if (m_dataDimension == XDescending) {
+ if (x == 0) {
+ return normal(m_vertices.at(p), m_vertices.at(p + 1),
+ m_vertices.at(p - m_columns));
+ } else {
+ return normal(m_vertices.at(p), m_vertices.at(p - m_columns),
+ m_vertices.at(p - 1));
+ }
+ } else if (m_dataDimension == ZDescending) {
+ if (x < m_columns - 1) {
+ return normal(m_vertices.at(p), m_vertices.at(p + m_columns),
+ m_vertices.at(p + 1));
+ } else {
+ return normal(m_vertices.at(p), m_vertices.at(p - 1),
+ m_vertices.at(p + m_columns));
+ }
+ } else { // BothDescending
+ if (x == 0) {
+ return normal(m_vertices.at(0), m_vertices.at(1),
+ m_vertices.at(m_columns));
+ } else {
+ return normal(m_vertices.at(p), m_vertices.at(p + m_columns),
+ m_vertices.at(p - 1));
+ }
+ }
+}
+
+void SurfaceObject::smoothUVs(const QSurfaceDataArray &dataArray,
+ const QSurfaceDataArray &modelArray)
+{
+ int columns = dataArray.at(0)->size();
+ int rows = dataArray.size();
+ float xRangeNormalizer = dataArray.at(0)->at(columns - 1).x() - dataArray.at(0)->at(0).x();
+ float zRangeNormalizer = dataArray.at(rows - 1)->at(0).z() - dataArray.at(0)->at(0).z();
+ float xMin = dataArray.at(0)->at(0).x();
+ float zMin = dataArray.at(0)->at(0).z();
+ const bool zDescending = m_dataDimension.testFlag(SurfaceObject::ZDescending);
+ const bool xDescending = m_dataDimension.testFlag(SurfaceObject::XDescending);
+
+ QVector<QVector2D> uvs;
+ uvs.resize(m_rows * m_columns);
+ int index = 0;
+ for (int i = 0; i < m_rows; i++) {
+ float y = (modelArray.at(i)->at(0).z() - zMin) / zRangeNormalizer;
+ if (zDescending)
+ y = 1.0f - y;
+ const QSurfaceDataRow &p = *modelArray.at(i);
+ for (int j = 0; j < m_columns; j++) {
+ float x = (p.at(j).x() - xMin) / xRangeNormalizer;
+ if (xDescending)
+ x = 1.0f - x;
+ uvs[index] = QVector2D(x, y);
+ index++;
+ }
+ }
+
+ glBindBuffer(GL_ARRAY_BUFFER, m_uvTextureBuffer);
+ glBufferData(GL_ARRAY_BUFFER, uvs.size() * sizeof(QVector2D),
+ &uvs.at(0), GL_STATIC_DRAW);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+ m_returnTextureBuffer = true;
+}
+
+void SurfaceObject::updateSmoothRow(const QSurfaceDataArray &dataArray, int rowIndex, bool polar)
{
// Update vertices
int p = rowIndex * m_columns;
const QSurfaceDataRow &dataRow = *dataArray.at(rowIndex);
- for (int j = 0; j < m_columns; j++) {
- const QSurfaceDataItem &data = dataRow.at(j);
- float normalizedX = m_axisCacheX.positionAt(data.x());
- float normalizedY = m_axisCacheY.positionAt(data.y());
- float normalizedZ = m_axisCacheZ.positionAt(data.z());
- m_vertices[p++] = QVector3D(normalizedX, normalizedY, normalizedZ);
- }
+ for (int j = 0; j < m_columns; j++)
+ getNormalizedVertex(dataRow.at(j), m_vertices[p++], polar, false);
// Create normals
- int colLimit = m_columns - 1;
+ bool upwards = (m_dataDimension == BothAscending) || (m_dataDimension == XDescending);
int startRow = rowIndex;
- if (startRow > 0)
+ if ((startRow > 0) && upwards)
startRow--;
+ int endRow = rowIndex;
+ if (!upwards && (rowIndex < m_rows - 1))
+ endRow++;
+ if ((endRow == m_rows - 1) && upwards)
+ endRow--;
int totalIndex = startRow * m_columns;
- int rowLimit = (rowIndex + 1) * m_columns;
- if (rowIndex == m_rows - 1)
- rowLimit = rowIndex * m_columns; // The rowIndex is top most row, special handling
- const bool flipNormal = checkFlipNormal(dataArray);
- for (int row = totalIndex; row < rowLimit; row += m_columns) {
- for (int j = 0; j < colLimit; j++) {
- // One right and one up
- m_normals[totalIndex++] = normal(m_vertices.at(row + j),
- m_vertices.at(row + j + 1),
- m_vertices.at(row + m_columns + j),
- flipNormal);
- }
- int p = row + colLimit;
- // One up and one left
- m_normals[totalIndex++] = normal(m_vertices.at(p),
- m_vertices.at(p + m_columns),
- m_vertices.at(p - 1),
- flipNormal);
+ if ((startRow == 0) && !upwards) {
+ createSmoothNormalUpperLine(totalIndex);
+ startRow++;
}
- if (rowIndex == m_rows - 1) {
- // Top most line, nothing above, must have different handling.
- // Take from one down and one right. Read till second-to-last
- rowLimit = (rowIndex + 1) * m_columns - 1;
- for (int j = rowIndex * m_columns; j < rowLimit; j++) {
- m_normals[totalIndex++] = normal(m_vertices.at(j),
- m_vertices.at(j - m_columns),
- m_vertices.at(j + 1),
- flipNormal);
- }
- // Top left corner. Take from one left and one down
- m_normals[totalIndex++] = normal(m_vertices.at(rowLimit),
- m_vertices.at(rowLimit - 1),
- m_vertices.at(rowLimit - m_columns),
- flipNormal);
- }
+ for (int row = startRow; row <= endRow; row++)
+ createSmoothNormalBodyLine(totalIndex, row * m_columns);
+
+ if ((rowIndex == m_rows - 1) && upwards)
+ createSmoothNormalUpperLine(totalIndex);
}
-void SurfaceObject::updateSmoothItem(const QSurfaceDataArray &dataArray, int row, int column)
+void SurfaceObject::updateSmoothItem(const QSurfaceDataArray &dataArray, int row, int column,
+ bool polar)
{
// Update a vertice
- const QSurfaceDataItem &data = dataArray.at(row)->at(column);
- float normalizedX = m_axisCacheX.positionAt(data.x());
- float normalizedY = m_axisCacheY.positionAt(data.y());
- float normalizedZ = m_axisCacheZ.positionAt(data.z());
- m_vertices[row * m_columns + column] = QVector3D(normalizedX, normalizedY, normalizedZ);
+ getNormalizedVertex(dataArray.at(row)->at(column),
+ m_vertices[row * m_columns + column], polar, false);
// Create normals
+ bool upwards = (m_dataDimension == BothAscending) || (m_dataDimension == XDescending);
+ bool rightwards = (m_dataDimension == BothAscending) || (m_dataDimension == ZDescending);
int startRow = row;
- if (startRow > 0)
- startRow--; // Change the normal for previous row also
+ if ((startRow > 0) && upwards)
+ startRow--;
+ int endRow = row;
+ if (!upwards && (row < m_rows - 1))
+ endRow++;
+ if ((endRow == m_rows - 1) && upwards)
+ endRow--;
int startCol = column;
- if (startCol > 0)
+ if ((startCol > 0) && rightwards)
startCol--;
- int rightCol = m_columns - 1;
- int topRow = m_rows - 1;
+ int endCol = column;
+ if ((endCol < m_columns - 1) && !rightwards)
+ endCol++;
- const bool flipNormal = checkFlipNormal(dataArray);
- for (int i = startRow; i <= row; i++) {
- for (int j = startCol; j <= column; j++) {
+ for (int i = startRow; i <= endRow; i++) {
+ for (int j = startCol; j <= endCol; j++) {
int p = i * m_columns + j;
- if (i < topRow) {
- if (j < rightCol) {
- // One right and one up
- m_normals[p] = normal(m_vertices.at(p),
- m_vertices.at(p + 1),
- m_vertices.at(p + m_columns),
- flipNormal);
- } else {
- // Last item, nothing on the right. One up and one left
- m_normals[p] = normal(m_vertices.at(p),
- m_vertices.at(p + m_columns),
- m_vertices.at(p - 1),
- flipNormal);
- }
- } else {
- // Top most line, nothing above, must have different handling.
- if (j < rightCol) {
- // Take from one down and one right. Read till second-to-last
- m_normals[p] = normal(m_vertices.at(p),
- m_vertices.at(p - m_columns),
- m_vertices.at(p + 1),
- flipNormal);
- } else {
- // Top left corner. Take from one left and one down
- m_normals[p] = normal(m_vertices.at(p),
- m_vertices.at(p - 1),
- m_vertices.at(p - m_columns),
- flipNormal);
- }
- }
- }
+ if ((i == 0) && !upwards)
+ m_normals[p] = createSmoothNormalUpperLineItem(j, i);
+ else if ((i == m_rows - 1) && upwards)
+ m_normals[p] = createSmoothNormalUpperLineItem(j, i);
+ else
+ m_normals[p] = createSmoothNormalBodyLineItem(j, i);
+ }
}
}
@@ -271,15 +423,38 @@ void SurfaceObject::createSmoothIndices(int x, int y, int endX, int endY)
int rowEnd = endY * m_columns;
for (int row = y * m_columns; row < rowEnd; row += m_columns) {
for (int j = x; j < endX; j++) {
- // Left triangle
- indices[p++] = row + j + 1;
- indices[p++] = row + m_columns + j;
- indices[p++] = row + j;
-
- // Right triangle
- indices[p++] = row + m_columns + j + 1;
- indices[p++] = row + m_columns + j;
- indices[p++] = row + j + 1;
+ if ((m_dataDimension == BothAscending) || (m_dataDimension == BothDescending)) {
+ // Left triangle
+ indices[p++] = row + j + 1;
+ indices[p++] = row + m_columns + j;
+ indices[p++] = row + j;
+
+ // Right triangle
+ indices[p++] = row + m_columns + j + 1;
+ indices[p++] = row + m_columns + j;
+ indices[p++] = row + j + 1;
+ } else if (m_dataDimension == XDescending) {
+ // Right triangle
+ indices[p++] = row + m_columns + j;
+ indices[p++] = row + m_columns + j + 1;
+ indices[p++] = row + j;
+
+ // Left triangle
+ indices[p++] = row + j;
+ indices[p++] = row + m_columns + j + 1;
+ indices[p++] = row + j + 1;
+ } else {
+ // Left triangle
+ indices[p++] = row + m_columns + j;
+ indices[p++] = row + m_columns + j + 1;
+ indices[p++] = row + j;
+
+ // Right triangle
+ indices[p++] = row + j;
+ indices[p++] = row + m_columns + j + 1;
+ indices[p++] = row + j + 1;
+
+ }
}
}
@@ -331,7 +506,7 @@ void SurfaceObject::createSmoothGridlineIndices(int x, int y, int endX, int endY
}
void SurfaceObject::setUpData(const QSurfaceDataArray &dataArray, const QRect &space,
- bool changeGeometry, bool flipXZ)
+ bool changeGeometry, bool polar, bool flipXZ)
{
m_columns = space.width();
m_rows = space.height();
@@ -339,6 +514,12 @@ void SurfaceObject::setUpData(const QSurfaceDataArray &dataArray, const QRect &s
GLfloat uvX = 1.0f / GLfloat(m_columns - 1);
GLfloat uvY = 1.0f / GLfloat(m_rows - 1);
+ checkDirections(dataArray);
+ bool indicesDirty = false;
+ if (m_dataDimension != m_oldDataDimension)
+ indicesDirty = true;
+ m_oldDataDimension = m_dataDimension;
+
m_surfaceType = SurfaceFlat;
// Create vertix table
@@ -355,17 +536,14 @@ void SurfaceObject::setUpData(const QSurfaceDataArray &dataArray, const QRect &s
int doubleColumns = m_columns * 2 - 2;
int rowColLimit = rowLimit * doubleColumns;
- AxisRenderCache &xCache = flipXZ ? m_axisCacheZ : m_axisCacheX;
- AxisRenderCache &zCache = flipXZ ? m_axisCacheX : m_axisCacheZ;
+ // Init min and max to ridiculous values
+ m_minY = 10000000.0;
+ m_maxY = -10000000.0f;
for (int i = 0; i < m_rows; i++) {
const QSurfaceDataRow &row = *dataArray.at(i);
for (int j = 0; j < m_columns; j++) {
- const QSurfaceDataItem &data = row.at(j);
- float normalizedX = xCache.positionAt(data.x());
- float normalizedY = m_axisCacheY.positionAt(data.y());
- float normalizedZ = zCache.positionAt(data.z());
- m_vertices[totalIndex] = QVector3D(normalizedX, normalizedY, normalizedZ);
+ getNormalizedVertex(row.at(j), m_vertices[totalIndex], polar, flipXZ);
if (changeGeometry)
uvs[totalIndex] = QVector2D(GLfloat(j) * uvX, GLfloat(i) * uvY);
@@ -389,42 +567,23 @@ void SurfaceObject::setUpData(const QSurfaceDataArray &dataArray, const QRect &s
// Create normals & indices table
GLint *indices = 0;
- int p = 0;
- if (changeGeometry) {
+ if (changeGeometry || indicesDirty) {
int normalCount = 2 * colLimit * rowLimit;
m_indexCount = 3 * normalCount;
indices = new GLint[m_indexCount];
m_normals.resize(normalCount);
}
+ int p = 0;
totalIndex = 0;
- const bool flipNormal = checkFlipNormal(dataArray);
for (int row = 0, upperRow = doubleColumns;
row < rowColLimit;
row += doubleColumns, upperRow += doubleColumns) {
for (int j = 0; j < doubleColumns; j += 2) {
- // Normal for the left triangle
- m_normals[totalIndex++] = normal(m_vertices.at(row + j),
- m_vertices.at(row + j + 1),
- m_vertices.at(upperRow + j),
- flipNormal);
-
- // Normal for the right triangle
- m_normals[totalIndex++] = normal(m_vertices.at(row + j + 1),
- m_vertices.at(upperRow + j + 1),
- m_vertices.at(upperRow + j),
- flipNormal);
-
- if (changeGeometry) {
- // Left triangle
- indices[p++] = row + j + 1;
- indices[p++] = upperRow + j;
- indices[p++] = row + j;
+ createNormals(totalIndex, row, upperRow, j);
- // Right triangle
- indices[p++] = upperRow + j + 1;
- indices[p++] = upperRow + j;
- indices[p++] = row + j + 1;
+ if (changeGeometry || indicesDirty) {
+ createCoarseIndices(indices, p, row, upperRow, j);
}
}
}
@@ -433,12 +592,54 @@ void SurfaceObject::setUpData(const QSurfaceDataArray &dataArray, const QRect &s
if (changeGeometry)
createCoarseGridlineIndices(0, 0, colLimit, rowLimit);
- createBuffers(m_vertices, uvs, m_normals, indices, changeGeometry);
+ createBuffers(m_vertices, uvs, m_normals, indices);
delete[] indices;
}
-void SurfaceObject::updateCoarseRow(const QSurfaceDataArray &dataArray, int rowIndex)
+void SurfaceObject::coarseUVs(const QSurfaceDataArray &dataArray,
+ const QSurfaceDataArray &modelArray)
+{
+ int columns = dataArray.at(0)->size();
+ int rows = dataArray.size();
+ float xRangeNormalizer = dataArray.at(0)->at(columns - 1).x() - dataArray.at(0)->at(0).x();
+ float zRangeNormalizer = dataArray.at(rows - 1)->at(0).z() - dataArray.at(0)->at(0).z();
+ float xMin = dataArray.at(0)->at(0).x();
+ float zMin = dataArray.at(0)->at(0).z();
+ const bool zDescending = m_dataDimension.testFlag(SurfaceObject::ZDescending);
+ const bool xDescending = m_dataDimension.testFlag(SurfaceObject::XDescending);
+
+ QVector<QVector2D> uvs;
+ uvs.resize(m_rows * m_columns * 2);
+ int index = 0;
+ int colLimit = m_columns - 1;
+ for (int i = 0; i < m_rows; i++) {
+ float y = (modelArray.at(i)->at(0).z() - zMin) / zRangeNormalizer;
+ if (zDescending)
+ y = 1.0f - y;
+ const QSurfaceDataRow &p = *modelArray.at(i);
+ for (int j = 0; j < m_columns; j++) {
+ float x = (p.at(j).x() - xMin) / xRangeNormalizer;
+ if (xDescending)
+ x = 1.0f - x;
+ uvs[index] = QVector2D(x, y);
+ index++;
+ if (j > 0 && j < colLimit) {
+ uvs[index] = uvs[index - 1];
+ index++;
+ }
+ }
+ }
+
+ glBindBuffer(GL_ARRAY_BUFFER, m_uvTextureBuffer);
+ glBufferData(GL_ARRAY_BUFFER, uvs.size() * sizeof(QVector2D),
+ &uvs.at(0), GL_STATIC_DRAW);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+ m_returnTextureBuffer = true;
+}
+
+void SurfaceObject::updateCoarseRow(const QSurfaceDataArray &dataArray, int rowIndex, bool polar)
{
int colLimit = m_columns - 1;
int doubleColumns = m_columns * 2 - 2;
@@ -447,12 +648,7 @@ void SurfaceObject::updateCoarseRow(const QSurfaceDataArray &dataArray, int rowI
const QSurfaceDataRow &dataRow = *dataArray.at(rowIndex);
for (int j = 0; j < m_columns; j++) {
- const QSurfaceDataItem &data = dataRow.at(j);
- float normalizedX = m_axisCacheX.positionAt(data.x());
- float normalizedY = m_axisCacheY.positionAt(data.y());
- float normalizedZ = m_axisCacheZ.positionAt(data.z());
- m_vertices[p++] = QVector3D(normalizedX, normalizedY, normalizedZ);
-
+ getNormalizedVertex(dataRow.at(j), m_vertices[p++], polar, false);
if (j > 0 && j < colLimit) {
m_vertices[p] = m_vertices[p - 1];
p++;
@@ -466,39 +662,23 @@ void SurfaceObject::updateCoarseRow(const QSurfaceDataArray &dataArray, int rowI
int rowLimit = (rowIndex + 1) * doubleColumns;
if (rowIndex == m_rows - 1)
rowLimit = rowIndex * doubleColumns; //Topmost row, no normals
- const bool flipNormal = checkFlipNormal(dataArray);
for (int row = p, upperRow = p + doubleColumns;
row < rowLimit;
row += doubleColumns, upperRow += doubleColumns) {
- for (int j = 0; j < doubleColumns; j += 2) {
- // Normal for the left triangle
- m_normals[p++] = normal(m_vertices.at(row + j),
- m_vertices.at(row + j + 1),
- m_vertices.at(upperRow + j),
- flipNormal);
-
- // Normal for the right triangle
- m_normals[p++] = normal(m_vertices.at(row + j + 1),
- m_vertices.at(upperRow + j + 1),
- m_vertices.at(upperRow + j),
- flipNormal);
- }
+ for (int j = 0; j < doubleColumns; j += 2)
+ createNormals(p, row, upperRow, j);
}
}
-void SurfaceObject::updateCoarseItem(const QSurfaceDataArray &dataArray, int row, int column)
+void SurfaceObject::updateCoarseItem(const QSurfaceDataArray &dataArray, int row, int column,
+ bool polar)
{
int colLimit = m_columns - 1;
int doubleColumns = m_columns * 2 - 2;
// Update a vertice
int p = row * doubleColumns + column * 2 - (column > 0);
- const QSurfaceDataItem &data = dataArray.at(row)->at(column);
- float normalizedX = m_axisCacheX.positionAt(data.x());
- float normalizedY = m_axisCacheY.positionAt(data.y());
- float normalizedZ = m_axisCacheZ.positionAt(data.z());
- m_vertices[p] = QVector3D(normalizedX, normalizedY, normalizedZ);
- p++;
+ getNormalizedVertex(dataArray.at(row)->at(column), m_vertices[p++], polar, false);
if (column > 0 && column < colLimit)
m_vertices[p] = m_vertices[p - 1];
@@ -515,27 +695,15 @@ void SurfaceObject::updateCoarseItem(const QSurfaceDataArray &dataArray, int row
if (column == m_columns - 1)
column--;
- const bool flipNormal = checkFlipNormal(dataArray);
for (int i = startRow; i <= row; i++) {
for (int j = startCol; j <= column; j++) {
p = i * doubleColumns + j * 2;
- // Normal for the left triangle
- m_normals[p] = normal(m_vertices.at(p),
- m_vertices.at(p + 1),
- m_vertices.at(p + doubleColumns),
- flipNormal);
- p++;
-
- // Normal for the right triangle
- m_normals[p] = normal(m_vertices.at(p),
- m_vertices.at(p + doubleColumns),
- m_vertices.at(p + doubleColumns - 1),
- flipNormal);
+ createNormals(p, i * doubleColumns, (i + 1) * doubleColumns, j * 2);
}
}
}
-void SurfaceObject::createCoarseIndices(int x, int y, int columns, int rows)
+void SurfaceObject::createCoarseSubSection(int x, int y, int columns, int rows)
{
if (columns > m_columns)
columns = m_columns;
@@ -557,17 +725,8 @@ void SurfaceObject::createCoarseIndices(int x, int y, int columns, int rows)
for (int row = y * doubleColumns, upperRow = (y + 1) * doubleColumns;
row < rowColLimit;
row += doubleColumns, upperRow += doubleColumns) {
- for (int j = 2 * x; j < doubleColumnsLimit; j += 2) {
- // Left triangle
- indices[p++] = row + j + 1;
- indices[p++] = upperRow + j;
- indices[p++] = row + j;
-
- // Right triangle
- indices[p++] = upperRow + j + 1;
- indices[p++] = upperRow + j;
- indices[p++] = row + j + 1;
- }
+ for (int j = 2 * x; j < doubleColumnsLimit; j += 2)
+ createCoarseIndices(indices, p, row, upperRow, j);
}
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_elementbuffer);
@@ -631,12 +790,11 @@ void SurfaceObject::createCoarseGridlineIndices(int x, int y, int endX, int endY
void SurfaceObject::uploadBuffers()
{
QVector<QVector2D> uvs; // Empty dummy
- createBuffers(m_vertices, uvs, m_normals, 0, false);
+ createBuffers(m_vertices, uvs, m_normals, 0);
}
void SurfaceObject::createBuffers(const QVector<QVector3D> &vertices, const QVector<QVector2D> &uvs,
- const QVector<QVector3D> &normals, const GLint *indices,
- bool changeGeometry)
+ const QVector<QVector3D> &normals, const GLint *indices)
{
// Move to buffers
glBindBuffer(GL_ARRAY_BUFFER, m_vertexbuffer);
@@ -647,30 +805,62 @@ void SurfaceObject::createBuffers(const QVector<QVector3D> &vertices, const QVec
glBufferData(GL_ARRAY_BUFFER, normals.size() * sizeof(QVector3D),
&normals.at(0), GL_DYNAMIC_DRAW);
- if (changeGeometry) {
+ if (uvs.size()) {
glBindBuffer(GL_ARRAY_BUFFER, m_uvbuffer);
glBufferData(GL_ARRAY_BUFFER, uvs.size() * sizeof(QVector2D),
&uvs.at(0), GL_STATIC_DRAW);
+ }
- if (indices) {
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_elementbuffer);
- glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_indexCount * sizeof(GLint),
- indices, GL_STATIC_DRAW);
- }
-
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ if (indices) {
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_elementbuffer);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_indexCount * sizeof(GLint),
+ indices, GL_STATIC_DRAW);
}
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
m_meshDataLoaded = true;
}
-bool SurfaceObject::checkFlipNormal(const QSurfaceDataArray &array)
+void SurfaceObject::checkDirections(const QSurfaceDataArray &array)
+{
+ m_dataDimension = BothAscending;
+
+ if (array.at(0)->at(0).x() > array.at(0)->at(array.at(0)->size() - 1).x())
+ m_dataDimension |= XDescending;
+ if (m_axisCacheX.reversed())
+ m_dataDimension ^= XDescending;
+
+ if (array.at(0)->at(0).z() > array.at(array.size() - 1)->at(0).z())
+ m_dataDimension |= ZDescending;
+ if (m_axisCacheZ.reversed())
+ m_dataDimension ^= ZDescending;
+}
+
+void SurfaceObject::getNormalizedVertex(const QSurfaceDataItem &data, QVector3D &vertex,
+ bool polar, bool flipXZ)
{
- const bool ascendingX = array.at(0)->at(0).x() < array.at(0)->at(array.at(0)->size() - 1).x();
- const bool ascendingZ = array.at(0)->at(0).z() < array.at(array.size() - 1)->at(0).z();
- return ascendingX != ascendingZ;
+ float normalizedX;
+ float normalizedZ;
+ if (polar) {
+ // Slice don't use polar, so don't care about flip
+ m_renderer->calculatePolarXZ(data.position(), normalizedX, normalizedZ);
+ } else {
+ if (flipXZ) {
+ normalizedX = m_axisCacheZ.positionAt(data.x());
+ normalizedZ = m_axisCacheX.positionAt(data.z());
+ } else {
+ normalizedX = m_axisCacheX.positionAt(data.x());
+ normalizedZ = m_axisCacheZ.positionAt(data.z());
+ }
+ }
+ float normalizedY = m_axisCacheY.positionAt(data.y());
+ m_minY = qMin(normalizedY, m_minY);
+ m_maxY = qMax(normalizedY, m_maxY);
+ vertex.setX(normalizedX);
+ vertex.setY(normalizedY);
+ vertex.setZ(normalizedZ);
}
GLuint SurfaceObject::gridElementBuf()
@@ -680,6 +870,17 @@ GLuint SurfaceObject::gridElementBuf()
return m_gridElementbuffer;
}
+GLuint SurfaceObject::uvBuf()
+{
+ if (!m_meshDataLoaded)
+ qFatal("No loaded object");
+
+ if (m_returnTextureBuffer)
+ return m_uvTextureBuffer;
+ else
+ return m_uvbuffer;
+}
+
GLuint SurfaceObject::gridIndexCount()
{
return m_gridIndexCount;
@@ -707,14 +908,74 @@ void SurfaceObject::clear()
m_normals.clear();
}
-QVector3D SurfaceObject::normal(const QVector3D &a, const QVector3D &b, const QVector3D &c,
- bool flipNormal)
+void SurfaceObject::createCoarseIndices(GLint *indices, int &p, int row, int upperRow, int j)
+{
+ if ((m_dataDimension == BothAscending) || (m_dataDimension == BothDescending)) {
+ // Left triangle
+ indices[p++] = row + j + 1;
+ indices[p++] = upperRow + j;
+ indices[p++] = row + j;
+
+ // Right triangle
+ indices[p++] = upperRow + j + 1;
+ indices[p++] = upperRow + j;
+ indices[p++] = row + j + 1;
+ } else if (m_dataDimension == XDescending) {
+ indices[p++] = upperRow + j;
+ indices[p++] = upperRow + j + 1;
+ indices[p++] = row + j;
+
+ indices[p++] = row + j;
+ indices[p++] = upperRow + j + 1;
+ indices[p++] = row + j + 1;
+ } else {
+ // Left triangle
+ indices[p++] = upperRow + j;
+ indices[p++] = upperRow + j + 1;
+ indices[p++] = row + j;
+
+ // Right triangle
+ indices[p++] = row + j;
+ indices[p++] = upperRow + j + 1;
+ indices[p++] = row + j + 1;
+ }
+}
+
+void SurfaceObject::createNormals(int &p, int row, int upperRow, int j)
+{
+ if ((m_dataDimension == BothAscending) || (m_dataDimension == BothDescending)) {
+ m_normals[p++] = normal(m_vertices.at(row + j),
+ m_vertices.at(row + j + 1),
+ m_vertices.at(upperRow + j));
+
+ m_normals[p++] = normal(m_vertices.at(row + j + 1),
+ m_vertices.at(upperRow + j + 1),
+ m_vertices.at(upperRow + j));
+ } else if (m_dataDimension == XDescending) {
+ m_normals[p++] = normal(m_vertices.at(row + j),
+ m_vertices.at(upperRow + j),
+ m_vertices.at(upperRow + j + 1));
+
+ m_normals[p++] = normal(m_vertices.at(row + j + 1),
+ m_vertices.at(row + j),
+ m_vertices.at(upperRow + j + 1));
+ } else {
+ m_normals[p++] = normal(m_vertices.at(row + j),
+ m_vertices.at(upperRow + j),
+ m_vertices.at(upperRow + j + 1));
+
+ m_normals[p++] = normal(m_vertices.at(row + j + 1),
+ m_vertices.at(row + j),
+ m_vertices.at(upperRow + j + 1));
+ }
+}
+
+QVector3D SurfaceObject::normal(const QVector3D &a, const QVector3D &b, const QVector3D &c)
{
QVector3D v1 = b - a;
QVector3D v2 = c - a;
QVector3D normal = QVector3D::crossProduct(v1, v2);
- if (flipNormal)
- normal *= -1;
+
return normal;
}
diff --git a/src/datavisualization/utils/surfaceobject_p.h b/src/datavisualization/utils/surfaceobject_p.h
index 9c18dcb2..e7b61310 100644
--- a/src/datavisualization/utils/surfaceobject_p.h
+++ b/src/datavisualization/utils/surfaceobject_p.h
@@ -49,34 +49,55 @@ public:
Undefined
};
+ enum DataDimension {
+ BothAscending = 0,
+ XDescending = 1,
+ ZDescending = 2,
+ BothDescending = XDescending | ZDescending
+ };
+ Q_DECLARE_FLAGS(DataDimensions, DataDimension)
+
public:
SurfaceObject(Surface3DRenderer *renderer);
- ~SurfaceObject();
+ virtual ~SurfaceObject();
void setUpData(const QSurfaceDataArray &dataArray, const QRect &space,
- bool changeGeometry, bool flipXZ = false);
+ bool changeGeometry, bool polar, bool flipXZ = false);
void setUpSmoothData(const QSurfaceDataArray &dataArray, const QRect &space,
- bool changeGeometry, bool flipXZ = false);
- void updateCoarseRow(const QSurfaceDataArray &dataArray, int rowIndex);
- void updateSmoothRow(const QSurfaceDataArray &dataArray, int startRow);
- void updateSmoothItem(const QSurfaceDataArray &dataArray, int row, int column);
- void updateCoarseItem(const QSurfaceDataArray &dataArray, int row, int column);
+ bool changeGeometry, bool polar, bool flipXZ = false);
+ void smoothUVs(const QSurfaceDataArray &dataArray, const QSurfaceDataArray &modelArray);
+ void coarseUVs(const QSurfaceDataArray &dataArray, const QSurfaceDataArray &modelArray);
+ void updateCoarseRow(const QSurfaceDataArray &dataArray, int rowIndex, bool polar);
+ void updateSmoothRow(const QSurfaceDataArray &dataArray, int startRow, bool polar);
+ void updateSmoothItem(const QSurfaceDataArray &dataArray, int row, int column, bool polar);
+ void updateCoarseItem(const QSurfaceDataArray &dataArray, int row, int column, bool polar);
void createSmoothIndices(int x, int y, int endX, int endY);
- void createCoarseIndices(int x, int y, int columns, int rows);
+ void createCoarseSubSection(int x, int y, int columns, int rows);
void createSmoothGridlineIndices(int x, int y, int endX, int endY);
void createCoarseGridlineIndices(int x, int y, int endX, int endY);
void uploadBuffers();
GLuint gridElementBuf();
+ GLuint uvBuf();
GLuint gridIndexCount();
QVector3D vertexAt(int column, int row);
void clear();
+ float minYValue() const { return m_minY; }
+ float maxYValue() const { return m_maxY; }
+ inline void activateSurfaceTexture(bool value) { m_returnTextureBuffer = value; }
private:
- QVector3D normal(const QVector3D &a, const QVector3D &b, const QVector3D &c, bool flipNormal);
+ void createCoarseIndices(GLint *indices, int &p, int row, int upperRow, int j);
+ void createNormals(int &p, int row, int upperRow, int j);
+ void createSmoothNormalBodyLine(int &totalIndex, int column);
+ void createSmoothNormalUpperLine(int &totalIndex);
+ QVector3D createSmoothNormalBodyLineItem(int x, int y);
+ QVector3D createSmoothNormalUpperLineItem(int x, int y);
+ QVector3D normal(const QVector3D &a, const QVector3D &b, const QVector3D &c);
void createBuffers(const QVector<QVector3D> &vertices, const QVector<QVector2D> &uvs,
- const QVector<QVector3D> &normals, const GLint *indices,
- bool changeGeometry);
- bool checkFlipNormal(const QSurfaceDataArray &array);
+ const QVector<QVector3D> &normals, const GLint *indices);
+ void checkDirections(const QSurfaceDataArray &array);
+ inline void getNormalizedVertex(const QSurfaceDataItem &data, QVector3D &vertex, bool polar,
+ bool flipXZ);
private:
SurfaceType m_surfaceType;
@@ -90,6 +111,13 @@ private:
AxisRenderCache &m_axisCacheX;
AxisRenderCache &m_axisCacheY;
AxisRenderCache &m_axisCacheZ;
+ Surface3DRenderer *m_renderer;
+ float m_minY;
+ float m_maxY;
+ GLuint m_uvTextureBuffer;
+ bool m_returnTextureBuffer;
+ SurfaceObject::DataDimensions m_dataDimension;
+ SurfaceObject::DataDimensions m_oldDataDimension;
};
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/utils/texturehelper.cpp b/src/datavisualization/utils/texturehelper.cpp
index 2a2a89dd..3944fb0c 100644
--- a/src/datavisualization/utils/texturehelper.cpp
+++ b/src/datavisualization/utils/texturehelper.cpp
@@ -21,12 +21,29 @@
#include <QtGui/QImage>
#include <QtGui/QPainter>
+#include <QtCore/QTime>
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
+// Defined in shaderhelper.cpp
+extern void discardDebugMsgs(QtMsgType type, const QMessageLogContext &context, const QString &msg);
+
TextureHelper::TextureHelper()
{
initializeOpenGLFunctions();
+#if !defined(QT_OPENGL_ES_2)
+ if (!Utils::isOpenGLES()) {
+ // Discard warnings about deprecated functions
+ QtMessageHandler handler = qInstallMessageHandler(discardDebugMsgs);
+
+ m_openGlFunctions_2_1 =
+ QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_2_1>();
+ m_openGlFunctions_2_1->initializeOpenGLFunctions();
+
+ // Restore original message handler
+ qInstallMessageHandler(handler);
+ }
+#endif
}
TextureHelper::~TextureHelper()
@@ -41,17 +58,16 @@ GLuint TextureHelper::create2DTexture(const QImage &image, bool useTrilinearFilt
QImage texImage = image;
-#if defined(QT_OPENGL_ES_2)
- GLuint temp;
- GLuint imageWidth = Utils::getNearestPowerOfTwo(image.width(), temp);
- GLuint imageHeight = Utils::getNearestPowerOfTwo(image.height(), temp);
- if (smoothScale) {
- texImage = image.scaled(imageWidth, imageHeight, Qt::IgnoreAspectRatio,
- Qt::SmoothTransformation);
- } else {
- texImage = image.scaled(imageWidth, imageHeight, Qt::IgnoreAspectRatio);
+ if (!Utils::isOpenGLES()) {
+ GLuint imageWidth = Utils::getNearestPowerOfTwo(image.width());
+ GLuint imageHeight = Utils::getNearestPowerOfTwo(image.height());
+ if (smoothScale) {
+ texImage = image.scaled(imageWidth, imageHeight, Qt::IgnoreAspectRatio,
+ Qt::SmoothTransformation);
+ } else {
+ texImage = image.scaled(imageWidth, imageHeight, Qt::IgnoreAspectRatio);
+ }
}
-#endif
GLuint textureId;
glGenTextures(1, &textureId);
@@ -73,6 +89,53 @@ GLuint TextureHelper::create2DTexture(const QImage &image, bool useTrilinearFilt
if (clampY)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindTexture(GL_TEXTURE_2D, 0);
+
+ return textureId;
+}
+
+GLuint TextureHelper::create3DTexture(const QVector<uchar> *data, int width, int height, int depth,
+ QImage::Format dataFormat)
+{
+ if (Utils::isOpenGLES() || !width || !height || !depth)
+ return 0;
+
+ GLuint textureId = 0;
+#if defined(QT_OPENGL_ES_2)
+ Q_UNUSED(dataFormat)
+ Q_UNUSED(data)
+#else
+ glEnable(GL_TEXTURE_3D);
+
+ glGenTextures(1, &textureId);
+ glBindTexture(GL_TEXTURE_3D, textureId);
+ glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+
+ GLenum status = glGetError();
+ // glGetError docs advise to call glGetError in loop to clear all error flags
+ while (status)
+ status = glGetError();
+
+ GLint internalFormat = 4;
+ GLint format = GL_BGRA;
+ if (dataFormat == QImage::Format_Indexed8) {
+ internalFormat = 1;
+ format = GL_RED;
+ // Align width to 32bits
+ width = width + width % 4;
+ }
+ m_openGlFunctions_2_1->glTexImage3D(GL_TEXTURE_3D, 0, internalFormat, width, height, depth, 0,
+ format, GL_UNSIGNED_BYTE, data->constData());
+ status = glGetError();
+ if (status)
+ qWarning() << __FUNCTION__ << "3D texture creation failed:" << status;
+
+ glBindTexture(GL_TEXTURE_3D, 0);
+ glDisable(GL_TEXTURE_3D);
+#endif
return textureId;
}
@@ -113,14 +176,27 @@ GLuint TextureHelper::createSelectionTexture(const QSize &size, GLuint &frameBuf
glBindTexture(GL_TEXTURE_2D, 0);
// Create render buffer
- if (!depthBuffer)
- glGenRenderbuffers(1, &depthBuffer);
+ if (depthBuffer)
+ glDeleteRenderbuffers(1, &depthBuffer);
+
+ glGenRenderbuffers(1, &depthBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, depthBuffer);
-#if !defined(QT_OPENGL_ES_2)
- glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, size.width(), size.height());
-#else
- glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, size.width(), size.height());
-#endif
+ GLenum status = glGetError();
+ // glGetError docs advise to call glGetError in loop to clear all error flags
+ while (status)
+ status = glGetError();
+ if (Utils::isOpenGLES())
+ glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, size.width(), size.height());
+ else
+ glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, size.width(), size.height());
+
+ status = glGetError();
+ if (status) {
+ qCritical() << "Selection texture render buffer creation failed:" << status;
+ glDeleteTextures(1, &textureid);
+ glBindRenderbuffer(GL_RENDERBUFFER, 0);
+ return 0;
+ }
glBindRenderbuffer(GL_RENDERBUFFER, 0);
// Create frame buffer
@@ -134,10 +210,11 @@ GLuint TextureHelper::createSelectionTexture(const QSize &size, GLuint &frameBuf
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthBuffer);
// Verify that the frame buffer is complete
- GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
- qCritical() << "Frame buffer creation failed" << status;
- return 0;
+ qCritical() << "Selection texture frame buffer creation failed:" << status;
+ glDeleteTextures(1, &textureid);
+ textureid = 0;
}
// Restore the default framebuffer
@@ -146,6 +223,31 @@ GLuint TextureHelper::createSelectionTexture(const QSize &size, GLuint &frameBuf
return textureid;
}
+GLuint TextureHelper::createCursorPositionTexture(const QSize &size, GLuint &frameBuffer)
+{
+ GLuint textureid;
+ glGenTextures(1, &textureid);
+ glBindTexture(GL_TEXTURE_2D, textureid);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.width(), size.height(), 0, GL_RGBA,
+ GL_UNSIGNED_BYTE, NULL);
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ glGenFramebuffers(1, &frameBuffer);
+ glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+ textureid, 0);
+
+ GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ if (status != GL_FRAMEBUFFER_COMPLETE) {
+ qCritical() << "Cursor position mapper frame buffer creation failed:" << status;
+ glDeleteTextures(1, &textureid);
+ textureid = 0;
+ }
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+
+ return textureid;
+}
+
GLuint TextureHelper::createUniformTexture(const QColor &color)
{
QImage image(QSize(int(uniformTextureWidth), int(uniformTextureHeight)),
@@ -170,76 +272,64 @@ GLuint TextureHelper::createGradientTexture(const QLinearGradient &gradient)
return create2DTexture(image, false, true, false, true);
}
-#if !defined(QT_OPENGL_ES_2)
GLuint TextureHelper::createDepthTexture(const QSize &size, GLuint textureSize)
{
- GLuint depthtextureid;
-
- // Create depth texture for the shadow mapping
- glGenTextures(1, &depthtextureid);
- glBindTexture(GL_TEXTURE_2D, depthtextureid);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, size.width() * textureSize,
- size.height() * textureSize, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
- glBindTexture(GL_TEXTURE_2D, 0);
-
+ GLuint depthtextureid = 0;
+#if defined(QT_OPENGL_ES_2)
+ Q_UNUSED(size)
+ Q_UNUSED(textureSize)
+#else
+ if (!Utils::isOpenGLES()) {
+ // Create depth texture for the shadow mapping
+ glGenTextures(1, &depthtextureid);
+ glBindTexture(GL_TEXTURE_2D, depthtextureid);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, size.width() * textureSize,
+ size.height() * textureSize, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
+ glBindTexture(GL_TEXTURE_2D, 0);
+ }
+#endif
return depthtextureid;
}
-#endif
-#if !defined(QT_OPENGL_ES_2)
GLuint TextureHelper::createDepthTextureFrameBuffer(const QSize &size, GLuint &frameBuffer,
GLuint textureSize)
{
GLuint depthtextureid = createDepthTexture(size, textureSize);
+#if defined(QT_OPENGL_ES_2)
+ Q_UNUSED(frameBuffer)
+#else
+ if (!Utils::isOpenGLES()) {
+ // Create frame buffer
+ if (!frameBuffer)
+ glGenFramebuffers(1, &frameBuffer);
+ glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
+
+ // Attach texture to depth attachment
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthtextureid, 0);
+
+ m_openGlFunctions_2_1->glDrawBuffer(GL_NONE);
+ m_openGlFunctions_2_1->glReadBuffer(GL_NONE);
+
+ // Verify that the frame buffer is complete
+ GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ if (status != GL_FRAMEBUFFER_COMPLETE) {
+ qCritical() << "Depth texture frame buffer creation failed" << status;
+ glDeleteTextures(1, &depthtextureid);
+ depthtextureid = 0;
+ }
- // Create frame buffer
- if (!frameBuffer)
- glGenFramebuffers(1, &frameBuffer);
- glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
-
- // Attach texture to depth attachment
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthtextureid, 0);
-
- glDrawBuffer(GL_NONE);
- glReadBuffer(GL_NONE);
-
- // Verify that the frame buffer is complete
- GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
- if (status != GL_FRAMEBUFFER_COMPLETE) {
- qCritical() << "Frame buffer creation failed" << status;
- return 0;
+ // Restore the default framebuffer
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
-
- // Restore the default framebuffer
- glBindFramebuffer(GL_FRAMEBUFFER, 0);
-
- return depthtextureid;
-}
#endif
-
-#if !defined(QT_OPENGL_ES_2)
-void TextureHelper::fillDepthTexture(GLuint texture,const QSize &size, GLuint textureSize,
- GLfloat value)
-{
- int nItems = size.width() * textureSize * size.height() * textureSize;
- GLfloat *bits = new GLfloat[nItems];
- for (int i = 0; i < nItems; i++)
- bits[i] = value;
-
- glBindTexture(GL_TEXTURE_2D, texture);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, size.width() * textureSize,
- size.height() * textureSize, 0, GL_DEPTH_COMPONENT, GL_FLOAT, bits);
- glBindTexture(GL_TEXTURE_2D, 0);
-
- delete[] bits;
+ return depthtextureid;
}
-#endif
void TextureHelper::deleteTexture(GLuint *texture)
{
diff --git a/src/datavisualization/utils/texturehelper_p.h b/src/datavisualization/utils/texturehelper_p.h
index aec137a4..6c0aa3de 100644
--- a/src/datavisualization/utils/texturehelper_p.h
+++ b/src/datavisualization/utils/texturehelper_p.h
@@ -32,6 +32,10 @@
#include "datavisualizationglobal_p.h"
#include <QtGui/QRgb>
#include <QtGui/QLinearGradient>
+#if !defined(QT_OPENGL_ES_2)
+// 3D Textures are not supported by ES set
+# include <QtGui/QOpenGLFunctions_2_1>
+#endif
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
@@ -44,17 +48,17 @@ class TextureHelper : protected QOpenGLFunctions
// Ownership of created texture is transferred to caller
GLuint create2DTexture(const QImage &image, bool useTrilinearFiltering = false,
bool convert = true, bool smoothScale = true, bool clampY = false);
+ GLuint create3DTexture(const QVector<uchar> *data, int width, int height, int depth,
+ QImage::Format dataFormat);
GLuint createCubeMapTexture(const QImage &image, bool useTrilinearFiltering = false);
// Returns selection texture and inserts generated framebuffers to framebuffer parameters
GLuint createSelectionTexture(const QSize &size, GLuint &frameBuffer, GLuint &depthBuffer);
+ GLuint createCursorPositionTexture(const QSize &size, GLuint &frameBuffer);
GLuint createUniformTexture(const QColor &color);
GLuint createGradientTexture(const QLinearGradient &gradient);
-#if !defined(QT_OPENGL_ES_2)
GLuint createDepthTexture(const QSize &size, GLuint textureSize);
// Returns depth texture and inserts generated framebuffer to parameter
GLuint createDepthTextureFrameBuffer(const QSize &size, GLuint &frameBuffer, GLuint textureSize);
- void fillDepthTexture(GLuint texture, const QSize &size, GLuint textureSize, GLfloat value);
-#endif
void deleteTexture(GLuint *texture);
private:
@@ -62,9 +66,13 @@ class TextureHelper : protected QOpenGLFunctions
void convertToGLFormatHelper(QImage &dstImage, const QImage &srcImage, GLenum texture_format);
QRgb qt_gl_convertToGLFormatHelper(QRgb src_pixel, GLenum texture_format);
+#if !defined(QT_OPENGL_ES_2)
+ QOpenGLFunctions_2_1 *m_openGlFunctions_2_1; // Not owned
+#endif
friend class Bars3DRenderer;
friend class Surface3DRenderer;
friend class Scatter3DRenderer;
+ friend class Abstract3DRenderer;
};
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/utils/utils.cpp b/src/datavisualization/utils/utils.cpp
index 2368d86a..cb64eb8f 100644
--- a/src/datavisualization/utils/utils.cpp
+++ b/src/datavisualization/utils/utils.cpp
@@ -17,6 +17,7 @@
****************************************************************************/
#include "utils_p.h"
+#include "qutils.h"
#include <QtGui/QPainter>
@@ -25,11 +26,12 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
#define NUM_IN_POWER(y, x) for (;y<x;y<<=1)
#define MIN_POWER 2
-GLuint Utils::getNearestPowerOfTwo(GLuint value, GLuint &padding)
+static GLint maxTextureSize = 0; // Safe, as all instances have the same texture size
+
+GLuint Utils::getNearestPowerOfTwo(GLuint value)
{
GLuint powOfTwoValue = MIN_POWER;
NUM_IN_POWER(powOfTwoValue, value);
- padding = powOfTwoValue - value;
return powOfTwoValue;
}
@@ -54,35 +56,76 @@ QImage Utils::printTextToImage(const QFont &font, const QString &text, const QCo
const QColor &txtColor, bool labelBackground,
bool borders, int maxLabelWidth)
{
+ if (maxTextureSize == 0) {
+ QOpenGLContext::currentContext()->functions()->glGetIntegerv(
+ GL_MAX_TEXTURE_SIZE, &maxTextureSize);
+ }
GLuint paddingWidth = 20;
GLuint paddingHeight = 20;
+ GLuint prePadding = 20;
+ GLint targetWidth = maxTextureSize;
+
// Calculate text dimensions
QFont valueFont = font;
valueFont.setPointSize(textureFontSize);
QFontMetrics valueFM(valueFont);
int valueStrWidth = valueFM.width(text);
- if (maxLabelWidth && labelBackground)
+
+ // ES2 needs to use maxLabelWidth always (when given) because of the power of 2 -issue.
+ if (maxLabelWidth && (labelBackground || Utils::isOpenGLES()))
valueStrWidth = maxLabelWidth;
int valueStrHeight = valueFM.height();
valueStrWidth += paddingWidth / 2; // Fix clipping problem with skewed fonts (italic or italic-style)
QSize labelSize;
+ qreal fontRatio = 1.0;
-#if defined(QT_OPENGL_ES_2)
- // ES2 can't handle textures with dimensions not in power of 2. Resize labels accordingly.
- // Add some padding before converting to power of two to avoid too tight fit
- GLuint prePadding = 5;
- // ES2 needs to use this always (when given) because of the power of 2 -issue.
- if (maxLabelWidth)
- valueStrWidth = maxLabelWidth + paddingWidth / 2;
- labelSize = QSize(valueStrWidth + prePadding, valueStrHeight + prePadding);
- labelSize.setWidth(getNearestPowerOfTwo(labelSize.width(), paddingWidth));
- labelSize.setHeight(getNearestPowerOfTwo(labelSize.height(), paddingHeight));
-#else
- if (!labelBackground)
- labelSize = QSize(valueStrWidth, valueStrHeight);
- else
- labelSize = QSize(valueStrWidth + paddingWidth * 2, valueStrHeight + paddingHeight * 2);
-#endif
+ if (Utils::isOpenGLES()) {
+ // Test if text with slighly smaller font would fit into one step smaller texture
+ // ie. if the text is just exceeded the smaller texture boundary, it would
+ // make a label with large empty space
+ uint testWidth = getNearestPowerOfTwo(valueStrWidth + prePadding) >> 1;
+ int diffToFit = (valueStrWidth + prePadding) - testWidth;
+ int maxSqueeze = int((valueStrWidth + prePadding) * 0.1f);
+ if (diffToFit < maxSqueeze && maxTextureSize > GLint(testWidth))
+ targetWidth = testWidth;
+ }
+
+ bool sizeOk = false;
+ int currentFontSize = textureFontSize;
+ do {
+ if (Utils::isOpenGLES()) {
+ // ES2 can't handle textures with dimensions not in power of 2. Resize labels accordingly.
+ // Add some padding before converting to power of two to avoid too tight fit
+ labelSize = QSize(valueStrWidth + prePadding, valueStrHeight + prePadding);
+ labelSize.setWidth(getNearestPowerOfTwo(labelSize.width()));
+ labelSize.setHeight(getNearestPowerOfTwo(labelSize.height()));
+ } else {
+ if (!labelBackground)
+ labelSize = QSize(valueStrWidth, valueStrHeight);
+ else
+ labelSize = QSize(valueStrWidth + paddingWidth * 2, valueStrHeight + paddingHeight * 2);
+ }
+
+ if (!maxTextureSize || (labelSize.width() <= maxTextureSize
+ && (labelSize.width() <= targetWidth || !Utils::isOpenGLES()))) {
+ // Make sure the label is not too wide
+ sizeOk = true;
+ } else if (--currentFontSize == 4) {
+ qCritical() << "Label" << text << "is too long to be generated.";
+ return QImage();
+ } else {
+ fontRatio = (qreal)currentFontSize / (qreal)textureFontSize;
+ // Reduce font size and try again
+ valueFont.setPointSize(currentFontSize);
+ QFontMetrics currentValueFM(valueFont);
+ if (maxLabelWidth && (labelBackground || Utils::isOpenGLES()))
+ valueStrWidth = maxLabelWidth * fontRatio;
+ else
+ valueStrWidth = currentValueFM.width(text);
+ valueStrHeight = currentValueFM.height();
+ valueStrWidth += paddingWidth / 2;
+ }
+ } while (!sizeOk);
// Create image
QImage image = QImage(labelSize, QImage::Format_ARGB32);
@@ -96,27 +139,30 @@ QImage Utils::printTextToImage(const QFont &font, const QString &text, const QCo
painter.setFont(valueFont);
if (!labelBackground) {
painter.setPen(txtColor);
-#if defined(QT_OPENGL_ES_2)
- painter.drawText((labelSize.width() - valueStrWidth) / 2.0f,
- (labelSize.height() - valueStrHeight) / 2.0f,
- valueStrWidth, valueStrHeight,
- Qt::AlignCenter | Qt::AlignVCenter,
- text);
-#else
- painter.drawText(0, 0,
- valueStrWidth, valueStrHeight,
- Qt::AlignCenter | Qt::AlignVCenter,
- text);
-#endif
+ if (Utils::isOpenGLES()) {
+ painter.drawText((labelSize.width() - valueStrWidth) / 2.0f,
+ (labelSize.height() - valueStrHeight) / 2.0f,
+ valueStrWidth, valueStrHeight,
+ Qt::AlignCenter | Qt::AlignVCenter,
+ text);
+ } else {
+ painter.drawText(0, 0,
+ valueStrWidth, valueStrHeight,
+ Qt::AlignCenter | Qt::AlignVCenter,
+ text);
+ }
} else {
painter.setBrush(QBrush(bgrColor));
+ qreal radius = 10.0 * fontRatio;
if (borders) {
- painter.setPen(QPen(QBrush(txtColor), 5, Qt::SolidLine, Qt::SquareCap, Qt::RoundJoin));
- painter.drawRoundedRect(5, 5, labelSize.width() - 10, labelSize.height() - 10,
- 10.0, 10.0);
+ painter.setPen(QPen(QBrush(txtColor), 5.0 * fontRatio,
+ Qt::SolidLine, Qt::SquareCap, Qt::RoundJoin));
+ painter.drawRoundedRect(5, 5,
+ labelSize.width() - 10, labelSize.height() - 10,
+ radius, radius);
} else {
painter.setPen(bgrColor);
- painter.drawRoundedRect(0, 0, labelSize.width(), labelSize.height(), 10.0, 10.0);
+ painter.drawRoundedRect(0, 0, labelSize.width(), labelSize.height(), radius, radius);
}
painter.setPen(txtColor);
painter.drawText((labelSize.width() - valueStrWidth) / 2.0f,
@@ -133,62 +179,74 @@ QVector4D Utils::getSelection(QPoint mousepos, int height)
// This is the only one that works with OpenGL ES 2.0, so we're forced to use it
// Item count will be limited to 256*256*256
GLubyte pixel[4] = {255, 255, 255, 255};
- glReadPixels(mousepos.x(), height - mousepos.y(), 1, 1, GL_RGBA, GL_UNSIGNED_BYTE,
- (void *)pixel);
+ QOpenGLContext::currentContext()->functions()->glReadPixels(mousepos.x(), height - mousepos.y(),
+ 1, 1, GL_RGBA, GL_UNSIGNED_BYTE,
+ (void *)pixel);
QVector4D selectedColor(pixel[0], pixel[1], pixel[2], pixel[3]);
return selectedColor;
}
-QImage Utils::getGradientImage(const QLinearGradient &gradient)
+QImage Utils::getGradientImage(QLinearGradient &gradient)
{
- QImage image(QSize(1, 101), QImage::Format_RGB32);
+ QImage image(QSize(gradientTextureWidth, gradientTextureHeight), QImage::Format_RGB32);
+ gradient.setFinalStop(qreal(gradientTextureWidth), qreal(gradientTextureHeight));
+ gradient.setStart(0.0, 0.0);
+
QPainter pmp(&image);
pmp.setBrush(QBrush(gradient));
pmp.setPen(Qt::NoPen);
- pmp.drawRect(0, 0, 1, 101);
+ pmp.drawRect(0, 0, int(gradientTextureWidth), int(gradientTextureHeight));
return image;
}
-Utils::ParamType Utils::mapFormatCharToParamType(const QChar &formatChar)
+Utils::ParamType Utils::preParseFormat(const QString &format, QString &preStr, QString &postStr,
+ int &precision, char &formatSpec)
{
- ParamType retVal = ParamTypeUnknown;
- if (formatChar == QLatin1Char('d')
- || formatChar == QLatin1Char('i')
- || formatChar == QLatin1Char('c')) {
- retVal = ParamTypeInt;
- } else if (formatChar == QLatin1Char('u')
- || formatChar == QLatin1Char('o')
- || formatChar == QLatin1Char('x')
- || formatChar == QLatin1Char('X')) {
- retVal = ParamTypeUInt;
- } else if (formatChar == QLatin1Char('f')
- || formatChar == QLatin1Char('F')
- || formatChar == QLatin1Char('e')
- || formatChar == QLatin1Char('E')
- || formatChar == QLatin1Char('g')
- || formatChar == QLatin1Char('G')) {
- retVal = ParamTypeReal;
+ static QRegExp formatMatcher(QStringLiteral("^([^%]*)%([\\-\\+#\\s\\d\\.lhjztL]*)([dicuoxfegXFEG])(.*)$"));
+ static QRegExp precisionMatcher(QStringLiteral("\\.(\\d+)"));
+
+ Utils::ParamType retVal;
+
+ if (formatMatcher.indexIn(format, 0) != -1) {
+ preStr = formatMatcher.cap(1);
+ // Six and 'g' are defaults in Qt API
+ precision = 6;
+ if (!formatMatcher.cap(2).isEmpty()) {
+ if (precisionMatcher.indexIn(formatMatcher.cap(2), 0) != -1)
+ precision = precisionMatcher.cap(1).toInt();
+ }
+ if (formatMatcher.cap(3).isEmpty())
+ formatSpec = 'g';
+ else
+ formatSpec = formatMatcher.cap(3).at(0).toLatin1();
+ postStr = formatMatcher.cap(4);
+ retVal = mapFormatCharToParamType(formatSpec);
+ } else {
+ retVal = ParamTypeUnknown;
+ // The out parameters are irrelevant in unknown case
}
return retVal;
}
-Utils::ParamType Utils::findFormatParamType(const QString &format)
+Utils::ParamType Utils::mapFormatCharToParamType(char formatSpec)
{
- static QRegExp formatMatcher(QStringLiteral("%[\\-\\+#\\s\\d\\.lhjztL]*([dicuoxfegXFEG])"));
-
- if (formatMatcher.indexIn(format, 0) != -1) {
- QString capStr = formatMatcher.cap(1);
- if (capStr.isEmpty())
- return ParamTypeUnknown;
- else
- return mapFormatCharToParamType(capStr.at(0));
+ ParamType retVal = ParamTypeUnknown;
+ if (formatSpec == 'd' || formatSpec == 'i' || formatSpec == 'c') {
+ retVal = ParamTypeInt;
+ } else if (formatSpec == 'u' || formatSpec == 'o'
+ || formatSpec == 'x'|| formatSpec == 'X') {
+ retVal = ParamTypeUInt;
+ } else if (formatSpec == 'f' || formatSpec == 'F'
+ || formatSpec == 'e' || formatSpec == 'E'
+ || formatSpec == 'g' || formatSpec == 'G') {
+ retVal = ParamTypeReal;
}
- return ParamTypeUnknown;
+ return retVal;
}
-QString Utils::formatLabel(const QByteArray &format, ParamType paramType, qreal value)
+QString Utils::formatLabelSprintf(const QByteArray &format, Utils::ParamType paramType, qreal value)
{
switch (paramType) {
case ParamTypeInt:
@@ -198,7 +256,24 @@ QString Utils::formatLabel(const QByteArray &format, ParamType paramType, qreal
case ParamTypeReal:
return QString().sprintf(format, value);
default:
- return QString::fromUtf8(format); // To detect errors
+ // Return format string to detect errors. Bars selection label logic also depends on this.
+ return QString::fromUtf8(format);
+ }
+}
+
+QString Utils::formatLabelLocalized(Utils::ParamType paramType, qreal value,
+ const QLocale &locale, const QString &preStr, const QString &postStr,
+ int precision, char formatSpec, const QByteArray &format)
+{
+ switch (paramType) {
+ case ParamTypeInt:
+ case ParamTypeUInt:
+ return preStr + locale.toString(qint64(value)) + postStr;
+ case ParamTypeReal:
+ return preStr + locale.toString(value, formatSpec, precision) + postStr;
+ default:
+ // Return format string to detect errors. Bars selection label logic also depends on this.
+ return QString::fromUtf8(format);
}
}
@@ -238,4 +313,41 @@ QQuaternion Utils::calculateRotation(const QVector3D &xyzRotations)
return totalRotation;
}
+bool Utils::isOpenGLES()
+{
+#if defined(QT_OPENGL_ES_2)
+ return true;
+#elif (QT_VERSION < QT_VERSION_CHECK(5, 3, 0))
+ return false;
+#else
+ static bool resolved = false;
+ static bool isES = false;
+ if (!resolved) {
+ QOpenGLContext *ctx = QOpenGLContext::currentContext();
+ QWindow *dummySurface = 0;
+ if (!ctx) {
+ QSurfaceFormat surfaceFormat = qDefaultSurfaceFormat();
+ dummySurface = new QWindow();
+ dummySurface->setSurfaceType(QWindow::OpenGLSurface);
+ dummySurface->setFormat(surfaceFormat);
+ dummySurface->create();
+ ctx = new QOpenGLContext;
+ ctx->setFormat(surfaceFormat);
+ ctx->create();
+ ctx->makeCurrent(dummySurface);
+ }
+
+ isES = ctx->isOpenGLES();
+ resolved = true;
+
+ if (dummySurface) {
+ ctx->doneCurrent();
+ delete ctx;
+ delete dummySurface;
+ }
+ }
+ return isES;
+#endif
+}
+
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/utils/utils.pri b/src/datavisualization/utils/utils.pri
index 30bfb156..904407b8 100644
--- a/src/datavisualization/utils/utils.pri
+++ b/src/datavisualization/utils/utils.pri
@@ -22,3 +22,5 @@ SOURCES += $$PWD/meshloader.cpp \
$$PWD/surfaceobject.cpp \
$$PWD/scatterobjectbufferhelper.cpp \
$$PWD/scatterpointbufferhelper.cpp
+
+INCLUDEPATH += $$PWD
diff --git a/src/datavisualization/utils/utils_p.h b/src/datavisualization/utils/utils_p.h
index d7187c16..1a46c731 100644
--- a/src/datavisualization/utils/utils_p.h
+++ b/src/datavisualization/utils/utils_p.h
@@ -45,7 +45,7 @@ public:
ParamTypeReal
};
- static GLuint getNearestPowerOfTwo(GLuint value, GLuint &padding);
+ static GLuint getNearestPowerOfTwo(GLuint value);
static QVector4D vectorFromColor(const QColor &color);
static QColor colorFromVector(const QVector3D &colorVector);
static QColor colorFromVector(const QVector4D &colorVector);
@@ -57,17 +57,22 @@ public:
bool borders = false,
int maxLabelWidth = 0);
static QVector4D getSelection(QPoint mousepos, int height);
- static QImage getGradientImage(const QLinearGradient &gradient);
+ static QImage getGradientImage(QLinearGradient &gradient);
- static ParamType findFormatParamType(const QString &format);
- static QString formatLabel(const QByteArray &format, ParamType paramType, qreal value);
+ static ParamType preParseFormat(const QString &format, QString &preStr, QString &postStr,
+ int &precision, char &formatSpec);
+ static QString formatLabelSprintf(const QByteArray &format, ParamType paramType, qreal value);
+ static QString formatLabelLocalized(ParamType paramType, qreal value,
+ const QLocale &locale, const QString &preStr, const QString &postStr,
+ int precision, char formatSpec, const QByteArray &format);
static QString defaultLabelFormat();
static float wrapValue(float value, float min, float max);
static QQuaternion calculateRotation(const QVector3D &xyzRotations);
+ static bool isOpenGLES();
private:
- static ParamType mapFormatCharToParamType(const QChar &formatChar);
+ static ParamType mapFormatCharToParamType(char formatSpec);
};
QT_END_NAMESPACE_DATAVISUALIZATION