Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qopenxritem.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
4#include "qopenxritem_p.h"
5#include "qopenxrview_p.h"
6#include <QtQuick3D/private/qquick3dnode_p_p.h>
7#include <QtQuick/private/qquickrectangle_p.h>
8#include <QColor>
9
11
12
68
69static inline qreal calculatePPU(qreal pxWidth, qreal pxHeight, qreal diagonal)
70{
71 return (diagonal > 0) ? std::sqrt((pxWidth * pxWidth) + (pxHeight * pxHeight)) / diagonal : 1.0;
72}
73
75{
77 return;
78 Q_Q(QOpenXRItem);
81 if (m_contentItem) {
83 qWarning() << "XrItem invalid pixelPerUnit" << m_pixelsPerUnit;
84 return;
85 }
86 qreal newScale;
88 newScale = 1.0 / m_pixelsPerUnit;
89
90 } else {
91 qreal diagonal = std::sqrt((m_width * m_width) + (m_height * m_height));
93 if (ppu <= 0)
94 ppu = 1.0;
95 q->setPixelsPerUnit(ppu);
96 newScale = 1.0 / ppu;
97 }
98 QSizeF newSize(m_width / newScale, m_height / newScale);
99 m_containerItem->setSize(newSize);
100 m_containerItem->setScale(newScale);
101 }
102}
103
135 : QQuick3DNode(*(new QOpenXRItemPrivate()), parent)
136{
137}
138
140{
141 Q_D(QOpenXRItem);
142 if (d->m_XrView)
143 d->m_XrView->unregisterXrItem(this);
144}
145
147{
148 Q_D(QOpenXRItem);
149 QQuick3DNode::componentComplete(); // Sets d->componentComplete, so must be called first
150
151 auto findView = [this]() -> QOpenXRView * {
153 while (parent) {
154 if (auto *xrView = qobject_cast<QOpenXRView*>(parent))
155 return xrView;
156 parent = parent->parentNode();
157 }
158 return nullptr;
159 };
160 d->m_XrView = findView();
161 if (d->m_XrView)
162 d->m_XrView->registerXrItem(this);
163 else
164 qWarning("Could not find XrView for XrItem");
165 d->updateContent();
166}
167
177{
178 Q_D(const QOpenXRItem);
179 return d->m_contentItem;
180}
181
183{
184 Q_D(QOpenXRItem);
185 if (d->m_contentItem == newContentItem)
186 return;
187
188 d->setContentItem(newContentItem);
190}
191
204{
205 Q_D(const QOpenXRItem);
206 return d->m_pixelsPerUnit;
207}
208
210{
211 Q_D(QOpenXRItem);
212 if (qFuzzyCompare(d->m_pixelsPerUnit, newPixelsPerUnit))
213 return;
214
215 d->m_pixelsPerUnit = newPixelsPerUnit;
216
217 if (d->m_manualPixelsPerUnit)
218 d->updateContent();
219
221}
222
235{
236 Q_D(const QOpenXRItem);
237 return d->m_manualPixelsPerUnit;
238}
239
240void QOpenXRItem::setManualPixelsPerUnit(bool newManualPixelsPerUnit)
241{
242 Q_D(QOpenXRItem);
243 if (d->m_manualPixelsPerUnit == newManualPixelsPerUnit)
244 return;
245 d->m_manualPixelsPerUnit = newManualPixelsPerUnit;
246 d->updateContent();
248}
249
258{
259 Q_D(const QOpenXRItem);
260 return d->m_width;
261}
262
264{
265 Q_D(QOpenXRItem);
266 if (d->m_width == newWidth)
267 return;
268 d->m_width = newWidth;
270
271 d->updateContent();
272}
273
282{
283 Q_D(const QOpenXRItem);
284 return d->m_height;
285}
286
288{
289 Q_D(QOpenXRItem);
290 if (d->m_height == newHeight)
291 return;
292 d->m_height = newHeight;
294
295 d->updateContent();
296}
297
298// Sends appropriate touch events.
299// Updates the touchState and returns true if this item grabs the touch point.
300// touchState is input/output, and input contains the previous state if touchState->grabbed is true
301
303{
304 Q_D(QOpenXRItem);
305
306 auto mappedPos = mapPositionFromScene(pos);
307
308 QPointF point = {mappedPos.x(), -mappedPos.y()};
309
310 constexpr qreal sideMargin = 20; // How far outside the rect do you have to go to cancel the grab (cm)
311 constexpr qreal cancelDepth = 50; // How far through the rect do you have to go to cancel the grab (cm)
312 constexpr qreal hoverHeight = 10; // How far above does the hover state begin (cm). NOTE: no hover events actually sent
313
314 constexpr qreal releaseHeight = 2; // How far to move towards/from the surface to count as a press/release when below
315 constexpr qreal smallDistance = 0.5; // Any movement shorter than this distance is ignored for press/release below the surface
316 constexpr qreal longDistance = 5; // Any in-surface movement larger than this distance means this is not a press/release below the surface
317 constexpr int releaseTime = 500; // How fast does the finger have to move to count as press/release below the surface
318 constexpr qreal releaseHeightSquared = releaseHeight * releaseHeight;
319 constexpr qreal smallDistanceSquared = smallDistance * smallDistance;
320 constexpr qreal longDistanceSquared = longDistance * longDistance;
321
322 const float z = mappedPos.z();
323
324 const bool wayOutside = point.x() < -sideMargin || point.x() > width() + sideMargin
325 || point.y() < -sideMargin || point.y() > height() + sideMargin || z < -cancelDepth;
326 const bool inside = point.x() >= 0 && point.x() <= width() && point.y() >= 0 && point.y() <= height() && !wayOutside;
327
328 const bool wasGrabbed = touchState->grabbed;
329 const bool wasPressed = touchState->pressed;
330
331 bool hover = z > 0 && z < hoverHeight;
332
333 bool pressed = false;
334 bool grab;
335 bool resetPrevious = false;
337
338 if (wasGrabbed) {
339 // We maintain a grab as long as we don't move too far away while below the surface, or if we hover inside
340
341 // We release if we go from below to above, or if we move upwards fast enough
342 // We press if we go from above to below, or if we push downwards fast enough
343 // We maintain press otherwise
344 // If we move outside when pressed, we should maintain pressed state, but send release event
345
346 QVector3D distFromPrev = mappedPos - touchState->previous;
347 qint64 msSincePrev = now - touchState->timestamp;
348
349 const qreal prevZ = touchState->previous.z();
350
351 if (prevZ > 0 && z <= 0) {
352 // New press from above
353 pressed = true;
354 resetPrevious = true;
355 } else if (msSincePrev > releaseTime || z > 0) {
356 resetPrevious = true;
357 // If the timestamp of the last significant move is older than the cutoff, we maintain the press state if we're below the surface
358 // We're never pressed if we're above the surface
359 pressed = z <= 0 && wasPressed;
360 } else {
361 // We know we're within the cutoff interval, and below the surface.
362 const qreal hDistSquared = distFromPrev.x() * distFromPrev.x() + distFromPrev.y() * distFromPrev.y();
363 const qreal vDistSquared = distFromPrev.z() * distFromPrev.z();
364 const qreal distSquared = hDistSquared + vDistSquared;
365
366 if (distSquared < smallDistanceSquared) {
367 // Ignore the movement if it's small.
368 resetPrevious = false;
369 pressed = wasPressed;
370 } else if (hDistSquared > longDistanceSquared) {
371 // It's not a press/release if it's a long move inside the surface
372 resetPrevious = true;
373 pressed = wasPressed;
374 } else if (vDistSquared > releaseHeightSquared) {
375 // Significant vertical move
376 resetPrevious = true;
377 pressed = distFromPrev.z() < 0;
378 } else {
379 resetPrevious = false;
380 pressed = wasPressed;
381 }
382 }
383
384 grab = (z <= 0 && !wayOutside) || (hover && inside);
385 } else {
386 // We don't want movement behind the surface to register as pressed, so we need at least one hover before a press
387 grab = hover && inside;
388 resetPrevious = true;
389 }
390
391 if (grab) {
392 float zOffset = qMin(z, 0.0);
393 if (offset)
394 *offset = sceneRotation() * QVector3D{ 0, 0, -zOffset };
395 touchState->grabbed = true;
396 touchState->target = this;
397 touchState->touchDistance = z;
398 touchState->pressed = pressed;
399 touchState->cursorPos = point;
400 if (resetPrevious) {
401 touchState->previous = mappedPos;
402 touchState->timestamp = now;
403 }
404 view->setTouchpoint(d->m_containerItem, point, touchState->pointId, pressed && inside); // pressed state maintained outside, but release/press events must be sent when leave/enter
405 return true;
406 }
407
408 if (wasGrabbed) {
409 touchState->grabbed = false;
410 touchState->target = nullptr;
411 view->setTouchpoint(d->m_containerItem, point, touchState->pointId, false);
412 }
413
414 return false;
415}
416
426{
427 Q_D(const QOpenXRItem);
428 return d->m_color;
429}
430
431void QOpenXRItem::setColor(const QColor &newColor)
432{
433 Q_D(QOpenXRItem);
434 if (d->m_color == newColor)
435 return;
436 d->m_color = newColor;
438 d->updateContent();
439}
440
Definition lalr.h:136
The QColor class provides colors based on RGB, HSV or CMYK values.
Definition qcolor.h:31
static qint64 currentMSecsSinceEpoch() noexcept
\inmodule QtCore Represents a handle to a signal-slot (or signal-functor) connection.
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
void setParent(QObject *parent)
Makes the object a child of parent.
Definition qobject.cpp:2195
static bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *member)
\threadsafe
Definition qobject.cpp:3236
void destroyed(QObject *=nullptr)
This signal is emitted immediately before the object obj is destroyed, after any instances of QPointe...
QPointer< QOpenXRView > m_XrView
QMetaObject::Connection m_contentItemDestroyedConnection
void setContentItem(QQuickItem *newContentItem)
QQuickRectangle * m_containerItem
QQuickItem * m_contentItem
void manualPixelsPerUnitChanged()
void setContentItem(QQuickItem *newContentItem)
bool manualPixelsPerUnit
qreal pixelsPerUnit
void setWidth(qreal newWidth)
void pixelsPerUnitChanged()
void setManualPixelsPerUnit(bool newManualPixelsPerUnit)
QOpenXRItem(QQuick3DNode *parent=nullptr)
\qmltype XrItem \inqmlmodule QtQuick3D.Xr \inherits Node
void componentComplete() override
Invoked after the root component that caused this instantiation has completed construction.
void contentItemChanged()
void setColor(const QColor &newColor)
void widthChanged()
~QOpenXRItem() override
bool handleVirtualTouch(QOpenXRView *view, const QVector3D &pos, TouchState *touchState, QVector3D *offset)
QQuickItem * contentItem
void setHeight(qreal newHeight)
void setPixelsPerUnit(qreal newPixelsPerUnit)
void colorChanged()
\inmodule QtCore\reentrant
Definition qpoint.h:217
constexpr qreal x() const noexcept
Returns the x coordinate of this point.
Definition qpoint.h:343
constexpr qreal y() const noexcept
Returns the y coordinate of this point.
Definition qpoint.h:348
Q_INVOKABLE QVector3D mapPositionFromScene(const QVector3D &scenePosition) const
\qmlmethod vector3d QtQuick3D::Node::mapPositionFromScene(vector3d scenePosition)
void componentComplete() override
Invoked after the root component that caused this instantiation has completed construction.
QQuick3DNode * parentNode() const
QQuaternion sceneRotation
QQmlListProperty< QObject > data()
QQuick3DObject * parent
\qmlproperty Object3D QtQuick3D::Object3D::parent This property holds the parent of the Object3D in a...
The QQuickItem class provides the most basic of all visual items in \l {Qt Quick}.
Definition qquickitem.h:63
void setSize(const QSizeF &size)
void setScale(qreal)
void setParentItem(QQuickItem *parent)
qreal width
This property holds the width of this item.
Definition qquickitem.h:75
qreal height
This property holds the height of this item.
Definition qquickitem.h:76
void setTransformOrigin(TransformOrigin)
void setColor(const QColor &)
\inmodule QtCore
Definition qsize.h:208
The QVector3D class represents a vector or vertex in 3D space.
Definition qvectornd.h:171
Combined button and popup list for selecting options.
@ white
Definition qnamespace.h:31
#define Q_UNLIKELY(x)
bool qFuzzyCompare(qfloat16 p1, qfloat16 p2) noexcept
Definition qfloat16.h:333
#define qWarning
Definition qlogging.h:166
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat z
GLenum GLuint GLintptr offset
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
static qreal calculatePPU(qreal pxWidth, qreal pxHeight, qreal diagonal)
#define emit
long long qint64
Definition qtypes.h:60
double qreal
Definition qtypes.h:187
QQuickView * view
[0]
Definition moc.h:23