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
qquick3dxrspatialanchorlistmodel.cpp
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// Qt-Security score:significant reason:default
4
5
8
9#if defined(Q_OS_VISIONOS)
10#include "visionos/qquick3dxranchormanager_visionos_p.h"
11#else
12#include "openxr/qquick3dxranchormanager_openxr_p.h"
13#endif
14
16
17/*!
18 \qmltype XrSpatialAnchorListModel
19 \inherits ListModel
20 \inqmlmodule QtQuick3D.Xr
21 \brief Provides a model containing spatial anchors.
22
23 This type provides a list of spatial anchors, which are points in
24 the physical world that can be tracked and associated with virtual content.
25
26 The list contains elements that have an \c anchor property with the type \l XrSpatialAnchor.
27
28 You can use it like this:
29
30 \qml
31 Repeater3D {
32 model: XrSpatialAnchorListModel {
33 }
34 delegate: Node {
35 required property XrSpatialAnchor anchor
36 position: anchor.position
37 rotation: anchor.rotation
38 // Further use of anchor properties...
39 }
40 }
41 \endqml
42*/
43
44static QString getClassificationString(QQuick3DXrSpatialAnchorListModel::ClassificationFlag classification)
45{
46 switch (classification) {
47 case QQuick3DXrSpatialAnchorListModel::ClassificationFlag::Wall:
48 return QStringLiteral("wall");
49 case QQuick3DXrSpatialAnchorListModel::ClassificationFlag::Ceiling:
50 return QStringLiteral("ceiling");
51 case QQuick3DXrSpatialAnchorListModel::ClassificationFlag::Floor:
52 return QStringLiteral("floor");
53 case QQuick3DXrSpatialAnchorListModel::ClassificationFlag::Table:
54 return QStringLiteral("table");
55 case QQuick3DXrSpatialAnchorListModel::ClassificationFlag::Seat:
56 return QStringLiteral("seat");
57 case QQuick3DXrSpatialAnchorListModel::ClassificationFlag::Window:
58 return QStringLiteral("window");
59 case QQuick3DXrSpatialAnchorListModel::ClassificationFlag::Door:
60 return QStringLiteral("door");
61 case QQuick3DXrSpatialAnchorListModel::ClassificationFlag::Other:
62 return QStringLiteral("other");
63 }
64
65 return {};
66}
67
68static QQuick3DXrSpatialAnchorListModel::ClassificationFlag getClassificationFlagType(QQuick3DXrSpatialAnchor::Classification classification)
69{
70 switch (classification) {
71 case QQuick3DXrSpatialAnchor::Classification::Unknown:
72 return QQuick3DXrSpatialAnchorListModel::ClassificationFlag::Other;
73 case QQuick3DXrSpatialAnchor::Classification::Wall:
74 return QQuick3DXrSpatialAnchorListModel::ClassificationFlag::Wall;
75 case QQuick3DXrSpatialAnchor::Classification::Ceiling:
76 return QQuick3DXrSpatialAnchorListModel::ClassificationFlag::Ceiling;
77 case QQuick3DXrSpatialAnchor::Classification::Floor:
78 return QQuick3DXrSpatialAnchorListModel::ClassificationFlag::Floor;
79 case QQuick3DXrSpatialAnchor::Classification::Table:
80 return QQuick3DXrSpatialAnchorListModel::ClassificationFlag::Table;
81 case QQuick3DXrSpatialAnchor::Classification::Seat:
82 return QQuick3DXrSpatialAnchorListModel::ClassificationFlag::Seat;
83 case QQuick3DXrSpatialAnchor::Classification::Window:
84 return QQuick3DXrSpatialAnchorListModel::ClassificationFlag::Window;
85 case QQuick3DXrSpatialAnchor::Classification::Door:
86 return QQuick3DXrSpatialAnchorListModel::ClassificationFlag::Door;
87 case QQuick3DXrSpatialAnchor::Classification::Other:
88 return QQuick3DXrSpatialAnchorListModel::ClassificationFlag::Other;
89 }
90
91 Q_UNREACHABLE_RETURN(QQuick3DXrSpatialAnchorListModel::ClassificationFlag::Other);
92}
93
96{
97 m_anchorManager = QQuick3DXrAnchorManager::instance();
98 if (m_anchorManager) {
99 connect(m_anchorManager, &QQuick3DXrAnchorManager::anchorAdded, this, &QQuick3DXrSpatialAnchorListModel::handleAnchorAdded);
100 connect(m_anchorManager, &QQuick3DXrAnchorManager::anchorUpdated, this, &QQuick3DXrSpatialAnchorListModel::handleAnchorUpdated);
101 connect(m_anchorManager, &QQuick3DXrAnchorManager::anchorRemoved, this, &QQuick3DXrSpatialAnchorListModel::handleAnchorRemoved);
102 connect(m_anchorManager, &QQuick3DXrAnchorManager::sceneCaptureCompleted, this, [this]{queryAnchors();});
103 queryAnchors();
104 } else {
105 qWarning("SpatialAnchorModel: Failed to get anchor manager instance");
106 }
107}
108
109int QQuick3DXrSpatialAnchorListModel::rowCount(const QModelIndex &parent) const
110{
111 if (parent.isValid() || m_anchorManager == nullptr)
112 return 0;
113
114 const auto &anchors = anchorsFiltered();
115
116 return anchors.size();
117}
118
119QVariant QQuick3DXrSpatialAnchorListModel::data(const QModelIndex &index, int role) const
120{
121 if (!index.isValid() || m_anchorManager == nullptr)
122 return QVariant();
123
124 const auto &anchors = anchorsFiltered();
125
126 // check bounds
127 if (index.row() < 0 || index.row() >= anchors.count())
128 return QVariant();
129
130 const auto &anchor = anchors.at(index.row());
131
132 if (role == Anchor)
133 return QVariant::fromValue(anchor);
134
135 // shouldn't be reachable under normal circumstances.
136 return QVariant();
137}
138
140{
141 QHash<int, QByteArray> roles;
142 roles[Anchor] = "anchor";
143 return roles;
144}
145
146/*!
147 \qmlmethod void XrSpatialAnchorListModel::requestSceneCapture()
148 \brief This method triggers a scan to capture or update spatial data for the current environment.
149
150 This method triggers the underlying XR system to perform a scene capture of the user's current physical environment,
151 to update or refine the spatial mesh, enabling more accurate placement and tracking of spatial anchors.
152
153 \note Some platforms do not perform this operation automatically.
154 For example, on Quest 3, if the user is in a previously uncaptured space, this method will not be called automatically,
155 resulting in no available anchors until a capture is manually triggered.
156 */
157
159{
160 if (m_anchorManager == nullptr)
161 return;
162
163 // not supported on the Simulator, this will be a no-op there
164 m_anchorManager->requestSceneCapture();
165}
166
168{
169 if (m_anchorManager == nullptr)
170 return;
171
172 m_anchorManager->queryAllAnchors();
173}
174
175void QQuick3DXrSpatialAnchorListModel::handleAnchorAdded(QQuick3DXrSpatialAnchor *anchor)
176{
177 Q_UNUSED(anchor)
178 if (matchesAnchorFilter(anchor)) {
179 // Brute Force :-p
180 beginResetModel();
181 endResetModel();
182 }
183}
184
185void QQuick3DXrSpatialAnchorListModel::handleAnchorRemoved(QUuid uuid)
186{
187 Q_UNUSED(uuid)
188 // Brute Force :-p
189 beginResetModel();
190 endResetModel();
191}
192
193void QQuick3DXrSpatialAnchorListModel::handleAnchorUpdated(QQuick3DXrSpatialAnchor *anchor)
194{
195 Q_UNUSED(anchor)
196 if (matchesAnchorFilter(anchor)) {
197 // Brute Force :-p
198 beginResetModel();
199 endResetModel();
200 }
201}
202
203/*!
204 \qmlproperty enumeration XrSpatialAnchorListModel::filterMode
205 \brief Specifies the filter mode for spatial anchors.
206
207 Holds the filter mode.
208 The filter mode can be one of the following:
209 \value XrSpatialAnchorListModel.All Show all spatial anchors.
210 \value XrSpatialAnchorListModel.Classification Show spatial anchors based on the provided classification filter flag.
211 \value XrSpatialAnchorListModel.Identifier Show spatial anchors based on matching the provided Identifiers.
212 */
213
218
219void QQuick3DXrSpatialAnchorListModel::setFilterMode(FilterMode newFilterMode)
220{
221 if (m_filterMode == newFilterMode)
222 return;
223 m_filterMode = newFilterMode;
224
225 // Make sure we reset the model when changing filter mode.
226 handleAnchorRemoved({});
227
228 emit filterModeChanged();
229}
230
231/*!
232 \qmlproperty list<string> XrSpatialAnchorListModel::identifierFilter
233 \brief Holds the list of identifiers for filtering spatial anchors.
234 */
235
237{
238 return m_uuids.values();
239}
240
242{
243 QSet<QString> newFilter { filter.cbegin(), filter.cend() };
244
245 if (m_uuids == newFilter)
246 return;
247
248 m_uuids = newFilter;
249
250 // Make sure we reset the model.
251 handleAnchorRemoved({});
252
253 emit identifierFilterChanged();
254}
255
256/*!
257 \qmlproperty enumeration XrSpatialAnchorListModel::classificationFilter
258 \brief Holds the classification flag used for filtering spatial anchors.
259
260 The ClassificationFlag filter is represented as a combination of flags:
261
262 \value XrSpatialAnchorListModel.Wall
263 \value XrSpatialAnchorListModel.Ceiling
264 \value XrSpatialAnchorListModel.Floor
265 \value XrSpatialAnchorListModel.Table
266 \value XrSpatialAnchorListModel.Seat
267 \value XrSpatialAnchorListModel.Window
268 \value XrSpatialAnchorListModel.Door
269 \value XrSpatialAnchorListModel.Other
270 */
271
276
277void QQuick3DXrSpatialAnchorListModel::setClassificationFilter(ClassificationFlags newClassFilter)
278{
279 if (m_classFilter == newClassFilter)
280 return;
281
282 m_classFilter = newClassFilter;
283
284 m_classStringFilter.clear();
285
286 constexpr size_t classifcationCount = (sizeof(std::underlying_type_t<ClassificationFlag>) * 8) - 1;
287 for (size_t i = 0; i < classifcationCount; ++i) {
288 ClassificationFlag classification = static_cast<ClassificationFlag>(size_t(m_classFilter) & (size_t(1) << i));
289 auto name = getClassificationString(classification);
290 if (!name.isEmpty())
291 m_classStringFilter.insert(name);
292 }
293
294 // Make sure we reset the model.
295 handleAnchorRemoved({});
296
297 emit classificationFilterChanged();
298}
299
300/*!
301 \qmlproperty list<string> XrSpatialAnchorListModel::classificationStringFilter
302 \brief Holds the classification strings used for filtering spatial anchors.
303
304 If the \l filterMode is set to \c Classification, this property can be used to provide a
305 list of additional classification string to filter on. These labels will then be matched against
306 the same value as reported by \l {XrSpatialAnchor::classificationString} property
307 of the spatial anchor.
308
309 \note Only \l {XrSpatialAnchor}{spatial anchors} that are classified as
310 \l {XrSpatialAnchor::Classification}{Other} will be checked against this filter.
311 */
313{
314 return m_classStringFilter.values();
315}
316
317void QQuick3DXrSpatialAnchorListModel::setClassificationStringFilter(const QStringList &newClassStringFilter)
318{
319 for (const auto &entry : newClassStringFilter) {
320 if (!entry.isEmpty())
321 m_classStringFilter.insert(entry.toLower());
322 }
323
324 // Make sure we reset the model.
325 handleAnchorRemoved({});
326
327 emit classificationStringFilterChanged();
328}
329
330bool QQuick3DXrSpatialAnchorListModel::matchesAnchorFilter(QQuick3DXrSpatialAnchor *anchor) const
331{
332 if (m_filterMode == FilterMode::Classification) {
333 QString classificationString;
334 const auto classification = anchor->classification();
335 if (classification != QQuick3DXrSpatialAnchor::Classification::Other) {
336 const auto classificationFlagType = getClassificationFlagType(classification);
337 classificationString = getClassificationString(classificationFlagType);
338 } else {
339 // NOTE: Other can mean there is no classification or the classification is not one that
340 // we have defined in our enums, so we will use the string as the user can specify any string.
341 classificationString = anchor->classificationString().toLower();
342 }
343 return m_classStringFilter.contains(classificationString.toLower());
344 } else if (m_filterMode == FilterMode::Identifier) {
345 const auto uuid = anchor->identifier();
346 return m_uuids.contains(uuid);
347 }
348
349 // 'All' mode
350 return true;
351}
352
353QList<QQuick3DXrSpatialAnchor *> QQuick3DXrSpatialAnchorListModel::anchorsFiltered() const
354{
355 QList<QQuick3DXrSpatialAnchor *> anchors;
356 const auto &unfilteredAnchors = m_anchorManager->anchors();
357 if (m_filterMode == FilterMode::All) {
358 anchors = unfilteredAnchors;
359 } else {
360 for (const auto &anchor : unfilteredAnchors) {
361 if (matchesAnchorFilter(anchor))
362 anchors.push_back(anchor);
363 }
364 }
365
366 return anchors;
367}
368
369QT_END_NAMESPACE
void anchorUpdated(QQuick3DXrSpatialAnchor *anchor)
QStringList classificationStringFilter() const
\qmlproperty list<string> XrSpatialAnchorListModel::classificationStringFilter
int rowCount(const QModelIndex &parent) const override
Returns the number of rows under the given parent.
ClassificationFlags classificationFilter() const
\qmlproperty enumeration XrSpatialAnchorListModel::classificationFilter
Q_INVOKABLE void requestSceneCapture()
\qmlmethod void XrSpatialAnchorListModel::requestSceneCapture()
QStringList identifierFilter() const
\qmlproperty list<string> XrSpatialAnchorListModel::identifierFilter
QVariant data(const QModelIndex &index, int role) const override
Returns the data stored under the given role for the item referred to by the index.
FilterMode filterMode() const
\qmlproperty enumeration XrSpatialAnchorListModel::filterMode
void setClassificationFilter(ClassificationFlags newClassFilter)
void setIdentifierFilter(const QStringList &filter)
void setClassificationStringFilter(const QStringList &newClassStringFilter)
QHash< int, QByteArray > roleNames() const override
Combined button and popup list for selecting options.
static QT_BEGIN_NAMESPACE QString getClassificationString(QQuick3DXrSpatialAnchorListModel::ClassificationFlag classification)
static QQuick3DXrSpatialAnchorListModel::ClassificationFlag getClassificationFlagType(QQuick3DXrSpatialAnchor::Classification classification)