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