Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qquick3dreflectionprobe.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5
8
9#include <QtQuick3DRuntimeRender/private/qssgrenderreflectionprobe_p.h>
10#include <QtQuick3DUtils/private/qssgutils_p.h>
11
13
14/*!
15 \qmltype ReflectionProbe
16 \inherits Node
17 \inqmlmodule QtQuick3D
18 \brief Defines a reflection probe in the scene.
19
20 A reflection probe is used to provide reflections of the current scene to the objects. The probe
21 provides properties to the runtime which are then used to render the scene to a cube map. The cube map
22 is then used as the reflection information for the reflecting objects.
23
24 \sa {Qt Quick 3D - Reflection Probes Example}
25*/
26
27QQuick3DReflectionProbe::QQuick3DReflectionProbe(QQuick3DNode *parent)
28 : QQuick3DNode(*(new QQuick3DNodePrivate(QQuick3DNodePrivate::Type::ReflectionProbe)), parent)
29{
30 QObject::connect(this, &QQuick3DReflectionProbe::scenePositionChanged, this, &QQuick3DReflectionProbe::updateDebugView);
31}
32
33void QQuick3DReflectionProbe::itemChange(QQuick3DObject::ItemChange change,
34 const QQuick3DObject::ItemChangeData &value)
35{
36 if (change == QQuick3DObject::ItemSceneChange)
37 updateSceneManager(value.sceneManager);
38}
39
40/*!
41 \qmlproperty enumeration ReflectionProbe::quality
42
43 Quality determines the resolution of the cube map.
44
45 Possible values are:
46 \value ReflectionProbe.VeryLow
47 Renders a reflection map using a 128x128 texture.
48 \value ReflectionProbe.Low
49 Renders a reflection map using a 256x256 texture.
50 \value ReflectionProbe.Medium
51 Renders a reflection map using a 512x512 texture.
52 \value ReflectionProbe.High
53 Renders a reflection map using a 1024x1024 texture.
54 \value ReflectionProbe.VeryHigh
55 Renders a reflection map using a 2048x2048 texture.
56
57 The default value is \c ReflectionProbe.Low
58*/
59QQuick3DReflectionProbe::ReflectionQuality QQuick3DReflectionProbe::quality() const
60{
61 return m_quality;
62}
63
64/*!
65 \qmlproperty Color ReflectionProbe::clearColor
66
67 Clear color is the color used to clear the cube map prior rendering the scene.
68*/
69QColor QQuick3DReflectionProbe::clearColor() const
70{
71 return m_clearColor;
72}
73
74/*!
75 \qmlproperty enumeration ReflectionProbe::refreshMode
76
77 Refresh mode tells the runtime how often the cube map should be updated.
78
79 Possible values are:
80 \value ReflectionProbe.FirstFrame
81 Renders the scene on the first frame.
82 \value ReflectionProbe.EveryFrame
83 Renders the scene every frame.
84
85 The default value is \c ReflectionProbe.EveryFrame
86 \note Use \c ReflectionProbe.FirstFrame for improved performance.
87*/
88QQuick3DReflectionProbe::ReflectionRefreshMode QQuick3DReflectionProbe::refreshMode() const
89{
90 return m_refreshMode;
91}
92
93/*!
94 \qmlproperty enumeration ReflectionProbe::timeSlicing
95
96 Time slicing determines how the cube map render is timed.
97
98 Possible values are:
99 \value ReflectionProbe.None
100 All faces of the cube map are rendered and prefiltered during one frame.
101
102 \value ReflectionProbe.AllFacesAtOnce
103 All faces are rendered during one frame but the prefiltering
104 is divided to subsquent frames with each mip level handled on
105 their own frame. Rough surface reflections are thus refreshed
106 every sixth frame while smooth surfaces have reflections
107 that refresh every frame.
108
109 \value ReflectionProbe.IndividualFaces
110 Each face is rendered and prefiltered in a separate frame.
111 Thus all reflections are refreshed every sixth frame.
112
113 The default value is \c ReflectionProbe.None
114 \note Use \c ReflectionProbe.AllFacesAtOnce or
115 \c ReflectionProbe.IndividualFaces to increase performance.
116*/
117QQuick3DReflectionProbe::ReflectionTimeSlicing QQuick3DReflectionProbe::timeSlicing() const
118{
119 return m_timeSlicing;
120}
121
122/*!
123 \qmlproperty bool ReflectionProbe::parallaxCorrection
124
125 By default the reflections provided by the reflection probe are assumed to be from an infinite distance similar
126 to the skybox. This works fine for environmental reflections but for tight spaces this causes perspective errors
127 in the reflections. To fix this parallax correction can be turned on. The distance of the reflection is then
128 determined by the \l ReflectionProbe::boxSize property.
129
130 \sa boxSize
131*/
132bool QQuick3DReflectionProbe::parallaxCorrection() const
133{
134 return m_parallaxCorrection;
135}
136
137/*!
138 \qmlproperty vector3d ReflectionProbe::boxSize
139
140 Box size is used to determine which objects get their reflections from this ReflectionProbe. Objects that are
141 inside the box are under the influence of this ReflectionProbe. If an object lies inside more than one reflection
142 probe at the same time, the object is considered to be inside the nearest reflection probe.
143 With \l ReflectionProbe::parallaxCorrection turned on the size is also used to calculate the distance of
144 the reflections in the cube map.
145
146 \sa parallaxCorrection
147*/
148QVector3D QQuick3DReflectionProbe::boxSize() const
149{
150 return m_boxSize;
151}
152
153/*!
154 \qmlproperty bool ReflectionProbe::debugView
155 \since 6.4
156
157 If this property is set to true, a wireframe is rendered to visualize the reflection probe box.
158*/
159bool QQuick3DReflectionProbe::debugView() const
160{
161 return m_debugView;
162}
163
164/*!
165 \qmlproperty vector3d ReflectionProbe::boxOffset
166
167 Box offset is used to move the box relative to the reflection probe position. Since the probe
168 captures the environment from its position, this property can be used to move the box around
169 without affecting the position where the probe capture the environment. This property
170 alongside with \l ReflectionProbe::boxSize will be used to determine the object that fall
171 inside the box.
172 With \l ReflectionProbe::parallaxCorrection turned on, this property can be used to position
173 the box to get more accurate reflections.
174
175 \sa parallaxCorrection
176*/
177QVector3D QQuick3DReflectionProbe::boxOffset() const
178{
179 return m_boxOffset;
180}
181
182/*!
183 \qmlproperty CubeMapTexture ReflectionProbe::texture
184 \since 6.5
185
186 Instead of rendering the scene, this cube map texture is used to show reflections
187 in objects affected by this reflection probe.
188
189 \sa CubeMapTexture
190*/
191QQuick3DCubeMapTexture *QQuick3DReflectionProbe::texture() const
192{
193 return m_texture;
194}
195
196/*!
197 \qmlmethod ReflectionProbe::scheduleUpdate()
198
199 Updates the reflection probe render when called while \l ReflectionProbe::refreshMode
200 is set as \c ReflectionProbe.FirstFrame.
201*/
202void QQuick3DReflectionProbe::scheduleUpdate()
203{
204 m_dirtyFlags.setFlag(DirtyFlag::RefreshModeDirty);
205 update();
206}
207
208void QQuick3DReflectionProbe::setQuality(QQuick3DReflectionProbe::ReflectionQuality reflectionQuality)
209{
210 if (m_quality == reflectionQuality)
211 return;
212
213 m_quality = reflectionQuality;
214 m_dirtyFlags.setFlag(DirtyFlag::QualityDirty);
215 emit qualityChanged();
216 update();
217}
218
219void QQuick3DReflectionProbe::setClearColor(const QColor &clearColor)
220{
221 if (m_clearColor == clearColor)
222 return;
223 m_clearColor = clearColor;
224 m_dirtyFlags.setFlag(DirtyFlag::ClearColorDirty);
225 emit clearColorChanged();
226 update();
227}
228
229void QQuick3DReflectionProbe::setRefreshMode(ReflectionRefreshMode newRefreshMode)
230{
231 if (m_refreshMode == newRefreshMode)
232 return;
233 m_refreshMode = newRefreshMode;
234 m_dirtyFlags.setFlag(DirtyFlag::RefreshModeDirty);
235 emit refreshModeChanged();
236 update();
237}
238
239void QQuick3DReflectionProbe::setTimeSlicing(ReflectionTimeSlicing newTimeSlicing)
240{
241 if (m_timeSlicing == newTimeSlicing)
242 return;
243 m_timeSlicing = newTimeSlicing;
244 m_dirtyFlags.setFlag(DirtyFlag::TimeSlicingDirty);
245 emit timeSlicingChanged();
246 update();
247}
248
249void QQuick3DReflectionProbe::setParallaxCorrection(bool parallaxCorrection)
250{
251 if (m_parallaxCorrection == parallaxCorrection)
252 return;
253 m_parallaxCorrection = parallaxCorrection;
254 m_dirtyFlags.setFlag(DirtyFlag::ParallaxCorrectionDirty);
255 emit parallaxCorrectionChanged();
256 update();
257}
258
259void QQuick3DReflectionProbe::setBoxSize(const QVector3D &boxSize)
260{
261 if (m_boxSize == boxSize)
262 return;
263 m_boxSize = boxSize;
264 m_dirtyFlags.setFlag(DirtyFlag::BoxDirty);
265 emit boxSizeChanged();
266 createDebugView();
267 updateDebugView();
268 update();
269}
270
271void QQuick3DReflectionProbe::setDebugView(bool debugView)
272{
273 if (m_debugView == debugView)
274 return;
275 m_debugView = debugView;
276 emit debugViewChanged();
277 createDebugView();
278 updateDebugView();
279}
280
281void QQuick3DReflectionProbe::setBoxOffset(const QVector3D &boxOffset)
282{
283 if (m_boxOffset == boxOffset)
284 return;
285 m_boxOffset = boxOffset;
286 m_dirtyFlags.setFlag(DirtyFlag::BoxDirty);
287 emit boxOffsetChanged();
288 createDebugView();
289 updateDebugView();
290 update();
291}
292
293void QQuick3DReflectionProbe::setTexture(QQuick3DCubeMapTexture *newTexture)
294{
295 if (m_texture == newTexture)
296 return;
297 QQuick3DObjectPrivate::attachWatcher(this, &QQuick3DReflectionProbe::setTexture, newTexture, m_texture);
298 m_texture = newTexture;
299 m_dirtyFlags.setFlag(DirtyFlag::TextureDirty);
300 emit textureChanged();
301 update();
302}
303
304QSSGRenderGraphObject *QQuick3DReflectionProbe::updateSpatialNode(QSSGRenderGraphObject *node)
305{
306 if (!node) {
307 markAllDirty();
308 node = new QSSGRenderReflectionProbe();
309 }
310
311 QQuick3DNode::updateSpatialNode(node);
312
313 QSSGRenderReflectionProbe *probe = static_cast<QSSGRenderReflectionProbe *>(node);
314
315 if (m_dirtyFlags.testFlag(DirtyFlag::QualityDirty)) {
316 m_dirtyFlags.setFlag(DirtyFlag::QualityDirty, false);
317 probe->reflectionMapRes = mapToReflectionResolution(m_quality);
318 }
319
320 if (m_dirtyFlags.testFlag(DirtyFlag::ClearColorDirty)) {
321 m_dirtyFlags.setFlag(DirtyFlag::ClearColorDirty, false);
322 probe->clearColor = m_clearColor;
323 }
324
325 if (m_dirtyFlags.testFlag(DirtyFlag::RefreshModeDirty)) {
326 m_dirtyFlags.setFlag(DirtyFlag::RefreshModeDirty, false);
327 switch (m_refreshMode) {
328 case ReflectionRefreshMode::FirstFrame:
329 probe->refreshMode = QSSGRenderReflectionProbe::ReflectionRefreshMode::FirstFrame;
330 break;
331 case ReflectionRefreshMode::EveryFrame:
332 probe->refreshMode = QSSGRenderReflectionProbe::ReflectionRefreshMode::EveryFrame;
333 break;
334 }
335 probe->hasScheduledUpdate = true;
336 }
337
338 if (m_dirtyFlags.testFlag(DirtyFlag::TimeSlicingDirty)) {
339 m_dirtyFlags.setFlag(DirtyFlag::TimeSlicingDirty, false);
340 switch (m_timeSlicing) {
341 case ReflectionTimeSlicing::None:
342 probe->timeSlicing = QSSGRenderReflectionProbe::ReflectionTimeSlicing::None;
343 break;
344 case ReflectionTimeSlicing::AllFacesAtOnce:
345 probe->timeSlicing = QSSGRenderReflectionProbe::ReflectionTimeSlicing::AllFacesAtOnce;
346 break;
347 case ReflectionTimeSlicing::IndividualFaces:
348 probe->timeSlicing = QSSGRenderReflectionProbe::ReflectionTimeSlicing::IndividualFaces;
349 break;
350 }
351 }
352
353 if (m_dirtyFlags.testFlag(DirtyFlag::ParallaxCorrectionDirty)) {
354 m_dirtyFlags.setFlag(DirtyFlag::ParallaxCorrectionDirty, false);
355 probe->parallaxCorrection = m_parallaxCorrection;
356 }
357
358 if (m_dirtyFlags.testFlag(DirtyFlag::BoxDirty)) {
359 m_dirtyFlags.setFlag(DirtyFlag::BoxDirty, false);
360 probe->boxSize = m_boxSize;
361 probe->boxOffset = m_boxOffset;
362 }
363
364 if (m_dirtyFlags.testFlag(DirtyFlag::TextureDirty)) {
365 m_dirtyFlags.setFlag(DirtyFlag::TextureDirty, false);
366 if (m_texture)
367 probe->texture = m_texture->getRenderImage();
368 else
369 probe->texture = nullptr;
370 }
371
372 return node;
373}
374
375void QQuick3DReflectionProbe::markAllDirty()
376{
377 m_dirtyFlags = QQuick3DReflectionProbe::AllDirty;
378
379 QQuick3DNode::markAllDirty();
380}
381
382void QQuick3DReflectionProbe::findSceneView()
383{
384 // If we have not specified a scene view we find the first available one
385 if (m_sceneView != nullptr)
386 return;
387
388 QObject *parent = this;
389 while (parent->parent() != nullptr) {
390 parent = parent->parent();
391 }
392
393 // Breath-first search through children
394 QList<QObject *> queue;
395 queue.append(parent);
396 while (!queue.empty()) {
397 auto node = queue.takeFirst();
398 if (auto converted = qobject_cast<QQuick3DViewport *>(node); converted != nullptr) {
399 m_sceneView = converted;
400 break;
401 }
402 queue.append(node->children());
403 }
404}
405
406void QQuick3DReflectionProbe::createDebugView()
407{
408
409 if (m_debugView) {
410 findSceneView();
411 if (!m_sceneView) {
412 qWarning() << "ReflectionProbe: Can not create debug view. A root View3D could not be found.";
413 return;
414 }
415
416 if (!m_debugViewGeometry)
417 m_debugViewGeometry = new QQuick3DGeometry(this);
418
419 m_debugViewGeometry->clear();
420 m_debugViewGeometry->addAttribute(QQuick3DGeometry::Attribute::PositionSemantic, 0,
421 QQuick3DGeometry::Attribute::ComponentType::F32Type);
422 m_debugViewGeometry->setStride(12);
423 m_debugViewGeometry->setPrimitiveType(QQuick3DGeometry::PrimitiveType::Lines);
424
425 QVector<QVector3D> m_vertices;
426 //Lines
427 m_vertices.append(QVector3D(-0.5, -0.5, 0.5));
428 m_vertices.append(QVector3D(0.5, -0.5, 0.5));
429
430 m_vertices.append(QVector3D(0.5, -0.5, 0.5));
431 m_vertices.append(QVector3D(0.5, 0.5, 0.5));
432
433 m_vertices.append(QVector3D(0.5, 0.5, 0.5));
434 m_vertices.append(QVector3D(-0.5, 0.5, 0.5));
435
436 m_vertices.append(QVector3D(-0.5, 0.5, 0.5));
437 m_vertices.append(QVector3D(-0.5, -0.5, 0.5));
438
439 m_vertices.append(QVector3D(-0.5, -0.5, -0.5));
440 m_vertices.append(QVector3D(0.5, -0.5, -0.5));
441
442 m_vertices.append(QVector3D(0.5, -0.5, -0.5));
443 m_vertices.append(QVector3D(0.5, 0.5, -0.5));
444
445 m_vertices.append(QVector3D(0.5, 0.5, -0.5));
446 m_vertices.append(QVector3D(-0.5, 0.5, -0.5));
447
448 m_vertices.append(QVector3D(-0.5, 0.5, -0.5));
449 m_vertices.append(QVector3D(-0.5, -0.5, -0.5));
450
451 m_vertices.append(QVector3D(-0.5, 0.5, -0.5));
452 m_vertices.append(QVector3D(-0.5, 0.5, 0.5));
453
454 m_vertices.append(QVector3D(0.5, 0.5, -0.5));
455 m_vertices.append(QVector3D(0.5, 0.5, 0.5));
456
457 m_vertices.append(QVector3D(-0.5, -0.5, -0.5));
458 m_vertices.append(QVector3D(-0.5, -0.5, 0.5));
459
460 m_vertices.append(QVector3D(0.5, -0.5, -0.5));
461 m_vertices.append(QVector3D(0.5, -0.5, 0.5));
462
463 QByteArray vertexData;
464 vertexData.resize(m_vertices.size() * 3 * sizeof(float));
465 float *data = reinterpret_cast<float *>(vertexData.data());
466 for (int i = 0; i < m_vertices.size(); i++) {
467 data[0] = m_vertices[i].x();
468 data[1] = m_vertices[i].y();
469 data[2] = m_vertices[i].z();
470 data += 3;
471 }
472
473 m_debugViewGeometry->setVertexData(vertexData);
474 m_debugViewGeometry->update();
475
476 if (!m_debugViewModel) {
477 m_debugViewModel = new QQuick3DModel();
478 m_debugViewModel->setParentItem(m_sceneView->scene());
479 m_debugViewModel->setParent(this);
480 m_debugViewModel->setCastsShadows(false);
481 m_debugViewModel->setCastsReflections(false);
482 m_debugViewModel->setGeometry(m_debugViewGeometry);
483 }
484
485 if (!m_debugViewMaterial) {
486 m_debugViewMaterial = new QQuick3DDefaultMaterial();
487 m_debugViewMaterial->setParentItem(m_debugViewModel);
488 m_debugViewMaterial->setParent(m_debugViewModel);
489 m_debugViewMaterial->setDiffuseColor(QColor(3, 252, 219));
490 m_debugViewMaterial->setLighting(QQuick3DDefaultMaterial::NoLighting);
491 m_debugViewMaterial->setCullMode(QQuick3DMaterial::NoCulling);
492 QQmlListReference materialsRef(m_debugViewModel, "materials");
493 materialsRef.append(m_debugViewMaterial);
494 }
495 } else {
496 if (m_debugViewModel) {
497 delete m_debugViewModel;
498 m_debugViewModel = nullptr;
499 m_debugViewMaterial = nullptr;
500 }
501
502 if (m_debugViewGeometry) {
503 delete m_debugViewGeometry;
504 m_debugViewGeometry = nullptr;
505 }
506 }
507}
508
509void QQuick3DReflectionProbe::updateDebugView()
510{
511 if (m_debugViewModel) {
512 m_debugViewModel->setPosition(scenePosition() + m_boxOffset);
513 m_debugViewModel->setScale(m_boxSize);
514 }
515}
516
517quint32 QQuick3DReflectionProbe::mapToReflectionResolution(ReflectionQuality quality)
518{
519 switch (quality) {
520 case ReflectionQuality::Low:
521 return 8;
522 case ReflectionQuality::Medium:
523 return 9;
524 case ReflectionQuality::High:
525 return 10;
526 case ReflectionQuality::VeryHigh:
527 return 11;
528 default:
529 break;
530 }
531 return 7;
532}
533
534void QQuick3DReflectionProbe::updateSceneManager(QQuick3DSceneManager *sceneManager)
535{
536 // Check all the resource value's scene manager, and update as necessary.
537 if (sceneManager)
538 QQuick3DObjectPrivate::refSceneManager(m_texture, *sceneManager);
539 else
540 QQuick3DObjectPrivate::derefSceneManager(m_texture);
541}
542
543
544QT_END_NAMESPACE
Combined button and popup list for selecting options.