7#include "openxr/qopenxrhelpers_p.h"
10#include <QtQuick3DUtils/private/qssgassert_p.h>
12#include <QtGui/qquaternion.h>
14#include <QLoggingCategory>
16#if defined(Q_OS_ANDROID)
17# include <QtCore/private/qandroidextras_p.h>
22Q_DECLARE_LOGGING_CATEGORY(lcQuick3DXr);
28static const char qssgXrRecognizedLabels[] =
"TABLE,COUCH,FLOOR,CEILING,WALL_FACE,WINDOW_FRAME,DOOR_FRAME,STORAGE,BED,SCREEN,LAMP,PLANT,WALL_ART,OTHER";
32 if (label == QStringLiteral(
"TABLE"))
33 return QQuick3DXrSpatialAnchor::Classification::Table;
34 if (label == QStringLiteral(
"COUCH"))
35 return QQuick3DXrSpatialAnchor::Classification::Seat;
36 if (label == QStringLiteral(
"FLOOR"))
37 return QQuick3DXrSpatialAnchor::Classification::Floor;
38 if (label == QStringLiteral(
"CEILING"))
39 return QQuick3DXrSpatialAnchor::Classification::Ceiling;
40 if (label == QStringLiteral(
"WALL_FACE"))
41 return QQuick3DXrSpatialAnchor::Classification::Wall;
42 if (label == QStringLiteral(
"WINDOW_FRAME"))
43 return QQuick3DXrSpatialAnchor::Classification::Window;
44 if (label == QStringLiteral(
"DOOR_FRAME"))
45 return QQuick3DXrSpatialAnchor::Classification::Door;
47 return QQuick3DXrSpatialAnchor::Classification::Other;
69#if defined(Q_OS_ANDROID)
70 auto res = QtAndroidPrivate::requestPermission(QLatin1StringView(
"com.oculus.permission.USE_SCENE"));
71 res.waitForFinished();
74 m_instance = instance;
80 "xrEnumerateSpaceSupportedComponentsFB",
81 (PFN_xrVoidFunction*)(&xrEnumerateSpaceSupportedComponentsFB)
);
84 "xrGetSpaceComponentStatusFB",
85 (PFN_xrVoidFunction*)(&xrGetSpaceComponentStatusFB)
);
88 "xrSetSpaceComponentStatusFB",
89 (PFN_xrVoidFunction*)(&xrSetSpaceComponentStatusFB)
);
93 (PFN_xrVoidFunction*)(&xrGetSpaceUuidFB)
);
97 (PFN_xrVoidFunction*)(&xrQuerySpacesFB)
);
100 "xrRetrieveSpaceQueryResultsFB",
101 (PFN_xrVoidFunction*)(&xrRetrieveSpaceQueryResultsFB)
);
104 "xrGetSpaceBoundingBox2DFB",
105 (PFN_xrVoidFunction*)(&xrGetSpaceBoundingBox2DFB)
);
108 "xrGetSpaceBoundingBox3DFB",
109 (PFN_xrVoidFunction*)(&xrGetSpaceBoundingBox3DFB)
);
112 "xrGetSpaceSemanticLabelsFB",
113 (PFN_xrVoidFunction*)(&xrGetSpaceSemanticLabelsFB)
);
116 "xrGetSpaceBoundary2DFB",
117 (PFN_xrVoidFunction*)(&xrGetSpaceBoundary2DFB)
);
120 "xrGetSpaceRoomLayoutFB",
121 (PFN_xrVoidFunction*)(&xrGetSpaceRoomLayoutFB)
);
124 "xrGetSpaceContainerFB",
125 (PFN_xrVoidFunction*)(&xrGetSpaceContainerFB)
);
128 "xrRequestSceneCaptureFB",
129 (PFN_xrVoidFunction*)(&xrRequestSceneCaptureFB)
);
141 XR_FB_SPATIAL_ENTITY_EXTENSION_NAME,
142 XR_FB_SPATIAL_ENTITY_QUERY_EXTENSION_NAME,
143 XR_FB_SPATIAL_ENTITY_STORAGE_EXTENSION_NAME,
144 XR_FB_SPATIAL_ENTITY_CONTAINER_EXTENSION_NAME,
145 XR_FB_SCENE_EXTENSION_NAME,
147 XR_FB_SCENE_CAPTURE_EXTENSION_NAME
154 if (event->type == XR_TYPE_EVENT_DATA_SPACE_SET_STATUS_COMPLETE_FB) {
155 qCDebug(lcQuick3DXr,
"QQuick3DXrAnchorManager::handleEvent: received XR_TYPE_EVENT_DATA_SPACE_SET_STATUS_COMPLETE_FB");
156 const XrEventDataSpaceSetStatusCompleteFB* setStatusComplete = (
const XrEventDataSpaceSetStatusCompleteFB*)(event);
157 if (setStatusComplete->result == XR_SUCCESS) {
158 if (setStatusComplete->componentType == XR_SPACE_COMPONENT_TYPE_LOCATABLE_FB) {
159 addAnchor(setStatusComplete->space, setStatusComplete->uuid);
162 }
else if (event->type == XR_TYPE_EVENT_DATA_SCENE_CAPTURE_COMPLETE_FB) {
163 qCDebug(lcQuick3DXr,
"QQuick3DXrAnchorManager::handleEvent: received XR_TYPE_EVENT_DATA_SCENE_CAPTURE_COMPLETE_FB");
164 const XrEventDataSceneCaptureCompleteFB* captureResult = (
const XrEventDataSceneCaptureCompleteFB*)(event);
165 if (captureResult->result == XR_SUCCESS) {
166 Q_EMIT sceneCaptureCompleted();
168 "QQuick3DXrAnchorManager::handleEvent: Scene capture (ID = %llu) succeeded",
169 static_cast<
long long unsigned int>(captureResult->requestId));
172 "QQuick3DXrAnchorManager::handleEvent: Scene capture (ID = %llu) failed with an error %d",
173 static_cast<
long long unsigned int>(captureResult->requestId),
174 captureResult->result);
177 }
else if (event->type == XR_TYPE_EVENT_DATA_SPACE_QUERY_RESULTS_AVAILABLE_FB) {
178 qCDebug(lcQuick3DXr,
"QQuick3DXrAnchorManager::handleEvent: received XR_TYPE_EVENT_DATA_SPACE_QUERY_RESULTS_AVAILABLE_FB");
179 const XrEventDataSpaceQueryResultsAvailableFB* resultsAvailable = (
const XrEventDataSpaceQueryResultsAvailableFB*)(event);
181 XrSpaceQueryResultsFB queryResults{};
182 queryResults.type = XR_TYPE_SPACE_QUERY_RESULTS_FB;
183 queryResults.resultCapacityInput = 0;
184 queryResults.resultCountOutput = 0;
185 queryResults.results =
nullptr;
187 if (!checkXrResult(retrieveSpaceQueryResults(resultsAvailable->requestId, &queryResults))) {
188 qWarning(
"Failed to retrieve space query results");
192 QVector<XrSpaceQueryResultFB> results(queryResults.resultCountOutput);
193 queryResults.resultCapacityInput = results.size();
194 queryResults.resultCountOutput = 0;
195 queryResults.results = results.data();
197 if (!checkXrResult(retrieveSpaceQueryResults(resultsAvailable->requestId, &queryResults))) {
198 qWarning(
"Failed to retrieve space query results");
202 qCDebug(lcQuick3DXr,
"retrieveSpaceQueryResults: num of results received: %d", queryResults.resultCountOutput);
203 for (
const auto &result : results) {
204 if (isComponentSupported(result.space, XR_SPACE_COMPONENT_TYPE_LOCATABLE_FB)) {
205 XrSpaceComponentStatusSetInfoFB request = {
206 XR_TYPE_SPACE_COMPONENT_STATUS_SET_INFO_FB,
208 XR_SPACE_COMPONENT_TYPE_LOCATABLE_FB,
212 XrAsyncRequestIdFB requestId;
213 XrResult res = setSpaceComponentStatus(result.space, &request, &requestId);
214 if (res == XR_ERROR_SPACE_COMPONENT_STATUS_ALREADY_SET_FB) {
215 addAnchor(result.space, result.uuid);
219 }
else if (event->type == XR_TYPE_EVENT_DATA_SPACE_QUERY_COMPLETE_FB) {
220 qCDebug(lcQuick3DXr,
"QQuick3DXrAnchorManager::handleEvent: received XR_TYPE_EVENT_DATA_SPACE_QUERY_COMPLETE_FB");
226 XrAsyncRequestIdFB requestId;
227 XrSceneCaptureRequestInfoFB request{};
228 request.type = XR_TYPE_SCENE_CAPTURE_REQUEST_INFO_FB;
229 request.requestByteCount = 0;
230 request.request =
nullptr;
231 if (!checkXrResult(requestSceneCapture(&request, &requestId)))
232 qWarning(
"Failed to request scene capture");
237 uint32_t numComponents = 0;
238 if (!checkXrResult(enumerateSpaceSupportedComponents(space, 0, &numComponents,
nullptr))) {
239 qWarning(
"Failed to enumerate supported space components");
243 QVector<XrSpaceComponentTypeFB> components(numComponents);
244 if (!checkXrResult(enumerateSpaceSupportedComponents(space, numComponents, &numComponents, components.data()))) {
245 qWarning(
"Failed to enumerate supported space components");
249 bool supported =
false;
250 for (
const auto &component : components) {
251 if (component == type) {
262 XrSpaceComponentStatusFB status = {XR_TYPE_SPACE_COMPONENT_STATUS_FB,
nullptr, 0, 0};
263 if (!checkXrResult(getSpaceComponentStatus(space, type, &status))) {
264 qWarning(
"Failed to get space component status");
267 return (status.enabled && !status.changePending);
274 XrRect2Df boundingBox2D;
275 if (!checkXrResult(getSpaceBoundingBox2D(space, &boundingBox2D))) {
276 qWarning(
"Failed to get bounding box 2D for space");
279 offset = QVector2D(boundingBox2D.offset.x, boundingBox2D.offset.y);
280 extent = QVector2D(boundingBox2D.extent.width, boundingBox2D.extent.height);
290 XrRect3DfFB boundingBox3D;
291 if (!checkXrResult(getSpaceBoundingBox3D(space, &boundingBox3D))) {
292 qWarning(
"Failed to get bounding box 3D for space");
295 offset = QVector3D(boundingBox3D.offset.x, boundingBox3D.offset.y, boundingBox3D.offset.z);
296 extent = QVector3D(boundingBox3D.extent.width, boundingBox3D.extent.height, boundingBox3D.extent.depth);
311 QSSG_ASSERT(space != XR_NULL_HANDLE,
return false);
313 const bool m_has2DBounds = getBoundingBox2D(space, offset2D, extent2D);
314 const bool m_has3DBounds = getBoundingBox3D(space, offset3D, extent3D);
318 anchor.setSpaceContainerUuids(collectSpaceContainerUuids(space));
321 anchor.setRoomLayoutUuids(collectRoomLayoutUuids(space));
325 anchor.setBounds2D(offset2D, extent2D);
327 anchor.setBounds3D(offset3D, extent3D);
329 auto stringLabel = getSemanticLabels(space);
330 auto semanticLable = getLabelForString(stringLabel);
332 anchor.setClassification(semanticLable);
333 anchor.setClassificationString(stringLabel);
339bool isValidUuid(
const XrUuidEXT& uuid) {
343 return !QtQuick3DXr::isNullUuid(uuid.data);
346QUuid fromXrUuidExt(XrUuidEXT uuid) {
347 return QUuid::fromBytes(uuid.data);
350XrUuidEXT fromQUuid(QUuid uuid) {
352 auto bytes = uuid.toBytes();
353 memcpy(xrUuid.data, bytes.data, XR_UUID_SIZE_EXT);
361 XrRoomLayoutFB roomLayout{};
362 roomLayout.type = XR_TYPE_ROOM_LAYOUT_FB;
363 QVector<XrUuidEXT> wallUuids;
367 if (!checkXrResult(getSpaceRoomLayout(space, &roomLayout))) {
368 qWarning(
"Failed to get room layout");
374 if (roomLayout.wallUuidCountOutput != 0) {
376 wallUuids.resize(roomLayout.wallUuidCountOutput);
377 roomLayout.wallUuidCapacityInput = wallUuids.size();
378 roomLayout.wallUuids = wallUuids.data();
379 if (!checkXrResult(getSpaceRoomLayout(space, &roomLayout))) {
380 qWarning(
"Failed to get room layout");
384 if (isValidUuid(roomLayout.floorUuid))
385 uuidSet.insert(fromXrUuidExt(roomLayout.floorUuid));
386 if (isValidUuid(roomLayout.ceilingUuid))
387 uuidSet.insert(fromXrUuidExt(roomLayout.ceilingUuid));
388 for (uint32_t i = 0; i < roomLayout.wallUuidCountOutput; i++)
389 uuidSet.insert(fromXrUuidExt(roomLayout.wallUuids[i]));
395 XrSpaceContainerFB spaceContainer{};
396 spaceContainer.type = XR_TYPE_SPACE_CONTAINER_FB;
399 if (!checkXrResult(getSpaceContainer(space, &spaceContainer))) {
400 qWarning(
"Failed to get container");
403 if (spaceContainer.uuidCountOutput != 0) {
405 QVector<XrUuidEXT> uuids(spaceContainer.uuidCountOutput);
406 spaceContainer.uuidCapacityInput = uuids.size();
407 spaceContainer.uuids = uuids.data();
408 if (!checkXrResult(getSpaceContainer(space, &spaceContainer))) {
409 qWarning(
"Failed to get container");
413 for (uint32_t i = 0; i < spaceContainer.uuidCountOutput; i++)
414 uuidSet.insert(fromXrUuidExt(spaceContainer.uuids[i]));
421 const XrSemanticLabelsSupportInfoFB semanticLabelsSupportInfo = {
422 XR_TYPE_SEMANTIC_LABELS_SUPPORT_INFO_FB,
424 XR_SEMANTIC_LABELS_SUPPORT_MULTIPLE_SEMANTIC_LABELS_BIT_FB | XR_SEMANTIC_LABELS_SUPPORT_ACCEPT_DESK_TO_TABLE_MIGRATION_BIT_FB,
428 XrSemanticLabelsFB labels{};
429 labels.type = XR_TYPE_SEMANTIC_LABELS_FB;
430 labels.next = &semanticLabelsSupportInfo;
436 if (!checkXrResult(getSpaceSemanticLabels(space, &labels))) {
437 qWarning(
"Failed to get semantic labels");
441 QByteArray labelData(labels.bufferCountOutput, Qt::Uninitialized);
442 labels.bufferCapacityInput = labelData.size();
443 labels.buffer = labelData.data();
444 if (!checkXrResult(getSpaceSemanticLabels(space, &labels))) {
445 qWarning(
"Failed to get semantic labels");
449 return QString::fromLocal8Bit(labelData);
453 XrSpaceQueryInfoFB queryInfo = {
454 XR_TYPE_SPACE_QUERY_INFO_FB,
456 XR_SPACE_QUERY_ACTION_LOAD_FB,
462 XrAsyncRequestIdFB requestId;
463 return checkXrResult(querySpaces((XrSpaceQueryInfoBaseHeaderFB*)&queryInfo, &requestId));
473 return m_anchors.count();
479 for (
auto &anchor : m_anchors) {
480 XrSpaceLocation spaceLocation{};
481 spaceLocation.type = XR_TYPE_SPACE_LOCATION;
482 XrResult res = xrLocateSpace(QtQuick3DXr::fromXrSpaceId<XrSpace>(anchor->space()), appSpace, predictedDisplayTime, &spaceLocation);
483 if (XR_UNQUALIFIED_SUCCESS(res)) {
484 if ((spaceLocation.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) != 0 &&
485 (spaceLocation.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) != 0) {
488 anchor->setPosition(QVector3D(spaceLocation.pose.position.x,
489 spaceLocation.pose.position.y,
490 spaceLocation.pose.position.z) * 100.0f);
491 anchor->setRotation(QQuaternion(spaceLocation.pose.orientation.w,
492 spaceLocation.pose.orientation.x,
493 spaceLocation.pose.orientation.y,
494 spaceLocation.pose.orientation.z));
500bool QQuick3DXrAnchorManager::queryAllAnchorsWithSpecificComponentEnabled(
const XrSpaceComponentTypeFB componentType) {
501 XrSpaceStorageLocationFilterInfoFB storageLocationFilterInfo = {
502 XR_TYPE_SPACE_STORAGE_LOCATION_FILTER_INFO_FB,
504 XR_SPACE_STORAGE_LOCATION_LOCAL_FB
507 XrSpaceComponentFilterInfoFB componentFilterInfo = {
508 XR_TYPE_SPACE_COMPONENT_FILTER_INFO_FB,
509 &storageLocationFilterInfo,
513 XrSpaceQueryInfoFB queryInfo = {
514 XR_TYPE_SPACE_QUERY_INFO_FB,
516 XR_SPACE_QUERY_ACTION_LOAD_FB,
519 (XrSpaceFilterInfoBaseHeaderFB*)&componentFilterInfo,
523 XrAsyncRequestIdFB requestId;
524 if (!checkXrResult(querySpaces((XrSpaceQueryInfoBaseHeaderFB*)&queryInfo, &requestId))) {
525 qWarning(
"Failed to query spaces");
532 if (uuidSet.isEmpty())
535 QVector<XrUuidEXT> uuidsToQuery;
537 for (
const auto &uuid : uuidSet) {
538 XrUuidEXT xrUuid = fromQUuid(uuid);
539 uuidsToQuery.append(xrUuid);
542 XrSpaceStorageLocationFilterInfoFB storageLocationFilterInfo = {
543 XR_TYPE_SPACE_STORAGE_LOCATION_FILTER_INFO_FB,
545 XR_SPACE_STORAGE_LOCATION_LOCAL_FB
548 XrSpaceUuidFilterInfoFB uuidFilterInfo = {
549 XR_TYPE_SPACE_UUID_FILTER_INFO_FB,
550 &storageLocationFilterInfo,
551 (uint32_t)uuidsToQuery.size(),
555 XrSpaceQueryInfoFB queryInfo = {
556 XR_TYPE_SPACE_QUERY_INFO_FB,
558 XR_SPACE_QUERY_ACTION_LOAD_FB,
561 (XrSpaceFilterInfoBaseHeaderFB*)&uuidFilterInfo,
565 XrAsyncRequestIdFB requestId;
566 if (!checkXrResult(querySpaces((XrSpaceQueryInfoBaseHeaderFB*)&queryInfo, &requestId))) {
567 qWarning(
"Failed to query spaces");
575 auto quuid = fromXrUuidExt(uuid);
577 if (m_anchorsByUuid.contains(quuid))
582 m_anchorsByUuid.insert(quuid, anchor);
583 m_anchors.append(anchor);
584 Q_EMIT anchorAdded(anchor);
588XrResult
QQuick3DXrAnchorManager::enumerateSpaceSupportedComponents(XrSpace space, uint32_t componentTypeCapacityInput, uint32_t *componentTypeCountOutput, XrSpaceComponentTypeFB *componentTypes)
590 return OpenXRHelpers::safeCall(xrEnumerateSpaceSupportedComponentsFB, space, componentTypeCapacityInput, componentTypeCountOutput, componentTypes);
593XrResult
QQuick3DXrAnchorManager::getSpaceComponentStatus(XrSpace space, XrSpaceComponentTypeFB componentType, XrSpaceComponentStatusFB *status)
595 return OpenXRHelpers::safeCall(xrGetSpaceComponentStatusFB, space, componentType, status);
598XrResult
QQuick3DXrAnchorManager::setSpaceComponentStatus(XrSpace space,
const XrSpaceComponentStatusSetInfoFB *info, XrAsyncRequestIdFB *requestId)
600 return OpenXRHelpers::safeCall(xrSetSpaceComponentStatusFB, space, info, requestId);
605 return OpenXRHelpers::safeCall(xrGetSpaceUuidFB, space, uuid);
608XrResult
QQuick3DXrAnchorManager::querySpaces(
const XrSpaceQueryInfoBaseHeaderFB *info, XrAsyncRequestIdFB *requestId)
610 return OpenXRHelpers::safeCall(xrQuerySpacesFB, m_session, info, requestId);
613XrResult
QQuick3DXrAnchorManager::retrieveSpaceQueryResults(XrAsyncRequestIdFB requestId, XrSpaceQueryResultsFB *results)
615 return OpenXRHelpers::safeCall(xrRetrieveSpaceQueryResultsFB, m_session, requestId, results);
620 return OpenXRHelpers::safeCall(xrGetSpaceBoundingBox2DFB, m_session, space, boundingBox2DOutput);
625 return OpenXRHelpers::safeCall(xrGetSpaceBoundingBox3DFB, m_session, space, boundingBox3DOutput);
628XrResult
QQuick3DXrAnchorManager::getSpaceSemanticLabels(XrSpace space, XrSemanticLabelsFB *semanticLabelsOutput)
630 return OpenXRHelpers::safeCall(xrGetSpaceSemanticLabelsFB, m_session, space, semanticLabelsOutput);
635 return OpenXRHelpers::safeCall(xrGetSpaceBoundary2DFB, m_session, space, boundary2DOutput);
640 return OpenXRHelpers::safeCall(xrGetSpaceRoomLayoutFB, m_session, space, roomLayoutOutput);
645 return OpenXRHelpers::safeCall(xrGetSpaceContainerFB, m_session, space, spaceContainerOutput);
648XrResult
QQuick3DXrAnchorManager::requestSceneCapture(
const XrSceneCaptureRequestInfoFB *info, XrAsyncRequestIdFB *requestId)
650 return OpenXRHelpers::safeCall(xrRequestSceneCaptureFB, m_session, info, requestId);
QList< const char * > requiredExtensions() const
bool getBoundingBox2D(XrSpace space, QVector2D &offset, QVector2D &extent)
const QList< QQuick3DXrSpatialAnchor * > & anchors() const
bool isComponentEnabled(XrSpace space, XrSpaceComponentTypeFB type)
bool getBoundingBox3D(XrSpace space, QVector3D &offset, QVector3D &extent)
void updateAnchors(XrTime predictedDisplayTime, XrSpace appSpace)
void handleEvent(const XrEventDataBaseHeader *event)
bool isComponentSupported(XrSpace space, XrSpaceComponentTypeFB type)
qsizetype anchorCount() const
bool setupSpatialAnchor(XrSpace space, QQuick3DXrSpatialAnchor &anchor)
void initialize(XrInstance instance, XrSession session)
QSet< QUuid > collectRoomLayoutUuids(XrSpace space)
QString getSemanticLabels(const XrSpace space)
void requestSceneCapture()
QSet< QUuid > collectSpaceContainerUuids(XrSpace space)
bool checkXrResult(XrResult result, XrInstance instance)
bool resolveXrFunction(XrInstance instance, const char *name, PFN_xrVoidFunction *function)
Combined button and popup list for selecting options.
static const uint32_t MAX_PERSISTENT_SPACES
static const char qssgXrRecognizedLabels[]
static QQuick3DXrSpatialAnchor::Classification getLabelForString(const QString &label)