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

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