5#include "openxr/qopenxrhelpers_p.h"
8#include <QtQuick3DUtils/private/qssgassert_p.h>
10#include <QtGui/qquaternion.h>
12#include <QLoggingCategory>
14#if defined(Q_OS_ANDROID)
15# include <QtCore/private/qandroidextras_p.h>
20Q_DECLARE_LOGGING_CATEGORY(lcQuick3DXr);
26static const char qssgXrRecognizedLabels[] =
"TABLE,COUCH,FLOOR,CEILING,WALL_FACE,WINDOW_FRAME,DOOR_FRAME,STORAGE,BED,SCREEN,LAMP,PLANT,WALL_ART,OTHER";
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;
45 return QQuick3DXrSpatialAnchor::Classification::Other;
67#if defined(Q_OS_ANDROID)
68 auto res = QtAndroidPrivate::requestPermission(QLatin1StringView(
"com.oculus.permission.USE_SCENE"));
69 res.waitForFinished();
72 m_instance = instance;
78 "xrEnumerateSpaceSupportedComponentsFB",
79 (PFN_xrVoidFunction*)(&xrEnumerateSpaceSupportedComponentsFB));
82 "xrGetSpaceComponentStatusFB",
83 (PFN_xrVoidFunction*)(&xrGetSpaceComponentStatusFB));
86 "xrSetSpaceComponentStatusFB",
87 (PFN_xrVoidFunction*)(&xrSetSpaceComponentStatusFB));
91 (PFN_xrVoidFunction*)(&xrGetSpaceUuidFB));
95 (PFN_xrVoidFunction*)(&xrQuerySpacesFB));
98 "xrRetrieveSpaceQueryResultsFB",
99 (PFN_xrVoidFunction*)(&xrRetrieveSpaceQueryResultsFB));
102 "xrGetSpaceBoundingBox2DFB",
103 (PFN_xrVoidFunction*)(&xrGetSpaceBoundingBox2DFB));
106 "xrGetSpaceBoundingBox3DFB",
107 (PFN_xrVoidFunction*)(&xrGetSpaceBoundingBox3DFB));
110 "xrGetSpaceSemanticLabelsFB",
111 (PFN_xrVoidFunction*)(&xrGetSpaceSemanticLabelsFB));
114 "xrGetSpaceBoundary2DFB",
115 (PFN_xrVoidFunction*)(&xrGetSpaceBoundary2DFB));
118 "xrGetSpaceRoomLayoutFB",
119 (PFN_xrVoidFunction*)(&xrGetSpaceRoomLayoutFB));
122 "xrGetSpaceContainerFB",
123 (PFN_xrVoidFunction*)(&xrGetSpaceContainerFB));
126 "xrRequestSceneCaptureFB",
127 (PFN_xrVoidFunction*)(&xrRequestSceneCaptureFB));
139 XR_FB_SPATIAL_ENTITY_EXTENSION_NAME,
140 XR_FB_SPATIAL_ENTITY_QUERY_EXTENSION_NAME,
141 XR_FB_SPATIAL_ENTITY_STORAGE_EXTENSION_NAME,
142 XR_FB_SPATIAL_ENTITY_CONTAINER_EXTENSION_NAME,
143 XR_FB_SCENE_EXTENSION_NAME,
145 XR_FB_SCENE_CAPTURE_EXTENSION_NAME
152 if (event->type == XR_TYPE_EVENT_DATA_SPACE_SET_STATUS_COMPLETE_FB) {
153 qCDebug(lcQuick3DXr,
"QQuick3DXrAnchorManager::handleEvent: received XR_TYPE_EVENT_DATA_SPACE_SET_STATUS_COMPLETE_FB");
154 const XrEventDataSpaceSetStatusCompleteFB* setStatusComplete = (
const XrEventDataSpaceSetStatusCompleteFB*)(event);
155 if (setStatusComplete->result == XR_SUCCESS) {
156 if (setStatusComplete->componentType == XR_SPACE_COMPONENT_TYPE_LOCATABLE_FB) {
157 addAnchor(setStatusComplete->space, setStatusComplete->uuid);
160 }
else if (event->type == XR_TYPE_EVENT_DATA_SCENE_CAPTURE_COMPLETE_FB) {
161 qCDebug(lcQuick3DXr,
"QQuick3DXrAnchorManager::handleEvent: received XR_TYPE_EVENT_DATA_SCENE_CAPTURE_COMPLETE_FB");
162 const XrEventDataSceneCaptureCompleteFB* captureResult = (
const XrEventDataSceneCaptureCompleteFB*)(event);
163 if (captureResult->result == XR_SUCCESS) {
164 Q_EMIT sceneCaptureCompleted();
166 "QQuick3DXrAnchorManager::handleEvent: Scene capture (ID = %llu) succeeded",
167 static_cast<
long long unsigned int>(captureResult->requestId));
170 "QQuick3DXrAnchorManager::handleEvent: Scene capture (ID = %llu) failed with an error %d",
171 static_cast<
long long unsigned int>(captureResult->requestId),
172 captureResult->result);
175 }
else if (event->type == XR_TYPE_EVENT_DATA_SPACE_QUERY_RESULTS_AVAILABLE_FB) {
176 qCDebug(lcQuick3DXr,
"QQuick3DXrAnchorManager::handleEvent: received XR_TYPE_EVENT_DATA_SPACE_QUERY_RESULTS_AVAILABLE_FB");
177 const XrEventDataSpaceQueryResultsAvailableFB* resultsAvailable = (
const XrEventDataSpaceQueryResultsAvailableFB*)(event);
179 XrSpaceQueryResultsFB queryResults{};
180 queryResults.type = XR_TYPE_SPACE_QUERY_RESULTS_FB;
181 queryResults.resultCapacityInput = 0;
182 queryResults.resultCountOutput = 0;
183 queryResults.results =
nullptr;
185 if (!checkXrResult(retrieveSpaceQueryResults(resultsAvailable->requestId, &queryResults))) {
186 qWarning(
"Failed to retrieve space query results");
190 QVector<XrSpaceQueryResultFB> results(queryResults.resultCountOutput);
191 queryResults.resultCapacityInput = results.size();
192 queryResults.resultCountOutput = 0;
193 queryResults.results = results.data();
195 if (!checkXrResult(retrieveSpaceQueryResults(resultsAvailable->requestId, &queryResults))) {
196 qWarning(
"Failed to retrieve space query results");
200 qCDebug(lcQuick3DXr,
"retrieveSpaceQueryResults: num of results received: %d", queryResults.resultCountOutput);
201 for (
const auto &result : results) {
202 if (isComponentSupported(result.space, XR_SPACE_COMPONENT_TYPE_LOCATABLE_FB)) {
203 XrSpaceComponentStatusSetInfoFB request = {
204 XR_TYPE_SPACE_COMPONENT_STATUS_SET_INFO_FB,
206 XR_SPACE_COMPONENT_TYPE_LOCATABLE_FB,
210 XrAsyncRequestIdFB requestId;
211 XrResult res = setSpaceComponentStatus(result.space, &request, &requestId);
212 if (res == XR_ERROR_SPACE_COMPONENT_STATUS_ALREADY_SET_FB) {
213 addAnchor(result.space, result.uuid);
217 }
else if (event->type == XR_TYPE_EVENT_DATA_SPACE_QUERY_COMPLETE_FB) {
218 qCDebug(lcQuick3DXr,
"QQuick3DXrAnchorManager::handleEvent: received XR_TYPE_EVENT_DATA_SPACE_QUERY_COMPLETE_FB");
224 XrAsyncRequestIdFB requestId;
225 XrSceneCaptureRequestInfoFB request{};
226 request.type = XR_TYPE_SCENE_CAPTURE_REQUEST_INFO_FB;
227 request.requestByteCount = 0;
228 request.request =
nullptr;
229 if (!checkXrResult(requestSceneCapture(&request, &requestId)))
230 qWarning(
"Failed to request scene capture");
235 uint32_t numComponents = 0;
236 if (!checkXrResult(enumerateSpaceSupportedComponents(space, 0, &numComponents,
nullptr))) {
237 qWarning(
"Failed to enumerate supported space components");
241 QVector<XrSpaceComponentTypeFB> components(numComponents);
242 if (!checkXrResult(enumerateSpaceSupportedComponents(space, numComponents, &numComponents, components.data()))) {
243 qWarning(
"Failed to enumerate supported space components");
247 bool supported =
false;
248 for (
const auto &component : components) {
249 if (component == type) {
260 XrSpaceComponentStatusFB status = {XR_TYPE_SPACE_COMPONENT_STATUS_FB,
nullptr, 0, 0};
261 if (!checkXrResult(getSpaceComponentStatus(space, type, &status))) {
262 qWarning(
"Failed to get space component status");
265 return (status.enabled && !status.changePending);
272 XrRect2Df boundingBox2D;
273 if (!checkXrResult(getSpaceBoundingBox2D(space, &boundingBox2D))) {
274 qWarning(
"Failed to get bounding box 2D for space");
277 offset = QVector2D(boundingBox2D.offset.x, boundingBox2D.offset.y);
278 extent = QVector2D(boundingBox2D.extent.width, boundingBox2D.extent.height);
288 XrRect3DfFB boundingBox3D;
289 if (!checkXrResult(getSpaceBoundingBox3D(space, &boundingBox3D))) {
290 qWarning(
"Failed to get bounding box 3D for space");
293 offset = QVector3D(boundingBox3D.offset.x, boundingBox3D.offset.y, boundingBox3D.offset.z);
294 extent = QVector3D(boundingBox3D.extent.width, boundingBox3D.extent.height, boundingBox3D.extent.depth);
309 QSSG_ASSERT(space != XR_NULL_HANDLE,
return false);
311 const bool m_has2DBounds = getBoundingBox2D(space, offset2D, extent2D);
312 const bool m_has3DBounds = getBoundingBox3D(space, offset3D, extent3D);
316 anchor.setSpaceContainerUuids(collectSpaceContainerUuids(space));
319 anchor.setRoomLayoutUuids(collectRoomLayoutUuids(space));
323 anchor.setBounds2D(offset2D, extent2D);
325 anchor.setBounds3D(offset3D, extent3D);
327 auto stringLabel = getSemanticLabels(space);
328 auto semanticLable = getLabelForString(stringLabel);
330 anchor.setClassification(semanticLable);
331 anchor.setClassificationString(stringLabel);
337bool isValidUuid(
const XrUuidEXT& uuid) {
341 return !QtQuick3DXr::isNullUuid(uuid.data);
344QUuid fromXrUuidExt(XrUuidEXT uuid) {
345 return QUuid::fromBytes(uuid.data);
348XrUuidEXT fromQUuid(QUuid uuid) {
350 auto bytes = uuid.toBytes();
351 memcpy(xrUuid.data, bytes.data, XR_UUID_SIZE_EXT);
359 XrRoomLayoutFB roomLayout{};
360 roomLayout.type = XR_TYPE_ROOM_LAYOUT_FB;
361 QVector<XrUuidEXT> wallUuids;
365 if (!checkXrResult(getSpaceRoomLayout(space, &roomLayout))) {
366 qWarning(
"Failed to get room layout");
372 if (roomLayout.wallUuidCountOutput != 0) {
374 wallUuids.resize(roomLayout.wallUuidCountOutput);
375 roomLayout.wallUuidCapacityInput = wallUuids.size();
376 roomLayout.wallUuids = wallUuids.data();
377 if (!checkXrResult(getSpaceRoomLayout(space, &roomLayout))) {
378 qWarning(
"Failed to get room layout");
382 if (isValidUuid(roomLayout.floorUuid))
383 uuidSet.insert(fromXrUuidExt(roomLayout.floorUuid));
384 if (isValidUuid(roomLayout.ceilingUuid))
385 uuidSet.insert(fromXrUuidExt(roomLayout.ceilingUuid));
386 for (uint32_t i = 0; i < roomLayout.wallUuidCountOutput; i++)
387 uuidSet.insert(fromXrUuidExt(roomLayout.wallUuids[i]));
393 XrSpaceContainerFB spaceContainer{};
394 spaceContainer.type = XR_TYPE_SPACE_CONTAINER_FB;
397 if (!checkXrResult(getSpaceContainer(space, &spaceContainer))) {
398 qWarning(
"Failed to get container");
401 if (spaceContainer.uuidCountOutput != 0) {
403 QVector<XrUuidEXT> uuids(spaceContainer.uuidCountOutput);
404 spaceContainer.uuidCapacityInput = uuids.size();
405 spaceContainer.uuids = uuids.data();
406 if (!checkXrResult(getSpaceContainer(space, &spaceContainer))) {
407 qWarning(
"Failed to get container");
411 for (uint32_t i = 0; i < spaceContainer.uuidCountOutput; i++)
412 uuidSet.insert(fromXrUuidExt(spaceContainer.uuids[i]));
419 const XrSemanticLabelsSupportInfoFB semanticLabelsSupportInfo = {
420 XR_TYPE_SEMANTIC_LABELS_SUPPORT_INFO_FB,
422 XR_SEMANTIC_LABELS_SUPPORT_MULTIPLE_SEMANTIC_LABELS_BIT_FB | XR_SEMANTIC_LABELS_SUPPORT_ACCEPT_DESK_TO_TABLE_MIGRATION_BIT_FB,
426 XrSemanticLabelsFB labels{};
427 labels.type = XR_TYPE_SEMANTIC_LABELS_FB;
428 labels.next = &semanticLabelsSupportInfo;
434 if (!checkXrResult(getSpaceSemanticLabels(space, &labels))) {
435 qWarning(
"Failed to get semantic labels");
439 QByteArray labelData(labels.bufferCountOutput, Qt::Uninitialized);
440 labels.bufferCapacityInput = labelData.size();
441 labels.buffer = labelData.data();
442 if (!checkXrResult(getSpaceSemanticLabels(space, &labels))) {
443 qWarning(
"Failed to get semantic labels");
447 return QString::fromLocal8Bit(labelData);
451 XrSpaceQueryInfoFB queryInfo = {
452 XR_TYPE_SPACE_QUERY_INFO_FB,
454 XR_SPACE_QUERY_ACTION_LOAD_FB,
460 XrAsyncRequestIdFB requestId;
461 return checkXrResult(querySpaces((XrSpaceQueryInfoBaseHeaderFB*)&queryInfo, &requestId));
471 return m_anchors.count();
477 for (
auto &anchor : m_anchors) {
478 XrSpaceLocation spaceLocation{};
479 spaceLocation.type = XR_TYPE_SPACE_LOCATION;
480 XrResult res = xrLocateSpace(QtQuick3DXr::fromXrSpaceId<XrSpace>(anchor->space()), appSpace, predictedDisplayTime, &spaceLocation);
481 if (XR_UNQUALIFIED_SUCCESS(res)) {
482 if ((spaceLocation.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) != 0 &&
483 (spaceLocation.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) != 0) {
486 anchor->setPosition(QVector3D(spaceLocation.pose.position.x,
487 spaceLocation.pose.position.y,
488 spaceLocation.pose.position.z) * 100.0f);
489 anchor->setRotation(QQuaternion(spaceLocation.pose.orientation.w,
490 spaceLocation.pose.orientation.x,
491 spaceLocation.pose.orientation.y,
492 spaceLocation.pose.orientation.z));
498bool QQuick3DXrAnchorManager::queryAllAnchorsWithSpecificComponentEnabled(
const XrSpaceComponentTypeFB componentType) {
499 XrSpaceStorageLocationFilterInfoFB storageLocationFilterInfo = {
500 XR_TYPE_SPACE_STORAGE_LOCATION_FILTER_INFO_FB,
502 XR_SPACE_STORAGE_LOCATION_LOCAL_FB
505 XrSpaceComponentFilterInfoFB componentFilterInfo = {
506 XR_TYPE_SPACE_COMPONENT_FILTER_INFO_FB,
507 &storageLocationFilterInfo,
511 XrSpaceQueryInfoFB queryInfo = {
512 XR_TYPE_SPACE_QUERY_INFO_FB,
514 XR_SPACE_QUERY_ACTION_LOAD_FB,
517 (XrSpaceFilterInfoBaseHeaderFB*)&componentFilterInfo,
521 XrAsyncRequestIdFB requestId;
522 if (!checkXrResult(querySpaces((XrSpaceQueryInfoBaseHeaderFB*)&queryInfo, &requestId))) {
523 qWarning(
"Failed to query spaces");
530 if (uuidSet.isEmpty())
533 QVector<XrUuidEXT> uuidsToQuery;
535 for (
const auto &uuid : uuidSet) {
536 XrUuidEXT xrUuid = fromQUuid(uuid);
537 uuidsToQuery.append(xrUuid);
540 XrSpaceStorageLocationFilterInfoFB storageLocationFilterInfo = {
541 XR_TYPE_SPACE_STORAGE_LOCATION_FILTER_INFO_FB,
543 XR_SPACE_STORAGE_LOCATION_LOCAL_FB
546 XrSpaceUuidFilterInfoFB uuidFilterInfo = {
547 XR_TYPE_SPACE_UUID_FILTER_INFO_FB,
548 &storageLocationFilterInfo,
549 (uint32_t)uuidsToQuery.size(),
553 XrSpaceQueryInfoFB queryInfo = {
554 XR_TYPE_SPACE_QUERY_INFO_FB,
556 XR_SPACE_QUERY_ACTION_LOAD_FB,
559 (XrSpaceFilterInfoBaseHeaderFB*)&uuidFilterInfo,
563 XrAsyncRequestIdFB requestId;
564 if (!checkXrResult(querySpaces((XrSpaceQueryInfoBaseHeaderFB*)&queryInfo, &requestId))) {
565 qWarning(
"Failed to query spaces");
573 auto quuid = fromXrUuidExt(uuid);
575 if (m_anchorsByUuid.contains(quuid))
580 m_anchorsByUuid.insert(quuid, anchor);
581 m_anchors.append(anchor);
582 Q_EMIT anchorAdded(anchor);
586XrResult
QQuick3DXrAnchorManager::enumerateSpaceSupportedComponents(XrSpace space, uint32_t componentTypeCapacityInput, uint32_t *componentTypeCountOutput, XrSpaceComponentTypeFB *componentTypes)
588 return OpenXRHelpers::safeCall(xrEnumerateSpaceSupportedComponentsFB, space, componentTypeCapacityInput, componentTypeCountOutput, componentTypes);
591XrResult
QQuick3DXrAnchorManager::getSpaceComponentStatus(XrSpace space, XrSpaceComponentTypeFB componentType, XrSpaceComponentStatusFB *status)
593 return OpenXRHelpers::safeCall(xrGetSpaceComponentStatusFB, space, componentType, status);
596XrResult
QQuick3DXrAnchorManager::setSpaceComponentStatus(XrSpace space,
const XrSpaceComponentStatusSetInfoFB *info, XrAsyncRequestIdFB *requestId)
598 return OpenXRHelpers::safeCall(xrSetSpaceComponentStatusFB, space, info, requestId);
603 return OpenXRHelpers::safeCall(xrGetSpaceUuidFB, space, uuid);
606XrResult
QQuick3DXrAnchorManager::querySpaces(
const XrSpaceQueryInfoBaseHeaderFB *info, XrAsyncRequestIdFB *requestId)
608 return OpenXRHelpers::safeCall(xrQuerySpacesFB, m_session, info, requestId);
611XrResult
QQuick3DXrAnchorManager::retrieveSpaceQueryResults(XrAsyncRequestIdFB requestId, XrSpaceQueryResultsFB *results)
613 return OpenXRHelpers::safeCall(xrRetrieveSpaceQueryResultsFB, m_session, requestId, results);
618 return OpenXRHelpers::safeCall(xrGetSpaceBoundingBox2DFB, m_session, space, boundingBox2DOutput);
623 return OpenXRHelpers::safeCall(xrGetSpaceBoundingBox3DFB, m_session, space, boundingBox3DOutput);
626XrResult
QQuick3DXrAnchorManager::getSpaceSemanticLabels(XrSpace space, XrSemanticLabelsFB *semanticLabelsOutput)
628 return OpenXRHelpers::safeCall(xrGetSpaceSemanticLabelsFB, m_session, space, semanticLabelsOutput);
633 return OpenXRHelpers::safeCall(xrGetSpaceBoundary2DFB, m_session, space, boundary2DOutput);
638 return OpenXRHelpers::safeCall(xrGetSpaceRoomLayoutFB, m_session, space, roomLayoutOutput);
643 return OpenXRHelpers::safeCall(xrGetSpaceContainerFB, m_session, space, spaceContainerOutput);
646XrResult
QQuick3DXrAnchorManager::requestSceneCapture(
const XrSceneCaptureRequestInfoFB *info, XrAsyncRequestIdFB *requestId)
648 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)
static const uint32_t MAX_PERSISTENT_SPACES
static const char qssgXrRecognizedLabels[]
static QQuick3DXrSpatialAnchor::Classification getLabelForString(const QString &label)