summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiikka Heikkinen <miikka.heikkinen@digia.com>2014-08-13 15:09:36 +0300
committerMiikka Heikkinen <miikka.heikkinen@digia.com>2014-08-18 09:11:30 +0300
commit169a4d638c6c1b6634ffcfd19c4fe3cb94cf27d5 (patch)
tree454810f2e9cc418bae9f1c6716c51c3fae38a968
parentbe7bdaa8930caf15fcc58a480d223e0c2b8af6ed (diff)
Implement volume rendering support
New subclass of QCustom3DItem, QCustom3DVolume is provided. The documentation for the example will be done in a separate commit. Change-Id: Idb3fdb0654c6bec7606ca012b75852a5a8412397 Reviewed-by: Tomi Korpipää <tomi.korpipaa@digia.com>
-rw-r--r--examples/datavisualization/datavisualization.pro3
-rw-r--r--examples/datavisualization/volumetric/doc/src/volumetric.qdoc27
-rw-r--r--examples/datavisualization/volumetric/main.cpp105
-rw-r--r--examples/datavisualization/volumetric/volumetric.cpp179
-rw-r--r--examples/datavisualization/volumetric/volumetric.h59
-rw-r--r--examples/datavisualization/volumetric/volumetric.pro15
-rw-r--r--src/datavisualization/data/customrenderitem.cpp25
-rw-r--r--src/datavisualization/data/customrenderitem_p.h35
-rw-r--r--src/datavisualization/data/data.pri7
-rw-r--r--src/datavisualization/data/qcustom3ditem.cpp6
-rw-r--r--src/datavisualization/data/qcustom3ditem_p.h1
-rw-r--r--src/datavisualization/data/qcustom3dvolume.cpp669
-rw-r--r--src/datavisualization/data/qcustom3dvolume.h106
-rw-r--r--src/datavisualization/data/qcustom3dvolume_p.h90
-rw-r--r--src/datavisualization/engine/abstract3drenderer.cpp176
-rw-r--r--src/datavisualization/engine/abstract3drenderer_p.h11
-rw-r--r--src/datavisualization/engine/bars3drenderer.cpp15
-rw-r--r--src/datavisualization/engine/drawer.cpp19
-rw-r--r--src/datavisualization/engine/drawer_p.h2
-rw-r--r--src/datavisualization/engine/engine.qrc3
-rw-r--r--src/datavisualization/engine/scatter3drenderer.cpp19
-rw-r--r--src/datavisualization/engine/shaders/default.frag1
-rw-r--r--src/datavisualization/engine/shaders/texture3d.frag70
-rw-r--r--src/datavisualization/engine/shaders/texture3d.vert12
-rw-r--r--src/datavisualization/engine/shaders/texture3dslice.frag119
-rw-r--r--src/datavisualization/engine/surface3drenderer.cpp23
-rw-r--r--src/datavisualization/utils/shaderhelper.cpp37
-rw-r--r--src/datavisualization/utils/shaderhelper_p.h9
-rw-r--r--src/datavisualization/utils/texturehelper.cpp60
-rw-r--r--src/datavisualization/utils/texturehelper_p.h11
-rw-r--r--tests/tests.pro3
-rw-r--r--tests/volumetrictest/logo.pngbin0 -> 2205 bytes
-rw-r--r--tests/volumetrictest/main.cpp105
-rw-r--r--tests/volumetrictest/volumetrictest.cpp282
-rw-r--r--tests/volumetrictest/volumetrictest.h61
-rw-r--r--tests/volumetrictest/volumetrictest.pro18
-rw-r--r--tests/volumetrictest/volumetrictest.qrc5
37 files changed, 2343 insertions, 45 deletions
diff --git a/examples/datavisualization/datavisualization.pro b/examples/datavisualization/datavisualization.pro
index cb861b81..eab15b6d 100644
--- a/examples/datavisualization/datavisualization.pro
+++ b/examples/datavisualization/datavisualization.pro
@@ -21,7 +21,8 @@ SUBDIRS += qmlbars \
rotations \
draggableaxes \
customitems \
- texturesurface
+ texturesurface \
+ volumetric
}
qtHaveModule(multimedia):!android:!ios: SUBDIRS += audiolevels
diff --git a/examples/datavisualization/volumetric/doc/src/volumetric.qdoc b/examples/datavisualization/volumetric/doc/src/volumetric.qdoc
new file mode 100644
index 00000000..48651cb1
--- /dev/null
+++ b/examples/datavisualization/volumetric/doc/src/volumetric.qdoc
@@ -0,0 +1,27 @@
+/****************************************************************************
+**
+** 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
+**
+****************************************************************************/
+
+/*!
+ \example volumetric
+ \title Volumetric rendering Example
+ \ingroup qtdatavisualization_examples
+ \brief Rendering volumetric objects.
+
+ TODO
+ \section1 Example contents
+*/
diff --git a/examples/datavisualization/volumetric/main.cpp b/examples/datavisualization/volumetric/main.cpp
new file mode 100644
index 00000000..7e4c9973
--- /dev/null
+++ b/examples/datavisualization/volumetric/main.cpp
@@ -0,0 +1,105 @@
+/****************************************************************************
+**
+** 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 "volumetric.h"
+
+#include <QtWidgets/QApplication>
+#include <QtWidgets/QWidget>
+#include <QtWidgets/QHBoxLayout>
+#include <QtWidgets/QVBoxLayout>
+#include <QtWidgets/QRadioButton>
+#include <QtWidgets/QSlider>
+#include <QtWidgets/QCheckBox>
+#include <QtWidgets/QLabel>
+#include <QtGui/QScreen>
+
+int main(int argc, char **argv)
+{
+ QApplication app(argc, argv);
+ Q3DScatter *graph = new Q3DScatter();
+ QWidget *container = QWidget::createWindowContainer(graph);
+
+ QSize screenSize = graph->screen()->size();
+ container->setMinimumSize(QSize(screenSize.width() / 2, screenSize.height() / 1.5));
+ container->setMaximumSize(screenSize);
+ container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ container->setFocusPolicy(Qt::StrongFocus);
+
+ QWidget *widget = new QWidget;
+ QHBoxLayout *hLayout = new QHBoxLayout(widget);
+ QVBoxLayout *vLayout = new QVBoxLayout();
+ hLayout->addWidget(container, 1);
+ hLayout->addLayout(vLayout);
+
+ widget->setWindowTitle(QStringLiteral("Volumetric Object Example"));
+
+ QCheckBox *sliceXCheckBox = new QCheckBox(widget);
+ sliceXCheckBox->setText(QStringLiteral("Slice volume on X axis"));
+ sliceXCheckBox->setChecked(false);
+ QCheckBox *sliceYCheckBox = new QCheckBox(widget);
+ sliceYCheckBox->setText(QStringLiteral("Slice volume on Y axis"));
+ sliceYCheckBox->setChecked(false);
+ QCheckBox *sliceZCheckBox = new QCheckBox(widget);
+ sliceZCheckBox->setText(QStringLiteral("Slice volume on Z axis"));
+ sliceZCheckBox->setChecked(false);
+
+ QSlider *sliceXSlider = new QSlider(Qt::Horizontal, widget);
+ sliceXSlider->setMinimum(0);
+ sliceXSlider->setMaximum(1024);
+ sliceXSlider->setValue(512);
+ sliceXSlider->setEnabled(true);
+ QSlider *sliceYSlider = new QSlider(Qt::Horizontal, widget);
+ sliceYSlider->setMinimum(0);
+ sliceYSlider->setMaximum(1024);
+ sliceYSlider->setValue(512);
+ sliceYSlider->setEnabled(true);
+ QSlider *sliceZSlider = new QSlider(Qt::Horizontal, widget);
+ sliceZSlider->setMinimum(0);
+ sliceZSlider->setMaximum(1024);
+ sliceZSlider->setValue(512);
+ sliceZSlider->setEnabled(true);
+
+ QLabel *fpsLabel = new QLabel(QStringLiteral("Fps: "), widget);
+
+ vLayout->addWidget(fpsLabel);
+ vLayout->addWidget(sliceXCheckBox);
+ vLayout->addWidget(sliceXSlider);
+ vLayout->addWidget(sliceYCheckBox);
+ vLayout->addWidget(sliceYSlider);
+ vLayout->addWidget(sliceZCheckBox);
+ vLayout->addWidget(sliceZSlider, 1, Qt::AlignTop);
+
+ VolumetricModifier *modifier = new VolumetricModifier(graph);
+ modifier->setFpsLabel(fpsLabel);
+
+ QObject::connect(sliceXCheckBox, &QCheckBox::stateChanged, modifier,
+ &VolumetricModifier::sliceX);
+ QObject::connect(sliceYCheckBox, &QCheckBox::stateChanged, modifier,
+ &VolumetricModifier::sliceY);
+ QObject::connect(sliceZCheckBox, &QCheckBox::stateChanged, modifier,
+ &VolumetricModifier::sliceZ);
+ QObject::connect(sliceXSlider, &QSlider::valueChanged, modifier,
+ &VolumetricModifier::adjustSliceX);
+ QObject::connect(sliceYSlider, &QSlider::valueChanged, modifier,
+ &VolumetricModifier::adjustSliceY);
+ QObject::connect(sliceZSlider, &QSlider::valueChanged, modifier,
+ &VolumetricModifier::adjustSliceZ);
+
+ widget->show();
+ return app.exec();
+}
diff --git a/examples/datavisualization/volumetric/volumetric.cpp b/examples/datavisualization/volumetric/volumetric.cpp
new file mode 100644
index 00000000..a9233cab
--- /dev/null
+++ b/examples/datavisualization/volumetric/volumetric.cpp
@@ -0,0 +1,179 @@
+/****************************************************************************
+**
+** 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 "volumetric.h"
+#include <QtDataVisualization/qvalue3daxis.h>
+#include <QtDataVisualization/q3dscene.h>
+#include <QtDataVisualization/q3dcamera.h>
+#include <QtDataVisualization/q3dtheme.h>
+#include <QtCore/qmath.h>
+#include <QtGui/QRgb>
+#include <QtWidgets/QLabel>
+#include <QtCore/QDebug>
+
+using namespace QtDataVisualization;
+
+const int textureSize = 256;
+
+VolumetricModifier::VolumetricModifier(Q3DScatter *scatter)
+ : m_graph(scatter),
+ m_volumeItem(0),
+ m_sliceIndexX(textureSize / 2),
+ m_sliceIndexY(textureSize / 4),
+ m_sliceIndexZ(textureSize / 2)
+{
+ m_graph->activeTheme()->setType(Q3DTheme::ThemeQt);
+ m_graph->setShadowQuality(QAbstract3DGraph::ShadowQualityNone);
+ m_graph->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPresetFront);
+ m_graph->setOrthoProjection(true);
+
+ createVolume();
+
+ m_graph->addCustomItem(m_volumeItem);
+ m_graph->setMeasureFps(true);
+
+ QObject::connect(m_graph->scene()->activeCamera(), &Q3DCamera::zoomLevelChanged, this,
+ &VolumetricModifier::handleZoomLevelChange);
+ QObject::connect(m_graph, &QAbstract3DGraph::currentFpsChanged, this,
+ &VolumetricModifier::handleFpsChange);
+}
+
+VolumetricModifier::~VolumetricModifier()
+{
+ delete m_graph;
+}
+
+void VolumetricModifier::setFpsLabel(QLabel *fpsLabel)
+{
+ m_fpsLabel = fpsLabel;
+}
+
+void VolumetricModifier::sliceX(int enabled)
+{
+ m_volumeItem->setSliceIndexX(enabled ? m_sliceIndexX : -1);
+}
+
+void VolumetricModifier::sliceY(int enabled)
+{
+ m_volumeItem->setSliceIndexY(enabled ? m_sliceIndexY : -1);
+}
+
+void VolumetricModifier::sliceZ(int enabled)
+{
+ m_volumeItem->setSliceIndexZ(enabled ? m_sliceIndexZ : -1);
+}
+
+void VolumetricModifier::adjustSliceX(int value)
+{
+ m_sliceIndexX = value / (1024 / textureSize);
+ if (m_volumeItem->sliceIndexX() != -1)
+ m_volumeItem->setSliceIndexX(m_sliceIndexX);
+}
+
+void VolumetricModifier::adjustSliceY(int value)
+{
+ m_sliceIndexY = value / (1024 / textureSize * 2);
+ if (m_volumeItem->sliceIndexY() != -1)
+ m_volumeItem->setSliceIndexY(m_sliceIndexY);
+}
+
+void VolumetricModifier::adjustSliceZ(int value)
+{
+ m_sliceIndexZ = value / (1024 / textureSize);
+ if (m_volumeItem->sliceIndexZ() != -1)
+ m_volumeItem->setSliceIndexZ(m_sliceIndexZ);
+}
+
+void VolumetricModifier::handleZoomLevelChange()
+{
+ // Zooming inside volumetric object causes ugly clipping issues, so restrict zoom level a bit
+ if (m_graph->scene()->activeCamera()->zoomLevel() > 220)
+ m_graph->scene()->activeCamera()->setZoomLevel(220);
+}
+
+void VolumetricModifier::handleFpsChange(qreal fps)
+{
+ const QString fpsFormat = QStringLiteral("Fps: %1");
+ int fps10 = int(fps * 10.0);
+ m_fpsLabel->setText(fpsFormat.arg(QString::number(qreal(fps10) / 10.0)));
+}
+
+void VolumetricModifier::createVolume()
+{
+ m_volumeItem = new QCustom3DVolume;
+ m_volumeItem->setScaling(QVector3D(2.0f, 1.0f, 2.0f));
+ m_volumeItem->setTextureWidth(textureSize);
+ m_volumeItem->setTextureHeight(textureSize / 2);
+ m_volumeItem->setTextureDepth(textureSize);
+ m_volumeItem->setTextureFormat(QImage::Format_Indexed8);
+
+ // Generate color table. First color is fully transparent used to fill outer
+ // portions of the volume. The second, red layer, is somewhat transparent.
+ // Rest of to colors are opaque.
+ QVector<QRgb> colors;
+ colors.resize(256);
+
+ colors[0] = qRgba(0, 0, 0, 0);
+ for (int i = 1; i < 256; i++) {
+ if (i < 60) {
+ colors[i] = qRgba((i * 2) + 120, 0, 0, 100);
+ } else if (i < 120) {
+ colors[i] = qRgba(0, ((i - 60) * 2) + 120, 0, 255);
+ } else if (i < 180) {
+ colors[i] = qRgba(0, 0, ((i - 120) * 2) + 120, 255);
+ } else {
+ colors[i] = qRgba(i, i, i, 255);
+ }
+ }
+ m_volumeItem->setColorTable(colors);
+
+ // Generate texture data for an half-ellipsoid.
+ // This can take a while if the dimensions are large.
+ // Note that in real world cases, the texture data is usually be supplied
+ // as a stack of slice images.
+
+ QVector<uchar> *textureData = new QVector<uchar>(textureSize * textureSize * textureSize / 2);
+
+ QVector3D midPoint(float(textureSize) / 2.0f,
+ float(textureSize) / 2.0f,
+ float(textureSize) / 2.0f);
+
+ int index = 0;
+ for (int i = 0; i < textureSize; i++) {
+ for (int j = 0; j < textureSize / 2; j++) {
+ for (int k = 0; k < textureSize; k++) {
+ int colorIndex = 0;
+ // Take a slice out of the ellipsoid
+ if (i >= textureSize / 2 || j >= textureSize / 4 || k >= textureSize / 2) {
+ QVector3D distVec = QVector3D(float(k), float(j * 2), float(i)) - midPoint;
+ float adjLen = qMin(255.0f, (distVec.length() * float(textureSize / 128)));
+ if (adjLen < 230)
+ colorIndex = 255 - int(adjLen);
+ else
+ colorIndex = 0;
+ }
+
+ (*textureData)[index] = colorIndex;
+ index++;
+ }
+ }
+ }
+
+ m_volumeItem->setTextureData(textureData);
+}
+
diff --git a/examples/datavisualization/volumetric/volumetric.h b/examples/datavisualization/volumetric/volumetric.h
new file mode 100644
index 00000000..722d4b48
--- /dev/null
+++ b/examples/datavisualization/volumetric/volumetric.h
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** 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 VOLUMETRICMODIFIER_H
+#define VOLUMETRICMODIFIER_H
+
+#include <QtDataVisualization/q3dscatter.h>
+#include <QtDataVisualization/qcustom3dvolume.h>
+
+class QLabel;
+
+using namespace QtDataVisualization;
+
+class VolumetricModifier : public QObject
+{
+ Q_OBJECT
+public:
+ explicit VolumetricModifier(Q3DScatter *scatter);
+ ~VolumetricModifier();
+
+ void setFpsLabel(QLabel *fpsLabel);
+
+public slots:
+ void sliceX(int enabled);
+ void sliceY(int enabled);
+ void sliceZ(int enabled);
+ void adjustSliceX(int value);
+ void adjustSliceY(int value);
+ void adjustSliceZ(int value);
+ void handleZoomLevelChange();
+ void handleFpsChange(qreal fps);
+
+private:
+ void createVolume();
+
+ Q3DScatter *m_graph;
+ QCustom3DVolume *m_volumeItem;
+ int m_sliceIndexX;
+ int m_sliceIndexY;
+ int m_sliceIndexZ;
+ QLabel *m_fpsLabel;
+};
+
+#endif
diff --git a/examples/datavisualization/volumetric/volumetric.pro b/examples/datavisualization/volumetric/volumetric.pro
new file mode 100644
index 00000000..c5e31891
--- /dev/null
+++ b/examples/datavisualization/volumetric/volumetric.pro
@@ -0,0 +1,15 @@
+android|ios {
+ error( "This example is not supported for android or ios." )
+}
+
+!include( ../examples.pri ) {
+ error( "Couldn't find the examples.pri file!" )
+}
+
+SOURCES += main.cpp volumetric.cpp
+HEADERS += volumetric.h
+
+QT += widgets
+
+OTHER_FILES += doc/src/* \
+ doc/images/*
diff --git a/src/datavisualization/data/customrenderitem.cpp b/src/datavisualization/data/customrenderitem.cpp
index a1c70057..b96a4957 100644
--- a/src/datavisualization/data/customrenderitem.cpp
+++ b/src/datavisualization/data/customrenderitem.cpp
@@ -23,9 +23,20 @@ QT_BEGIN_NAMESPACE_DATAVISUALIZATION
CustomRenderItem::CustomRenderItem()
: AbstractRenderItem(),
m_texture(0),
+ m_absolute(false),
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_textureWidth(0),
+ m_textureHeight(0),
+ m_textureDepth(0),
+ m_isVolume(false)
{
}
@@ -39,4 +50,16 @@ void CustomRenderItem::setMesh(const QString &meshFile)
ObjectHelper::resetObjectHelper(m_renderer, m_object, meshFile);
}
+void CustomRenderItem::setColorTable(const QVector<QRgb> &colors)
+{
+ m_colorTable.resize(colors.size());
+ for (int i = 0; i < m_colorTable.size(); i++) {
+ 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);
+ }
+}
+
QT_END_NAMESPACE_DATAVISUALIZATION
diff --git a/src/datavisualization/data/customrenderitem_p.h b/src/datavisualization/data/customrenderitem_p.h
index 4fb94276..e21b6f39 100644
--- a/src/datavisualization/data/customrenderitem_p.h
+++ b/src/datavisualization/data/customrenderitem_p.h
@@ -31,6 +31,8 @@
#include "abstractrenderitem_p.h"
#include "objecthelper_p.h"
+#include <QtGui/QRgb>
+#include <QtGui/QImage>
QT_BEGIN_NAMESPACE_DATAVISUALIZATION
@@ -69,6 +71,28 @@ public:
inline bool isFacingCamera() const { return m_isFacingCamera; }
inline void setRenderer(Abstract3DRenderer *renderer) { m_renderer = renderer; }
+ // Volume specific
+ inline void setTextureWidth(int width) { m_textureWidth = width; }
+ inline int textureWidth() const { return m_textureWidth; }
+ inline void setTextureHeight(int height) { m_textureHeight = height; }
+ inline int textureHeight() const { return m_textureHeight; }
+ inline void setTextureDepth(int depth) { m_textureDepth = depth; }
+ 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; }
+ inline void setSliceIndexY(int index) { m_sliceIndexY = index; }
+ inline void setSliceIndexZ(int index) { m_sliceIndexZ = index; }
+ int sliceIndexX() const { return m_sliceIndexX; }
+ int sliceIndexY() const { return m_sliceIndexY; }
+ int sliceIndexZ() const { return m_sliceIndexZ; }
+
private:
Q_DISABLE_COPY(CustomRenderItem)
@@ -85,6 +109,17 @@ private:
bool m_isFacingCamera;
QCustom3DItem *m_item;
Abstract3DRenderer *m_renderer;
+
+ // 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;
};
typedef QHash<QCustom3DItem *, CustomRenderItem *> CustomRenderItemArray;
diff --git a/src/datavisualization/data/data.pri b/src/datavisualization/data/data.pri
index 37a8d084..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,6 +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/qcustom3ditem.cpp b/src/datavisualization/data/qcustom3ditem.cpp
index efee3b15..1d419cee 100644
--- a/src/datavisualization/data/qcustom3ditem.cpp
+++ b/src/datavisualization/data/qcustom3ditem.cpp
@@ -373,7 +373,8 @@ QCustom3DItemPrivate::QCustom3DItemPrivate(QCustom3DItem *q) :
m_rotation(QQuaternion(0.0f, 0.0f, 0.0f, 0.0f)),
m_visible(true),
m_shadowCasting(true),
- m_isLabelItem(false)
+ m_isLabelItem(false),
+ m_isVolumeItem(false)
{
}
@@ -388,7 +389,8 @@ QCustom3DItemPrivate::QCustom3DItemPrivate(QCustom3DItem *q, const QString &mesh
m_rotation(rotation),
m_visible(true),
m_shadowCasting(true),
- m_isLabelItem(false)
+ m_isLabelItem(false),
+ m_isVolumeItem(false)
{
}
diff --git a/src/datavisualization/data/qcustom3ditem_p.h b/src/datavisualization/data/qcustom3ditem_p.h
index c1ce5996..d766bcf3 100644
--- a/src/datavisualization/data/qcustom3ditem_p.h
+++ b/src/datavisualization/data/qcustom3ditem_p.h
@@ -82,6 +82,7 @@ public:
bool m_shadowCasting;
bool m_isLabelItem;
+ bool m_isVolumeItem;
QCustomItemDirtyBitField m_dirtyBits;
diff --git a/src/datavisualization/data/qcustom3dvolume.cpp b/src/datavisualization/data/qcustom3dvolume.cpp
new file mode 100644
index 00000000..766f1589
--- /dev/null
+++ b/src/datavisualization/data/qcustom3dvolume.cpp
@@ -0,0 +1,669 @@
+/****************************************************************************
+**
+** 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.
+ *
+ * \note Volumetric objects are only supported with orthographic projection.
+ *
+ * \sa QAbstract3DGraph::addCustomItem(), QAbstract3DGraph::orthoProjection
+ */
+
+/*!
+ * \qmltype Custom3DVolume
+ * \inqmlmodule QtDataVisualization
+ * \since QtDataVisualization 1.2
+ * \ingroup datavisualization_qml
+ * \instantiates QCustom3DVolume
+ * \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.
+ *
+ * \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 QML ui.
+ *
+ * \note Volumetric objects are only supported with orthographic projection.
+ *
+ * \sa AbstractGraph3D::orthoProjection
+ */
+
+/*! \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 for that dimension is drawn.
+ * If all dimensions are negative, no slices are drawn and the volume is drawn normally.
+ * Defaults to \c{-1}.
+ *
+ * \sa QCustom3DVolume::textureData
+ */
+
+/*! \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 for that dimension is drawn.
+ * If all dimensions are negative, no slices are drawn and the volume is drawn normally.
+ * Defaults to \c{-1}.
+ *
+ * \sa QCustom3DVolume::textureData
+ */
+
+/*! \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 for that dimension is drawn.
+ * If all dimensions are negative, no slices are drawn and the volume is drawn normally.
+ * Defaults to \c{-1}.
+ *
+ * \sa QCustom3DVolume::textureData
+ */
+
+/*!
+ * 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, textureFormat, 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, textureFormat, 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, textureFormat
+ */
+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, textureFormat
+ */
+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);
+ setTextureWidth(height);
+ setTextureWidth(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 for that dimension is drawn.
+ * If all dimensions are negative, no slices are drawn and the volume is drawn normally.
+ * Defaults to \c{-1}.
+ *
+ * \sa textureData
+ */
+void QCustom3DVolume::setSliceIndexX(int value)
+{
+ if (dptr()->m_sliceIndexX != value) {
+ dptr()->m_sliceIndexX = value;
+ dptr()->m_dirtyBitsVolume.sliceIndicesDirty = 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 for that dimension is drawn.
+ * If all dimensions are negative, no slices are drawn and the volume is drawn normally.
+ * Defaults to \c{-1}.
+ *
+ * \sa textureData
+ */
+void QCustom3DVolume::setSliceIndexY(int value)
+{
+ if (dptr()->m_sliceIndexY != value) {
+ dptr()->m_sliceIndexY = value;
+ dptr()->m_dirtyBitsVolume.sliceIndicesDirty = 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 for that dimension is drawn.
+ * If all dimensions are negative, no slices are drawn and the volume is drawn normally.
+ * Defaults to \c{-1}.
+ *
+ * \sa textureData
+ */
+void QCustom3DVolume::setSliceIndexZ(int value)
+{
+ if (dptr()->m_sliceIndexZ != value) {
+ dptr()->m_sliceIndexZ = value;
+ dptr()->m_dirtyBitsVolume.sliceIndicesDirty = 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, textureFormat, 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.
+ *
+ * Defaults to \c{0}.
+ *
+ * \sa colorTable, textureFormat, 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, textureFormat
+ */
+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.
+ * The \a depthIndex parameter specifies the subtexture to set.
+ * The texture\a data must be in the format specified by textureFormat property and have size of
+ * (\c{textureDataWidth * textureHeight * texture format color depth in bytes}).
+ *
+ * \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.
+ *
+ * \sa textureData
+ */
+void QCustom3DVolume::setSubTextureData(int depthIndex, const uchar *data)
+{
+ if (data) {
+ int frameSize = textureDataWidth() * dptr()->m_textureHeight;
+ int startIndex = depthIndex * frameSize;
+
+ if (depthIndex >= dptr()->m_textureDepth
+ || (startIndex + frameSize) > dptr()->m_textureData->size()) {
+ qWarning() << __FUNCTION__ << "Attempted to set invalid subtexture.";
+ } else {
+ void *subTexPtr = dptr()->m_textureData->data() + startIndex;
+ 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 to a source \a image;
+ * The \a depthIndex parameter specifies the subtexture to set.
+ * The 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 (\c{textureWidth * textureHeight}).
+ *
+ * \sa textureData
+ */
+void QCustom3DVolume::setSubTextureData(int depthIndex, const QImage &image)
+{
+ if (image.width() == dptr()->m_textureWidth
+ && image.height() == dptr()->m_textureHeight
+ && (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(depthIndex, convertedImage.bits());
+ } else {
+ qWarning() << __FUNCTION__ << "Invalid image size or format.";
+ }
+}
+
+/*! \property QCustom3DVolume::textureFormat
+ *
+ * The format of the textureData. 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.";
+ }
+}
+
+QImage::Format QCustom3DVolume::textureFormat() const
+{
+ return dptrc()->m_textureFormat;
+}
+
+
+/*!
+ * \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_sliceIndexX(-1),
+ m_sliceIndexY(-1),
+ m_sliceIndexZ(-1),
+ m_textureWidth(0),
+ m_textureHeight(0),
+ m_textureDepth(0),
+ m_textureFormat(QImage::Format_ARGB32),
+ m_textureData(0)
+{
+ 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_sliceIndexX(-1),
+ m_sliceIndexY(-1),
+ m_sliceIndexZ(-1),
+ m_textureWidth(textureWidth),
+ m_textureHeight(textureHeight),
+ m_textureDepth(textureDepth),
+ m_textureFormat(textureFormat),
+ m_colorTable(colorTable),
+ m_textureData(textureData)
+{
+ 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_colorTable.size() != 256)
+ m_colorTable.clear();
+
+ 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.sliceIndicesDirty = false;
+ m_dirtyBitsVolume.colorTableDirty = false;
+ m_dirtyBitsVolume.textureDataDirty = false;
+ m_dirtyBitsVolume.textureFormatDirty = false;
+}
+
+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..498922e8
--- /dev/null
+++ b/src/datavisualization/data/qcustom3dvolume.h
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** 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(QImage::Format textureFormat READ textureFormat WRITE setTextureFormat NOTIFY textureFormatChanged)
+
+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(int depthIndex, const uchar *data);
+ void setSubTextureData(int depthIndex, const QImage &image);
+
+ void setTextureFormat(QImage::Format format);
+ QImage::Format textureFormat() const;
+
+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);
+
+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..69dd1eb2
--- /dev/null
+++ b/src/datavisualization/data/qcustom3dvolume_p.h
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** 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 sliceIndicesDirty : 1;
+ bool colorTableDirty : 1;
+ bool textureDataDirty : 1;
+ bool textureFormatDirty : 1;
+
+ QCustomVolumeDirtyBitField()
+ : textureDimensionsDirty(false),
+ sliceIndicesDirty(false),
+ colorTableDirty(false),
+ textureDataDirty(false),
+ textureFormatDirty(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();
+
+ 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;
+
+ QCustomVolumeDirtyBitField m_dirtyBitsVolume;
+
+private:
+ friend class QCustom3DVolume;
+};
+
+QT_END_NAMESPACE_DATAVISUALIZATION
+
+#endif
diff --git a/src/datavisualization/engine/abstract3drenderer.cpp b/src/datavisualization/engine/abstract3drenderer.cpp
index 14cf7109..cbb90af0 100644
--- a/src/datavisualization/engine/abstract3drenderer.cpp
+++ b/src/datavisualization/engine/abstract3drenderer.cpp
@@ -24,6 +24,7 @@
#include "shaderhelper_p.h"
#include "qcustom3ditem_p.h"
#include "qcustom3dlabel_p.h"
+#include "qcustom3dvolume_p.h"
#include <QtCore/qmath.h>
@@ -57,6 +58,8 @@ Abstract3DRenderer::Abstract3DRenderer(Abstract3DController *controller)
m_selectionLabelItem(0),
m_visibleSeriesCount(0),
m_customItemShader(0),
+ m_volumeTextureShader(0),
+ m_volumeTextureSliceShader(0),
m_useOrthoProjection(false),
m_xFlipped(false),
m_yFlipped(false),
@@ -96,6 +99,8 @@ Abstract3DRenderer::~Abstract3DRenderer()
delete m_cachedTheme;
delete m_selectionLabelItem;
delete m_customItemShader;
+ delete m_volumeTextureShader;
+ delete m_volumeTextureSliceShader;
foreach (SeriesRenderCache *cache, m_renderCacheList) {
cache->cleanup(m_textureHelper);
@@ -196,6 +201,20 @@ void Abstract3DRenderer::initCustomItemShaders(const QString &vertexShader,
m_customItemShader->initialize();
}
+void Abstract3DRenderer::initVolumeTextureShaders(const QString &vertexShader,
+ const QString &fragmentShader,
+ const QString &sliceShader)
+{
+ if (m_volumeTextureShader)
+ delete m_volumeTextureShader;
+ m_volumeTextureShader = new ShaderHelper(this, vertexShader, fragmentShader);
+ m_volumeTextureShader->initialize();
+ if (m_volumeTextureSliceShader)
+ delete m_volumeTextureSliceShader;
+ m_volumeTextureSliceShader = new ShaderHelper(this, vertexShader, sliceShader);
+ m_volumeTextureSliceShader->initialize();
+}
+
void Abstract3DRenderer::updateTheme(Q3DTheme *theme)
{
// Synchronize the controller theme with renderer
@@ -279,6 +298,9 @@ void Abstract3DRenderer::reInitShaders()
initCustomItemShaders(QStringLiteral(":/shaders/vertexTexture"),
QStringLiteral(":/shaders/fragmentTexture"));
}
+ initVolumeTextureShaders(QStringLiteral(":/shaders/vertexTexture3D"),
+ QStringLiteral(":/shaders/fragmentTexture3D"),
+ QStringLiteral(":/shaders/fragmentTexture3DSlice"));
#else
initGradientShaders(QStringLiteral(":/shaders/vertex"),
QStringLiteral(":/shaders/fragmentColorOnYES2"));
@@ -906,6 +928,7 @@ CustomRenderItem *Abstract3DRenderer::addCustomItem(QCustom3DItem *item)
QVector3D scaling = item->scaling();
QImage textureImage = item->d_ptr->textureImage();
bool facingCamera = false;
+ GLuint texture;
if (item->d_ptr->m_isLabelItem) {
QCustom3DLabel *labelItem = static_cast<QCustom3DLabel *>(item);
float pointSize = labelItem->font().pointSizeF();
@@ -925,13 +948,39 @@ CustomRenderItem *Abstract3DRenderer::addCustomItem(QCustom3DItem *item)
scaling.setY(scaling.y() * textureImage.height() * scaledFontSize);
// Check if facing camera
facingCamera = labelItem->isFacingCamera();
+#if !defined(QT_OPENGL_ES_2)
+ } else if (item->d_ptr->m_isVolumeItem) {
+ 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());
+#endif
}
newItem->setScaling(scaling);
newItem->setRotation(item->rotation());
newItem->setPosition(item->position());
newItem->setPositionAbsolute(item->isPositionAbsolute());
- newItem->setBlendNeeded(textureImage.hasAlphaChannel());
- GLuint texture = m_textureHelper->create2DTexture(textureImage, true, true, true);
+#if !defined(QT_OPENGL_ES_2)
+ // In OpenGL ES we simply draw volumes as regular custom item placeholders.
+ if (!item->d_ptr->m_isVolumeItem)
+#endif
+ {
+ newItem->setBlendNeeded(textureImage.hasAlphaChannel());
+ texture = m_textureHelper->create2DTexture(textureImage, true, true, true);
+ }
newItem->setTexture(texture);
item->d_ptr->clearTextureImage();
QVector3D translation = convertPositionToTranslation(item->position(),
@@ -996,12 +1045,17 @@ void Abstract3DRenderer::updateCustomItem(CustomRenderItem *renderItem)
m_cachedTheme->isLabelBorderEnabled());
textureImage = item->d_ptr->textureImage();
}
+ } else
+#if !defined(QT_OPENGL_ES_2)
+ if (!item->d_ptr->m_isVolumeItem)
+#endif
+ {
+ 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;
}
@@ -1028,6 +1082,39 @@ void Abstract3DRenderer::updateCustomItem(CustomRenderItem *renderItem)
renderItem->setFacingCamera(labelItem->isFacingCamera());
labelItem->dptr()->m_facingCameraDirty = false;
}
+#if !defined(QT_OPENGL_ES_2)
+ } else if (item->d_ptr->m_isVolumeItem) {
+ 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.sliceIndicesDirty) {
+ renderItem->setSliceIndexX(volumeItem->sliceIndexX());
+ renderItem->setSliceIndexY(volumeItem->sliceIndexY());
+ renderItem->setSliceIndexZ(volumeItem->sliceIndexZ());
+ volumeItem->dptr()->m_dirtyBitsVolume.sliceIndicesDirty = false;
+ }
+#endif
}
}
@@ -1042,7 +1129,9 @@ void Abstract3DRenderer::updateCustomItemPositions()
#ifdef USE_REFLECTIONS
void Abstract3DRenderer::drawCustomItems(RenderingState state,
- ShaderHelper *shader,
+ ShaderHelper *regularShader,
+ ShaderHelper *volumeShader,
+ ShaderHelper *volumeSliceShader,
const QMatrix4x4 &viewMatrix,
const QMatrix4x4 &projectionViewMatrix,
const QMatrix4x4 &depthProjectionViewMatrix,
@@ -1051,7 +1140,9 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state,
GLfloat reflection)
#else
void Abstract3DRenderer::drawCustomItems(RenderingState state,
- ShaderHelper *shader,
+ ShaderHelper *regularShader,
+ ShaderHelper *volumeShader,
+ ShaderHelper *volumeSliceShader,
const QMatrix4x4 &viewMatrix,
const QMatrix4x4 &projectionViewMatrix,
const QMatrix4x4 &depthProjectionViewMatrix,
@@ -1059,11 +1150,17 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state,
GLfloat shadowQuality)
#endif
{
+#if defined(QT_OPENGL_ES_2)
+ Q_UNUSED(volumeShader)
+ Q_UNUSED(volumeSliceShader)
+#endif
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(),
@@ -1137,6 +1234,22 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state,
if (RenderingNormal == state) {
// Normal render
+#if !defined(QT_OPENGL_ES_2)
+ ShaderHelper *prevShader = shader;
+ if (item->isVolume()) {
+ if (item->sliceIndexX() >= 0
+ || item->sliceIndexY() >= 0
+ || item->sliceIndexZ() >= 0) {
+ shader = volumeSliceShader;
+ } else {
+ shader = volumeShader;
+ }
+ } else {
+ shader = regularShader;
+ }
+ if (shader != prevShader)
+ shader->bind();
+#endif
shader->setUniformValue(shader->model(), modelMatrix);
shader->setUniformValue(shader->MVP(), MVPMatrix);
shader->setUniformValue(shader->nModel(), itModelMatrix.inverted().transposed());
@@ -1144,14 +1257,17 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state,
if (item->isBlendNeeded()) {
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- glDisable(GL_CULL_FACE);
+#if !defined(QT_OPENGL_ES_2)
+ if (!item->isVolume())
+#endif
+ glDisable(GL_CULL_FACE);
} else {
glDisable(GL_BLEND);
glEnable(GL_CULL_FACE);
}
#if !defined(QT_OPENGL_ES_2)
- if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone) {
+ if (m_cachedShadowQuality > QAbstract3DGraph::ShadowQualityNone && !item->isVolume()) {
// Set shadow shader bindings
shader->setUniformValue(shader->shadowQ(), shadowQuality);
shader->setUniformValue(shader->depth(), depthProjectionViewMatrix * modelMatrix);
@@ -1164,8 +1280,34 @@ void Abstract3DRenderer::drawCustomItems(RenderingState state,
#endif
{
// Set shadowless shader bindings
- shader->setUniformValue(shader->lightS(), m_cachedTheme->lightStrength());
- m_drawer->drawObject(shader, item->mesh(), item->texture());
+#if !defined(QT_OPENGL_ES_2)
+ if (item->isVolume()) {
+ // Volume shaders repurpose light position for camera position relative to item
+ QVector3D cameraPos = m_cachedScene->activeCamera()->position();
+ cameraPos = MVPMatrix.inverted().map(cameraPos);
+ 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);
+ if (shader == volumeSliceShader) {
+ QVector3D slices((float(item->sliceIndexX()) + 0.5f)
+ / float(item->textureWidth()) * 2.0 - 1.0,
+ (float(item->sliceIndexY()) + 0.5f)
+ / float(item->textureHeight()) * 2.0 - 1.0,
+ (float(item->sliceIndexZ()) + 0.5f)
+ / float(item->textureDepth()) * 2.0 - 1.0);
+ shader->setUniformValue(shader->volumeSliceIndices(), slices);
+ }
+ m_drawer->drawObject(shader, item->mesh(), 0, 0, item->texture());
+ } else
+#endif
+ {
+ shader->setUniformValue(shader->lightS(), m_cachedTheme->lightStrength());
+ m_drawer->drawObject(shader, item->mesh(), item->texture());
+ }
}
} else if (RenderingSelection == state) {
// Selection render
@@ -1203,6 +1345,9 @@ void Abstract3DRenderer::drawRadialGrid(ShaderHelper *shader, float yFloorLinePo
const QMatrix4x4 &projectionViewMatrix,
const QMatrix4x4 &depthMatrix)
{
+#if defined(QT_OPENGL_ES_2)
+ Q_UNUSED(depthMatrix)
+#endif
static QVector<QQuaternion> lineRotations;
if (!lineRotations.size()) {
lineRotations.resize(polarGridRoundness);
@@ -1264,6 +1409,9 @@ void Abstract3DRenderer::drawAngularGrid(ShaderHelper *shader, float yFloorLineP
const QMatrix4x4 &projectionViewMatrix,
const QMatrix4x4 &depthMatrix)
{
+#if defined(QT_OPENGL_ES_2)
+ Q_UNUSED(depthMatrix)
+#endif
float halfRatio((m_polarRadius + (labelMargin / 2.0f)) / 2.0f);
QVector3D gridLineScaler(gridLineWidth, gridLineWidth, halfRatio);
int gridLineCount = m_axisCacheX.gridLineCount();
diff --git a/src/datavisualization/engine/abstract3drenderer_p.h b/src/datavisualization/engine/abstract3drenderer_p.h
index 50370954..7874a1ad 100644
--- a/src/datavisualization/engine/abstract3drenderer_p.h
+++ b/src/datavisualization/engine/abstract3drenderer_p.h
@@ -94,6 +94,9 @@ public:
const QString &fragmentShader) = 0;
virtual void initCustomItemShaders(const QString &vertexShader,
const QString &fragmentShader);
+ virtual void initVolumeTextureShaders(const QString &vertexShader,
+ const QString &fragmentShader,
+ const QString &sliceShader);
virtual void updateAxisType(QAbstract3DAxis::AxisOrientation orientation,
QAbstract3DAxis::AxisType type);
virtual void updateAxisTitle(QAbstract3DAxis::AxisOrientation orientation,
@@ -145,13 +148,15 @@ public:
QString &selectionLabel();
#ifdef USE_REFLECTIONS
- void drawCustomItems(RenderingState state, ShaderHelper *shader,
+ void drawCustomItems(RenderingState state, ShaderHelper *regularShader,
+ ShaderHelper *volumeShader, ShaderHelper *volumeSliceShader,
const QMatrix4x4 &viewMatrix,
const QMatrix4x4 &projectionViewMatrix,
const QMatrix4x4 &depthProjectionViewMatrix,
GLuint depthTexture, GLfloat shadowQuality, GLfloat reflection = 1.0f);
#else
- void drawCustomItems(RenderingState state, ShaderHelper *shader,
+ void drawCustomItems(RenderingState state, ShaderHelper *regularShader,
+ ShaderHelper *volumeShader, ShaderHelper *volumeSliceShader,
const QMatrix4x4 &viewMatrix,
const QMatrix4x4 &projectionViewMatrix,
const QMatrix4x4 &depthProjectionViewMatrix,
@@ -247,6 +252,8 @@ protected:
int m_visibleSeriesCount;
ShaderHelper *m_customItemShader;
+ ShaderHelper *m_volumeTextureShader;
+ ShaderHelper *m_volumeTextureSliceShader;
bool m_useOrthoProjection;
bool m_xFlipped;
diff --git a/src/datavisualization/engine/bars3drenderer.cpp b/src/datavisualization/engine/bars3drenderer.cpp
index 38fa3147..085b881e 100644
--- a/src/datavisualization/engine/bars3drenderer.cpp
+++ b/src/datavisualization/engine/bars3drenderer.cpp
@@ -1093,9 +1093,11 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
}
}
- Abstract3DRenderer::drawCustomItems(RenderingDepth, m_depthShader, viewMatrix,
- projectionViewMatrix, depthProjectionViewMatrix,
- m_depthTexture, m_shadowQualityToShader);
+ Abstract3DRenderer::drawCustomItems(RenderingDepth, m_depthShader, m_volumeTextureShader,
+ m_volumeTextureSliceShader, viewMatrix,
+ projectionViewMatrix,
+ depthProjectionViewMatrix, m_depthTexture,
+ m_shadowQualityToShader);
// Disable drawing to depth framebuffer (= enable drawing to screen)
glBindFramebuffer(GL_FRAMEBUFFER, defaultFboHandle);
@@ -1179,7 +1181,9 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
}
}
glCullFace(GL_BACK);
- Abstract3DRenderer::drawCustomItems(RenderingSelection, m_selectionShader, viewMatrix,
+ Abstract3DRenderer::drawCustomItems(RenderingSelection, m_selectionShader,
+ m_volumeTextureShader, m_volumeTextureSliceShader,
+ viewMatrix,
projectionViewMatrix, depthProjectionViewMatrix,
m_depthTexture, m_shadowQualityToShader);
drawLabels(true, activeCamera, viewMatrix, projectionMatrix);
@@ -1267,7 +1271,8 @@ void Bars3DRenderer::drawScene(GLuint defaultFboHandle)
drawGridLines(depthProjectionViewMatrix, projectionViewMatrix, viewMatrix);
// Draw custom items
- Abstract3DRenderer::drawCustomItems(RenderingNormal, m_customItemShader, viewMatrix,
+ Abstract3DRenderer::drawCustomItems(RenderingNormal, m_customItemShader, m_volumeTextureShader,
+ m_volumeTextureSliceShader, viewMatrix,
projectionViewMatrix, depthProjectionViewMatrix,
m_depthTexture, m_shadowQualityToShader);
diff --git a/src/datavisualization/engine/drawer.cpp b/src/datavisualization/engine/drawer.cpp
index b8726840..4191efc4 100644
--- a/src/datavisualization/engine/drawer.cpp
+++ b/src/datavisualization/engine/drawer.cpp
@@ -93,8 +93,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 +111,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());
@@ -139,6 +150,12 @@ void Drawer::drawObject(ShaderHelper *shader, AbstractObjectHelper *object, GLui
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);
diff --git a/src/datavisualization/engine/drawer_p.h b/src/datavisualization/engine/drawer_p.h
index ffcea315..709503dd 100644
--- a/src/datavisualization/engine/drawer_p.h
+++ b/src/datavisualization/engine/drawer_p.h
@@ -75,7 +75,7 @@ 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);
diff --git a/src/datavisualization/engine/engine.qrc b/src/datavisualization/engine/engine.qrc
index f788b5df..936ed53f 100644
--- a/src/datavisualization/engine/engine.qrc
+++ b/src/datavisualization/engine/engine.qrc
@@ -59,5 +59,8 @@
<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>
</qresource>
</RCC>
diff --git a/src/datavisualization/engine/scatter3drenderer.cpp b/src/datavisualization/engine/scatter3drenderer.cpp
index 09225b99..13f6e02c 100644
--- a/src/datavisualization/engine/scatter3drenderer.cpp
+++ b/src/datavisualization/engine/scatter3drenderer.cpp
@@ -538,9 +538,11 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
}
}
- Abstract3DRenderer::drawCustomItems(RenderingDepth, m_depthShader, viewMatrix,
- projectionViewMatrix, depthProjectionViewMatrix,
- m_depthTexture, m_shadowQualityToShader);
+ Abstract3DRenderer::drawCustomItems(RenderingDepth, m_depthShader, m_volumeTextureShader,
+ m_volumeTextureSliceShader, viewMatrix,
+ projectionViewMatrix,
+ depthProjectionViewMatrix, m_depthTexture,
+ m_shadowQualityToShader);
// Disable drawing to framebuffer (= enable drawing to screen)
glBindFramebuffer(GL_FRAMEBUFFER, defaultFboHandle);
@@ -641,9 +643,11 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
}
}
- Abstract3DRenderer::drawCustomItems(RenderingSelection, m_selectionShader, viewMatrix,
- projectionViewMatrix, depthProjectionViewMatrix,
- m_depthTexture, m_shadowQualityToShader);
+ Abstract3DRenderer::drawCustomItems(RenderingSelection, m_selectionShader,
+ m_volumeTextureShader, m_volumeTextureSliceShader,
+ viewMatrix, projectionViewMatrix,
+ depthProjectionViewMatrix, m_depthTexture,
+ m_shadowQualityToShader);
drawLabels(true, activeCamera, viewMatrix, projectionMatrix);
@@ -1407,7 +1411,8 @@ void Scatter3DRenderer::drawScene(const GLuint defaultFboHandle)
}
}
- Abstract3DRenderer::drawCustomItems(RenderingNormal, m_customItemShader, viewMatrix,
+ Abstract3DRenderer::drawCustomItems(RenderingNormal, m_customItemShader, m_volumeTextureShader,
+ m_volumeTextureSliceShader, viewMatrix,
projectionViewMatrix, depthProjectionViewMatrix,
m_depthTexture, m_shadowQualityToShader);
diff --git a/src/datavisualization/engine/shaders/default.frag b/src/datavisualization/engine/shaders/default.frag
index c03b1054..0276ed95 100644
--- a/src/datavisualization/engine/shaders/default.frag
+++ b/src/datavisualization/engine/shaders/default.frag
@@ -33,5 +33,6 @@ void main() {
materialDiffuseColor * lightStrength * pow(cosTheta, 2) / distance +
materialSpecularColor * lightStrength * pow(cosAlpha, 5) / distance;
gl_FragColor.a = color_mdl.a;
+ gl_FragColor = clamp(gl_FragColor, 0.0, 1.0);
}
diff --git a/src/datavisualization/engine/shaders/texture3d.frag b/src/datavisualization/engine/shaders/texture3d.frag
new file mode 100644
index 00000000..054b59cb
--- /dev/null
+++ b/src/datavisualization/engine/shaders/texture3d.frag
@@ -0,0 +1,70 @@
+#version 120
+
+varying highp vec3 pos;
+
+uniform highp sampler3D textureSampler;
+uniform highp vec3 cameraPositionRelativeToModel;
+uniform highp vec4 colorIndex[256];
+uniform highp int color8Bit;
+
+const float maxDist = sqrt(2.0);
+const int sampleCount = 1024;
+const float alphaThreshold = 0.001;
+void main() {
+ // Raytrace into volume, need to sample pixels along the eye ray until we hit opacity 1
+
+ // Find out where ray intersects the object
+ highp vec3 rayDir = -(cameraPositionRelativeToModel - pos);
+ highp vec3 invRayDir = 1.0 / rayDir;
+ highp vec3 minCorner = vec3(-1.0);
+ highp vec3 maxCorner = vec3(1.0);
+ highp vec3 t1 = invRayDir * (minCorner - pos);
+ highp vec3 t2 = invRayDir * (maxCorner - pos);
+ highp vec3 tmin = min(t1, t2);
+ highp vec3 tmax = max(t1, t2);
+ highp vec2 t = max(tmin.xx, tmin.yz);
+ t = min(tmax.xx, tmax.yz);
+ float tFar = min(t.x, t.y);
+ highp vec3 rayStart = pos;
+ // Flip Y and Z so QImage bits work directly for texture and first image is in the front
+ rayStart.yz = -rayStart.yz;
+ highp vec3 rayStop = pos + rayDir * tFar;
+ rayStop.yz = -rayStop.yz;
+
+ // Convert intersections to texture coords
+ rayStart = 0.5 * (rayStart + 1.0);
+ rayStop = 0.5 * (rayStop + 1.0);
+
+ highp vec3 curPos = rayStart;
+ highp float fullDist = distance(rayStop, rayStart);
+ highp float stepSize = maxDist / float(sampleCount); // TODO: Stepsize needs to be improved
+ highp vec3 step = normalize(rayStop - rayStart) * stepSize;
+ highp float totalDist = 0.0;
+ highp float totalAlpha = 0.0;
+ highp vec4 destColor = vec4(0, 0, 0, 0);
+ highp vec4 curColor = vec4(0, 0, 0, 0);
+ highp vec3 curRgb = vec3(0, 0, 0);
+ highp float curAlpha = 0.0;
+
+ for (int i = 0; i < sampleCount; i++) {
+ curColor = texture3D(textureSampler, curPos);
+ if (color8Bit != 0)
+ curColor = colorIndex[int(curColor.r * 255.0)];
+
+ curAlpha = curColor.a;
+ if (curAlpha > alphaThreshold) {
+ curRgb = curColor.rgb * curAlpha * (1.0 - totalAlpha);
+ destColor.rgb += curRgb;
+ totalAlpha += curAlpha;
+ }
+ curPos += step;
+ totalDist += stepSize;
+ if (totalDist > fullDist || totalAlpha >= 1.0) {
+ break;
+ }
+ }
+
+ destColor.a = totalAlpha;
+ 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..cad1ce06
--- /dev/null
+++ b/src/datavisualization/engine/shaders/texture3d.vert
@@ -0,0 +1,12 @@
+uniform highp mat4 MVP;
+
+attribute highp vec3 vertexPosition_mdl;
+attribute highp vec2 vertexUV;
+attribute highp vec3 vertexNormal_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/texture3dslice.frag b/src/datavisualization/engine/shaders/texture3dslice.frag
new file mode 100644
index 00000000..641b32a5
--- /dev/null
+++ b/src/datavisualization/engine/shaders/texture3dslice.frag
@@ -0,0 +1,119 @@
+#version 120
+
+varying highp vec3 pos;
+
+uniform highp sampler3D textureSampler;
+uniform highp vec3 cameraPositionRelativeToModel;
+uniform highp vec3 volumeSliceIndices;
+uniform highp vec4 colorIndex[256];
+uniform highp int color8Bit;
+
+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);
+
+const float alphaThreshold = 0.001;
+void main() {
+ // Raytrace into volume, need to sample pixels along the eye ray until we hit opacity 1
+
+ // Find out where ray intersects the slice planes
+ highp vec3 rayDir = -(cameraPositionRelativeToModel - pos);
+ highp vec3 rayStart = pos;
+ // Flip Y and Z so QImage bits work directly for texture and first image is in the front
+ rayStart.yz = -rayStart.yz;
+ rayDir.yz = -rayDir.yz;
+ highp vec3 invRayDir = 1.0 / rayDir;
+ highp vec3 minCorner = vec3(-1.0);
+ highp vec3 maxCorner = vec3(1.0);
+ highp vec3 t1 = invRayDir * (minCorner - rayStart);
+ highp vec3 t2 = invRayDir * (maxCorner - rayStart);
+ highp vec3 tmin = min(t1, t2);
+ highp vec3 tmax = max(t1, t2);
+ highp vec2 t = max(tmin.xx, tmin.yz);
+ t = min(tmax.xx, tmax.yz);
+ float tFar = min(t.x, t.y);
+
+ 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 = tFar + 1.0;
+ highp float secondD = firstD;
+ highp float thirdD = firstD;
+ if (volumeSliceIndices.x >= -1.0) {
+ highp float dx = dot(xPoint - rayStart, xPlaneNormal) / dot(rayDir, xPlaneNormal);
+ if (dx >= 0.0 && dx <= tFar)
+ firstD = min(dx, firstD);
+ }
+ if (volumeSliceIndices.y >= -1.0) {
+ highp float dy = dot(yPoint - rayStart, yPlaneNormal) / dot(rayDir, yPlaneNormal);
+ if (dy >= 0.0 && dy <= tFar) {
+ if (dy < firstD) {
+ secondD = firstD;
+ firstD = dy;
+ } else {
+ secondD = dy;
+ }
+ }
+ }
+ if (volumeSliceIndices.z >= -1.0) {
+ highp float dz = dot(zPoint - rayStart, zPlaneNormal) / dot(rayDir, zPlaneNormal);
+ if (dz >= 0.0) {
+ if (dz < firstD && dz <= tFar) {
+ 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 float totalAlpha = 0.0;
+ highp vec3 curRgb = vec3(0, 0, 0);
+
+ // Convert intersection to texture coords
+
+ if (firstD <= tFar) {
+ highp vec3 firstTex = rayStart + rayDir * firstD;
+ firstTex = 0.5 * (firstTex + 1.0);
+ highp vec4 firstColor = texture3D(textureSampler, firstTex);
+ if (color8Bit != 0)
+ firstColor = colorIndex[int(firstColor.r * 255.0)];
+
+ if (firstColor.a > alphaThreshold) {
+ destColor.rgb = firstColor.rgb * firstColor.a;
+ totalAlpha = firstColor.a;
+ }
+ if (secondD <= tFar && totalAlpha < 1.0) {
+ highp vec3 secondTex = rayStart + rayDir * secondD;
+ secondTex = 0.5 * (secondTex + 1.0);
+ highp vec4 secondColor = texture3D(textureSampler, secondTex);
+ if (color8Bit != 0)
+ secondColor = colorIndex[int(secondColor.r * 255.0)];
+ if (secondColor.a > alphaThreshold) {
+ curRgb = secondColor.rgb * secondColor.a * (1.0 - totalAlpha);
+ destColor.rgb += curRgb;
+ totalAlpha += secondColor.a;
+ }
+ if (thirdD <= tFar && totalAlpha < 1.0) {
+ highp vec3 thirdTex = rayStart + rayDir * thirdD;
+ thirdTex = 0.5 * (thirdTex + 1.0);
+ highp vec4 thirdColor = texture3D(textureSampler, thirdTex);
+ if (color8Bit != 0)
+ thirdColor = colorIndex[int(thirdColor.r * 255.0)];
+ if (thirdColor.a > alphaThreshold) {
+ curRgb = thirdColor.rgb * thirdColor.a * (1.0 - totalAlpha);
+ destColor.rgb += curRgb;
+ totalAlpha += thirdColor.a;
+ }
+ }
+ }
+ }
+ destColor.a = totalAlpha;
+ gl_FragColor = clamp(destColor, 0.0, 1.0);
+}
+
diff --git a/src/datavisualization/engine/surface3drenderer.cpp b/src/datavisualization/engine/surface3drenderer.cpp
index 1607f66a..17be3278 100644
--- a/src/datavisualization/engine/surface3drenderer.cpp
+++ b/src/datavisualization/engine/surface3drenderer.cpp
@@ -1196,9 +1196,11 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
glEnable(GL_CULL_FACE);
glCullFace(GL_FRONT);
- Abstract3DRenderer::drawCustomItems(RenderingDepth, m_depthShader, viewMatrix,
- projectionViewMatrix, depthProjectionViewMatrix,
- m_depthTexture, m_shadowQualityToShader);
+ Abstract3DRenderer::drawCustomItems(RenderingDepth, m_depthShader, m_volumeTextureShader,
+ m_volumeTextureSliceShader, viewMatrix,
+ projectionViewMatrix,
+ depthProjectionViewMatrix, m_depthTexture,
+ m_shadowQualityToShader);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D,
m_depthModelTexture, 0);
@@ -1232,9 +1234,11 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
glDisableVertexAttribArray(m_depthShader->posAtt());
- Abstract3DRenderer::drawCustomItems(RenderingDepth, m_depthShader, viewMatrix,
- projectionViewMatrix, depthProjectionViewMatrix,
- m_depthTexture, m_shadowQualityToShader);
+ Abstract3DRenderer::drawCustomItems(RenderingDepth, m_depthShader, m_volumeTextureShader,
+ m_volumeTextureSliceShader, viewMatrix,
+ projectionViewMatrix,
+ depthProjectionViewMatrix, m_depthTexture,
+ m_shadowQualityToShader);
// Disable drawing to depth framebuffer (= enable drawing to screen)
glBindFramebuffer(GL_FRAMEBUFFER, defaultFboHandle);
@@ -1287,7 +1291,9 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
}
}
m_surfaceGridShader->bind();
- Abstract3DRenderer::drawCustomItems(RenderingSelection, m_surfaceGridShader, viewMatrix,
+ Abstract3DRenderer::drawCustomItems(RenderingSelection, m_surfaceGridShader,
+ m_volumeTextureShader, m_volumeTextureSliceShader,
+ viewMatrix,
projectionViewMatrix, depthProjectionViewMatrix,
m_depthTexture, m_shadowQualityToShader);
drawLabels(true, activeCamera, viewMatrix, projectionMatrix);
@@ -1858,7 +1864,8 @@ void Surface3DRenderer::drawScene(GLuint defaultFboHandle)
}
}
- Abstract3DRenderer::drawCustomItems(RenderingNormal, m_customItemShader, viewMatrix,
+ Abstract3DRenderer::drawCustomItems(RenderingNormal, m_customItemShader, m_volumeTextureShader,
+ m_volumeTextureSliceShader, viewMatrix,
projectionViewMatrix, depthProjectionViewMatrix,
m_depthTexture, m_shadowQualityToShader);
diff --git a/src/datavisualization/utils/shaderhelper.cpp b/src/datavisualization/utils/shaderhelper.cpp
index 7fb237c6..3c3d93ac 100644
--- a/src/datavisualization/utils/shaderhelper.cpp
+++ b/src/datavisualization/utils/shaderhelper.cpp
@@ -93,6 +93,10 @@ void ShaderHelper::initialize()
m_gradientMinUniform = m_program->uniformLocation("gradMin");
m_gradientHeightUniform = m_program->uniformLocation("gradHeight");
m_lightColorUniform = m_program->uniformLocation("lightColor");
+ m_volumeSliceIndices = m_program->uniformLocation("volumeSliceIndices");
+ m_colorIndex = m_program->uniformLocation("colorIndex");
+ m_cameraPositionRelativeToModel = m_program->uniformLocation("cameraPositionRelativeToModel");
+ m_color8Bit = m_program->uniformLocation("color8Bit");
m_initialized = true;
}
@@ -150,6 +154,11 @@ void ShaderHelper::setUniformValue(GLuint uniform, GLint value)
m_program->setUniformValue(uniform, value);
}
+void ShaderHelper::setUniformValueArray(GLuint uniform, const QVector4D *values, int count)
+{
+ m_program->setUniformValueArray(uniform, values, count);
+}
+
GLuint ShaderHelper::MVP()
{
if (!m_initialized)
@@ -255,6 +264,34 @@ GLuint ShaderHelper::lightColor()
return m_lightColorUniform;
}
+GLuint ShaderHelper::volumeSliceIndices()
+{
+ if (!m_initialized)
+ qFatal("Shader not initialized");
+ return m_volumeSliceIndices;
+}
+
+GLuint ShaderHelper::colorIndex()
+{
+ if (!m_initialized)
+ qFatal("Shader not initialized");
+ return m_colorIndex;
+}
+
+GLuint ShaderHelper::cameraPositionRelativeToModel()
+{
+ if (!m_initialized)
+ qFatal("Shader not initialized");
+ return m_cameraPositionRelativeToModel;
+}
+
+GLuint ShaderHelper::color8Bit()
+{
+ if (!m_initialized)
+ qFatal("Shader not initialized");
+ return m_color8Bit;
+}
+
GLuint ShaderHelper::posAtt()
{
if (!m_initialized)
diff --git a/src/datavisualization/utils/shaderhelper_p.h b/src/datavisualization/utils/shaderhelper_p.h
index fdef0dff..67e8a2f3 100644
--- a/src/datavisualization/utils/shaderhelper_p.h
+++ b/src/datavisualization/utils/shaderhelper_p.h
@@ -57,6 +57,7 @@ class ShaderHelper
void setUniformValue(GLuint uniform, const QMatrix4x4 &value);
void setUniformValue(GLuint uniform, GLfloat value);
void setUniformValue(GLuint uniform, GLint value);
+ void setUniformValueArray(GLuint uniform, const QVector4D *values, int count);
GLuint MVP();
GLuint view();
@@ -73,6 +74,10 @@ class ShaderHelper
GLuint gradientMin();
GLuint gradientHeight();
GLuint lightColor();
+ GLuint volumeSliceIndices();
+ GLuint colorIndex();
+ GLuint cameraPositionRelativeToModel();
+ GLuint color8Bit();
GLuint posAtt();
GLuint uvAtt();
@@ -107,6 +112,10 @@ class ShaderHelper
GLuint m_gradientMinUniform;
GLuint m_gradientHeightUniform;
GLuint m_lightColorUniform;
+ GLuint m_volumeSliceIndices;
+ GLuint m_colorIndex;
+ GLuint m_cameraPositionRelativeToModel;
+ GLuint m_color8Bit;
GLboolean m_initialized;
};
diff --git a/src/datavisualization/utils/texturehelper.cpp b/src/datavisualization/utils/texturehelper.cpp
index 616c1981..5e21b5cd 100644
--- a/src/datavisualization/utils/texturehelper.cpp
+++ b/src/datavisualization/utils/texturehelper.cpp
@@ -21,11 +21,25 @@
#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()
{
+#if !defined(QT_OPENGL_ES_2)
+ // Discard warnings about deprecated functions
+ QtMessageHandler handler = qInstallMessageHandler(discardDebugMsgs);
+
+ m_openGlFunctions_2_1 = new QOpenGLFunctions_2_1;
+ m_openGlFunctions_2_1->initializeOpenGLFunctions();
+
+ // Restore original message handler
+ qInstallMessageHandler(handler);
+#endif
initializeOpenGLFunctions();
}
@@ -73,8 +87,54 @@ 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;
+}
+
+#if !defined(QT_OPENGL_ES_2)
+GLuint TextureHelper::create3DTexture(const QVector<uchar> *data, int width, int height, int depth,
+ QImage::Format dataFormat)
+{
+ if (!width || !height || !depth)
+ return 0;
+
+ glEnable(GL_TEXTURE_3D);
+
+ GLuint textureId;
+ 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);
+
return textureId;
}
+#endif
GLuint TextureHelper::createCubeMapTexture(const QImage &image, bool useTrilinearFiltering)
{
diff --git a/src/datavisualization/utils/texturehelper_p.h b/src/datavisualization/utils/texturehelper_p.h
index c20f293c..c1dfe8c9 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,6 +48,10 @@ 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);
+#if !defined(QT_OPENGL_ES_2)
+ GLuint create3DTexture(const QVector<uchar> *data, int width, int height, int depth,
+ QImage::Format dataFormat);
+#endif
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);
@@ -61,6 +69,9 @@ 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;
+#endif
friend class Bars3DRenderer;
friend class Surface3DRenderer;
friend class Scatter3DRenderer;
diff --git a/tests/tests.pro b/tests/tests.pro
index 8bbdc3f2..21036b59 100644
--- a/tests/tests.pro
+++ b/tests/tests.pro
@@ -16,7 +16,8 @@ SUBDIRS += barstest \
directional \
qmlmultiwindow \
itemmodeltest \
- qmlmultitest
+ qmlmultitest \
+ volumetrictest
#SUBDIRS += kinectsurface
diff --git a/tests/volumetrictest/logo.png b/tests/volumetrictest/logo.png
new file mode 100644
index 00000000..1e7ed4cf
--- /dev/null
+++ b/tests/volumetrictest/logo.png
Binary files differ
diff --git a/tests/volumetrictest/main.cpp b/tests/volumetrictest/main.cpp
new file mode 100644
index 00000000..46edf576
--- /dev/null
+++ b/tests/volumetrictest/main.cpp
@@ -0,0 +1,105 @@
+/****************************************************************************
+**
+** 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 "volumetrictest.h"
+
+#include <QtWidgets/QApplication>
+#include <QtWidgets/QWidget>
+#include <QtWidgets/QHBoxLayout>
+#include <QtWidgets/QVBoxLayout>
+#include <QtWidgets/QRadioButton>
+#include <QtWidgets/QSlider>
+#include <QtWidgets/QCheckBox>
+#include <QtWidgets/QLabel>
+#include <QtGui/QScreen>
+
+int main(int argc, char **argv)
+{
+ QApplication app(argc, argv);
+ Q3DScatter *graph = new Q3DScatter();
+ QWidget *container = QWidget::createWindowContainer(graph);
+
+ QSize screenSize = graph->screen()->size();
+ container->setMinimumSize(QSize(screenSize.width() / 2, screenSize.height() / 1.5));
+ container->setMaximumSize(screenSize);
+ container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ container->setFocusPolicy(Qt::StrongFocus);
+
+ QWidget *widget = new QWidget;
+ QHBoxLayout *hLayout = new QHBoxLayout(widget);
+ QVBoxLayout *vLayout = new QVBoxLayout();
+ hLayout->addWidget(container, 1);
+ hLayout->addLayout(vLayout);
+
+ widget->setWindowTitle(QStringLiteral("Volumetric TEST"));
+
+ QCheckBox *sliceXCheckBox = new QCheckBox(widget);
+ sliceXCheckBox->setText(QStringLiteral("Slice volume on X axis"));
+ sliceXCheckBox->setChecked(false);
+ QCheckBox *sliceYCheckBox = new QCheckBox(widget);
+ sliceYCheckBox->setText(QStringLiteral("Slice volume on Y axis"));
+ sliceYCheckBox->setChecked(false);
+ QCheckBox *sliceZCheckBox = new QCheckBox(widget);
+ sliceZCheckBox->setText(QStringLiteral("Slice volume on Z axis"));
+ sliceZCheckBox->setChecked(false);
+
+ QSlider *sliceXSlider = new QSlider(Qt::Horizontal, widget);
+ sliceXSlider->setMinimum(0);
+ sliceXSlider->setMaximum(1024);
+ sliceXSlider->setValue(512);
+ sliceXSlider->setEnabled(true);
+ QSlider *sliceYSlider = new QSlider(Qt::Horizontal, widget);
+ sliceYSlider->setMinimum(0);
+ sliceYSlider->setMaximum(1024);
+ sliceYSlider->setValue(512);
+ sliceYSlider->setEnabled(true);
+ QSlider *sliceZSlider = new QSlider(Qt::Horizontal, widget);
+ sliceZSlider->setMinimum(0);
+ sliceZSlider->setMaximum(1024);
+ sliceZSlider->setValue(512);
+ sliceZSlider->setEnabled(true);
+
+ QLabel *fpsLabel = new QLabel(QStringLiteral("Fps: "), widget);
+
+ vLayout->addWidget(fpsLabel);
+ vLayout->addWidget(sliceXCheckBox);
+ vLayout->addWidget(sliceXSlider);
+ vLayout->addWidget(sliceYCheckBox);
+ vLayout->addWidget(sliceYSlider);
+ vLayout->addWidget(sliceZCheckBox);
+ vLayout->addWidget(sliceZSlider, 1, Qt::AlignTop);
+
+ VolumetricModifier *modifier = new VolumetricModifier(graph);
+ modifier->setFpsLabel(fpsLabel);
+
+ QObject::connect(sliceXCheckBox, &QCheckBox::stateChanged, modifier,
+ &VolumetricModifier::sliceX);
+ QObject::connect(sliceYCheckBox, &QCheckBox::stateChanged, modifier,
+ &VolumetricModifier::sliceY);
+ QObject::connect(sliceZCheckBox, &QCheckBox::stateChanged, modifier,
+ &VolumetricModifier::sliceZ);
+ QObject::connect(sliceXSlider, &QSlider::valueChanged, modifier,
+ &VolumetricModifier::adjustSliceX);
+ QObject::connect(sliceYSlider, &QSlider::valueChanged, modifier,
+ &VolumetricModifier::adjustSliceY);
+ QObject::connect(sliceZSlider, &QSlider::valueChanged, modifier,
+ &VolumetricModifier::adjustSliceZ);
+
+ widget->show();
+ return app.exec();
+}
diff --git a/tests/volumetrictest/volumetrictest.cpp b/tests/volumetrictest/volumetrictest.cpp
new file mode 100644
index 00000000..04aad052
--- /dev/null
+++ b/tests/volumetrictest/volumetrictest.cpp
@@ -0,0 +1,282 @@
+/****************************************************************************
+**
+** 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 "volumetrictest.h"
+#include <QtDataVisualization/qvalue3daxis.h>
+#include <QtDataVisualization/q3dscene.h>
+#include <QtDataVisualization/q3dcamera.h>
+#include <QtDataVisualization/q3dtheme.h>
+#include <QtCore/qmath.h>
+#include <QtGui/QRgb>
+#include <QtGui/QImage>
+#include <QtWidgets/QLabel>
+#include <QtCore/QDebug>
+
+using namespace QtDataVisualization;
+
+const int imageCount = 512;
+
+VolumetricModifier::VolumetricModifier(Q3DScatter *scatter)
+ : m_graph(scatter),
+ m_volumeItem(0),
+ m_sliceIndexX(0),
+ m_sliceIndexY(0),
+ m_sliceIndexZ(0)
+{
+ m_graph->activeTheme()->setType(Q3DTheme::ThemeQt);
+ m_graph->setShadowQuality(QAbstract3DGraph::ShadowQualityNone);
+ m_graph->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPresetFront);
+ m_graph->setOrthoProjection(true);
+ //m_graph->scene()->activeCamera()->setTarget(QVector3D(0.5f, 0.5f, 0.5f));
+
+ createVolume();
+ createAnotherVolume();
+
+ m_graph->addCustomItem(m_volumeItem);
+ m_graph->addCustomItem(m_volumeItem2);
+ m_graph->setMeasureFps(true);
+
+ QObject::connect(m_graph->scene()->activeCamera(), &Q3DCamera::zoomLevelChanged, this,
+ &VolumetricModifier::handleZoomLevelChange);
+ QObject::connect(m_graph, &QAbstract3DGraph::currentFpsChanged, this,
+ &VolumetricModifier::handleFpsChange);
+}
+
+VolumetricModifier::~VolumetricModifier()
+{
+ delete m_graph;
+}
+
+void VolumetricModifier::setFpsLabel(QLabel *fpsLabel)
+{
+ m_fpsLabel = fpsLabel;
+}
+
+void VolumetricModifier::sliceX(int enabled)
+{
+ m_volumeItem->setSliceIndexX(enabled ? m_sliceIndexX : -1);
+ m_volumeItem2->setSliceIndexX(enabled ? m_sliceIndexX : -1);
+}
+
+void VolumetricModifier::sliceY(int enabled)
+{
+ m_volumeItem->setSliceIndexY(enabled ? m_sliceIndexY : -1);
+ m_volumeItem2->setSliceIndexY(enabled ? m_sliceIndexY : -1);
+}
+
+void VolumetricModifier::sliceZ(int enabled)
+{
+ m_volumeItem->setSliceIndexZ(enabled ? m_sliceIndexZ : -1);
+ m_volumeItem2->setSliceIndexZ(enabled ? m_sliceIndexZ : -1);
+}
+
+void VolumetricModifier::adjustSliceX(int value)
+{
+ m_sliceIndexX = int(float(value) / (1024.0f / m_volumeItem->textureWidth()));
+ if (m_sliceIndexX == m_volumeItem->textureWidth())
+ m_sliceIndexX--;
+ qDebug() << "m_sliceIndexX:" << m_sliceIndexX;
+ if (m_volumeItem->sliceIndexX() != -1) {
+ m_volumeItem->setSliceIndexX(m_sliceIndexX);
+ m_volumeItem2->setSliceIndexX(m_sliceIndexX);
+ }
+}
+
+void VolumetricModifier::adjustSliceY(int value)
+{
+ m_sliceIndexY = int(float(value) / (1024.0f / m_volumeItem->textureHeight()));
+ if (m_sliceIndexY == m_volumeItem->textureHeight())
+ m_sliceIndexY--;
+ qDebug() << "m_sliceIndexY:" << m_sliceIndexY;
+ if (m_volumeItem->sliceIndexY() != -1) {
+ m_volumeItem->setSliceIndexY(m_sliceIndexY);
+ m_volumeItem2->setSliceIndexY(m_sliceIndexY);
+ }
+}
+
+void VolumetricModifier::adjustSliceZ(int value)
+{
+ m_sliceIndexZ = int(float(value) / (1024.0f / m_volumeItem->textureDepth()));
+ if (m_sliceIndexZ == m_volumeItem->textureDepth())
+ m_sliceIndexZ--;
+ qDebug() << "m_sliceIndexZ:" << m_sliceIndexZ;
+ if (m_volumeItem->sliceIndexZ() != -1) {
+ m_volumeItem->setSliceIndexZ(m_sliceIndexZ);
+ m_volumeItem2->setSliceIndexZ(m_sliceIndexZ);
+ }
+}
+
+void VolumetricModifier::handleZoomLevelChange()
+{
+ // Zooming inside volumetric object causes ugly clipping issues, so restrict zoom level a bit
+ if (m_graph->scene()->activeCamera()->zoomLevel() > 220)
+ m_graph->scene()->activeCamera()->setZoomLevel(220);
+}
+
+void VolumetricModifier::handleFpsChange(qreal fps)
+{
+ const QString fpsFormat = QStringLiteral("Fps: %1");
+ int fps10 = int(fps * 10.0);
+ m_fpsLabel->setText(fpsFormat.arg(QString::number(qreal(fps10) / 10.0)));
+}
+
+void VolumetricModifier::createVolume()
+{
+ m_volumeItem = new QCustom3DVolume;
+ m_volumeItem->setTextureFormat(QImage::Format_ARGB32);
+ m_volumeItem->setRotation(QQuaternion::fromAxisAndAngle(1.0f, 1.0f, 0.0f, 10.0f));
+ m_volumeItem->setPosition(QVector3D(-0.5f, 0.0f, 0.0f));
+
+ QImage logo;
+ logo.load(QStringLiteral(":/logo.png"));
+ qDebug() << "image dimensions:" << logo.width() << logo.height()
+ << logo.byteCount() << (logo.width() * logo.height())
+ << logo.bytesPerLine();
+
+ QVector<QImage *> imageArray(imageCount);
+ for (int i = 0; i < imageCount; i++) {
+ QImage *newImage = new QImage(logo);
+ imageArray[i] = newImage;
+ }
+
+ m_volumeItem->createTextureData(imageArray);
+
+ m_sliceIndexX = m_volumeItem->textureWidth() / 2;
+ m_sliceIndexY = m_volumeItem->textureWidth() / 2;
+ m_sliceIndexZ = m_volumeItem->textureWidth() / 2;
+
+ QVector<QRgb> colorTable = m_volumeItem->colorTable();
+ colorTable.append(qRgba(255, 0, 0, 255));
+ colorTable.append(qRgba(0, 255, 0, 255));
+ colorTable.append(qRgba(0, 0, 255, 255));
+ m_volumeItem->setColorTable(colorTable);
+ int redIndex = colorTable.size() - 3;
+ int greenIndex = colorTable.size() - 2;
+ int blueIndex = colorTable.size() - 1;
+ int width = m_volumeItem->textureDataWidth();
+ int height = m_volumeItem->textureHeight();
+ int depth = m_volumeItem->textureDepth();
+ int frameSize = width * height;
+ qDebug() << width << height << depth << m_volumeItem->textureData()->size();
+ m_volumeItem->setScaling(QVector3D(float(width) / float(depth) * 2.0f,
+ float(height) / float(depth) * 2.0f,
+ 2.0f));
+
+ uchar *data = m_volumeItem->textureData()->data();
+ uchar *p = data;
+
+ // Change one picture using subtexture replacement
+ QImage flipped = logo.mirrored();
+ m_volumeItem->setSubTextureData(101, flipped);
+
+ // Red first subtexture
+// for (int j = 0; j < height; j++) {
+// for (int i = 0; i < width; i++) {
+// *p = redIndex;
+// p++;
+// }
+// }
+
+ // Red last subtexture
+ p = data + frameSize * (imageCount - 1);
+ for (int j = 0; j < height; j++) {
+ for (int i = 0; i < width; i++) {
+ *p = redIndex;
+ p++;
+ }
+ }
+
+ // Blue x = 0
+ p = data;
+ for (int k = 0; k < depth; k++) {
+ for (int j = 0; j < height; j++) {
+ *p = blueIndex;
+ p += width;
+ }
+ }
+
+ // Blue x = max
+ p = data + width - 1;
+ for (int k = 0; k < depth; k++) {
+ for (int j = 0; j < height; j++) {
+ *p = blueIndex;
+ p += width;
+ }
+ }
+
+ // Green y = 0
+// p = data;
+// for (int k = 0; k < depth; k++) {
+// for (int i = 0; i < width; i++) {
+// *p = greenIndex;
+// p++;
+// }
+// p += (frameSize - width);
+// }
+
+ // Green y = max
+ p = data + frameSize - width;
+ for (int k = 0; k < depth; k++) {
+ for (int i = 0; i < width; i++) {
+ *p = greenIndex;
+ p++;
+ }
+ p += (frameSize - width);
+ }
+}
+
+void VolumetricModifier::createAnotherVolume()
+{
+ m_volumeItem2 = new QCustom3DVolume;
+ m_volumeItem2->setTextureFormat(QImage::Format_ARGB32);
+ m_volumeItem2->setPosition(QVector3D(0.5f, 0.0f, 0.0f));
+
+ QImage logo;
+ logo.load(QStringLiteral(":/logo.png"));
+ //logo = logo.convertToFormat(QImage::Format_ARGB8555_Premultiplied);
+ qDebug() << "second image dimensions:" << logo.width() << logo.height()
+ << logo.byteCount() << (logo.width() * logo.height())
+ << logo.bytesPerLine();
+
+ logo.save("d:/qt/goobar.png");
+
+ QVector<QImage *> imageArray(imageCount);
+ for (int i = 0; i < imageCount; i++) {
+ QImage *newImage = new QImage(logo);
+ imageArray[i] = newImage;
+ }
+
+ m_volumeItem2->createTextureData(imageArray);
+
+ m_sliceIndexX = m_volumeItem2->textureWidth() / 2;
+ m_sliceIndexY = m_volumeItem2->textureWidth() / 2;
+ m_sliceIndexZ = m_volumeItem2->textureWidth() / 2;
+
+ int width = m_volumeItem2->textureWidth();
+ int height = m_volumeItem2->textureHeight();
+ int depth = m_volumeItem2->textureDepth();
+ qDebug() << width << height << depth << m_volumeItem2->textureData()->size();
+ m_volumeItem2->setScaling(QVector3D(float(width) / float(depth) * 2.0f,
+ float(height) / float(depth) * 2.0f,
+ 2.0f));
+
+ // Change one picture using subtexture replacement
+ QImage flipped = logo.mirrored();
+ m_volumeItem2->setSubTextureData(101, flipped);
+}
+
diff --git a/tests/volumetrictest/volumetrictest.h b/tests/volumetrictest/volumetrictest.h
new file mode 100644
index 00000000..677a1231
--- /dev/null
+++ b/tests/volumetrictest/volumetrictest.h
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc
+** All rights reserved.
+** For any questions to Digia, please use contact form at http://qt.digia.com
+**
+** This file is part of the QtDataVisualization module.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://qt.digia.com
+**
+****************************************************************************/
+
+#ifndef VOLUMETRICMODIFIER_H
+#define VOLUMETRICMODIFIER_H
+
+#include <QtDataVisualization/q3dscatter.h>
+#include <QtDataVisualization/qcustom3dvolume.h>
+
+class QLabel;
+
+using namespace QtDataVisualization;
+
+class VolumetricModifier : public QObject
+{
+ Q_OBJECT
+public:
+ explicit VolumetricModifier(Q3DScatter *scatter);
+ ~VolumetricModifier();
+
+ void setFpsLabel(QLabel *fpsLabel);
+
+public slots:
+ void sliceX(int enabled);
+ void sliceY(int enabled);
+ void sliceZ(int enabled);
+ void adjustSliceX(int value);
+ void adjustSliceY(int value);
+ void adjustSliceZ(int value);
+ void handleZoomLevelChange();
+ void handleFpsChange(qreal fps);
+
+private:
+ void createVolume();
+ void createAnotherVolume();
+
+ Q3DScatter *m_graph;
+ QCustom3DVolume *m_volumeItem;
+ QCustom3DVolume *m_volumeItem2;
+ int m_sliceIndexX;
+ int m_sliceIndexY;
+ int m_sliceIndexZ;
+ QLabel *m_fpsLabel;
+};
+
+#endif
diff --git a/tests/volumetrictest/volumetrictest.pro b/tests/volumetrictest/volumetrictest.pro
new file mode 100644
index 00000000..137e5bab
--- /dev/null
+++ b/tests/volumetrictest/volumetrictest.pro
@@ -0,0 +1,18 @@
+android|ios {
+ error( "This example is not supported for android or ios." )
+}
+
+!include( ../tests.pri ) {
+ error( "Couldn't find the tests.pri file!" )
+}
+
+SOURCES += main.cpp volumetrictest.cpp
+HEADERS += volumetrictest.h
+
+QT += widgets
+
+OTHER_FILES += doc/src/* \
+ doc/images/*
+
+RESOURCES += \
+ volumetrictest.qrc
diff --git a/tests/volumetrictest/volumetrictest.qrc b/tests/volumetrictest/volumetrictest.qrc
new file mode 100644
index 00000000..90517ff1
--- /dev/null
+++ b/tests/volumetrictest/volumetrictest.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/">
+ <file>logo.png</file>
+ </qresource>
+</RCC>