Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qquick3dxranchormanager_visionos.mm
Go to the documentation of this file.
1// Copyright (C) 2024 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
5
6#include "../qquick3dxrspatialanchor_p.h"
7#include "visionos/qquick3dxrinputmanager_visionos_p.h"
8
9#include <QtQuick3DUtils/private/qssgassert_p.h>
10#include <QtQuick3DUtils/private/qssgutils_p.h>
11
12
14
15Q_DECLARE_LOGGING_CATEGORY(lcQuick3DXr);
16
18{
19 static QQuick3DXrAnchorManager instance;
20 return &instance;
21}
22
23QQuick3DXrAnchorManager::QQuick3DXrAnchorManager(QObject *parent)
24 : QObject(parent)
25{
26}
27
28QQuick3DXrAnchorManager::~QQuick3DXrAnchorManager()
29{
30
31}
32
34{
35
36}
37
39{
40 anchor->moveToThread(qApp->thread());
41
42 m_anchorsByUuid.insert(anchor->uuid(), anchor);
43
44 Q_EMIT anchorAdded(anchor);
45}
46
48{
49 if (QQuick3DXrSpatialAnchor *anchor = m_anchorsByUuid.take(uuid))
50 anchor->deleteLater();
51 else
52 qWarning() << "Anchor not found for removal: " << uuid;
53
54 Q_EMIT anchorRemoved(uuid);
55}
56
58{
59 anchor->moveToThread(qApp->thread());
60
61 Q_EMIT anchorUpdated(anchor);
62}
63
65{
66 m_anchors = m_anchorsByUuid.values();
67}
68
82
84
92 {ar_plane_classification_status_unknown, AnchorClassifcation::Unknown, QQuick3DXrSpatialAnchor::Classification::Unknown, "Unknown"},
93 {ar_plane_classification_status_not_available, AnchorClassifcation::NotAvailable, QQuick3DXrSpatialAnchor::Classification::Unknown, "Not Available"},
94 {ar_plane_classification_status_undetermined, AnchorClassifcation::Undetermined, QQuick3DXrSpatialAnchor::Classification::Unknown, "Undetermined"},
95 {ar_plane_classification_wall, AnchorClassifcation::Wall, QQuick3DXrSpatialAnchor::Classification::Wall, "Wall"},
96 {ar_plane_classification_ceiling, AnchorClassifcation::Ceiling, QQuick3DXrSpatialAnchor::Classification::Ceiling, "Ceiling"},
97 {ar_plane_classification_floor, AnchorClassifcation::Floor, QQuick3DXrSpatialAnchor::Classification::Floor, "Floor"},
98 {ar_plane_classification_table, AnchorClassifcation::Table, QQuick3DXrSpatialAnchor::Classification::Table, "Table"},
99 {ar_plane_classification_seat, AnchorClassifcation::Seat, QQuick3DXrSpatialAnchor::Classification::Seat, "Seat"},
100 {ar_plane_classification_window, AnchorClassifcation::Window, QQuick3DXrSpatialAnchor::Classification::Window, "Window"},
101 {ar_plane_classification_door, AnchorClassifcation::Door, QQuick3DXrSpatialAnchor::Classification::Door, "Door"},
102};
103
104static const AnchorClassificationMap &getAnchorClassificationName(ar_plane_classification_t classification, bool *identified = nullptr)
105{
106 size_t foundIndex = 0;
107 for (size_t i = 0, end = std::size(anchorClassificationMap); i != end; ++i) {
108 if (anchorClassificationMap[i].classification == classification) {
109 foundIndex = i;
110 break;
111 }
112 }
113
114 if (identified) // If the caller wants to know if the classification was found (I.e. not Unknown, Not Available, or Undetermined)
115 *identified = (foundIndex >= anchorClassificationStart);
116
117 return anchorClassificationMap[foundIndex];
118}
119
120static void updateAnchorProperties(QQuick3DXrSpatialAnchor &anchor, ar_plane_anchor_t planeAnchor)
121{
122 static const QQuaternion s_rot90X = QQuaternion::fromEulerAngles({-90.0f, 0.0f, 0.0f});
123
124 simd_float4x4 originFromAnchorTransform = ar_anchor_get_origin_from_anchor_transform(planeAnchor);
125 QMatrix4x4 transform{originFromAnchorTransform.columns[0].x, originFromAnchorTransform.columns[1].x, originFromAnchorTransform.columns[2].x, originFromAnchorTransform.columns[3].x,
126 originFromAnchorTransform.columns[0].y, originFromAnchorTransform.columns[1].y, originFromAnchorTransform.columns[2].y, originFromAnchorTransform.columns[3].y,
127 originFromAnchorTransform.columns[0].z, originFromAnchorTransform.columns[1].z, originFromAnchorTransform.columns[2].z, originFromAnchorTransform.columns[3].z,
128 0.0f, 0.0f, 0.0f, 1.0f};
129
130 ar_plane_classification_t classification = ar_plane_anchor_get_plane_classification(planeAnchor);
131 const auto &classificationEntry = getAnchorClassificationName(classification);
132
133 if (anchor.classification() != classificationEntry.label) {
134 anchor.setClassification(classificationEntry.label);
135 anchor.setClassificationString(QString::fromLatin1(classificationEntry.classificationName));
136 }
137
138 ar_plane_geometry_t planeGeometry = ar_plane_anchor_get_geometry(planeAnchor);
139 ar_plane_extent_t planeExtent = ar_plane_geometry_get_plane_extent(planeGeometry);
140
141 const float width = ar_plane_extent_get_width(planeExtent);
142 const float height = ar_plane_extent_get_height(planeExtent);
143
144 // position and rotation
145 QVector3D pos;
146 QVector3D scale;
147 QQuaternion rot;
148 QSSGUtils::mat44::decompose(transform, pos, scale, rot);
149
150 // The y-axis of the plane anchor is the plane’s normal vector.
151 rot = rot * s_rot90X;
152
153 anchor.setPosition(pos * 100.0f);
154 anchor.setRotation(rot);
155
156 QVector2D offset2D{};
157 QVector2D extent2D(width, height);
158 anchor.setBounds2D(offset2D, extent2D);
159}
160
161void QQuick3DXrAnchorManager::planeUpdateHandler(void *context, ar_plane_anchors_t added_anchors, ar_plane_anchors_t updated_anchors, ar_plane_anchors_t removed_anchors)
162{
163 QSSG_ASSERT_X(context != nullptr, "Context is null!", return);
164
165 QQuick3DXrAnchorManager *that = reinterpret_cast<QQuick3DXrAnchorManager *>(context);
166 // Lock for writes to the anchors list (Might as well use a simple mutex, the data is only modified by this handler).
167 QWriteLocker locker(&that->m_anchorsLock);
168
169 // 1. Remove
170 if (removed_anchors) {
171 ar_plane_anchors_enumerate_anchors_f(removed_anchors, context, [](void *context, ar_plane_anchor_t planeAnchor) -> bool {
172 QSSG_ASSERT_X(QSSG_DEBUG_COND(context != nullptr), "Unexpected context!", return false);
173
174 QQuick3DXrAnchorManager *that = reinterpret_cast<QQuick3DXrAnchorManager *>(context);
175
176 unsigned char identifier[16] {};
177 ar_anchor_get_identifier(planeAnchor, identifier);
178
179 if (Q_UNLIKELY(QtQuick3DXr::isNullUuid(identifier))) {
180 qWarning() << "Invalid UUID for anchor";
181 return false;
182 }
183
184 QUuid uuid = QUuid::fromRfc4122(QByteArrayView(reinterpret_cast<const char *>(identifier), 16));
185 that->removeAnchor(uuid);
186
187 return true;
188 });
189 }
190
191 // 2. Add
192 if (added_anchors) {
193 ar_plane_anchors_enumerate_anchors_f(added_anchors, context, [](void *context, ar_plane_anchor_t planeAnchor) -> bool {
194 QSSG_ASSERT_X(QSSG_DEBUG_COND(context != nullptr), "Unexpected context!", return false);
195
196 QQuick3DXrAnchorManager *that = reinterpret_cast<QQuick3DXrAnchorManager *>(context);
197
198 // NOTE: The API documentation for this is severely lacking, but the swift docs have some more information
199 // saying the identifier follows RFC 4122 for UUIDs, meaning the identifier is a 128-bit value (Big Endian)
200 unsigned char identifier[16] {};
201 ar_anchor_get_identifier(planeAnchor, identifier);
202
203 if (Q_UNLIKELY(QtQuick3DXr::isNullUuid(identifier))) {
204 qWarning() << "Invalid UUID for anchor";
205 return false;
206 }
207
208 QUuid uuid = QUuid::fromRfc4122(QByteArrayView(reinterpret_cast<const char *>(identifier), 16));
209 const QtQuick3DXr::XrSpaceId space = that->getCurrentSpaceId();
210 QQuick3DXrSpatialAnchor *anchor = new QQuick3DXrSpatialAnchor(space, uuid);
211 updateAnchorProperties(*anchor, planeAnchor);
212 that->addAnchor(anchor);
213
214 return true;
215 });
216 }
217
218 // 3. Update
219 if (updated_anchors) {
220 ar_plane_anchors_enumerate_anchors_f(updated_anchors, context, [](void *context, ar_plane_anchor_t planeAnchor) -> bool {
221 QSSG_ASSERT_X(QSSG_DEBUG_COND(context != nullptr), "Unexpected context!", return false);
222
223 QQuick3DXrAnchorManager *that = reinterpret_cast<QQuick3DXrAnchorManager *>(context);
224
225 unsigned char identifier[16] {};
226 ar_anchor_get_identifier(planeAnchor, identifier);
227
228 if (Q_UNLIKELY(QtQuick3DXr::isNullUuid(identifier))) {
229 qWarning() << "Invalid UUID for anchor";
230 return false;
231 }
232
233 QUuid uuid = QUuid::fromRfc4122(QByteArrayView(reinterpret_cast<const char *>(identifier), 16));
234
235 QQuick3DXrSpatialAnchor *anchor = that->m_anchorsByUuid.value(uuid);
236 // This does occur in some cases, so we need to create a new anchor if it's not found
237 // NOTE: Ideally this should not happen, but it does, reason unknown atm.
238 if (!anchor) {
239 anchor = new QQuick3DXrSpatialAnchor(that->getCurrentSpaceId(), uuid);
240 updateAnchorProperties(*anchor, planeAnchor);
241 that->addAnchor(anchor);
242 } else {
243 updateAnchorProperties(*anchor, planeAnchor);
244 that->updateAnchor(anchor);
245 }
246
247 return true;
248 });
249 }
250
252}
253
254void QQuick3DXrAnchorManager::prepareAnchorManager(ar_data_providers_t dataProviders)
255{
256 QSSG_ASSERT_X(!m_isInitialized, "Anchor manager already initialized", return);
257
258 m_isPlaneDetectionSupported = (m_requestedAnchorType == AnchorType::Plane) && ar_plane_detection_provider_is_supported();
259 if (m_isPlaneDetectionSupported) {
260 ar_plane_detection_configuration_t planeDetectionConfiguration = ar_plane_detection_configuration_create();
261 m_planeDetectionProvider = ar_plane_detection_provider_create(planeDetectionConfiguration);
262 ar_plane_detection_provider_set_update_handler_f(m_planeDetectionProvider, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), this, &planeUpdateHandler);
263 ar_data_providers_add_data_provider(dataProviders, m_planeDetectionProvider);
264 } else {
265 qCWarning(lcQuick3DXr, "Plane detection is not supported on this device.");
266 }
267}
268
269void QQuick3DXrAnchorManager::initAnchorManager()
270{
271 QSSG_ASSERT_X(!m_isInitialized, "Anchor manager already initialized", return);
272
273 m_isInitialized = m_isPlaneDetectionSupported;
274}
275
277{
278 // Scene is continuously captured in the background,
279 // no need to request it explicitly.
280}
281
283{
284 return true;
285}
286
288{
289 QReadLocker locker(&m_anchorsLock);
290 return m_anchors;
291}
292
294{
295 QReadLocker locker(&m_anchorsLock);
296 return m_anchors.size();
297}
298
299QT_END_NAMESPACE
void updateAnchor(QQuick3DXrSpatialAnchor *anchor)
const QList< QQuick3DXrSpatialAnchor * > & anchors() const
void addAnchor(QQuick3DXrSpatialAnchor *anchor)
The QVector2D class represents a vector or vertex in 2D space.
Definition qvectornd.h:31
The QVector3D class represents a vector or vertex in 3D space.
Definition qvectornd.h:171
Combined button and popup list for selecting options.
static const AnchorClassificationMap & getAnchorClassificationName(ar_plane_classification_t classification, bool *identified=nullptr)
static const AnchorClassificationMap anchorClassificationMap[]
static constexpr size_t anchorClassificationStart
static void updateAnchorProperties(QQuick3DXrSpatialAnchor &anchor, ar_plane_anchor_t planeAnchor)
struct ar_data_providers_s * ar_data_providers_t
struct ar_plane_anchors_s * ar_plane_anchors_t
QQuick3DXrSpatialAnchor::Classification label