1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt Data Visualization module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 or (at your option) any later version
20** approved by the KDE Free Qt Foundation. The licenses are as published by
21** the Free Software Foundation and appearing in the file LICENSE.GPL3
22** included in the packaging of this file. Please review the following
23** information to ensure the GNU General Public License requirements will
24** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25**
26** $QT_END_LICENSE$
27**
28****************************************************************************/
29
30#include "volumetric.h"
31#include <QtDataVisualization/qvalue3daxis.h>
32#include <QtDataVisualization/q3dscene.h>
33#include <QtDataVisualization/q3dcamera.h>
34#include <QtDataVisualization/q3dtheme.h>
35#include <QtDataVisualization/qcustom3dlabel.h>
36#include <QtDataVisualization/q3dscatter.h>
37#include <QtDataVisualization/q3dinputhandler.h>
38#include <QtCore/qmath.h>
39#include <QtWidgets/QLabel>
40#include <QtWidgets/QRadioButton>
41#include <QtWidgets/QSlider>
42#include <QtCore/QDebug>
43#include <QtGui/QOpenGLContext>
44
45using namespace QtDataVisualization;
46
47const int lowDetailSize(128);
48const int mediumDetailSize(256);
49const int highDetailSize(512);
50const int colorTableSize(256);
51const int layerDataSize(512);
52const int mineShaftDiameter(1);
53
54const int airColorIndex(254);
55const int mineShaftColorIndex(255);
56const int layerColorThickness(60);
57const int heightToColorDiv(140);
58const int magmaColorsMin(0);
59const int magmaColorsMax(layerColorThickness);
60const int aboveWaterGroundColorsMin(magmaColorsMax + 1);
61const int aboveWaterGroundColorsMax(aboveWaterGroundColorsMin + layerColorThickness);
62const int underWaterGroundColorsMin(aboveWaterGroundColorsMax + 1);
63const int underWaterGroundColorsMax(underWaterGroundColorsMin + layerColorThickness);
64const int waterColorsMin(underWaterGroundColorsMax + 1);
65const int waterColorsMax(waterColorsMin + layerColorThickness);
66const int terrainTransparency(12);
67
68static bool isOpenGLES()
69{
70#if defined(QT_OPENGL_ES_2)
71 return true;
72#elif (QT_VERSION < QT_VERSION_CHECK(5, 3, 0))
73 return false;
74#else
75 return QOpenGLContext::currentContext()->isOpenGLES();
76#endif
77}
78
79VolumetricModifier::VolumetricModifier(Q3DScatter *scatter)
80 : m_graph(scatter),
81 m_volumeItem(0),
82 m_sliceIndexX(lowDetailSize / 2),
83 m_sliceIndexY(lowDetailSize / 4),
84 m_sliceIndexZ(lowDetailSize / 2),
85 m_slicingX(false),
86 m_slicingY(false),
87 m_slicingZ(false),
88 m_mediumDetailRB(0),
89 m_highDetailRB(0),
90 m_lowDetailData(0),
91 m_mediumDetailData(0),
92 m_highDetailData(0),
93 m_mediumDetailIndex(0),
94 m_highDetailIndex(0),
95 m_mediumDetailShaftIndex(0),
96 m_highDetailShaftIndex(0),
97 m_sliceSliderX(0),
98 m_sliceSliderY(0),
99 m_sliceSliderZ(0),
100 m_usingPrimaryTable(true),
101 m_sliceLabelX(0),
102 m_sliceLabelY(0),
103 m_sliceLabelZ(0)
104{
105 m_graph->activeTheme()->setType(Q3DTheme::ThemeQt);
106 m_graph->setShadowQuality(QAbstract3DGraph::ShadowQualityNone);
107 m_graph->scene()->activeCamera()->setCameraPreset(Q3DCamera::CameraPresetFront);
108 //! [6]
109 m_graph->setOrthoProjection(true);
110 //! [6]
111 m_graph->activeTheme()->setBackgroundEnabled(false);
112
113 // Only allow zooming at the center and limit the zoom to 200% to avoid clipping issues
114 static_cast<Q3DInputHandler *>(m_graph->activeInputHandler())->setZoomAtTargetEnabled(false);
115 m_graph->scene()->activeCamera()->setMaxZoomLevel(200.0f);
116
117 toggleAreaAll(enabled: true);
118
119 if (!isOpenGLES()) {
120 m_lowDetailData = new QVector<uchar>(lowDetailSize * lowDetailSize * lowDetailSize / 2);
121 m_mediumDetailData = new QVector<uchar>(mediumDetailSize * mediumDetailSize * mediumDetailSize / 2);
122 m_highDetailData = new QVector<uchar>(highDetailSize * highDetailSize * highDetailSize / 2);
123
124 initHeightMap(QStringLiteral(":/heightmaps/layer_ground.png"), layerData&: m_groundLayer);
125 initHeightMap(QStringLiteral(":/heightmaps/layer_water.png"), layerData&: m_waterLayer);
126 initHeightMap(QStringLiteral(":/heightmaps/layer_magma.png"), layerData&: m_magmaLayer);
127
128 initMineShaftArray();
129
130 createVolume(textureSize: lowDetailSize, startIndex: 0, count: lowDetailSize, textureData: m_lowDetailData);
131 excavateMineShaft(textureSize: lowDetailSize, startIndex: 0, count: m_mineShaftArray.size(), textureData: m_lowDetailData);
132
133 //! [0]
134 m_volumeItem = new QCustom3DVolume;
135 // Adjust water level to zero with a minor tweak to y-coordinate position and scaling
136 m_volumeItem->setScaling(
137 QVector3D(m_graph->axisX()->max() - m_graph->axisX()->min(),
138 (m_graph->axisY()->max() - m_graph->axisY()->min()) * 0.91f,
139 m_graph->axisZ()->max() - m_graph->axisZ()->min()));
140 m_volumeItem->setPosition(
141 QVector3D((m_graph->axisX()->max() + m_graph->axisX()->min()) / 2.0f,
142 -0.045f * (m_graph->axisY()->max() - m_graph->axisY()->min()) +
143 (m_graph->axisY()->max() + m_graph->axisY()->min()) / 2.0f,
144 (m_graph->axisZ()->max() + m_graph->axisZ()->min()) / 2.0f));
145 m_volumeItem->setScalingAbsolute(false);
146 //! [0]
147 //! [1]
148 m_volumeItem->setTextureWidth(lowDetailSize);
149 m_volumeItem->setTextureHeight(lowDetailSize / 2);
150 m_volumeItem->setTextureDepth(lowDetailSize);
151 m_volumeItem->setTextureFormat(QImage::Format_Indexed8);
152 m_volumeItem->setTextureData(new QVector<uchar>(*m_lowDetailData));
153 //! [1]
154
155 // Generate color tables.
156 m_colorTable1.resize(asize: colorTableSize);
157 m_colorTable2.resize(asize: colorTableSize);
158
159 for (int i = 0; i < colorTableSize - 2; i++) {
160 if (i < magmaColorsMax) {
161 m_colorTable1[i] = qRgba(r: 130 - (i * 2), g: 0, b: 0, a: 255);
162 } else if (i < aboveWaterGroundColorsMax) {
163 m_colorTable1[i] = qRgba(r: (i - magmaColorsMax) * 4,
164 g: ((i - magmaColorsMax) * 2) + 120,
165 b: (i - magmaColorsMax) * 5, a: terrainTransparency);
166 } else if (i < underWaterGroundColorsMax) {
167 m_colorTable1[i] = qRgba(r: ((layerColorThickness - i - aboveWaterGroundColorsMax)) + 70,
168 g: ((layerColorThickness - i - aboveWaterGroundColorsMax) * 2) + 20,
169 b: ((layerColorThickness - i - aboveWaterGroundColorsMax)) + 50,
170 a: terrainTransparency);
171 } else if (i < waterColorsMax) {
172 m_colorTable1[i] = qRgba(r: 0, g: 0, b: ((i - underWaterGroundColorsMax) * 2) + 120,
173 a: terrainTransparency);
174 } else {
175 m_colorTable1[i] = qRgba(r: 0, g: 0, b: 0, a: 0); // Not used
176 }
177 }
178 m_colorTable1[airColorIndex] = qRgba(r: 0, g: 0, b: 0, a: 0);
179 m_colorTable1[mineShaftColorIndex] = qRgba(r: 50, g: 50, b: 50, a: 255);
180
181 // The alternate color table just has gray gradients for all terrain except water
182 for (int i = 0; i < colorTableSize - 2; i++) {
183 if (i < magmaColorsMax) {
184 m_colorTable2[i] = qRgba(r: ((i - aboveWaterGroundColorsMax) * 2),
185 g: ((i - aboveWaterGroundColorsMax) * 2),
186 b: ((i - aboveWaterGroundColorsMax) * 2), a: 255);
187 } else if (i < underWaterGroundColorsMax) {
188 m_colorTable2[i] = qRgba(r: ((i - aboveWaterGroundColorsMax) * 2),
189 g: ((i - aboveWaterGroundColorsMax) * 2),
190 b: ((i - aboveWaterGroundColorsMax) * 2), a: terrainTransparency);
191 } else if (i < waterColorsMax) {
192 m_colorTable2[i] = qRgba(r: 0, g: 0, b: ((i - underWaterGroundColorsMax) * 2) + 120,
193 a: terrainTransparency);
194 } else {
195 m_colorTable2[i] = qRgba(r: 0, g: 0, b: 0, a: 0); // Not used
196 }
197 }
198 m_colorTable2[airColorIndex] = qRgba(r: 0, g: 0, b: 0, a: 0);
199 m_colorTable2[mineShaftColorIndex] = qRgba(r: 255, g: 255, b: 0, a: 255);
200
201 //! [2]
202 m_volumeItem->setColorTable(m_colorTable1);
203 //! [2]
204
205 //! [5]
206 m_volumeItem->setSliceFrameGaps(QVector3D(0.01f, 0.02f, 0.01f));
207 m_volumeItem->setSliceFrameThicknesses(QVector3D(0.0025f, 0.005f, 0.0025f));
208 m_volumeItem->setSliceFrameWidths(QVector3D(0.0025f, 0.005f, 0.0025f));
209 m_volumeItem->setDrawSliceFrames(false);
210 //! [5]
211 handleSlicingChanges();
212
213 //! [3]
214 m_graph->addCustomItem(item: m_volumeItem);
215 //! [3]
216
217 m_timer.start(msec: 0);
218 } else {
219 // OpenGL ES2 doesn't support 3D textures, so show a warning label instead
220 QCustom3DLabel *warningLabel = new QCustom3DLabel(
221 "QCustom3DVolume is not supported with OpenGL ES2",
222 QFont(),
223 QVector3D(0.0f, 0.5f, 0.0f),
224 QVector3D(1.5f, 1.5f, 0.0f),
225 QQuaternion());
226 warningLabel->setPositionAbsolute(true);
227 warningLabel->setFacingCamera(true);
228 m_graph->addCustomItem(item: warningLabel);
229 }
230
231 QObject::connect(sender: m_graph, signal: &QAbstract3DGraph::currentFpsChanged, receiver: this,
232 slot: &VolumetricModifier::handleFpsChange);
233 QObject::connect(sender: &m_timer, signal: &QTimer::timeout, receiver: this,
234 slot: &VolumetricModifier::handleTimeout);
235
236}
237
238VolumetricModifier::~VolumetricModifier()
239{
240 delete m_graph;
241}
242
243void VolumetricModifier::setFpsLabel(QLabel *fpsLabel)
244{
245 m_fpsLabel = fpsLabel;
246}
247
248void VolumetricModifier::setMediumDetailRB(QRadioButton *button)
249{
250 m_mediumDetailRB = button;
251}
252
253void VolumetricModifier::setHighDetailRB(QRadioButton *button)
254{
255 m_highDetailRB = button;
256}
257
258void VolumetricModifier::setSliceLabels(QLabel *xLabel, QLabel *yLabel, QLabel *zLabel)
259{
260 m_sliceLabelX = xLabel;
261 m_sliceLabelY = yLabel;
262 m_sliceLabelZ = zLabel;
263
264 adjustSliceX(value: m_sliceSliderX->value());
265 adjustSliceY(value: m_sliceSliderY->value());
266 adjustSliceZ(value: m_sliceSliderZ->value());
267}
268
269void VolumetricModifier::setAlphaMultiplierLabel(QLabel *label)
270{
271 m_alphaMultiplierLabel = label;
272}
273
274void VolumetricModifier::sliceX(int enabled)
275{
276 m_slicingX = enabled;
277 handleSlicingChanges();
278}
279
280void VolumetricModifier::sliceY(int enabled)
281{
282 m_slicingY = enabled;
283 handleSlicingChanges();
284}
285
286void VolumetricModifier::sliceZ(int enabled)
287{
288 m_slicingZ = enabled;
289 handleSlicingChanges();
290}
291
292void VolumetricModifier::adjustSliceX(int value)
293{
294 if (m_volumeItem) {
295 m_sliceIndexX = value / (1024 / m_volumeItem->textureWidth());
296 if (m_sliceIndexX == m_volumeItem->textureWidth())
297 m_sliceIndexX--;
298 if (m_volumeItem->sliceIndexX() != -1)
299 //! [7]
300 m_volumeItem->setSliceIndexX(m_sliceIndexX);
301 //! [7]
302 //! [9]
303 m_sliceLabelX->setPixmap(
304 QPixmap::fromImage(image: m_volumeItem->renderSlice(axis: Qt::XAxis, index: m_sliceIndexX)));
305 //! [9]
306 }
307}
308
309void VolumetricModifier::adjustSliceY(int value)
310{
311 if (m_volumeItem) {
312 m_sliceIndexY = value / (1024 / m_volumeItem->textureHeight());
313 if (m_sliceIndexY == m_volumeItem->textureHeight())
314 m_sliceIndexY--;
315 if (m_volumeItem->sliceIndexY() != -1)
316 m_volumeItem->setSliceIndexY(m_sliceIndexY);
317 m_sliceLabelY->setPixmap(
318 QPixmap::fromImage(image: m_volumeItem->renderSlice(axis: Qt::YAxis, index: m_sliceIndexY)));
319 }
320}
321
322void VolumetricModifier::adjustSliceZ(int value)
323{
324 if (m_volumeItem) {
325 m_sliceIndexZ = value / (1024 / m_volumeItem->textureDepth());
326 if (m_sliceIndexZ == m_volumeItem->textureDepth())
327 m_sliceIndexZ--;
328 if (m_volumeItem->sliceIndexZ() != -1)
329 m_volumeItem->setSliceIndexZ(m_sliceIndexZ);
330 m_sliceLabelZ->setPixmap(
331 QPixmap::fromImage(image: m_volumeItem->renderSlice(axis: Qt::ZAxis, index: m_sliceIndexZ)));
332 }
333}
334
335void VolumetricModifier::handleFpsChange(qreal fps)
336{
337 const QString fpsFormat = QStringLiteral("FPS: %1");
338 int fps10 = int(fps * 10.0);
339 m_fpsLabel->setText(fpsFormat.arg(a: qreal(fps10) / 10.0));
340}
341
342void VolumetricModifier::handleTimeout()
343{
344 if (!m_mediumDetailRB->isEnabled()) {
345 if (m_mediumDetailIndex != mediumDetailSize) {
346 m_mediumDetailIndex = createVolume(textureSize: mediumDetailSize, startIndex: m_mediumDetailIndex, count: 4,
347 textureData: m_mediumDetailData);
348 } else if (m_mediumDetailShaftIndex != m_mineShaftArray.size()) {
349 m_mediumDetailShaftIndex = excavateMineShaft(textureSize: mediumDetailSize, startIndex: m_mediumDetailShaftIndex,
350 count: 1, textureData: m_mediumDetailData );
351 } else {
352 m_mediumDetailRB->setEnabled(true);
353 QString label = QStringLiteral("Medium (%1x%2x%1)");
354 m_mediumDetailRB->setText(label.arg(a: mediumDetailSize).arg(a: mediumDetailSize / 2));
355 }
356 } else if (!m_highDetailRB->isEnabled()) {
357 if (m_highDetailIndex != highDetailSize) {
358 m_highDetailIndex = createVolume(textureSize: highDetailSize, startIndex: m_highDetailIndex, count: 1,
359 textureData: m_highDetailData);
360 } else if (m_highDetailShaftIndex != m_mineShaftArray.size()) {
361 m_highDetailShaftIndex = excavateMineShaft(textureSize: highDetailSize, startIndex: m_highDetailShaftIndex, count: 1,
362 textureData: m_highDetailData);
363 } else {
364 m_highDetailRB->setEnabled(true);
365 QString label = QStringLiteral("High (%1x%2x%1)");
366 m_highDetailRB->setText(label.arg(a: highDetailSize).arg(a: highDetailSize / 2));
367 m_timer.stop();
368 }
369 }
370}
371
372void VolumetricModifier::toggleLowDetail(bool enabled)
373{
374 if (enabled && m_volumeItem) {
375 m_volumeItem->setTextureData(new QVector<uchar>(*m_lowDetailData));
376 m_volumeItem->setTextureDimensions(width: lowDetailSize, height: lowDetailSize / 2, depth: lowDetailSize);
377 adjustSliceX(value: m_sliceSliderX->value());
378 adjustSliceY(value: m_sliceSliderY->value());
379 adjustSliceZ(value: m_sliceSliderZ->value());
380 }
381}
382
383void VolumetricModifier::toggleMediumDetail(bool enabled)
384{
385 if (enabled && m_volumeItem) {
386 m_volumeItem->setTextureData(new QVector<uchar>(*m_mediumDetailData));
387 m_volumeItem->setTextureDimensions(width: mediumDetailSize, height: mediumDetailSize / 2, depth: mediumDetailSize);
388 adjustSliceX(value: m_sliceSliderX->value());
389 adjustSliceY(value: m_sliceSliderY->value());
390 adjustSliceZ(value: m_sliceSliderZ->value());
391 }
392}
393
394void VolumetricModifier::toggleHighDetail(bool enabled)
395{
396 if (enabled && m_volumeItem) {
397 m_volumeItem->setTextureData(new QVector<uchar>(*m_highDetailData));
398 m_volumeItem->setTextureDimensions(width: highDetailSize, height: highDetailSize / 2, depth: highDetailSize);
399 adjustSliceX(value: m_sliceSliderX->value());
400 adjustSliceY(value: m_sliceSliderY->value());
401 adjustSliceZ(value: m_sliceSliderZ->value());
402 }
403}
404
405void VolumetricModifier::setFpsMeasurement(bool enabled)
406{
407 m_graph->setMeasureFps(enabled);
408 if (enabled)
409 m_fpsLabel->setText(QStringLiteral("Measuring..."));
410 else
411 m_fpsLabel->setText(QString());
412}
413
414void VolumetricModifier::setSliceSliders(QSlider *sliderX, QSlider *sliderY, QSlider *sliderZ)
415{
416 m_sliceSliderX = sliderX;
417 m_sliceSliderY = sliderY;
418 m_sliceSliderZ = sliderZ;
419
420 // Set sliders to interesting values
421 m_sliceSliderX->setValue(715);
422 m_sliceSliderY->setValue(612);
423 m_sliceSliderZ->setValue(715);
424}
425
426void VolumetricModifier::changeColorTable(int enabled)
427{
428 if (m_volumeItem) {
429 if (enabled)
430 m_volumeItem->setColorTable(m_colorTable2);
431 else
432 m_volumeItem->setColorTable(m_colorTable1);
433
434 m_usingPrimaryTable = !enabled;
435
436 // Rerender image labels
437 adjustSliceX(value: m_sliceSliderX->value());
438 adjustSliceY(value: m_sliceSliderY->value());
439 adjustSliceZ(value: m_sliceSliderZ->value());
440 }
441}
442
443void VolumetricModifier::setPreserveOpacity(bool enabled)
444{
445
446 if (m_volumeItem) {
447 //! [10]
448 m_volumeItem->setPreserveOpacity(enabled);
449 //! [10]
450
451 // Rerender image labels
452 adjustSliceX(value: m_sliceSliderX->value());
453 adjustSliceY(value: m_sliceSliderY->value());
454 adjustSliceZ(value: m_sliceSliderZ->value());
455 }
456}
457
458void VolumetricModifier::setTransparentGround(bool enabled)
459{
460 if (m_volumeItem) {
461 //! [12]
462 int newAlpha = enabled ? terrainTransparency : 255;
463 for (int i = aboveWaterGroundColorsMin; i < underWaterGroundColorsMax; i++) {
464 QRgb oldColor1 = m_colorTable1.at(i);
465 QRgb oldColor2 = m_colorTable2.at(i);
466 m_colorTable1[i] = qRgba(r: qRed(rgb: oldColor1), g: qGreen(rgb: oldColor1), b: qBlue(rgb: oldColor1), a: newAlpha);
467 m_colorTable2[i] = qRgba(r: qRed(rgb: oldColor2), g: qGreen(rgb: oldColor2), b: qBlue(rgb: oldColor2), a: newAlpha);
468 }
469 if (m_usingPrimaryTable)
470 m_volumeItem->setColorTable(m_colorTable1);
471 else
472 m_volumeItem->setColorTable(m_colorTable2);
473 //! [12]
474 adjustSliceX(value: m_sliceSliderX->value());
475 adjustSliceY(value: m_sliceSliderY->value());
476 adjustSliceZ(value: m_sliceSliderZ->value());
477 }
478}
479
480void VolumetricModifier::setUseHighDefShader(bool enabled)
481{
482 if (m_volumeItem) {
483 //! [13]
484 m_volumeItem->setUseHighDefShader(enabled);
485 //! [13]
486 }
487}
488
489void VolumetricModifier::adjustAlphaMultiplier(int value)
490{
491 if (m_volumeItem) {
492 float mult;
493 if (value > 100)
494 mult = float(value - 99) / 2.0f;
495 else
496 mult = float(value) / float(500 - value * 4);
497 //! [11]
498 m_volumeItem->setAlphaMultiplier(mult);
499 //! [11]
500 QString labelFormat = QStringLiteral("Alpha multiplier: %1");
501 m_alphaMultiplierLabel->setText(labelFormat.arg(
502 a: QString::number(m_volumeItem->alphaMultiplier(), f: 'f', prec: 3)));
503
504 // Rerender image labels
505 adjustSliceX(value: m_sliceSliderX->value());
506 adjustSliceY(value: m_sliceSliderY->value());
507 adjustSliceZ(value: m_sliceSliderZ->value());
508 }
509}
510
511void VolumetricModifier::toggleAreaAll(bool enabled)
512{
513 if (enabled) {
514 m_graph->axisX()->setRange(min: 0.0f, max: 1000.0f);
515 m_graph->axisY()->setRange(min: -600.0f, max: 600.0f);
516 m_graph->axisZ()->setRange(min: 0.0f, max: 1000.0f);
517 m_graph->axisX()->setSegmentCount(5);
518 m_graph->axisY()->setSegmentCount(6);
519 m_graph->axisZ()->setSegmentCount(5);
520 }
521}
522
523void VolumetricModifier::toggleAreaMine(bool enabled)
524{
525 if (enabled) {
526 m_graph->axisX()->setRange(min: 350.0f, max: 850.0f);
527 m_graph->axisY()->setRange(min: -500.0f, max: 100.0f);
528 m_graph->axisZ()->setRange(min: 350.0f, max: 900.0f);
529 m_graph->axisX()->setSegmentCount(10);
530 m_graph->axisY()->setSegmentCount(6);
531 m_graph->axisZ()->setSegmentCount(11);
532 }
533}
534
535void VolumetricModifier::toggleAreaMountain(bool enabled)
536{
537 if (enabled) {
538 m_graph->axisX()->setRange(min: 300.0f, max: 600.0f);
539 m_graph->axisY()->setRange(min: -100.0f, max: 400.0f);
540 m_graph->axisZ()->setRange(min: 300.0f, max: 600.0f);
541 m_graph->axisX()->setSegmentCount(9);
542 m_graph->axisY()->setSegmentCount(5);
543 m_graph->axisZ()->setSegmentCount(9);
544 }
545}
546
547void VolumetricModifier::setDrawSliceFrames(int enabled)
548{
549 if (m_volumeItem)
550 m_volumeItem->setDrawSliceFrames(enabled);
551}
552
553void VolumetricModifier::initHeightMap(QString fileName, QVector<uchar> &layerData)
554{
555 QImage heightImage(fileName);
556
557 layerData.resize(asize: layerDataSize * layerDataSize);
558 const uchar *bits = heightImage.bits();
559 int index = 0;
560 QVector<QRgb> colorTable = heightImage.colorTable();
561 for (int i = 0; i < layerDataSize; i++) {
562 for (int j = 0; j < layerDataSize; j++) {
563 layerData[index] = qRed(rgb: colorTable.at(i: bits[index]));
564 index++;
565 }
566 }
567}
568
569int VolumetricModifier::createVolume(int textureSize, int startIndex, int count,
570 QVector<uchar> *textureData)
571{
572 // Generate volume from layer data.
573 int index = startIndex * textureSize * textureSize / 2.0f;
574 int endIndex = startIndex + count;
575 if (endIndex > textureSize)
576 endIndex = textureSize;
577 QVector<uchar> magmaHeights(textureSize);
578 QVector<uchar> waterHeights(textureSize);
579 QVector<uchar> groundHeights(textureSize);
580 float multiplier = float(layerDataSize) / float(textureSize);
581 for (int i = startIndex; i < endIndex; i++) {
582 // Generate layer height arrays
583 for (int l = 0; l < textureSize; l++) {
584 int layerIndex = (int(i * multiplier) * layerDataSize + int(l * multiplier));
585 magmaHeights[l] = int(m_magmaLayer.at(i: layerIndex));
586 waterHeights[l] = int(m_waterLayer.at(i: layerIndex));
587 groundHeights[l] = int(m_groundLayer.at(i: layerIndex));
588 }
589 for (int j = 0; j < textureSize / 2; j++) {
590 for (int k = 0; k < textureSize; k++) {
591 int colorIndex;
592 int height((layerDataSize - (j * 2 * multiplier)) / 2);
593 if (height < magmaHeights.at(i: k)) {
594 // Magma layer
595 colorIndex = int((float(height) / heightToColorDiv)
596 * float(layerColorThickness)) + magmaColorsMin;
597 } else if (height < groundHeights.at(i: k) && height < waterHeights.at(i: k)) {
598 // Ground layer below water
599 colorIndex = int((float(waterHeights.at(i: k) - height) / heightToColorDiv)
600 * float(layerColorThickness)) + underWaterGroundColorsMin;
601 } else if (height < waterHeights.at(i: k)) {
602 // Water layer where water goes over ground
603 colorIndex = int((float(height - magmaHeights.at(i: k)) / heightToColorDiv)
604 * float(layerColorThickness)) + waterColorsMin;
605 } else if (height <= groundHeights.at(i: k)) {
606 // Ground above water
607 colorIndex = int((float(height - waterHeights.at(i: k)) / heightToColorDiv)
608 * float(layerColorThickness)) + aboveWaterGroundColorsMin;
609 } else {
610 // Rest is air
611 colorIndex = airColorIndex;
612 }
613
614 (*textureData)[index] = colorIndex;
615 index++;
616 }
617 }
618 }
619 return endIndex;
620}
621
622int VolumetricModifier::excavateMineShaft(int textureSize, int startIndex, int count,
623 QVector<uchar> *textureData)
624{
625 int endIndex = startIndex + count;
626 if (endIndex > m_mineShaftArray.size())
627 endIndex = m_mineShaftArray.size();
628 int shaftSize = mineShaftDiameter * textureSize / lowDetailSize;
629 for (int i = startIndex; i < endIndex; i++) {
630 QVector3D shaftStart(m_mineShaftArray.at(i).first);
631 QVector3D shaftEnd(m_mineShaftArray.at(i).second);
632 int shaftLen = (shaftEnd - shaftStart).length() * lowDetailSize;
633 int dataX = shaftStart.x() * textureSize - (shaftSize / 2);
634 int dataY = (shaftStart.y() * textureSize - (shaftSize / 2)) / 2;
635 int dataZ = shaftStart.z() * textureSize - (shaftSize / 2);
636 int dataIndex = dataX + (dataY * textureSize) + dataZ * (textureSize * textureSize / 2);
637 if (shaftStart.x() != shaftEnd.x()) {
638 for (int j = 0; j <= shaftLen; j++) {
639 excavateMineBlock(textureSize, dataIndex, size: shaftSize, textureData);
640 dataIndex += shaftSize;
641 }
642 } else if (shaftStart.y() != shaftEnd.y()) {
643 shaftLen /= 2; // Vertical shafts are half as long
644 for (int j = 0; j <= shaftLen; j++) {
645 excavateMineBlock(textureSize, dataIndex, size: shaftSize, textureData);
646 dataIndex += textureSize * shaftSize;
647 }
648 } else {
649 for (int j = 0; j <= shaftLen; j++) {
650 excavateMineBlock(textureSize, dataIndex, size: shaftSize, textureData);
651 dataIndex += (textureSize * textureSize / 2) * shaftSize;
652 }
653 }
654
655
656 }
657 return endIndex;
658}
659
660void VolumetricModifier::excavateMineBlock(int textureSize, int dataIndex, int size,
661 QVector<uchar> *textureData)
662{
663 for (int k = 0; k < size; k++) {
664 int curIndex = dataIndex + (k * textureSize * textureSize / 2);
665 for (int l = 0; l < size; l++) {
666 curIndex = dataIndex + (k * textureSize * textureSize / 2)
667 + (l * textureSize);
668 for (int m = 0; m < size; m++) {
669 if (textureData->at(i: curIndex) != airColorIndex)
670 (*textureData)[curIndex] = mineShaftColorIndex;
671 curIndex++;
672 }
673
674 }
675 }
676}
677
678void VolumetricModifier::handleSlicingChanges()
679{
680 if (m_volumeItem) {
681 if (m_slicingX || m_slicingY || m_slicingZ) {
682 // Only show slices of selected dimensions
683 //! [8]
684 m_volumeItem->setDrawSlices(true);
685 //! [8]
686 m_volumeItem->setSliceIndexX(m_slicingX ? m_sliceIndexX : -1);
687 m_volumeItem->setSliceIndexY(m_slicingY ? m_sliceIndexY : -1);
688 m_volumeItem->setSliceIndexZ(m_slicingZ ? m_sliceIndexZ : -1);
689 } else {
690 // Show slice frames for all dimenstions when not actually slicing
691 m_volumeItem->setDrawSlices(false);
692 m_volumeItem->setSliceIndexX(m_sliceIndexX);
693 m_volumeItem->setSliceIndexY(m_sliceIndexY);
694 m_volumeItem->setSliceIndexZ(m_sliceIndexZ);
695 }
696 }
697}
698
699void VolumetricModifier::initMineShaftArray()
700{
701 m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.7f, 0.1f, 0.7f),
702 QVector3D(0.7f, 0.8f, 0.7f));
703 m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.7f, 0.7f, 0.5f),
704 QVector3D(0.7f, 0.7f, 0.7f));
705
706 m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.4f, 0.7f, 0.7f),
707 QVector3D(0.7f, 0.7f, 0.7f));
708 m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.4f, 0.7f, 0.7f),
709 QVector3D(0.4f, 0.7f, 0.8f));
710 m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.45f, 0.7f, 0.7f),
711 QVector3D(0.45f, 0.7f, 0.8f));
712 m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.5f, 0.7f, 0.7f),
713 QVector3D(0.5f, 0.7f, 0.8f));
714 m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.55f, 0.7f, 0.7f),
715 QVector3D(0.55f, 0.7f, 0.8f));
716 m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.7f, 0.7f),
717 QVector3D(0.6f, 0.7f, 0.8f));
718 m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.65f, 0.7f, 0.7f),
719 QVector3D(0.65f, 0.7f, 0.8f));
720
721 m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.5f, 0.6f, 0.7f),
722 QVector3D(0.7f, 0.6f, 0.7f));
723 m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.5f, 0.6f, 0.7f),
724 QVector3D(0.5f, 0.6f, 0.8f));
725 m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.55f, 0.6f, 0.7f),
726 QVector3D(0.55f, 0.6f, 0.8f));
727 m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.6f, 0.7f),
728 QVector3D(0.6f, 0.6f, 0.8f));
729 m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.65f, 0.6f, 0.7f),
730 QVector3D(0.65f, 0.6f, 0.8f));
731
732 m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.7f, 0.6f, 0.4f),
733 QVector3D(0.7f, 0.6f, 0.7f));
734 m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.6f, 0.45f),
735 QVector3D(0.8f, 0.6f, 0.45f));
736 m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.6f, 0.5f),
737 QVector3D(0.8f, 0.6f, 0.5f));
738 m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.6f, 0.55f),
739 QVector3D(0.8f, 0.6f, 0.55f));
740 m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.6f, 0.6f),
741 QVector3D(0.8f, 0.6f, 0.6f));
742 m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.6f, 0.65f),
743 QVector3D(0.8f, 0.6f, 0.65f));
744 m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.6f, 0.7f),
745 QVector3D(0.8f, 0.6f, 0.7f));
746
747 m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.7f, 0.7f, 0.4f),
748 QVector3D(0.7f, 0.7f, 0.7f));
749 m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.7f, 0.45f),
750 QVector3D(0.8f, 0.7f, 0.45f));
751 m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.7f, 0.5f),
752 QVector3D(0.8f, 0.7f, 0.5f));
753 m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.7f, 0.55f),
754 QVector3D(0.8f, 0.7f, 0.55f));
755 m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.7f, 0.6f),
756 QVector3D(0.8f, 0.7f, 0.6f));
757 m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.7f, 0.65f),
758 QVector3D(0.8f, 0.7f, 0.65f));
759 m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.7f, 0.7f),
760 QVector3D(0.8f, 0.7f, 0.7f));
761
762 m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.7f, 0.8f, 0.5f),
763 QVector3D(0.7f, 0.8f, 0.7f));
764 m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.8f, 0.55f),
765 QVector3D(0.8f, 0.8f, 0.55f));
766 m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.8f, 0.6f),
767 QVector3D(0.8f, 0.8f, 0.6f));
768 m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.8f, 0.65f),
769 QVector3D(0.8f, 0.8f, 0.65f));
770 m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.6f, 0.8f, 0.7f),
771 QVector3D(0.8f, 0.8f, 0.7f));
772
773 m_mineShaftArray << QPair<QVector3D, QVector3D>(QVector3D(0.7f, 0.1f, 0.4f),
774 QVector3D(0.7f, 0.7f, 0.4f));
775}
776

source code of qtdatavis3d/examples/datavisualization/volumetric/volumetric.cpp