1// Copyright (C) 2024 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "qquick3dxranchormanager_openxr_p.h"
5#include "openxr/qopenxrhelpers_p.h"
6#include "qquick3dxrspatialanchor_p.h"
7
8#include <QtQuick3DUtils/private/qssgassert_p.h>
9
10#include <QtGui/qquaternion.h>
11
12#include <QLoggingCategory>
13
14#if defined(Q_OS_ANDROID)
15# include <QtCore/private/qandroidextras_p.h>
16#endif
17
18QT_BEGIN_NAMESPACE
19
20Q_DECLARE_LOGGING_CATEGORY(lcQuick3DXr);
21
22//<uses-permission android:name="com.oculus.permission.USE_ANCHOR_API" />
23
24static const uint32_t MAX_PERSISTENT_SPACES = 100;
25
26static const char qssgXrRecognizedLabels[] = "TABLE,COUCH,FLOOR,CEILING,WALL_FACE,WINDOW_FRAME,DOOR_FRAME,STORAGE,BED,SCREEN,LAMP,PLANT,WALL_ART,OTHER";
27
28[[nodiscard]] static QQuick3DXrSpatialAnchor::Classification getLabelForString(const QString &label)
29{
30 if (label == QStringLiteral("TABLE"))
31 return QQuick3DXrSpatialAnchor::Classification::Table;
32 if (label == QStringLiteral("COUCH"))
33 return QQuick3DXrSpatialAnchor::Classification::Seat;
34 if (label == QStringLiteral("FLOOR"))
35 return QQuick3DXrSpatialAnchor::Classification::Floor;
36 if (label == QStringLiteral("CEILING"))
37 return QQuick3DXrSpatialAnchor::Classification::Ceiling;
38 if (label == QStringLiteral("WALL_FACE"))
39 return QQuick3DXrSpatialAnchor::Classification::Wall;
40 if (label == QStringLiteral("WINDOW_FRAME"))
41 return QQuick3DXrSpatialAnchor::Classification::Window;
42 if (label == QStringLiteral("DOOR_FRAME"))
43 return QQuick3DXrSpatialAnchor::Classification::Door;
44
45 return QQuick3DXrSpatialAnchor::Classification::Other;
46}
47
48
49QQuick3DXrAnchorManager::QQuick3DXrAnchorManager()
50{
51
52}
53
54QQuick3DXrAnchorManager::~QQuick3DXrAnchorManager()
55{
56
57}
58
59QQuick3DXrAnchorManager *QQuick3DXrAnchorManager::instance()
60{
61 static QQuick3DXrAnchorManager instance;
62 return &instance;
63}
64
65void QQuick3DXrAnchorManager::initialize(XrInstance instance, XrSession session)
66{
67#if defined(Q_OS_ANDROID)
68 auto res = QtAndroidPrivate::requestPermission(QLatin1StringView("com.oculus.permission.USE_SCENE"));
69 res.waitForFinished();
70#endif
71
72 m_instance = instance;
73 m_session = session;
74
75 // Get the function pointers
76 resolveXrFunction(
77 name: "xrEnumerateSpaceSupportedComponentsFB",
78 function: (PFN_xrVoidFunction*)(&xrEnumerateSpaceSupportedComponentsFB));
79 resolveXrFunction(
80 name: "xrGetSpaceComponentStatusFB",
81 function: (PFN_xrVoidFunction*)(&xrGetSpaceComponentStatusFB));
82 resolveXrFunction(
83 name: "xrSetSpaceComponentStatusFB",
84 function: (PFN_xrVoidFunction*)(&xrSetSpaceComponentStatusFB));
85 resolveXrFunction(
86 name: "xrGetSpaceUuidFB",
87 function: (PFN_xrVoidFunction*)(&xrGetSpaceUuidFB));
88 resolveXrFunction(
89 name: "xrQuerySpacesFB",
90 function: (PFN_xrVoidFunction*)(&xrQuerySpacesFB));
91 resolveXrFunction(
92 name: "xrRetrieveSpaceQueryResultsFB",
93 function: (PFN_xrVoidFunction*)(&xrRetrieveSpaceQueryResultsFB));
94 resolveXrFunction(
95 name: "xrGetSpaceBoundingBox2DFB",
96 function: (PFN_xrVoidFunction*)(&xrGetSpaceBoundingBox2DFB));
97 resolveXrFunction(
98 name: "xrGetSpaceBoundingBox3DFB",
99 function: (PFN_xrVoidFunction*)(&xrGetSpaceBoundingBox3DFB));
100 resolveXrFunction(
101 name: "xrGetSpaceSemanticLabelsFB",
102 function: (PFN_xrVoidFunction*)(&xrGetSpaceSemanticLabelsFB));
103 resolveXrFunction(
104 name: "xrGetSpaceBoundary2DFB",
105 function: (PFN_xrVoidFunction*)(&xrGetSpaceBoundary2DFB));
106 resolveXrFunction(
107 name: "xrGetSpaceRoomLayoutFB",
108 function: (PFN_xrVoidFunction*)(&xrGetSpaceRoomLayoutFB));
109 resolveXrFunction(
110 name: "xrGetSpaceContainerFB",
111 function: (PFN_xrVoidFunction*)(&xrGetSpaceContainerFB));
112 resolveXrFunction(
113 name: "xrRequestSceneCaptureFB",
114 function: (PFN_xrVoidFunction*)(&xrRequestSceneCaptureFB));
115}
116
117void QQuick3DXrAnchorManager::teardown()
118{
119
120}
121
122QList<const char *> QQuick3DXrAnchorManager::requiredExtensions() const
123{
124 return {
125 // XR_EXT_UUID_EXTENSION_NAME, // ### Crashes on Quest 3. Theoretically required for XR_FB_spatial_entity (would work anyway, but is a validation error)
126 XR_FB_SPATIAL_ENTITY_EXTENSION_NAME,
127 XR_FB_SPATIAL_ENTITY_QUERY_EXTENSION_NAME,
128 XR_FB_SPATIAL_ENTITY_STORAGE_EXTENSION_NAME,
129 XR_FB_SPATIAL_ENTITY_CONTAINER_EXTENSION_NAME,
130 XR_FB_SCENE_EXTENSION_NAME,
131#ifdef Q_OS_ANDROID // no scene capture with Simulator as of version 57
132 XR_FB_SCENE_CAPTURE_EXTENSION_NAME
133#endif
134 };
135}
136
137void QQuick3DXrAnchorManager::handleEvent(const XrEventDataBaseHeader *event)
138{
139 if (event->type == XR_TYPE_EVENT_DATA_SPACE_SET_STATUS_COMPLETE_FB) {
140 qCDebug(lcQuick3DXr, "QQuick3DXrAnchorManager::handleEvent: received XR_TYPE_EVENT_DATA_SPACE_SET_STATUS_COMPLETE_FB");
141 const XrEventDataSpaceSetStatusCompleteFB* setStatusComplete = (const XrEventDataSpaceSetStatusCompleteFB*)(event);
142 if (setStatusComplete->result == XR_SUCCESS) {
143 if (setStatusComplete->componentType == XR_SPACE_COMPONENT_TYPE_LOCATABLE_FB) {
144 addAnchor(space: setStatusComplete->space, uuid: setStatusComplete->uuid);
145 }
146 }
147 } else if (event->type == XR_TYPE_EVENT_DATA_SCENE_CAPTURE_COMPLETE_FB) {
148 qCDebug(lcQuick3DXr, "QQuick3DXrAnchorManager::handleEvent: received XR_TYPE_EVENT_DATA_SCENE_CAPTURE_COMPLETE_FB");
149 const XrEventDataSceneCaptureCompleteFB* captureResult = (const XrEventDataSceneCaptureCompleteFB*)(event);
150 if (captureResult->result == XR_SUCCESS) {
151 Q_EMIT sceneCaptureCompleted();
152 qCDebug(lcQuick3DXr,
153 "QQuick3DXrAnchorManager::handleEvent: Scene capture (ID = %llu) succeeded",
154 static_cast<long long unsigned int>(captureResult->requestId));
155 } else {
156 qCDebug(lcQuick3DXr,
157 "QQuick3DXrAnchorManager::handleEvent: Scene capture (ID = %llu) failed with an error %d",
158 static_cast<long long unsigned int>(captureResult->requestId),
159 captureResult->result);
160 }
161
162 } else if (event->type == XR_TYPE_EVENT_DATA_SPACE_QUERY_RESULTS_AVAILABLE_FB) {
163 qCDebug(lcQuick3DXr, "QQuick3DXrAnchorManager::handleEvent: received XR_TYPE_EVENT_DATA_SPACE_QUERY_RESULTS_AVAILABLE_FB");
164 const XrEventDataSpaceQueryResultsAvailableFB* resultsAvailable = (const XrEventDataSpaceQueryResultsAvailableFB*)(event);
165
166 XrSpaceQueryResultsFB queryResults{};
167 queryResults.type = XR_TYPE_SPACE_QUERY_RESULTS_FB;
168 queryResults.resultCapacityInput = 0;
169 queryResults.resultCountOutput = 0;
170 queryResults.results = nullptr;
171
172 if (!checkXrResult(result: retrieveSpaceQueryResults(requestId: resultsAvailable->requestId, results: &queryResults))) {
173 qWarning(msg: "Failed to retrieve space query results");
174 return;
175 }
176
177 QVector<XrSpaceQueryResultFB> results(queryResults.resultCountOutput);
178 queryResults.resultCapacityInput = results.size();
179 queryResults.resultCountOutput = 0;
180 queryResults.results = results.data();
181
182 if (!checkXrResult(result: retrieveSpaceQueryResults(requestId: resultsAvailable->requestId, results: &queryResults))) {
183 qWarning(msg: "Failed to retrieve space query results");
184 return;
185 }
186
187 qCDebug(lcQuick3DXr, "retrieveSpaceQueryResults: num of results received: %d", queryResults.resultCountOutput);
188 for (const auto &result : results) {
189 if (isComponentSupported(space: result.space, type: XR_SPACE_COMPONENT_TYPE_LOCATABLE_FB)) {
190 XrSpaceComponentStatusSetInfoFB request = {
191 .type: XR_TYPE_SPACE_COMPONENT_STATUS_SET_INFO_FB,
192 .next: nullptr,
193 .componentType: XR_SPACE_COMPONENT_TYPE_LOCATABLE_FB,
194 .enabled: true,
195 .timeout: 0
196 };
197 XrAsyncRequestIdFB requestId;
198 XrResult res = setSpaceComponentStatus(space: result.space, info: &request, requestId: &requestId);
199 if (res == XR_ERROR_SPACE_COMPONENT_STATUS_ALREADY_SET_FB) {
200 addAnchor(space: result.space, uuid: result.uuid);
201 }
202 }
203 }
204 } else if (event->type == XR_TYPE_EVENT_DATA_SPACE_QUERY_COMPLETE_FB) {
205 qCDebug(lcQuick3DXr, "QQuick3DXrAnchorManager::handleEvent: received XR_TYPE_EVENT_DATA_SPACE_QUERY_COMPLETE_FB");
206 }
207}
208
209void QQuick3DXrAnchorManager::requestSceneCapture()
210{
211 XrAsyncRequestIdFB requestId;
212 XrSceneCaptureRequestInfoFB request{};
213 request.type = XR_TYPE_SCENE_CAPTURE_REQUEST_INFO_FB;
214 request.requestByteCount = 0;
215 request.request = nullptr;
216 if (!checkXrResult(result: requestSceneCapture(info: &request, requestId: &requestId)))
217 qWarning(msg: "Failed to request scene capture");
218}
219
220bool QQuick3DXrAnchorManager::isComponentSupported(XrSpace space, XrSpaceComponentTypeFB type)
221{
222 uint32_t numComponents = 0;
223 if (!checkXrResult(result: enumerateSpaceSupportedComponents(space, componentTypeCapacityInput: 0, componentTypeCountOutput: &numComponents, componentTypes: nullptr))) {
224 qWarning(msg: "Failed to enumerate supported space components");
225 return false;
226 }
227
228 QVector<XrSpaceComponentTypeFB> components(numComponents);
229 if (!checkXrResult(result: enumerateSpaceSupportedComponents(space, componentTypeCapacityInput: numComponents, componentTypeCountOutput: &numComponents, componentTypes: components.data()))) {
230 qWarning(msg: "Failed to enumerate supported space components");
231 return false;
232 }
233
234 bool supported = false;
235 for (const auto &component : components) {
236 if (component == type) {
237 supported = true;
238 break;
239 }
240 }
241
242 return supported;
243}
244
245bool QQuick3DXrAnchorManager::isComponentEnabled(XrSpace space, XrSpaceComponentTypeFB type)
246{
247 XrSpaceComponentStatusFB status = {.type: XR_TYPE_SPACE_COMPONENT_STATUS_FB, .next: nullptr, .enabled: 0, .changePending: 0};
248 if (!checkXrResult(result: getSpaceComponentStatus(space, componentType: type, status: &status))) {
249 qWarning(msg: "Failed to get space component status");
250 return false;
251 }
252 return (status.enabled && !status.changePending);
253}
254
255bool QQuick3DXrAnchorManager::getBoundingBox2D(XrSpace space, QVector2D &offset, QVector2D &extent)
256{
257 if (isComponentEnabled(space, type: XR_SPACE_COMPONENT_TYPE_BOUNDED_2D_FB)) {
258 // Get the 2D bounds
259 XrRect2Df boundingBox2D;
260 if (!checkXrResult(result: getSpaceBoundingBox2D(space, boundingBox2DOutput: &boundingBox2D))) {
261 qWarning(msg: "Failed to get bounding box 2D for space");
262 return false;
263 }
264 offset = QVector2D(boundingBox2D.offset.x, boundingBox2D.offset.y);
265 extent = QVector2D(boundingBox2D.extent.width, boundingBox2D.extent.height);
266 return true;
267 }
268 return false;
269}
270
271bool QQuick3DXrAnchorManager::getBoundingBox3D(XrSpace space, QVector3D &offset, QVector3D &extent)
272{
273 if (isComponentEnabled(space, type: XR_SPACE_COMPONENT_TYPE_BOUNDED_3D_FB)) {
274 // Get the 3D bounds
275 XrRect3DfFB boundingBox3D;
276 if (!checkXrResult(result: getSpaceBoundingBox3D(space, boundingBox3DOutput: &boundingBox3D))) {
277 qWarning(msg: "Failed to get bounding box 3D for space");
278 return false;
279 }
280 offset = QVector3D(boundingBox3D.offset.x, boundingBox3D.offset.y, boundingBox3D.offset.z);
281 extent = QVector3D(boundingBox3D.extent.width, boundingBox3D.extent.height, boundingBox3D.extent.depth);
282 return true;
283 }
284
285 return false;
286}
287
288bool QQuick3DXrAnchorManager::setupSpatialAnchor(XrSpace space, QQuick3DXrSpatialAnchor &anchor)
289{
290 QVector2D extent2D;
291 QVector2D offset2D;
292
293 QVector3D extent3D;
294 QVector3D offset3D;
295
296 QSSG_ASSERT(space != XR_NULL_HANDLE, return false);
297
298 const bool m_has2DBounds = getBoundingBox2D(space, offset&: offset2D, extent&: extent2D);
299 const bool m_has3DBounds = getBoundingBox3D(space, offset&: offset3D, extent&: extent3D);
300 if (isComponentSupported(space, type: XR_SPACE_COMPONENT_TYPE_SPACE_CONTAINER_FB) &&
301 isComponentEnabled(space, type: XR_SPACE_COMPONENT_TYPE_SPACE_CONTAINER_FB)) {
302 // Get the space container UUIDs
303 anchor.setSpaceContainerUuids(collectSpaceContainerUuids(space));
304 } else if (isComponentSupported(space, type: XR_SPACE_COMPONENT_TYPE_ROOM_LAYOUT_FB) &&
305 isComponentEnabled(space, type: XR_SPACE_COMPONENT_TYPE_ROOM_LAYOUT_FB)) {
306 anchor.setRoomLayoutUuids(collectRoomLayoutUuids(space));
307 }
308
309 if (m_has2DBounds)
310 anchor.setBounds2D(offset: offset2D, extent: extent2D);
311 if (m_has3DBounds)
312 anchor.setBounds3D(offset: offset3D, extent: extent3D);
313
314 auto stringLabel = getSemanticLabels(space);
315 auto semanticLable = getLabelForString(label: stringLabel);
316
317 anchor.setClassification(semanticLable);
318 anchor.setClassificationString(stringLabel);
319
320 return true;
321}
322
323namespace {
324bool isValidUuid(const XrUuidEXT& uuid) {
325 // The best, and reasonable way we can say if a uuid is valid, is to check if it's not null.
326 // Anyting more then that is outside the scope of this function (There's no real way to check if a uuid is valid
327 // that makese sense here anyways).
328 return !QtQuick3DXr::isNullUuid(uuid: uuid.data);
329}
330
331QUuid fromXrUuidExt(XrUuidEXT uuid) {
332 return QUuid::fromBytes(bytes: uuid.data);
333}
334
335XrUuidEXT fromQUuid(QUuid uuid) {
336 XrUuidEXT xrUuid;
337 auto bytes = uuid.toBytes();
338 memcpy(dest: xrUuid.data, src: bytes.data, XR_UUID_SIZE_EXT);
339 return xrUuid;
340}
341
342}
343
344QSet<QUuid> QQuick3DXrAnchorManager::collectRoomLayoutUuids(XrSpace space)
345{
346 XrRoomLayoutFB roomLayout{};
347 roomLayout.type = XR_TYPE_ROOM_LAYOUT_FB;
348 QVector<XrUuidEXT> wallUuids;
349 QSet<QUuid> uuidSet;
350
351 // First call
352 if (!checkXrResult(result: getSpaceRoomLayout(space, roomLayoutOutput: &roomLayout))) {
353 qWarning(msg: "Failed to get room layout");
354 return uuidSet;
355 }
356
357 // If wallUuidCountOutput == 0, no walls in the component. The UUIDs of the ceiling and floor
358 // has been returned if available
359 if (roomLayout.wallUuidCountOutput != 0) {
360 // Second call
361 wallUuids.resize(size: roomLayout.wallUuidCountOutput);
362 roomLayout.wallUuidCapacityInput = wallUuids.size();
363 roomLayout.wallUuids = wallUuids.data();
364 if (!checkXrResult(result: getSpaceRoomLayout(space, roomLayoutOutput: &roomLayout))) {
365 qWarning(msg: "Failed to get room layout");
366 return uuidSet;
367 }
368 }
369 if (isValidUuid(uuid: roomLayout.floorUuid))
370 uuidSet.insert(value: fromXrUuidExt(uuid: roomLayout.floorUuid));
371 if (isValidUuid(uuid: roomLayout.ceilingUuid))
372 uuidSet.insert(value: fromXrUuidExt(uuid: roomLayout.ceilingUuid));
373 for (uint32_t i = 0; i < roomLayout.wallUuidCountOutput; i++)
374 uuidSet.insert(value: fromXrUuidExt(uuid: roomLayout.wallUuids[i]));
375 return uuidSet;
376}
377
378QSet<QUuid> QQuick3DXrAnchorManager::collectSpaceContainerUuids(XrSpace space)
379{
380 XrSpaceContainerFB spaceContainer{};
381 spaceContainer.type = XR_TYPE_SPACE_CONTAINER_FB;
382 QSet<QUuid> uuidSet;
383 // First call
384 if (!checkXrResult(result: getSpaceContainer(space, spaceContainerOutput: &spaceContainer))) {
385 qWarning(msg: "Failed to get container");
386 return uuidSet;
387 }
388 if (spaceContainer.uuidCountOutput != 0) {
389 // Second call
390 QVector<XrUuidEXT> uuids(spaceContainer.uuidCountOutput);
391 spaceContainer.uuidCapacityInput = uuids.size();
392 spaceContainer.uuids = uuids.data();
393 if (!checkXrResult(result: getSpaceContainer(space, spaceContainerOutput: &spaceContainer))) {
394 qWarning(msg: "Failed to get container");
395 return uuidSet;
396 }
397
398 for (uint32_t i = 0; i < spaceContainer.uuidCountOutput; i++)
399 uuidSet.insert(value: fromXrUuidExt(uuid: spaceContainer.uuids[i]));
400 }
401
402 return uuidSet;
403}
404
405QString QQuick3DXrAnchorManager::getSemanticLabels(const XrSpace space) {
406 const XrSemanticLabelsSupportInfoFB semanticLabelsSupportInfo = {
407 .type: XR_TYPE_SEMANTIC_LABELS_SUPPORT_INFO_FB,
408 .next: nullptr,
409 .flags: XR_SEMANTIC_LABELS_SUPPORT_MULTIPLE_SEMANTIC_LABELS_BIT_FB | XR_SEMANTIC_LABELS_SUPPORT_ACCEPT_DESK_TO_TABLE_MIGRATION_BIT_FB,
410 .recognizedLabels: qssgXrRecognizedLabels
411 };
412
413 XrSemanticLabelsFB labels{};
414 labels.type = XR_TYPE_SEMANTIC_LABELS_FB;
415 labels.next = &semanticLabelsSupportInfo;
416
417 if (!isComponentEnabled(space, type: XR_SPACE_COMPONENT_TYPE_SEMANTIC_LABELS_FB))
418 return QString();
419
420 // First call.
421 if (!checkXrResult(result: getSpaceSemanticLabels(space, semanticLabelsOutput: &labels))) {
422 qWarning(msg: "Failed to get semantic labels");
423 return {};
424 }
425 // Second call
426 QByteArray labelData(labels.bufferCountOutput, Qt::Uninitialized);
427 labels.bufferCapacityInput = labelData.size();
428 labels.buffer = labelData.data();
429 if (!checkXrResult(result: getSpaceSemanticLabels(space, semanticLabelsOutput: &labels))) {
430 qWarning(msg: "Failed to get semantic labels");
431 return {};
432 }
433
434 return QString::fromLocal8Bit(ba: labelData);
435}
436
437bool QQuick3DXrAnchorManager::queryAllAnchors() {
438 XrSpaceQueryInfoFB queryInfo = {
439 .type: XR_TYPE_SPACE_QUERY_INFO_FB,
440 .next: nullptr,
441 .queryAction: XR_SPACE_QUERY_ACTION_LOAD_FB,
442 .maxResultCount: MAX_PERSISTENT_SPACES,
443 .timeout: 0,
444 .filter: nullptr,
445 .excludeFilter: nullptr};
446
447 XrAsyncRequestIdFB requestId;
448 return checkXrResult(result: querySpaces(info: (XrSpaceQueryInfoBaseHeaderFB*)&queryInfo, requestId: &requestId));
449}
450
451const QList<QQuick3DXrSpatialAnchor *> &QQuick3DXrAnchorManager::anchors() const
452{
453 return m_anchors;
454}
455
456qsizetype QQuick3DXrAnchorManager::anchorCount() const
457{
458 return m_anchors.count();
459}
460
461void QQuick3DXrAnchorManager::updateAnchors(XrTime predictedDisplayTime, XrSpace appSpace)
462{
463 // basically for each anchor, we need to call xrLocateSpace
464 for (auto &anchor : m_anchors) {
465 XrSpaceLocation spaceLocation{};
466 spaceLocation.type = XR_TYPE_SPACE_LOCATION;
467 XrResult res = xrLocateSpace(space: QtQuick3DXr::fromXrSpaceId<XrSpace>(space: anchor->space()), baseSpace: appSpace, time: predictedDisplayTime, location: &spaceLocation);
468 if (XR_UNQUALIFIED_SUCCESS(res)) {
469 if ((spaceLocation.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) != 0 &&
470 (spaceLocation.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) != 0) {
471
472 // Update transform
473 anchor->setPosition(QVector3D(spaceLocation.pose.position.x,
474 spaceLocation.pose.position.y,
475 spaceLocation.pose.position.z) * 100.0f);
476 anchor->setRotation(QQuaternion(spaceLocation.pose.orientation.w,
477 spaceLocation.pose.orientation.x,
478 spaceLocation.pose.orientation.y,
479 spaceLocation.pose.orientation.z));
480 }
481 }
482 }
483}
484
485bool QQuick3DXrAnchorManager::queryAllAnchorsWithSpecificComponentEnabled(const XrSpaceComponentTypeFB componentType) {
486 XrSpaceStorageLocationFilterInfoFB storageLocationFilterInfo = {
487 .type: XR_TYPE_SPACE_STORAGE_LOCATION_FILTER_INFO_FB,
488 .next: nullptr,
489 .location: XR_SPACE_STORAGE_LOCATION_LOCAL_FB
490 };
491
492 XrSpaceComponentFilterInfoFB componentFilterInfo = {
493 .type: XR_TYPE_SPACE_COMPONENT_FILTER_INFO_FB,
494 .next: &storageLocationFilterInfo,
495 .componentType: componentType
496 };
497
498 XrSpaceQueryInfoFB queryInfo = {
499 .type: XR_TYPE_SPACE_QUERY_INFO_FB,
500 .next: nullptr,
501 .queryAction: XR_SPACE_QUERY_ACTION_LOAD_FB,
502 .maxResultCount: MAX_PERSISTENT_SPACES,
503 .timeout: 0,
504 .filter: (XrSpaceFilterInfoBaseHeaderFB*)&componentFilterInfo,
505 .excludeFilter: nullptr
506 };
507
508 XrAsyncRequestIdFB requestId;
509 if (!checkXrResult(result: querySpaces(info: (XrSpaceQueryInfoBaseHeaderFB*)&queryInfo, requestId: &requestId))) {
510 qWarning(msg: "Failed to query spaces");
511 return false;
512 }
513 return true;
514}
515
516bool QQuick3DXrAnchorManager::queryAnchorsByUuids(const QSet<QUuid>& uuidSet) {
517 if (uuidSet.isEmpty())
518 return false;
519
520 QVector<XrUuidEXT> uuidsToQuery;
521
522 for (const auto &uuid : uuidSet) {
523 XrUuidEXT xrUuid = fromQUuid(uuid);
524 uuidsToQuery.append(t: xrUuid);
525 }
526
527 XrSpaceStorageLocationFilterInfoFB storageLocationFilterInfo = {
528 .type: XR_TYPE_SPACE_STORAGE_LOCATION_FILTER_INFO_FB,
529 .next: nullptr,
530 .location: XR_SPACE_STORAGE_LOCATION_LOCAL_FB
531 };
532
533 XrSpaceUuidFilterInfoFB uuidFilterInfo = {
534 .type: XR_TYPE_SPACE_UUID_FILTER_INFO_FB,
535 .next: &storageLocationFilterInfo,
536 .uuidCount: (uint32_t)uuidsToQuery.size(),
537 .uuids: uuidsToQuery.data()
538 };
539
540 XrSpaceQueryInfoFB queryInfo = {
541 .type: XR_TYPE_SPACE_QUERY_INFO_FB,
542 .next: nullptr,
543 .queryAction: XR_SPACE_QUERY_ACTION_LOAD_FB,
544 .maxResultCount: MAX_PERSISTENT_SPACES,
545 .timeout: 0,
546 .filter: (XrSpaceFilterInfoBaseHeaderFB*)&uuidFilterInfo,
547 .excludeFilter: nullptr
548 };
549
550 XrAsyncRequestIdFB requestId;
551 if (!checkXrResult(result: querySpaces(info: (XrSpaceQueryInfoBaseHeaderFB*)&queryInfo, requestId: &requestId))) {
552 qWarning(msg: "Failed to query spaces");
553 return false;
554 }
555 return true;
556}
557
558void QQuick3DXrAnchorManager::addAnchor(XrSpace space, XrUuidEXT uuid)
559{
560 auto quuid = fromXrUuidExt(uuid);
561 // Check if we already have this anchor
562 if (m_anchorsByUuid.contains(key: quuid))
563 return;
564
565 QQuick3DXrSpatialAnchor *anchor = new QQuick3DXrSpatialAnchor(QtQuick3DXr::toXrSpaceId(space), quuid, this);
566 setupSpatialAnchor(space, anchor&: *anchor);
567 m_anchorsByUuid.insert(key: quuid, value: anchor);
568 m_anchors.append(t: anchor);
569 Q_EMIT anchorAdded(anchor);
570}
571
572
573XrResult QQuick3DXrAnchorManager::enumerateSpaceSupportedComponents(XrSpace space, uint32_t componentTypeCapacityInput, uint32_t *componentTypeCountOutput, XrSpaceComponentTypeFB *componentTypes)
574{
575 return OpenXRHelpers::safeCall(f: xrEnumerateSpaceSupportedComponentsFB, args: space, args: componentTypeCapacityInput, args: componentTypeCountOutput, args: componentTypes);
576}
577
578XrResult QQuick3DXrAnchorManager::getSpaceComponentStatus(XrSpace space, XrSpaceComponentTypeFB componentType, XrSpaceComponentStatusFB *status)
579{
580 return OpenXRHelpers::safeCall(f: xrGetSpaceComponentStatusFB, args: space, args: componentType, args: status);
581}
582
583XrResult QQuick3DXrAnchorManager::setSpaceComponentStatus(XrSpace space, const XrSpaceComponentStatusSetInfoFB *info, XrAsyncRequestIdFB *requestId)
584{
585 return OpenXRHelpers::safeCall(f: xrSetSpaceComponentStatusFB, args: space, args: info, args: requestId);
586}
587
588XrResult QQuick3DXrAnchorManager::getSpaceUuid(XrSpace space, XrUuidEXT *uuid)
589{
590 return OpenXRHelpers::safeCall(f: xrGetSpaceUuidFB, args: space, args: uuid);
591}
592
593XrResult QQuick3DXrAnchorManager::querySpaces(const XrSpaceQueryInfoBaseHeaderFB *info, XrAsyncRequestIdFB *requestId)
594{
595 return OpenXRHelpers::safeCall(f: xrQuerySpacesFB, args: m_session, args: info, args: requestId);
596}
597
598XrResult QQuick3DXrAnchorManager::retrieveSpaceQueryResults(XrAsyncRequestIdFB requestId, XrSpaceQueryResultsFB *results)
599{
600 return OpenXRHelpers::safeCall(f: xrRetrieveSpaceQueryResultsFB, args: m_session, args: requestId, args: results);
601}
602
603XrResult QQuick3DXrAnchorManager::getSpaceBoundingBox2D(XrSpace space, XrRect2Df *boundingBox2DOutput)
604{
605 return OpenXRHelpers::safeCall(f: xrGetSpaceBoundingBox2DFB, args: m_session, args: space, args: boundingBox2DOutput);
606}
607
608XrResult QQuick3DXrAnchorManager::getSpaceBoundingBox3D(XrSpace space, XrRect3DfFB *boundingBox3DOutput)
609{
610 return OpenXRHelpers::safeCall(f: xrGetSpaceBoundingBox3DFB, args: m_session, args: space, args: boundingBox3DOutput);
611}
612
613XrResult QQuick3DXrAnchorManager::getSpaceSemanticLabels(XrSpace space, XrSemanticLabelsFB *semanticLabelsOutput)
614{
615 return OpenXRHelpers::safeCall(f: xrGetSpaceSemanticLabelsFB, args: m_session, args: space, args: semanticLabelsOutput);
616}
617
618XrResult QQuick3DXrAnchorManager::getSpaceBoundary2D(XrSpace space, XrBoundary2DFB *boundary2DOutput)
619{
620 return OpenXRHelpers::safeCall(f: xrGetSpaceBoundary2DFB, args: m_session, args: space, args: boundary2DOutput);
621}
622
623XrResult QQuick3DXrAnchorManager::getSpaceRoomLayout(XrSpace space, XrRoomLayoutFB *roomLayoutOutput)
624{
625 return OpenXRHelpers::safeCall(f: xrGetSpaceRoomLayoutFB, args: m_session, args: space, args: roomLayoutOutput);
626}
627
628XrResult QQuick3DXrAnchorManager::getSpaceContainer(XrSpace space, XrSpaceContainerFB *spaceContainerOutput)
629{
630 return OpenXRHelpers::safeCall(f: xrGetSpaceContainerFB, args: m_session, args: space, args: spaceContainerOutput);
631}
632
633XrResult QQuick3DXrAnchorManager::requestSceneCapture(const XrSceneCaptureRequestInfoFB *info, XrAsyncRequestIdFB *requestId)
634{
635 return OpenXRHelpers::safeCall(f: xrRequestSceneCaptureFB, args: m_session, args: info, args: requestId);
636}
637
638bool QQuick3DXrAnchorManager::checkXrResult(const XrResult &result)
639{
640 return OpenXRHelpers::checkXrResult(result, instance: m_instance);
641}
642
643bool QQuick3DXrAnchorManager::resolveXrFunction(const char *name, PFN_xrVoidFunction *function)
644{
645 XrResult result = xrGetInstanceProcAddr(instance: m_instance, name, function);
646 if (!OpenXRHelpers::checkXrResult(result, instance: m_instance)) {
647 qWarning(msg: "Failed to resolve OpenXR function %s", name);
648 *function = nullptr;
649 return false;
650 }
651 return true;
652}
653
654QT_END_NAMESPACE
655

source code of qtquick3d/src/xr/quick3dxr/openxr/qquick3dxranchormanager_openxr.cpp