summaryrefslogtreecommitdiffstats
path: root/examples/datavisualization/volumetric
diff options
context:
space:
mode:
Diffstat (limited to 'examples/datavisualization/volumetric')
-rw-r--r--examples/datavisualization/volumetric/doc/images/volumetric-example.pngbin0 -> 177203 bytes
-rw-r--r--examples/datavisualization/volumetric/doc/src/volumetric.qdoc129
-rw-r--r--examples/datavisualization/volumetric/layer_ground.pngbin0 -> 63473 bytes
-rw-r--r--examples/datavisualization/volumetric/layer_magma.pngbin0 -> 17332 bytes
-rw-r--r--examples/datavisualization/volumetric/layer_water.pngbin0 -> 27124 bytes
-rw-r--r--examples/datavisualization/volumetric/main.cpp247
-rw-r--r--examples/datavisualization/volumetric/volumetric.cpp764
-rw-r--r--examples/datavisualization/volumetric/volumetric.h116
-rw-r--r--examples/datavisualization/volumetric/volumetric.pro17
-rw-r--r--examples/datavisualization/volumetric/volumetric.qrc7
10 files changed, 1280 insertions, 0 deletions
diff --git a/examples/datavisualization/volumetric/doc/images/volumetric-example.png b/examples/datavisualization/volumetric/doc/images/volumetric-example.png
new file mode 100644
index 00000000..277d4fe4
--- /dev/null
+++ b/examples/datavisualization/volumetric/doc/images/volumetric-example.png
Binary files differ
diff --git a/examples/datavisualization/volumetric/doc/src/volumetric.qdoc b/examples/datavisualization/volumetric/doc/src/volumetric.qdoc
new file mode 100644
index 00000000..39616670
--- /dev/null
+++ b/examples/datavisualization/volumetric/doc/src/volumetric.qdoc
@@ -0,0 +1,129 @@
+/****************************************************************************
+**
+** 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.
+ \since QtDataVisualization 1.2
+
+ This example shows how to use QCustom3DVolume items to display volumetric data.
+
+ \image volumetric-example.png
+
+ \section1 Initializing volume item
+
+ The QCustom3DVolume items are special custom items (see QCustom3DItem), which can be used
+ to display volumetric data. The volume items are only supported with orthographic projection,
+ so first we make sure the graph is using it:
+
+ \snippet volumetric/volumetric.cpp 6
+
+ The following code shows how to create a volumetric item tied to the data ranges of the axes:
+
+ \snippet volumetric/volumetric.cpp 0
+
+ By setting the QCustom3DItem::scalingAbsolute property to \c{false}, we indicate that the
+ scaling of the volume should follow the changes in the data ranges. Next we define the
+ internal contents of the volume:
+
+ \snippet volumetric/volumetric.cpp 1
+
+ We use eight bit indexed color for our texture, as it is compact and makes it easy to adjust the
+ colors without needing to reset the whole texture. For the texture data we use the data we
+ created earlier based on some height maps.
+ Typically the data for volume items comes pregenerated in a form of a stack of images, so we are
+ not going to explain the data generation in detail. Please refer to the example code if you
+ are interested in the actual data generation process.
+
+ Since we are using eight bit indexed colors, we need a color table to map the eight bit color
+ indexes to actual colors. We use one we populated on our own, but in a typical use case you
+ would get the color table from the source images:
+
+ \snippet volumetric/volumetric.cpp 2
+
+ We want to optionally show slice frames around the volume, so we initialize their properties.
+ Initially, the frames will be hidden:
+
+ \snippet volumetric/volumetric.cpp 5
+
+ Finally we add the volume as a custom item to the graph to display it:
+
+ \snippet volumetric/volumetric.cpp 3
+
+ \section1 Slicing into the volume
+
+ Unless the volume is largely transparent, you can only see the surface of it, which is often
+ not very helpful. One way to inspect the internal structure of the volume is to view the slices
+ of the volume. QCustom3DVolume provides two ways to display the slices. The first is to show
+ the selected slices in place of the volume. For example, to specify a slice perpendicular to
+ the X-axis, you can use the following method:
+
+ \snippet volumetric/volumetric.cpp 7
+
+ To actually draw the slice specified above, the QCustom3DVolume::drawSlices property must be
+ also set:
+
+ \snippet volumetric/volumetric.cpp 8
+
+ The second way to view slices is to use QCustom3DVolume::renderSlice() method, which produces
+ a QImage from the specified slice. This image can then be displayed on another widget, such
+ as a QLabel here:
+
+ \snippet volumetric/volumetric.cpp 9
+
+ \section1 Adjusting volume transparency
+
+ Sometimes viewing just the slices doesn't give you a good understanding of the volume's internal
+ structure. QCustom3DVolume provides two properties that can be used to adjust the volume
+ transparency:
+
+ \snippet volumetric/volumetric.cpp 11
+ \dots
+ \snippet volumetric/volumetric.cpp 10
+
+ The QCustom3DVolume::alphaMultiplier is a general multiplier that is applied to the alpha value
+ of each voxel of the volume. It makes it possible to add uniform transparency to the already
+ somewhat transparent portions of the volume to reveal internal opaque details. This multiplier
+ doesn't affect colors that are fully opaque, unless the QCustom3DVolume::preserveOpacity
+ property is set to \c{false}.
+
+ An alternative way to adjust the transparency of the volume is adjust the alpha values of the
+ voxels directly. For eight bit indexed textures, this is done simply by modifying and
+ resetting the color table:
+
+ \snippet volumetric/volumetric.cpp 12
+
+ \section1 High definition vs. low definition shader
+
+ By default the volume rendering uses the high definition shader. It accounts for each
+ voxel of the volume with correct weight when ray-tracing the volume contents,
+ providing an accurate representation of even the finer details of the volume.
+ However, this is computationally very expensive, so the frame rate suffers.
+ If rendering speed is more important than pixel-perfect
+ accuracy of the volume contents, you can take the much faster low definition shader into use
+ by setting \c{false} for QCustom3DVolume::useHighDefShader property. The low definition shader
+ achieves the speed by making compromises on the accuracy, so it doesn't guarantee each voxel
+ of the volume will be sampled. This can lead to flickering and/or other rendering artifacts
+ on the finer details of the volume.
+
+ \snippet volumetric/volumetric.cpp 13
+
+ \section1 Example contents
+*/
diff --git a/examples/datavisualization/volumetric/layer_ground.png b/examples/datavisualization/volumetric/layer_ground.png
new file mode 100644
index 00000000..3f96a122
--- /dev/null
+++ b/examples/datavisualization/volumetric/layer_ground.png
Binary files differ
diff --git a/examples/datavisualization/volumetric/layer_magma.png b/examples/datavisualization/volumetric/layer_magma.png
new file mode 100644
index 00000000..01434d35
--- /dev/null
+++ b/examples/datavisualization/volumetric/layer_magma.png
Binary files differ
diff --git a/examples/datavisualization/volumetric/layer_water.png b/examples/datavisualization/volumetric/layer_water.png
new file mode 100644
index 00000000..4d57563e
--- /dev/null
+++ b/examples/datavisualization/volumetric/layer_water.png
Binary files differ
diff --git a/examples/datavisualization/volumetric/main.cpp b/examples/datavisualization/volumetric/main.cpp
new file mode 100644
index 00000000..faf379ec
--- /dev/null
+++ b/examples/datavisualization/volumetric/main.cpp
@@ -0,0 +1,247 @@
+/****************************************************************************
+**
+** 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 <QtWidgets/QGroupBox>
+#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() / 3, screenSize.height() / 3));
+ 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();
+ QVBoxLayout *vLayout2 = new QVBoxLayout();
+ hLayout->addWidget(container, 1);
+ hLayout->addLayout(vLayout);
+ hLayout->addLayout(vLayout2);
+
+ widget->setWindowTitle(QStringLiteral("Volumetric object example - 3D terrain"));
+
+ 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);
+
+ QCheckBox *fpsCheckBox = new QCheckBox(widget);
+ fpsCheckBox->setText(QStringLiteral("Show FPS"));
+ fpsCheckBox->setChecked(false);
+ QLabel *fpsLabel = new QLabel(QStringLiteral(""), widget);
+
+ QGroupBox *textureDetailGroupBox = new QGroupBox(QStringLiteral("Texture detail"));
+
+ QRadioButton *lowDetailRB = new QRadioButton(widget);
+ lowDetailRB->setText(QStringLiteral("Low (128x64x128)"));
+ lowDetailRB->setChecked(true);
+
+ QRadioButton *mediumDetailRB = new QRadioButton(widget);
+ mediumDetailRB->setText(QStringLiteral("Generating..."));
+ mediumDetailRB->setChecked(false);
+ mediumDetailRB->setEnabled(false);
+
+ QRadioButton *highDetailRB = new QRadioButton(widget);
+ highDetailRB->setText(QStringLiteral("Generating..."));
+ highDetailRB->setChecked(false);
+ highDetailRB->setEnabled(false);
+
+ QVBoxLayout *textureDetailVBox = new QVBoxLayout;
+ textureDetailVBox->addWidget(lowDetailRB);
+ textureDetailVBox->addWidget(mediumDetailRB);
+ textureDetailVBox->addWidget(highDetailRB);
+ textureDetailGroupBox->setLayout(textureDetailVBox);
+
+ QGroupBox *areaGroupBox = new QGroupBox(QStringLiteral("Show area"));
+
+ QRadioButton *areaAllRB = new QRadioButton(widget);
+ areaAllRB->setText(QStringLiteral("Whole region"));
+ areaAllRB->setChecked(true);
+
+ QRadioButton *areaMineRB = new QRadioButton(widget);
+ areaMineRB->setText(QStringLiteral("The mine"));
+ areaMineRB->setChecked(false);
+
+ QRadioButton *areaMountainRB = new QRadioButton(widget);
+ areaMountainRB->setText(QStringLiteral("The mountain"));
+ areaMountainRB->setChecked(false);
+
+ QVBoxLayout *areaVBox = new QVBoxLayout;
+ areaVBox->addWidget(areaAllRB);
+ areaVBox->addWidget(areaMineRB);
+ areaVBox->addWidget(areaMountainRB);
+ areaGroupBox->setLayout(areaVBox);
+
+ QCheckBox *colorTableCheckBox = new QCheckBox(widget);
+ colorTableCheckBox->setText(QStringLiteral("Alternate color table"));
+ colorTableCheckBox->setChecked(false);
+
+ QLabel *sliceImageXLabel = new QLabel(widget);
+ QLabel *sliceImageYLabel = new QLabel(widget);
+ QLabel *sliceImageZLabel = new QLabel(widget);
+ sliceImageXLabel->setMinimumSize(QSize(200, 100));
+ sliceImageYLabel->setMinimumSize(QSize(200, 200));
+ sliceImageZLabel->setMinimumSize(QSize(200, 100));
+ sliceImageXLabel->setMaximumSize(QSize(200, 100));
+ sliceImageYLabel->setMaximumSize(QSize(200, 200));
+ sliceImageZLabel->setMaximumSize(QSize(200, 100));
+ sliceImageXLabel->setFrameShape(QFrame::Box);
+ sliceImageYLabel->setFrameShape(QFrame::Box);
+ sliceImageZLabel->setFrameShape(QFrame::Box);
+ sliceImageXLabel->setScaledContents(true);
+ sliceImageYLabel->setScaledContents(true);
+ sliceImageZLabel->setScaledContents(true);
+
+ QSlider *alphaMultiplierSlider = new QSlider(Qt::Horizontal, widget);
+ alphaMultiplierSlider->setMinimum(0);
+ alphaMultiplierSlider->setMaximum(139);
+ alphaMultiplierSlider->setValue(100);
+ alphaMultiplierSlider->setEnabled(true);
+ QLabel *alphaMultiplierLabel = new QLabel(QStringLiteral("Alpha multiplier: 1.0"));
+
+ QCheckBox *preserveOpacityCheckBox = new QCheckBox(widget);
+ preserveOpacityCheckBox->setText(QStringLiteral("Preserve opacity"));
+ preserveOpacityCheckBox->setChecked(true);
+
+ QCheckBox *transparentGroundCheckBox = new QCheckBox(widget);
+ transparentGroundCheckBox->setText(QStringLiteral("Transparent ground"));
+ transparentGroundCheckBox->setChecked(false);
+
+ QCheckBox *useHighDefShaderCheckBox = new QCheckBox(widget);
+ useHighDefShaderCheckBox->setText(QStringLiteral("Use HD shader"));
+ useHighDefShaderCheckBox->setChecked(true);
+
+ QLabel *performanceNoteLabel =
+ new QLabel(QStringLiteral(
+ "Note: A high end graphics card is\nrecommended with the HD shader\nwhen the volume contains a lot of\ntransparent areas."));
+ performanceNoteLabel->setFrameShape(QFrame::Box);
+
+ QCheckBox *drawSliceFramesCheckBox = new QCheckBox(widget);
+ drawSliceFramesCheckBox->setText(QStringLiteral("Draw slice frames"));
+ drawSliceFramesCheckBox->setChecked(false);
+
+ vLayout->addWidget(sliceXCheckBox);
+ vLayout->addWidget(sliceXSlider);
+ vLayout->addWidget(sliceImageXLabel);
+ vLayout->addWidget(sliceYCheckBox);
+ vLayout->addWidget(sliceYSlider);
+ vLayout->addWidget(sliceImageYLabel);
+ vLayout->addWidget(sliceZCheckBox);
+ vLayout->addWidget(sliceZSlider);
+ vLayout->addWidget(sliceImageZLabel);
+ vLayout->addWidget(drawSliceFramesCheckBox, 1, Qt::AlignTop);
+ vLayout2->addWidget(fpsCheckBox);
+ vLayout2->addWidget(fpsLabel);
+ vLayout2->addWidget(textureDetailGroupBox);
+ vLayout2->addWidget(areaGroupBox);
+ vLayout2->addWidget(colorTableCheckBox);
+ vLayout2->addWidget(alphaMultiplierLabel);
+ vLayout2->addWidget(alphaMultiplierSlider);
+ vLayout2->addWidget(preserveOpacityCheckBox);
+ vLayout2->addWidget(transparentGroundCheckBox);
+ vLayout2->addWidget(useHighDefShaderCheckBox);
+ vLayout2->addWidget(performanceNoteLabel, 1, Qt::AlignTop);
+
+ VolumetricModifier *modifier = new VolumetricModifier(graph);
+ modifier->setFpsLabel(fpsLabel);
+ modifier->setMediumDetailRB(mediumDetailRB);
+ modifier->setHighDetailRB(highDetailRB);
+ modifier->setSliceSliders(sliceXSlider, sliceYSlider, sliceZSlider);
+ modifier->setSliceLabels(sliceImageXLabel, sliceImageYLabel, sliceImageZLabel);
+ modifier->setAlphaMultiplierLabel(alphaMultiplierLabel);
+ modifier->setTransparentGround(transparentGroundCheckBox->isChecked());
+
+ QObject::connect(fpsCheckBox, &QCheckBox::stateChanged, modifier,
+ &VolumetricModifier::setFpsMeasurement);
+ 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);
+ QObject::connect(lowDetailRB, &QRadioButton::toggled, modifier,
+ &VolumetricModifier::toggleLowDetail);
+ QObject::connect(mediumDetailRB, &QRadioButton::toggled, modifier,
+ &VolumetricModifier::toggleMediumDetail);
+ QObject::connect(highDetailRB, &QRadioButton::toggled, modifier,
+ &VolumetricModifier::toggleHighDetail);
+ QObject::connect(colorTableCheckBox, &QCheckBox::stateChanged, modifier,
+ &VolumetricModifier::changeColorTable);
+ QObject::connect(preserveOpacityCheckBox, &QCheckBox::stateChanged, modifier,
+ &VolumetricModifier::setPreserveOpacity);
+ QObject::connect(transparentGroundCheckBox, &QCheckBox::stateChanged, modifier,
+ &VolumetricModifier::setTransparentGround);
+ QObject::connect(useHighDefShaderCheckBox, &QCheckBox::stateChanged, modifier,
+ &VolumetricModifier::setUseHighDefShader);
+ QObject::connect(alphaMultiplierSlider, &QSlider::valueChanged, modifier,
+ &VolumetricModifier::adjustAlphaMultiplier);
+ QObject::connect(areaAllRB, &QRadioButton::toggled, modifier,
+ &VolumetricModifier::toggleAreaAll);
+ QObject::connect(areaMineRB, &QRadioButton::toggled, modifier,
+ &VolumetricModifier::toggleAreaMine);
+ QObject::connect(areaMountainRB, &QRadioButton::toggled, modifier,
+ &VolumetricModifier::toggleAreaMountain);
+ QObject::connect(drawSliceFramesCheckBox, &QCheckBox::stateChanged, modifier,
+ &VolumetricModifier::setDrawSliceFrames);
+
+ 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..20338598
--- /dev/null
+++ b/examples/datavisualization/volumetric/volumetric.cpp
@@ -0,0 +1,764 @@
+/****************************************************************************
+**
+** 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 <QtDataVisualization/qcustom3dlabel.h>
+#include <QtDataVisualization/q3dscatter.h>
+#include <QtDataVisualization/q3dinputhandler.h>
+#include <QtCore/qmath.h>
+#include <QtWidgets/QLabel>
+#include <QtWidgets/QRadioButton>
+#include <QtWidgets/QSlider>
+#include <QtCore/QDebug>
+#include <QtGui/QOpenGLContext>
+
+using namespace QtDataVisualization;
+
+const int lowDetailSize(128);
+const int mediumDetailSize(256);
+const int highDetailSize(512);
+const int colorTableSize(256);
+const int layerDataSize(512);
+const int mineShaftDiameter(1);
+
+const int airColorIndex(254);
+const int mineShaftColorIndex(255);
+const int layerColorThickness(60);
+const int heightToColorDiv(140);
+const int magmaColorsMin(0);
+const int magmaColorsMax(layerColorThickness);
+const int aboveWaterGroundColorsMin(magmaColorsMax + 1);
+const int aboveWaterGroundColorsMax(aboveWaterGroundColorsMin + layerColorThickness);
+const int underWaterGroundColorsMin(aboveWaterGroundColorsMax + 1);
+const int underWaterGroundColorsMax(underWaterGroundColorsMin + layerColorThickness);
+const int waterColorsMin(underWaterGroundColorsMax + 1);
+const int waterColorsMax(waterColorsMin + layerColorThickness);
+const int terrainTransparency(12);
+
+static bool isOpenGLES()
+{
+#if defined(QT_OPENGL_ES_2)
+ return true;
+#elif (QT_VERSION < QT_VERSION_CHECK(5, 3, 0))
+ return false;
+#else
+ return QOpenGLContext::currentContext()->isOpenGLES();
+#endif
+}
+
+VolumetricModifier::VolumetricModifier(Q3DScatter *scatter)
+ : m_graph(scatter),
+ m_volumeItem(0),
+ m_sliceIndexX(lowDetailSize / 2),
+ m_sliceIndexY(lowDetailSize / 4),
+ m_sliceIndexZ(lowDetailSize / 2),
+ m_slicingX(false),
+ m_slicingY(false),
+ m_slicingZ(false),
+ m_mediumDetailRB(0),
+ m_highDetailRB(0),
+ m_lowDetailData(0),
+ m_mediumDetailData(0),
+ m_highDetailData(0),
+ m_mediumDetailIndex(0),
+ m_highDetailIndex(0),
+ m_mediumDetailShaftIndex(0),
+ m_highDetailShaftIndex(0),
+ m_sliceSliderX(0),
+ m_sliceSliderY(0),
+ m_sliceSliderZ(0),
+ m_usingPrimaryTable(true),
+ m_sliceLabelX(0),
+ m_sliceLabelY(0),
+ m_sliceLabelZ(0)
+{
+ m_graph->activeTheme()->setType(Q3DTheme::ThemeQt);
+ m_graph->setShadowQuality(QAbstract3DGraph::ShadowQualityNone);
+ m_graph->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPresetFront);
+ //! [6]
+ m_graph->setOrthoProjection(true);
+ //! [6]
+ m_graph->activeTheme()->setBackgroundEnabled(false);
+
+ // Only allow zooming at the center and limit the zoom to 200% to avoid clipping issues
+ static_cast<Q3DInputHandler *>(m_graph->activeInputHandler())->setZoomAtTargetEnabled(false);
+ m_graph->scene()->activeCamera()->setMaxZoomLevel(200.0f);
+
+ toggleAreaAll(true);
+
+ if (!isOpenGLES()) {
+ m_lowDetailData = new QVector<uchar>(lowDetailSize * lowDetailSize * lowDetailSize / 2);
+ m_mediumDetailData = new QVector<uchar>(mediumDetailSize * mediumDetailSize * mediumDetailSize / 2);
+ m_highDetailData = new QVector<uchar>(highDetailSize * highDetailSize * highDetailSize / 2);
+
+ initHeightMap(QStringLiteral(":/heightmaps/layer_ground.png"), m_groundLayer);
+ initHeightMap(QStringLiteral(":/heightmaps/layer_water.png"), m_waterLayer);
+ initHeightMap(QStringLiteral(":/heightmaps/layer_magma.png"), m_magmaLayer);
+
+ initMineShaftArray();
+
+ createVolume(lowDetailSize, 0, lowDetailSize, m_lowDetailData);
+ excavateMineShaft(lowDetailSize, 0, m_mineShaftArray.size(), m_lowDetailData);
+
+ //! [0]
+ m_volumeItem = new QCustom3DVolume;
+ // Adjust water level to zero with a minor tweak to y-coordinate position and scaling
+ m_volumeItem->setScaling(
+ QVector3D(m_graph->axisX()->max() - m_graph->axisX()->min(),
+ (m_graph->axisY()->max() - m_graph->axisY()->min()) * 0.91f,
+ m_graph->axisZ()->max() - m_graph->axisZ()->min()));
+ m_volumeItem->setPosition(
+ QVector3D((m_graph->axisX()->max() + m_graph->axisX()->min()) / 2.0f,
+ -0.045f * (m_graph->axisY()->max() - m_graph->axisY()->min()) +
+ (m_graph->axisY()->max() + m_graph->axisY()->min()) / 2.0f,
+ (m_graph->axisZ()->max() + m_graph->axisZ()->min()) / 2.0f));
+ m_volumeItem->setScalingAbsolute(false);
+ //! [0]
+ //! [1]
+ m_volumeItem->setTextureWidth(lowDetailSize);
+ m_volumeItem->setTextureHeight(lowDetailSize / 2);
+ m_volumeItem->setTextureDepth(lowDetailSize);
+ m_volumeItem->setTextureFormat(QImage::Format_Indexed8);
+ m_volumeItem->setTextureData(new QVector<uchar>(*m_lowDetailData));
+ //! [1]
+
+ // Generate color tables.
+ m_colorTable1.resize(colorTableSize);
+ m_colorTable2.resize(colorTableSize);
+
+ for (int i = 0; i < colorTableSize - 2; i++) {
+ if (i < magmaColorsMax) {
+ m_colorTable1[i] = qRgba(130 - (i * 2), 0, 0, 255);
+ } else if (i < aboveWaterGroundColorsMax) {
+ m_colorTable1[i] = qRgba((i - magmaColorsMax) * 4,
+ ((i - magmaColorsMax) * 2) + 120,
+ (i - magmaColorsMax) * 5, terrainTransparency);
+ } else if (i < underWaterGroundColorsMax) {
+ m_colorTable1[i] = qRgba(((layerColorThickness - i - aboveWaterGroundColorsMax)) + 70,
+ ((layerColorThickness - i - aboveWaterGroundColorsMax) * 2) + 20,
+ ((layerColorThickness - i - aboveWaterGroundColorsMax)) + 50,
+ terrainTransparency);
+ } else if (i < waterColorsMax) {
+ m_colorTable1[i] = qRgba(0, 0, ((i - underWaterGroundColorsMax) * 2) + 120,
+ terrainTransparency);
+ } else {
+ m_colorTable1[i] = qRgba(0, 0, 0, 0); // Not used
+ }
+ }
+ m_colorTable1[airColorIndex] = qRgba(0, 0, 0, 0);
+ m_colorTable1[mineShaftColorIndex] = qRgba(50, 50, 50, 255);
+
+ // The alternate color table just has gray gradients for all terrain except water
+ for (int i = 0; i < colorTableSize - 2; i++) {
+ if (i < magmaColorsMax) {
+ m_colorTable2[i] = qRgba(((i - aboveWaterGroundColorsMax) * 2),
+ ((i - aboveWaterGroundColorsMax) * 2),
+ ((i - aboveWaterGroundColorsMax) * 2), 255);
+ } else if (i < underWaterGroundColorsMax) {
+ m_colorTable2[i] = qRgba(((i - aboveWaterGroundColorsMax) * 2),
+ ((i - aboveWaterGroundColorsMax) * 2),
+ ((i - aboveWaterGroundColorsMax) * 2), terrainTransparency);
+ } else if (i < waterColorsMax) {
+ m_colorTable2[i] = qRgba(0, 0, ((i - underWaterGroundColorsMax) * 2) + 120,
+ terrainTransparency);
+ } else {
+ m_colorTable2[i] = qRgba(0, 0, 0, 0); // Not used
+ }
+ }
+ m_colorTable2[airColorIndex] = qRgba(0, 0, 0, 0);
+ m_colorTable2[mineShaftColorIndex] = qRgba(255, 255, 0, 255);
+
+ //! [2]
+ m_volumeItem->setColorTable(m_colorTable1);
+ //! [2]
+
+ //! [5]
+ m_volumeItem->setSliceFrameGaps(QVector3D(0.01f, 0.02f, 0.01f));
+ m_volumeItem->setSliceFrameThicknesses(QVector3D(0.0025f, 0.005f, 0.0025f));
+ m_volumeItem->setSliceFrameWidths(QVector3D(0.0025f, 0.005f, 0.0025f));
+ m_volumeItem->setDrawSliceFrames(false);
+ //! [5]
+ handleSlicingChanges();
+
+ //! [3]
+ m_graph->addCustomItem(m_volumeItem);
+ //! [3]
+
+ m_timer.start(0);
+ } else {
+ // OpenGL ES2 doesn't support 3D textures, so show a warning label instead
+ QCustom3DLabel *warningLabel = new QCustom3DLabel(
+ "QCustom3DVolume is not supported with OpenGL ES2",
+ QFont(),
+ QVector3D(0.0f, 0.5f, 0.0f),
+ QVector3D(1.5f, 1.5f, 0.0f),
+ QQuaternion());
+ warningLabel->setPositionAbsolute(true);
+ warningLabel->setFacingCamera(true);
+ m_graph->addCustomItem(warningLabel);
+ }
+
+ QObject::connect(m_graph, &QAbstract3DGraph::currentFpsChanged, this,
+ &VolumetricModifier::handleFpsChange);
+ QObject::connect(&m_timer, &QTimer::timeout, this,
+ &VolumetricModifier::handleTimeout);
+
+}
+
+VolumetricModifier::~VolumetricModifier()
+{
+ delete m_graph;
+}
+
+void VolumetricModifier::setFpsLabel(QLabel *fpsLabel)
+{
+ m_fpsLabel = fpsLabel;
+}
+
+void VolumetricModifier::setMediumDetailRB(QRadioButton *button)
+{
+ m_mediumDetailRB = button;
+}
+
+void VolumetricModifier::setHighDetailRB(QRadioButton *button)
+{
+ m_highDetailRB = button;
+}
+
+void VolumetricModifier::setSliceLabels(QLabel *xLabel, QLabel *yLabel, QLabel *zLabel)
+{
+ m_sliceLabelX = xLabel;
+ m_sliceLabelY = yLabel;
+ m_sliceLabelZ = zLabel;
+
+ adjustSliceX(m_sliceSliderX->value());
+ adjustSliceY(m_sliceSliderY->value());
+ adjustSliceZ(m_sliceSliderZ->value());
+}
+
+void VolumetricModifier::setAlphaMultiplierLabel(QLabel *label)
+{
+ m_alphaMultiplierLabel = label;
+}
+
+void VolumetricModifier::sliceX(int enabled)
+{
+ m_slicingX = enabled;
+ handleSlicingChanges();
+}
+
+void VolumetricModifier::sliceY(int enabled)
+{
+ m_slicingY = enabled;
+ handleSlicingChanges();
+}
+
+void VolumetricModifier::sliceZ(int enabled)
+{
+ m_slicingZ = enabled;
+ handleSlicingChanges();
+}
+
+void VolumetricModifier::adjustSliceX(int value)
+{
+ if (m_volumeItem) {
+ m_sliceIndexX = value / (1024 / m_volumeItem->textureWidth());
+ if (m_sliceIndexX == m_volumeItem->textureWidth())
+ m_sliceIndexX--;
+ if (m_volumeItem->sliceIndexX() != -1)
+ //! [7]
+ m_volumeItem->setSliceIndexX(m_sliceIndexX);
+ //! [7]
+ //! [9]
+ m_sliceLabelX->setPixmap(
+ QPixmap::fromImage(m_volumeItem->renderSlice(Qt::XAxis, m_sliceIndexX)));
+ //! [9]
+ }
+}
+
+void VolumetricModifier::adjustSliceY(int value)
+{
+ if (m_volumeItem) {
+ m_sliceIndexY = value / (1024 / m_volumeItem->textureHeight());
+ if (m_sliceIndexY == m_volumeItem->textureHeight())
+ m_sliceIndexY--;
+ if (m_volumeItem->sliceIndexY() != -1)
+ m_volumeItem->setSliceIndexY(m_sliceIndexY);
+ m_sliceLabelY->setPixmap(
+ QPixmap::fromImage(m_volumeItem->renderSlice(Qt::YAxis, m_sliceIndexY)));
+ }
+}
+
+void VolumetricModifier::adjustSliceZ(int value)
+{
+ if (m_volumeItem) {
+ m_sliceIndexZ = value / (1024 / m_volumeItem->textureDepth());
+ if (m_sliceIndexZ == m_volumeItem->textureDepth())
+ m_sliceIndexZ--;
+ if (m_volumeItem->sliceIndexZ() != -1)
+ m_volumeItem->setSliceIndexZ(m_sliceIndexZ);
+ m_sliceLabelZ->setPixmap(
+ QPixmap::fromImage(m_volumeItem->renderSlice(Qt::ZAxis, m_sliceIndexZ)));
+ }
+}
+
+void VolumetricModifier::handleFpsChange(qreal fps)
+{
+ const QString fpsFormat = QStringLiteral("FPS: %1");
+ int fps10 = int(fps * 10.0);
+ m_fpsLabel->setText(fpsFormat.arg(qreal(fps10) / 10.0));
+}
+
+void VolumetricModifier::handleTimeout()
+{
+ if (!m_mediumDetailRB->isEnabled()) {
+ if (m_mediumDetailIndex != mediumDetailSize) {
+ m_mediumDetailIndex = createVolume(mediumDetailSize, m_mediumDetailIndex, 4,
+ m_mediumDetailData);
+ } else if (m_mediumDetailShaftIndex != m_mineShaftArray.size()) {
+ m_mediumDetailShaftIndex = excavateMineShaft(mediumDetailSize, m_mediumDetailShaftIndex,
+ 1, m_mediumDetailData );
+ } else {
+ m_mediumDetailRB->setEnabled(true);
+ QString label = QStringLiteral("Medium (%1x%2x%1)");
+ m_mediumDetailRB->setText(label.arg(mediumDetailSize).arg(mediumDetailSize / 2));
+ }
+ } else if (!m_highDetailRB->isEnabled()) {
+ if (m_highDetailIndex != highDetailSize) {
+ m_highDetailIndex = createVolume(highDetailSize, m_highDetailIndex, 1,
+ m_highDetailData);
+ } else if (m_highDetailShaftIndex != m_mineShaftArray.size()) {
+ m_highDetailShaftIndex = excavateMineShaft(highDetailSize, m_highDetailShaftIndex, 1,
+ m_highDetailData);
+ } else {
+ m_highDetailRB->setEnabled(true);
+ QString label = QStringLiteral("High (%1x%2x%1)");
+ m_highDetailRB->setText(label.arg(highDetailSize).arg(highDetailSize / 2));
+ m_timer.stop();
+ }
+ }
+}
+
+void VolumetricModifier::toggleLowDetail(bool enabled)
+{
+ if (enabled && m_volumeItem) {
+ m_volumeItem->setTextureData(new QVector<uchar>(*m_lowDetailData));
+ m_volumeItem->setTextureDimensions(lowDetailSize, lowDetailSize / 2, lowDetailSize);
+ adjustSliceX(m_sliceSliderX->value());
+ adjustSliceY(m_sliceSliderY->value());
+ adjustSliceZ(m_sliceSliderZ->value());
+ }
+}
+
+void VolumetricModifier::toggleMediumDetail(bool enabled)
+{
+ if (enabled && m_volumeItem) {
+ m_volumeItem->setTextureData(new QVector<uchar>(*m_mediumDetailData));
+ m_volumeItem->setTextureDimensions(mediumDetailSize, mediumDetailSize / 2, mediumDetailSize);
+ adjustSliceX(m_sliceSliderX->value());
+ adjustSliceY(m_sliceSliderY->value());
+ adjustSliceZ(m_sliceSliderZ->value());
+ }
+}
+
+void VolumetricModifier::toggleHighDetail(bool enabled)
+{
+ if (enabled && m_volumeItem) {
+ m_volumeItem->setTextureData(new QVector<uchar>(*m_highDetailData));
+ m_volumeItem->setTextureDimensions(highDetailSize, highDetailSize / 2, highDetailSize);
+ adjustSliceX(m_sliceSliderX->value());
+ adjustSliceY(m_sliceSliderY->value());
+ adjustSliceZ(m_sliceSliderZ->value());
+ }
+}
+
+void VolumetricModifier::setFpsMeasurement(bool enabled)
+{
+ m_graph->setMeasureFps(enabled);
+ if (enabled)
+ m_fpsLabel->setText(QStringLiteral("Measuring..."));
+ else
+ m_fpsLabel->setText(QString());
+}
+
+void VolumetricModifier::setSliceSliders(QSlider *sliderX, QSlider *sliderY, QSlider *sliderZ)
+{
+ m_sliceSliderX = sliderX;
+ m_sliceSliderY = sliderY;
+ m_sliceSliderZ = sliderZ;
+
+ // Set sliders to interesting values
+ m_sliceSliderX->setValue(715);
+ m_sliceSliderY->setValue(612);
+ m_sliceSliderZ->setValue(715);
+}
+
+void VolumetricModifier::changeColorTable(int enabled)
+{
+ if (m_volumeItem) {
+ if (enabled)
+ m_volumeItem->setColorTable(m_colorTable2);
+ else
+ m_volumeItem->setColorTable(m_colorTable1);
+
+ m_usingPrimaryTable = !enabled;
+
+ // Rerender image labels
+ adjustSliceX(m_sliceSliderX->value());
+ adjustSliceY(m_sliceSliderY->value());
+ adjustSliceZ(m_sliceSliderZ->value());
+ }
+}
+
+void VolumetricModifier::setPreserveOpacity(bool enabled)
+{
+
+ if (m_volumeItem) {
+ //! [10]
+ m_volumeItem->setPreserveOpacity(enabled);
+ //! [10]
+
+ // Rerender image labels
+ adjustSliceX(m_sliceSliderX->value());
+ adjustSliceY(m_sliceSliderY->value());
+ adjustSliceZ(m_sliceSliderZ->value());
+ }
+}
+
+void VolumetricModifier::setTransparentGround(bool enabled)
+{
+ if (m_volumeItem) {
+ //! [12]
+ int newAlpha = enabled ? terrainTransparency : 255;
+ for (int i = aboveWaterGroundColorsMin; i < underWaterGroundColorsMax; i++) {
+ QRgb oldColor1 = m_colorTable1.at(i);
+ QRgb oldColor2 = m_colorTable2.at(i);
+ m_colorTable1[i] = qRgba(qRed(oldColor1), qGreen(oldColor1), qBlue(oldColor1), newAlpha);
+ m_colorTable2[i] = qRgba(qRed(oldColor2), qGreen(oldColor2), qBlue(oldColor2), newAlpha);
+ }
+ if (m_usingPrimaryTable)
+ m_volumeItem->setColorTable(m_colorTable1);
+ else
+ m_volumeItem->setColorTable(m_colorTable2);
+ //! [12]
+ adjustSliceX(m_sliceSliderX->value());
+ adjustSliceY(m_sliceSliderY->value());
+ adjustSliceZ(m_sliceSliderZ->value());
+ }
+}
+
+void VolumetricModifier::setUseHighDefShader(bool enabled)
+{
+ if (m_volumeItem) {
+ //! [13]
+ m_volumeItem->setUseHighDefShader(enabled);
+ //! [13]
+ }
+}
+
+void VolumetricModifier::adjustAlphaMultiplier(int value)
+{
+ if (m_volumeItem) {
+ float mult;
+ if (value > 100)
+ mult = float(value - 99) / 2.0f;
+ else
+ mult = float(value) / float(500 - value * 4);
+ //! [11]
+ m_volumeItem->setAlphaMultiplier(mult);
+ //! [11]
+ QString labelFormat = QStringLiteral("Alpha multiplier: %1");
+ m_alphaMultiplierLabel->setText(labelFormat.arg(
+ QString::number(m_volumeItem->alphaMultiplier(), 'f', 3)));
+
+ // Rerender image labels
+ adjustSliceX(m_sliceSliderX->value());
+ adjustSliceY(m_sliceSliderY->value());
+ adjustSliceZ(m_sliceSliderZ->value());
+ }
+}
+
+void VolumetricModifier::toggleAreaAll(bool enabled)
+{
+ if (enabled) {
+ m_graph->axisX()->setRange(0.0f, 1000.0f);
+ m_graph->axisY()->setRange(-600.0f, 600.0f);
+ m_graph->axisZ()->setRange(0.0f, 1000.0f);
+ m_graph->axisX()->setSegmentCount(5);
+ m_graph->axisY()->setSegmentCount(6);
+ m_graph->axisZ()->setSegmentCount(5);
+ }
+}
+
+void VolumetricModifier::toggleAreaMine(bool enabled)
+{
+ if (enabled) {
+ m_graph->axisX()->setRange(350.0f, 850.0f);
+ m_graph->axisY()->setRange(-500.0f, 100.0f);
+ m_graph->axisZ()->setRange(350.0f, 900.0f);
+ m_graph->axisX()->setSegmentCount(10);
+ m_graph->axisY()->setSegmentCount(6);
+ m_graph->axisZ()->setSegmentCount(11);
+ }
+}
+
+void VolumetricModifier::toggleAreaMountain(bool enabled)
+{
+ if (enabled) {
+ m_graph->axisX()->setRange(300.0f, 600.0f);
+ m_graph->axisY()->setRange(-100.0f, 400.0f);
+ m_graph->axisZ()->setRange(300.0f, 600.0f);
+ m_graph->axisX()->setSegmentCount(9);
+ m_graph->axisY()->setSegmentCount(5);
+ m_graph->axisZ()->setSegmentCount(9);
+ }
+}
+
+void VolumetricModifier::setDrawSliceFrames(int enabled)
+{
+ if (m_volumeItem)
+ m_volumeItem->setDrawSliceFrames(enabled);
+}
+
+void VolumetricModifier::initHeightMap(QString fileName, QVector<uchar> &layerData)
+{
+ QImage heightImage(fileName);
+
+ layerData.resize(layerDataSize * layerDataSize);
+ const uchar *bits = heightImage.bits();
+ int index = 0;
+ QVector<QRgb> colorTable = heightImage.colorTable();
+ for (int i = 0; i < layerDataSize; i++) {
+ for (int j = 0; j < layerDataSize; j++) {
+ layerData[index] = qRed(colorTable.at(bits[index]));
+ index++;
+ }
+ }
+}
+
+int VolumetricModifier::createVolume(int textureSize, int startIndex, int count,
+ QVector<uchar> *textureData)
+{
+ // Generate volume from layer data.
+ int index = startIndex * textureSize * textureSize / 2.0f;
+ int endIndex = startIndex + count;
+ if (endIndex > textureSize)
+ endIndex = textureSize;
+ QVector<uchar> magmaHeights(textureSize);
+ QVector<uchar> waterHeights(textureSize);
+ QVector<uchar> groundHeights(textureSize);
+ float multiplier = float(layerDataSize) / float(textureSize);
+ for (int i = startIndex; i < endIndex; i++) {
+ // Generate layer height arrays
+ for (int l = 0; l < textureSize; l++) {
+ int layerIndex = (int(i * multiplier) * layerDataSize + int(l * multiplier));
+ magmaHeights[l] = int(m_magmaLayer.at(layerIndex));
+ waterHeights[l] = int(m_waterLayer.at(layerIndex));
+ groundHeights[l] = int(m_groundLayer.at(layerIndex));
+ }
+ for (int j = 0; j < textureSize / 2; j++) {
+ for (int k = 0; k < textureSize; k++) {
+ int colorIndex;
+ int height((layerDataSize - (j * 2 * multiplier)) / 2);
+ if (height < magmaHeights.at(k)) {
+ // Magma layer
+ colorIndex = int((float(height) / heightToColorDiv)
+ * float(layerColorThickness)) + magmaColorsMin;
+ } else if (height < groundHeights.at(k) && height < waterHeights.at(k)) {
+ // Ground layer below water
+ colorIndex = int((float(waterHeights.at(k) - height) / heightToColorDiv)
+ * float(layerColorThickness)) + underWaterGroundColorsMin;
+ } else if (height < waterHeights.at(k)) {
+ // Water layer where water goes over ground
+ colorIndex = int((float(height - magmaHeights.at(k)) / heightToColorDiv)
+ * float(layerColorThickness)) + waterColorsMin;
+ } else if (height <= groundHeights.at(k)) {
+ // Ground above water
+ colorIndex = int((float(height - waterHeights.at(k)) / heightToColorDiv)
+ * float(layerColorThickness)) + aboveWaterGroundColorsMin;
+ } else {
+ // Rest is air
+ colorIndex = airColorIndex;
+ }
+
+ (*textureData)[index] = colorIndex;
+ index++;
+ }
+ }
+ }
+ return endIndex;
+}
+
+int VolumetricModifier::excavateMineShaft(int textureSize, int startIndex, int count,
+ QVector<uchar> *textureData)
+{
+ int endIndex = startIndex + count;
+ if (endIndex > m_mineShaftArray.size())
+ endIndex = m_mineShaftArray.size();
+ int shaftSize = mineShaftDiameter * textureSize / lowDetailSize;
+ for (int i = startIndex; i < endIndex; i++) {
+ QVector3D shaftStart(m_mineShaftArray.at(i).first);
+ QVector3D shaftEnd(m_mineShaftArray.at(i).second);
+ int shaftLen = (shaftEnd - shaftStart).length() * lowDetailSize;
+ int dataX = shaftStart.x() * textureSize - (shaftSize / 2);
+ int dataY = (shaftStart.y() * textureSize - (shaftSize / 2)) / 2;
+ int dataZ = shaftStart.z() * textureSize - (shaftSize / 2);
+ int dataIndex = dataX + (dataY * textureSize) + dataZ * (textureSize * textureSize / 2);
+ if (shaftStart.x() != shaftEnd.x()) {
+ for (int j = 0; j <= shaftLen; j++) {
+ excavateMineBlock(textureSize, dataIndex, shaftSize, textureData);
+ dataIndex += shaftSize;
+ }
+ } else if (shaftStart.y() != shaftEnd.y()) {
+ shaftLen /= 2; // Vertical shafts are half as long
+ for (int j = 0; j <= shaftLen; j++) {
+ excavateMineBlock(textureSize, dataIndex, shaftSize, textureData);
+ dataIndex += textureSize * shaftSize;
+ }
+ } else {
+ for (int j = 0; j <= shaftLen; j++) {
+ excavateMineBlock(textureSize, dataIndex, shaftSize, textureData);
+ dataIndex += (textureSize * textureSize / 2) * shaftSize;
+ }
+ }
+
+
+ }
+ return endIndex;
+}
+
+void VolumetricModifier::excavateMineBlock(int textureSize, int dataIndex, int size,
+ QVector<uchar> *textureData)
+{
+ for (int k = 0; k < size; k++) {
+ int curIndex = dataIndex + (k * textureSize * textureSize / 2);
+ for (int l = 0; l < size; l++) {
+ curIndex = dataIndex + (k * textureSize * textureSize / 2)
+ + (l * textureSize);
+ for (int m = 0; m < size; m++) {
+ if (textureData->at(curIndex) != airColorIndex)
+ (*textureData)[curIndex] = mineShaftColorIndex;
+ curIndex++;
+ }
+
+ }
+ }
+}
+
+void VolumetricModifier::handleSlicingChanges()
+{
+ if (m_volumeItem) {
+ if (m_slicingX || m_slicingY || m_slicingZ) {
+ // Only show slices of selected dimensions
+ //! [8]
+ m_volumeItem->setDrawSlices(true);
+ //! [8]
+ m_volumeItem->setSliceIndexX(m_slicingX ? m_sliceIndexX : -1);
+ m_volumeItem->setSliceIndexY(m_slicingY ? m_sliceIndexY : -1);
+ m_volumeItem->setSliceIndexZ(m_slicingZ ? m_sliceIndexZ : -1);
+ } else {
+ // Show slice frames for all dimenstions when not actually slicing
+ m_volumeItem->setDrawSlices(false);
+ m_volumeItem->setSliceIndexX(m_sliceIndexX);
+ m_volumeItem->setSliceIndexY(m_sliceIndexY);
+ m_volumeItem->setSliceIndexZ(m_sliceIndexZ);
+ }
+ }
+}
+
+void VolumetricModifier::initMineShaftArray()
+{
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.7f, 0.1f, 0.7f),
+ QVector3D(0.7f, 0.8f, 0.7f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.7f, 0.7f, 0.5f),
+ QVector3D(0.7f, 0.7f, 0.7f));
+
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.4f, 0.7f, 0.7f),
+ QVector3D(0.7f, 0.7f, 0.7f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.4f, 0.7f, 0.7f),
+ QVector3D(0.4f, 0.7f, 0.8f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.45f, 0.7f, 0.7f),
+ QVector3D(0.45f, 0.7f, 0.8f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.5f, 0.7f, 0.7f),
+ QVector3D(0.5f, 0.7f, 0.8f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.55f, 0.7f, 0.7f),
+ QVector3D(0.55f, 0.7f, 0.8f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.7f, 0.7f),
+ QVector3D(0.6f, 0.7f, 0.8f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.65f, 0.7f, 0.7f),
+ QVector3D(0.65f, 0.7f, 0.8f));
+
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.5f, 0.6f, 0.7f),
+ QVector3D(0.7f, 0.6f, 0.7f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.5f, 0.6f, 0.7f),
+ QVector3D(0.5f, 0.6f, 0.8f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.55f, 0.6f, 0.7f),
+ QVector3D(0.55f, 0.6f, 0.8f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.6f, 0.7f),
+ QVector3D(0.6f, 0.6f, 0.8f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.65f, 0.6f, 0.7f),
+ QVector3D(0.65f, 0.6f, 0.8f));
+
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.7f, 0.6f, 0.4f),
+ QVector3D(0.7f, 0.6f, 0.7f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.6f, 0.45f),
+ QVector3D(0.8f, 0.6f, 0.45f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.6f, 0.5f),
+ QVector3D(0.8f, 0.6f, 0.5f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.6f, 0.55f),
+ QVector3D(0.8f, 0.6f, 0.55f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.6f, 0.6f),
+ QVector3D(0.8f, 0.6f, 0.6f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.6f, 0.65f),
+ QVector3D(0.8f, 0.6f, 0.65f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.6f, 0.7f),
+ QVector3D(0.8f, 0.6f, 0.7f));
+
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.7f, 0.7f, 0.4f),
+ QVector3D(0.7f, 0.7f, 0.7f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.7f, 0.45f),
+ QVector3D(0.8f, 0.7f, 0.45f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.7f, 0.5f),
+ QVector3D(0.8f, 0.7f, 0.5f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.7f, 0.55f),
+ QVector3D(0.8f, 0.7f, 0.55f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.7f, 0.6f),
+ QVector3D(0.8f, 0.7f, 0.6f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.7f, 0.65f),
+ QVector3D(0.8f, 0.7f, 0.65f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.7f, 0.7f),
+ QVector3D(0.8f, 0.7f, 0.7f));
+
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.7f, 0.8f, 0.5f),
+ QVector3D(0.7f, 0.8f, 0.7f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.8f, 0.55f),
+ QVector3D(0.8f, 0.8f, 0.55f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.8f, 0.6f),
+ QVector3D(0.8f, 0.8f, 0.6f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.8f, 0.65f),
+ QVector3D(0.8f, 0.8f, 0.65f));
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.8f, 0.7f),
+ QVector3D(0.8f, 0.8f, 0.7f));
+
+ m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.7f, 0.1f, 0.4f),
+ QVector3D(0.7f, 0.7f, 0.4f));
+}
diff --git a/examples/datavisualization/volumetric/volumetric.h b/examples/datavisualization/volumetric/volumetric.h
new file mode 100644
index 00000000..8d28b524
--- /dev/null
+++ b/examples/datavisualization/volumetric/volumetric.h
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** 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>
+#include <QtCore/QTimer>
+#include <QtGui/QRgb>
+
+class QLabel;
+class QRadioButton;
+class QSlider;
+
+using namespace QtDataVisualization;
+
+class VolumetricModifier : public QObject
+{
+ Q_OBJECT
+public:
+ explicit VolumetricModifier(Q3DScatter *scatter);
+ ~VolumetricModifier();
+
+ void setFpsLabel(QLabel *fpsLabel);
+ void setMediumDetailRB(QRadioButton *button);
+ void setHighDetailRB(QRadioButton *button);
+ void setSliceLabels(QLabel *xLabel, QLabel *yLabel, QLabel *zLabel);
+ void setAlphaMultiplierLabel(QLabel *label);
+
+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 handleFpsChange(qreal fps);
+ void handleTimeout();
+ void toggleLowDetail(bool enabled);
+ void toggleMediumDetail(bool enabled);
+ void toggleHighDetail(bool enabled);
+ void setFpsMeasurement(bool enabled);
+ void setSliceSliders(QSlider *sliderX, QSlider *sliderY, QSlider *sliderZ);
+ void changeColorTable(int enabled);
+ void setPreserveOpacity(bool enabled);
+ void setTransparentGround(bool enabled);
+ void setUseHighDefShader(bool enabled);
+ void adjustAlphaMultiplier(int value);
+ void toggleAreaAll(bool enabled);
+ void toggleAreaMine(bool enabled);
+ void toggleAreaMountain(bool enabled);
+ void setDrawSliceFrames(int enabled);
+
+private:
+
+ void initHeightMap(QString fileName, QVector<uchar> &layerData);
+ void initMineShaftArray();
+ int createVolume(int textureSize, int startIndex, int count,
+ QVector<uchar> *textureData);
+ int excavateMineShaft(int textureSize, int startIndex, int count,
+ QVector<uchar> *textureData);
+ void excavateMineBlock(int textureSize, int dataIndex, int size, QVector<uchar> *textureData);
+ void handleSlicingChanges();
+
+ Q3DScatter *m_graph;
+ QCustom3DVolume *m_volumeItem;
+ int m_sliceIndexX;
+ int m_sliceIndexY;
+ int m_sliceIndexZ;
+ bool m_slicingX;
+ bool m_slicingY;
+ bool m_slicingZ;
+ QLabel *m_fpsLabel;
+ QRadioButton *m_mediumDetailRB;
+ QRadioButton *m_highDetailRB;
+ QVector<uchar> *m_lowDetailData;
+ QVector<uchar> *m_mediumDetailData;
+ QVector<uchar> *m_highDetailData;
+ QTimer m_timer;
+ int m_mediumDetailIndex;
+ int m_highDetailIndex;
+ int m_mediumDetailShaftIndex;
+ int m_highDetailShaftIndex;
+ QSlider *m_sliceSliderX;
+ QSlider *m_sliceSliderY;
+ QSlider *m_sliceSliderZ;
+ QVector<QRgb> m_colorTable1;
+ QVector<QRgb> m_colorTable2;
+ bool m_usingPrimaryTable;
+ QLabel *m_sliceLabelX;
+ QLabel *m_sliceLabelY;
+ QLabel *m_sliceLabelZ;
+ QLabel *m_alphaMultiplierLabel;
+ QVector<uchar> m_magmaLayer;
+ QVector<uchar> m_waterLayer;
+ QVector<uchar> m_groundLayer;
+ QVector<QPair<QVector3D, QVector3D> > m_mineShaftArray;
+};
+
+#endif
diff --git a/examples/datavisualization/volumetric/volumetric.pro b/examples/datavisualization/volumetric/volumetric.pro
new file mode 100644
index 00000000..fa355692
--- /dev/null
+++ b/examples/datavisualization/volumetric/volumetric.pro
@@ -0,0 +1,17 @@
+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/*
+
+RESOURCES += volumetric.qrc
diff --git a/examples/datavisualization/volumetric/volumetric.qrc b/examples/datavisualization/volumetric/volumetric.qrc
new file mode 100644
index 00000000..920fd1d2
--- /dev/null
+++ b/examples/datavisualization/volumetric/volumetric.qrc
@@ -0,0 +1,7 @@
+<RCC>
+ <qresource prefix="/heightmaps">
+ <file>layer_ground.png</file>
+ <file>layer_magma.png</file>
+ <file>layer_water.png</file>
+ </qresource>
+</RCC>