Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qopenxrview.cpp
Go to the documentation of this file.
1// Copyright (C) 2023 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "qopenxritem_p.h"
5#include "qopenxrview_p.h"
6#include <QQuickWindow>
7#include <QQuickItem>
8
9#if defined(Q_NO_TEMPORARY_DISABLE_XR_API)
11#endif // Q_NO_TEMPORARY_DISABLE_XR_API
12
14
16 : m_openXRRuntimeInfo(&m_openXRManager)
17{
18 init();
19}
20
22{
23 m_inDestructor = true;
24}
25
27{
28 return m_openXRManager.m_xrOrigin;
29}
30
32{
33 return m_openXRManager.m_vrViewport ? m_openXRManager.m_vrViewport->environment() : nullptr;
34}
35
37{
38#if !defined(Q_OS_VISIONOS)
39 return m_openXRManager.m_inputManager ? m_openXRManager.m_inputManager->leftHandInput() : nullptr;
40#else
41 return nullptr;
42#endif
43}
44
46{
47#if !defined(Q_OS_VISIONOS)
48 return m_openXRManager.m_inputManager ? m_openXRManager.m_inputManager->rightHandInput() : nullptr;
49#else
50 return nullptr;
51#endif
52}
53
55{
56#if !defined(Q_OS_VISIONOS)
57 return m_openXRManager.m_inputManager->leftHandTrackerInput();
58#else
59 return nullptr;
60#endif
61}
62
64{
65#if !defined(Q_OS_VISIONOS)
66 return m_openXRManager.m_inputManager->rightHandTrackerInput();
67#else
68 return nullptr;
69#endif
70}
71
73{
74#if !defined(Q_OS_VISIONOS)
75 return m_openXRManager.m_inputManager ? m_openXRManager.m_inputManager->gamepadInput() : nullptr;
76#else
77 return nullptr;
78#endif
79}
80
81QQuick3DViewport *QOpenXRView::view3d() const
82{
83 return m_openXRManager.m_vrViewport;
84}
85
87{
88 return m_openXRManager.m_enablePassthrough;
89}
90
92{
93 return &m_openXRRuntimeInfo;
94}
95
97{
98 if (environment != m_sceneEnvironment)
99 m_sceneEnvironment = environment;
100
101 if (!m_openXRManager.m_vrViewport)
102 return;
103
104 auto oldEnvironment = m_openXRManager.m_vrViewport->environment();
105 if (oldEnvironment == environment)
106 return;
107
108 disconnect(oldEnvironment, &QQuick3DSceneEnvironment::backgroundModeChanged, this, &QOpenXRView::handleClearColorChanged);
109 disconnect(oldEnvironment, &QQuick3DSceneEnvironment::clearColorChanged, this, &QOpenXRView::handleClearColorChanged);
110 disconnect(oldEnvironment, &QQuick3DSceneEnvironment::antialiasingModeChanged, this, &QOpenXRView::handleAAChanged);
111 disconnect(oldEnvironment, &QQuick3DSceneEnvironment::antialiasingQualityChanged, this, &QOpenXRView::handleAAChanged);
112
113 m_openXRManager.m_vrViewport->setEnvironment(environment);
114 handleClearColorChanged();
115 handleAAChanged();
116
117 connect(environment, &QQuick3DSceneEnvironment::backgroundModeChanged, this, &QOpenXRView::handleClearColorChanged);
118 connect(environment, &QQuick3DSceneEnvironment::clearColorChanged, this, &QOpenXRView::handleClearColorChanged);
119 connect(environment, &QQuick3DSceneEnvironment::antialiasingModeChanged, this, &QOpenXRView::handleAAChanged);
120 connect(environment, &QQuick3DSceneEnvironment::antialiasingQualityChanged, this, &QOpenXRView::handleAAChanged);
121
122 emit environmentChanged(m_openXRManager.m_vrViewport->environment());
123}
124
126{
127 if (!m_openXRManager.isValid())
128 return false;
129
130 return m_openXRManager.supportsPassthrough();
131}
132
134{
135 if (!m_openXRManager.isValid())
136 return;
137
138 if (m_openXRManager.m_enablePassthrough == enable)
139 return;
140
141 // bail if passthrough is not supported
142 if (enable && !m_openXRManager.supportsPassthrough()) {
143 qWarning("Enabling Passthrough is not supported.");
144 return;
145 }
146
147 m_openXRManager.setPassthroughEnabled(enable);
148
150}
151
153{
154#if !defined(Q_OS_VISIONOS)
155 if (!m_openXRManager.isValid())
156 return NoFoveation;
157
158 return FoveationLevel(m_openXRManager.m_foveationLevel);
159#else
160 // Foveation is not configurable on VisionOS
162#endif
163}
164
166{
167#if !defined(Q_OS_VISIONOS)
168 if (!m_openXRManager.isValid())
169 return;
170
171 const XrFoveationLevelFB xrLevel = XrFoveationLevelFB(level);
172 if (m_openXRManager.m_foveationLevel == xrLevel)
173 return;
174
175 m_openXRManager.m_foveationLevel = xrLevel;
176 m_openXRManager.setupMetaQuestFoveation();
177
179#else
180 // Foveation is not configurable on VisionOS
182#endif
183}
184
186{
187 return m_quitOnSessionEnd;
188}
189
191{
192 if (m_quitOnSessionEnd == enable)
193 return;
194
195 m_quitOnSessionEnd = enable;
197}
198
200{
201 return m_openXRManager.m_vrViewport ? m_openXRManager.m_vrViewport->renderStats() : nullptr;
202}
203
204void QOpenXRView::updateViewportGeometry()
205{
206 auto contentItem = m_openXRManager.m_quickWindow->contentItem();
207 auto viewport = m_openXRManager.m_vrViewport;
208 if (viewport->height() != contentItem->height())
209 viewport->setHeight(contentItem->height());
210 if (viewport->width() != contentItem->width())
211 viewport->setWidth(contentItem->width());
212 if (viewport->x() != contentItem->x())
213 viewport->setX(contentItem->x());
214 if (viewport->y() != contentItem->y())
215 viewport->setY(contentItem->y());
216}
217
218void QOpenXRView::handleSessionEnded()
219{
221 if (m_quitOnSessionEnd)
223}
224
225void QOpenXRView::handleClearColorChanged()
226{
227 auto env = environment();
228
229 if (env) {
230 if (env->backgroundMode() == QQuick3DSceneEnvironment::Color)
231 m_openXRManager.m_quickWindow->setColor(env->clearColor());
232 else if (env->backgroundMode() == QQuick3DSceneEnvironment::Transparent)
233 m_openXRManager.m_quickWindow->setColor(Qt::transparent);
234 }
235}
236
237void QOpenXRView::handleAAChanged()
238{
239 auto env = environment();
240 int samples = 1;
241 if (env && env->antialiasingMode() == QQuick3DSceneEnvironment::MSAA) {
242 switch (env->antialiasingQuality()) {
244 samples = 2;
245 break;
247 samples = 4;
248 break;
250 samples = 8;
251 break;
252 }
253 }
254 m_openXRManager.setSamples(samples);
255}
256
257bool QOpenXRView::init()
258{
259 if (m_isInitialized) {
260 qWarning("Already initialized!");
261 return false;
262 }
263
264 if (!m_openXRManager.isReady() && !m_openXRManager.initialize()) {
265 qDebug() << "Waiting for OpenXR to be initialized...";
266 connect(&m_openXRManager, &QOpenXRManager::initialized, this, &QOpenXRView::init, Qt::UniqueConnection);
267 return false;
268 }
269
270 if (!m_openXRManager.initialize()) {
271 QString errorString = m_openXRManager.errorString();
272 if (errorString.isEmpty())
273 errorString = tr("Failed to initialize OpenXR");
274 qWarning("\n%s\n", qPrintable(errorString));
275 QMetaObject::invokeMethod(this, "initializeFailed", Qt::QueuedConnection, errorString);
276 return false;
277 }
278
279 // Create View3D
280 auto viewport = new QQuick3DViewport();
281 viewport->setRenderMode(QQuick3DViewport::Underlay);
282 auto contentItem = m_openXRManager.m_quickWindow->contentItem();
283 viewport->setParentItem(contentItem);
284 m_openXRManager.m_vrViewport = viewport;
286
287 contentItem->forceActiveFocus(Qt::MouseFocusReason);
288
289 connect(contentItem, &QQuickItem::heightChanged, this, &QOpenXRView::updateViewportGeometry);
290 connect(contentItem, &QQuickItem::widthChanged, this, &QOpenXRView::updateViewportGeometry);
291 connect(contentItem, &QQuickItem::xChanged, this, &QOpenXRView::updateViewportGeometry);
292 connect(contentItem, &QQuickItem::yChanged, this, &QOpenXRView::updateViewportGeometry);
293
294 connect(environment(), &QQuick3DSceneEnvironment::backgroundModeChanged, this, &QOpenXRView::handleClearColorChanged);
295 connect(environment(), &QQuick3DSceneEnvironment::clearColorChanged, this, &QOpenXRView::handleClearColorChanged);
296 connect(environment(), &QQuick3DSceneEnvironment::antialiasingModeChanged, this, &QOpenXRView::handleAAChanged);
297 connect(environment(), &QQuick3DSceneEnvironment::antialiasingQualityChanged, this, &QOpenXRView::handleAAChanged);
298
299 connect(&m_openXRManager, &QOpenXRManager::sessionEnded, this, &QOpenXRView::handleSessionEnded);
302
303 // NOTE: If we're called async we need to make sure the environment etc. is set again
304 setEnvironment(m_sceneEnvironment);
305
306 m_openXRManager.update();
307
308 m_isInitialized = true;
309
310 return m_isInitialized;
311}
312
325{
326 return m_openXRManager.m_vrViewport->rayPick(origin, direction);
327}
328
343QList<QQuick3DPickResult> QOpenXRView::rayPickAll(const QVector3D &origin, const QVector3D &direction) const
344{
345 return m_openXRManager.m_vrViewport->rayPickAll(origin, direction);
346}
347
357void QOpenXRView::setTouchpoint(QQuickItem *target, const QPointF &position, int pointId, bool pressed)
358{
359 view3d()->setTouchpoint(target, position, pointId, pressed);
360}
361
362// TODO: Maybe do a proper QOpenXRViewPrivate instead
364{
365 QHash<int, QOpenXRItem::TouchState> points;
366};
367
384{
386 if (m_xrItems.isEmpty())
387 return offset;
388
389 if (!m_touchState)
390 m_touchState = new XrTouchState;
391 QOpenXRItem::TouchState &state = m_touchState->points[pointId];
392 state.pointId = pointId; // in case it's a new point that was default-constructed
393
394 auto *prevTarget = state.target;
395 bool grabbed = false;
396 if (prevTarget) {
397 grabbed = prevTarget->handleVirtualTouch(this, pos, &state, &offset);
398 }
399 for (auto *item : std::as_const(m_xrItems)) {
400 if (grabbed)
401 break;
402 if (item != prevTarget)
403 grabbed = item->handleVirtualTouch(this, pos, &state, &offset);
404 }
405
406 return offset;
407}
408
444#define Q_TOUCHPOINT_STATE(prop) { QStringLiteral(#prop), QVariant::fromValue(it->prop) }
446{
448 auto it = m_touchState ? m_touchState->points.constFind(pointId) : end;
449
450 if (it == end)
451 return { { QStringLiteral("grabbed"), QVariant::fromValue(false) } };
452
453 return { Q_TOUCHPOINT_STATE(target),
454 Q_TOUCHPOINT_STATE(grabbed),
455 Q_TOUCHPOINT_STATE(pressed),
456 Q_TOUCHPOINT_STATE(cursorPos),
457 Q_TOUCHPOINT_STATE(touchDistance) };
458}
459#undef Q_TOUCHPOINT_STATE
460
461#if defined(Q_NO_TEMPORARY_DISABLE_XR_API)
462namespace {
463
464XrReferenceSpaceType getXrReferenceSpaceType(QOpenXRView::ReferenceSpace referenceSpace)
465{
466 switch (referenceSpace) {
468 return XR_REFERENCE_SPACE_TYPE_LOCAL;
470 return XR_REFERENCE_SPACE_TYPE_STAGE;
472 return XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR_EXT;
473 default:
474 return XR_REFERENCE_SPACE_TYPE_LOCAL;
475 }
476}
477
478QOpenXRView::ReferenceSpace getReferenceSpaceType(XrReferenceSpaceType referenceSpace)
479{
480 switch (referenceSpace) {
481 case XR_REFERENCE_SPACE_TYPE_LOCAL:
483 case XR_REFERENCE_SPACE_TYPE_STAGE:
485 case XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR_EXT:
487 default:
489 }
490}
491
492}
493
494#endif // Q_NO_TEMPORARY_DISABLE_XR_API
495
497{
498#if !defined(Q_OS_VISIONOS)
499 return getReferenceSpaceType(m_openXRManager.m_referenceSpace);
500#else
501 // I am not sure exactly what reference space is default
502 // or what is supported etc.
504#endif
505}
506
508{
509#if !defined(Q_OS_VISIONOS)
510 XrReferenceSpaceType referenceSpace = getXrReferenceSpaceType(newReferenceSpace);
511 if (m_openXRManager.m_referenceSpace == referenceSpace)
512 return;
513
514 m_openXRManager.m_requestedReferenceSpace = referenceSpace;
515
516 // we do not emit a changed signal here because it hasn't
517 // changed yet.
518#else
519 // I'm not sure if it's possible to set a reference space on VisionOS
520 Q_UNUSED(newReferenceSpace);
521#endif
522}
523
525{
526#if !defined(Q_OS_VISIONOS)
527 if (!m_openXRManager.isValid())
528 return false;
529
530 return m_openXRManager.m_submitLayerDepth;
531#else
532 // Depth submission is required on VisionOS
533 return true;
534#endif
535}
536
538{
539 m_xrItems.append(newXrItem);
540}
541
543{
544 m_xrItems.removeAll(xrItem);
545}
546
548{
549#if !defined(Q_OS_VISIONOS)
550 if (!m_openXRManager.isValid()) {
551 qWarning("Attempted to set depth submission mode without having m_openXRManager initialized");
552 return;
553 }
554
555 if (m_openXRManager.m_submitLayerDepth == enable)
556 return;
557
558 if (m_openXRManager.m_compositionLayerDepthSupported) {
559 if (enable)
560 qDebug("Enabling submitLayerDepth");
561 m_openXRManager.m_submitLayerDepth = enable;
563 }
564#else
565 // VisionOS requires depth submission to be enabled
567#endif
568}
569
static void quit()
\threadsafe
const_iterator constFind(const Key &key) const noexcept
Definition qhash.h:1299
friend class const_iterator
Definition qhash.h:1182
bool isEmpty() const noexcept
Definition qlist.h:401
qsizetype removeAll(const AT &t)
Definition qlist.h:592
void append(parameter_type t)
Definition qlist.h:458
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
void setPassthroughEnabled(bool enabled)
void sessionEnded()
void referenceSpaceChanged()
void setSamples(int samples)
QString errorString() const
void initialized()
bool isValid() const
bool isReady() const
void frameReady(QRhiTexture *colorBuffer)
Q_INVOKABLE QVariantMap touchpointState(int pointId) const
bool isPassthroughSupported() const
QOpenXRRuntimeInfo * runtimeInfo
void frameReady(QRhiTexture *colorBuffer)
void sessionEnded()
QOpenXRHandTrackerInput * rightHandTrackerInput
Q_INVOKABLE QList< QQuick3DPickResult > rayPickAll(const QVector3D &origin, const QVector3D &direction) const
\qmlmethod List<PickResult> XrView::rayPickAll(vector3d origin, vector3d direction)
void fixedFoveationChanged()
Q_INVOKABLE void setTouchpoint(QQuickItem *target, const QPointF &position, int pointId, bool active)
\qmlmethod XrView::setTouchpoint(Item target, point position, int pointId, bool pressed)
void enableDepthSubmissionChanged()
QOpenXRHandTrackerInput * leftHandTrackerInput
void enablePassthroughChanged(bool enable)
void unregisterXrItem(QOpenXRItem *xrItem)
QOpenXRHandInput * leftHandInput
QQuick3DSceneEnvironment * environment
void quitOnSessionEndChanged()
void referenceSpaceChanged()
QOpenXRGamepadInput * gamepadInput
void setFixedFoveation(FoveationLevel level)
QOpenXROrigin * xrOrigin
FoveationLevel fixedFoveation
QQuick3DRenderStats * renderStats
void environmentChanged(QQuick3DSceneEnvironment *environment)
void setReferenceSpace(ReferenceSpace newReferenceSpace)
void setEnableDepthSubmission(bool enable)
Q_INVOKABLE QVector3D processTouch(const QVector3D &pos, int pointId)
\qmlmethod vector3d XrView::processTouch(vector3d position, int pointId)
Q_INVOKABLE QQuick3DPickResult rayPick(const QVector3D &origin, const QVector3D &direction) const
\qmlmethod PickResult XrView::rayPick(vector3d origin, vector3d direction)
bool isDepthSubmissionEnabled() const
bool enablePassthrough
void registerXrItem(QOpenXRItem *newXrItem)
void setQuitOnSessionEnd(bool enable)
void setEnablePassthrough(bool enable)
ReferenceSpace referenceSpace
QOpenXRHandInput * rightHandInput
bool isQuitOnSessionEndEnabled() const
void setEnvironment(QQuick3DSceneEnvironment *environment)
\inmodule QtCore\reentrant
Definition qpoint.h:217
QVector3D position
void setImportScene(QQuick3DNode *inScene)
void setEnvironment(QQuick3DSceneEnvironment *environment)
QQuick3DRenderStats * renderStats
QQuick3DSceneEnvironment * environment
The QQuickItem class provides the most basic of all visual items in \l {Qt Quick}.
Definition qquickitem.h:63
void xChanged()
void heightChanged()
void widthChanged()
void setParentItem(QQuickItem *parent)
void setHeight(qreal)
void yChanged()
QQuickItem * contentItem
\qmlattachedproperty Item Window::contentItem
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
static auto fromValue(T &&value) noexcept(std::is_nothrow_copy_constructible_v< T > &&Private::CanUseInternalSpace< T >) -> std::enable_if_t< std::conjunction_v< std::is_copy_constructible< T >, std::is_destructible< T > >, QVariant >
Definition qvariant.h:536
The QVector3D class represents a vector or vertex in 3D space.
Definition qvectornd.h:171
QSet< QString >::iterator it
direction
else opt state
[0]
Combined button and popup list for selecting options.
Q_MULTIMEDIA_EXPORT QString errorString(HRESULT hr)
@ transparent
Definition qnamespace.h:47
@ QueuedConnection
@ UniqueConnection
@ MouseFocusReason
#define qDebug
[1]
Definition qlogging.h:164
#define qWarning
Definition qlogging.h:166
GLsizei samples
GLenum GLuint GLint level
GLuint GLuint end
GLenum target
GLboolean enable
GLenum GLuint GLintptr offset
#define Q_TOUCHPOINT_STATE(prop)
\qmlmethod object XrView::touchpointState(int pointId)
static qreal position(const QQuickItem *item, QQuickAnchors::Anchor anchorLine)
#define qPrintable(string)
Definition qstring.h:1531
#define QStringLiteral(str)
#define tr(X)
#define emit
#define Q_UNUSED(x)
myObject disconnect()
[26]
QGraphicsItem * item
view viewport() -> scroll(dx, dy, deviceRect)
static bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType, QGenericReturnArgument ret, QGenericArgument val0=QGenericArgument(nullptr), QGenericArgument val1=QGenericArgument(), QGenericArgument val2=QGenericArgument(), QGenericArgument val3=QGenericArgument(), QGenericArgument val4=QGenericArgument(), QGenericArgument val5=QGenericArgument(), QGenericArgument val6=QGenericArgument(), QGenericArgument val7=QGenericArgument(), QGenericArgument val8=QGenericArgument(), QGenericArgument val9=QGenericArgument())
\threadsafe This is an overloaded member function, provided for convenience. It differs from the abov...
QHash< int, QOpenXRItem::TouchState > points