diff options
Diffstat (limited to 'examples/datavisualization/volumetric')
-rw-r--r-- | examples/datavisualization/volumetric/doc/images/volumetric-example.png | bin | 0 -> 177203 bytes | |||
-rw-r--r-- | examples/datavisualization/volumetric/doc/src/volumetric.qdoc | 129 | ||||
-rw-r--r-- | examples/datavisualization/volumetric/layer_ground.png | bin | 0 -> 63473 bytes | |||
-rw-r--r-- | examples/datavisualization/volumetric/layer_magma.png | bin | 0 -> 17332 bytes | |||
-rw-r--r-- | examples/datavisualization/volumetric/layer_water.png | bin | 0 -> 27124 bytes | |||
-rw-r--r-- | examples/datavisualization/volumetric/main.cpp | 247 | ||||
-rw-r--r-- | examples/datavisualization/volumetric/volumetric.cpp | 764 | ||||
-rw-r--r-- | examples/datavisualization/volumetric/volumetric.h | 116 | ||||
-rw-r--r-- | examples/datavisualization/volumetric/volumetric.pro | 17 | ||||
-rw-r--r-- | examples/datavisualization/volumetric/volumetric.qrc | 7 |
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 Binary files differnew file mode 100644 index 00000000..277d4fe4 --- /dev/null +++ b/examples/datavisualization/volumetric/doc/images/volumetric-example.png 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 Binary files differnew file mode 100644 index 00000000..3f96a122 --- /dev/null +++ b/examples/datavisualization/volumetric/layer_ground.png diff --git a/examples/datavisualization/volumetric/layer_magma.png b/examples/datavisualization/volumetric/layer_magma.png Binary files differnew file mode 100644 index 00000000..01434d35 --- /dev/null +++ b/examples/datavisualization/volumetric/layer_magma.png diff --git a/examples/datavisualization/volumetric/layer_water.png b/examples/datavisualization/volumetric/layer_water.png Binary files differnew file mode 100644 index 00000000..4d57563e --- /dev/null +++ b/examples/datavisualization/volumetric/layer_water.png 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> |