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 | |
45 | using namespace QtDataVisualization; |
46 | |
47 | const int lowDetailSize(128); |
48 | const int mediumDetailSize(256); |
49 | const int highDetailSize(512); |
50 | const int colorTableSize(256); |
51 | const int layerDataSize(512); |
52 | const int mineShaftDiameter(1); |
53 | |
54 | const int airColorIndex(254); |
55 | const int mineShaftColorIndex(255); |
56 | const int layerColorThickness(60); |
57 | const int heightToColorDiv(140); |
58 | const int magmaColorsMin(0); |
59 | const int magmaColorsMax(layerColorThickness); |
60 | const int aboveWaterGroundColorsMin(magmaColorsMax + 1); |
61 | const int aboveWaterGroundColorsMax(aboveWaterGroundColorsMin + layerColorThickness); |
62 | const int underWaterGroundColorsMin(aboveWaterGroundColorsMax + 1); |
63 | const int underWaterGroundColorsMax(underWaterGroundColorsMin + layerColorThickness); |
64 | const int waterColorsMin(underWaterGroundColorsMax + 1); |
65 | const int waterColorsMax(waterColorsMin + layerColorThickness); |
66 | const int terrainTransparency(12); |
67 | |
68 | static 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 | |
79 | VolumetricModifier::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 | |
238 | VolumetricModifier::~VolumetricModifier() |
239 | { |
240 | delete m_graph; |
241 | } |
242 | |
243 | void VolumetricModifier::setFpsLabel(QLabel *fpsLabel) |
244 | { |
245 | m_fpsLabel = fpsLabel; |
246 | } |
247 | |
248 | void VolumetricModifier::setMediumDetailRB(QRadioButton *button) |
249 | { |
250 | m_mediumDetailRB = button; |
251 | } |
252 | |
253 | void VolumetricModifier::setHighDetailRB(QRadioButton *button) |
254 | { |
255 | m_highDetailRB = button; |
256 | } |
257 | |
258 | void 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 | |
269 | void VolumetricModifier::setAlphaMultiplierLabel(QLabel *label) |
270 | { |
271 | m_alphaMultiplierLabel = label; |
272 | } |
273 | |
274 | void VolumetricModifier::sliceX(int enabled) |
275 | { |
276 | m_slicingX = enabled; |
277 | handleSlicingChanges(); |
278 | } |
279 | |
280 | void VolumetricModifier::sliceY(int enabled) |
281 | { |
282 | m_slicingY = enabled; |
283 | handleSlicingChanges(); |
284 | } |
285 | |
286 | void VolumetricModifier::sliceZ(int enabled) |
287 | { |
288 | m_slicingZ = enabled; |
289 | handleSlicingChanges(); |
290 | } |
291 | |
292 | void 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 | |
309 | void 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 | |
322 | void 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 | |
335 | void 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 | |
342 | void 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 | |
372 | void 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 | |
383 | void 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 | |
394 | void 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 | |
405 | void 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 | |
414 | void 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 | |
426 | void 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 | |
443 | void 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 | |
458 | void 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 | |
480 | void VolumetricModifier::setUseHighDefShader(bool enabled) |
481 | { |
482 | if (m_volumeItem) { |
483 | //! [13] |
484 | m_volumeItem->setUseHighDefShader(enabled); |
485 | //! [13] |
486 | } |
487 | } |
488 | |
489 | void 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 | |
511 | void 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 | |
523 | void 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 | |
535 | void 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 | |
547 | void VolumetricModifier::setDrawSliceFrames(int enabled) |
548 | { |
549 | if (m_volumeItem) |
550 | m_volumeItem->setDrawSliceFrames(enabled); |
551 | } |
552 | |
553 | void 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 | |
569 | int 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 | |
622 | int 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 | |
660 | void 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 | |
678 | void 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 | |
699 | void 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 | |