11#include <QLoggingCategory>
13#include <QtQuick3DUtils/private/qssgassert_p.h>
19Q_DECLARE_LOGGING_CATEGORY(lcQuick3DXr);
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
47QQuick3DXrView::QQuick3DXrView()
48 : m_xrRuntimeInfo(&m_xrManager)
53QQuick3DXrView::~QQuick3DXrView()
55 m_inDestructor =
true;
59
60
61
62
63
64
65
66
67
68
69
70
72QQuick3DXrOrigin *QQuick3DXrView::xrOrigin()
const
78
79
80
82QQuick3DSceneEnvironment *QQuick3DXrView::environment()
const
84 return m_xrManager.m_vrViewport ? m_xrManager.m_vrViewport->environment() :
nullptr;
87QQuick3DViewport *QQuick3DXrView::view3d()
const
89 return m_xrManager.m_vrViewport;
94struct ClosestPointResult
100 QVector3D scenePosition;
102 QVector3D sceneNormal;
103 QVector2D uvPosition;
105 QQuickItem *quickItem =
nullptr;
111static ClosestPointResult closestPointOnXrItem(
const QQuick3DXrItem *xrItem,
const QVector3D &pos,
const float margin = 0)
113 const auto mappedPos = xrItem->mapPositionFromScene(pos);
114 const qreal width = xrItem->width();
115 const qreal height = xrItem->height();
117 const float x = mappedPos.x();
118 const float y = mappedPos.y();
119 const float z = mappedPos.z();
122 const float y1 = -height;
123 const float x2 = width;
126 const bool insideMargin = x1 - margin <= x && x <= x2 + margin && y1 - margin <= y && y <= y2 + margin;
129 const bool inside = x1 <= x && x <= x2 && y1 <= y && y <= y2 && z <= 0;
131 const float distance = mappedPos.z();
133 const QVector3D position{ mappedPos.x(), mappedPos.y(), 0 };
134 const QVector3D scenePosition = xrItem->mapPositionToScene(position);
135 const QVector3D normal{ 0, 0, 1 };
136 const QVector3D sceneNormal = xrItem->mapDirectionToScene(normal);
147 QPointF{mappedPos.x(), -mappedPos.y()},
148 xrItem->contentItem()
153static ClosestPointResult closestPointOnModel(QQuick3DXrView *view, QQuick3DModel *model,
const QVector3D &pos,
const float radius = 0)
155 auto pickResult = view->closestPointPick(pos, radius, model);
156 if (pickResult.objectHit() != model)
159 const QVector3D offset = pos - pickResult.scenePosition();
161 const float signedDistance = QVector3D::dotProduct(offset, pickResult.sceneNormal());
162 const bool inside = signedDistance < 0;
165 QQuickItem *quickItem = pickResult.itemHit();
166 auto uv = pickResult.uvPosition();
168 quickPoint = QPointF(quickItem->x() + uv.x() * quickItem->width(),
169 quickItem->y() - uv.y() * quickItem->height() + quickItem->height());
175 pickResult.position(),
176 pickResult.scenePosition(),
178 pickResult.sceneNormal().normalized(),
186static inline ClosestPointResult closestPointOnItem(QQuick3DXrView *view, QQuick3DXrView::TouchTarget target,
const QVector3D &pos,
const float margin)
188 if (
auto *asXrItem = std::get_if<QQuick3DXrItem*>(&target))
189 return closestPointOnXrItem(*asXrItem, pos, margin);
190 if (
auto *asModel =std::get_if<QQuick3DModel*>(&target))
191 return closestPointOnModel(view, *asModel, pos, margin);
195static inline void sendTouch(QQuick3DXrView *view,
const ClosestPointResult &point,
int pointId,
bool active)
198 view->setTouchpoint(point.quickItem, point.point2D, pointId, active);
207bool QQuick3DXrView::handleVirtualTouch(TouchTarget target,
const QVector3D &pos, TouchState *touchState, QVector3D *offset)
209 constexpr qreal sideMargin = 10;
210 constexpr qreal hoverHeight = 10;
211 constexpr qreal cancelDepth = 50;
213 constexpr qreal releaseHeight = 2;
214 constexpr qreal releaseHeightSquared = releaseHeight * releaseHeight;
215 constexpr qreal smallDistance = 0.5;
216 constexpr qreal sidewaysMax = 3;
217 constexpr qreal sidewaysMaxSquared = sidewaysMax * sidewaysMax;
218 constexpr qreal releaseSpeed = 5.0 / 1000;
219 constexpr int interval = 100;
221 const bool wasGrabbed = touchState->grabbed;
222 const bool wasPressed = touchState->pressed;
223 const qreal wasInside = wasGrabbed && touchState->touchDepth < 0.0;
225 const QVector3D oldScenePos = touchState->previous;
226 const QVector3D oldSceneNormal = touchState->normal;
233 auto closest = closestPointOnItem(
this, target, pos, sideMargin);
236 bool pressed =
false;
238 bool resetTimer =
false;
239 float z = closest.distance;
243 grab = closest.grab && z > -cancelDepth && z < hoverHeight;
252 if (wasInside && closest.inside) {
254 int duration = touchState->timer.elapsed();
255 pressed = wasPressed;
256 const QVector3D movement = pos - oldScenePos;
257 const qreal verticalDistance = QVector3D::dotProduct(movement, oldSceneNormal);
258 const qreal verticalDistanceSquared = verticalDistance * verticalDistance;
259 const qreal horizontalDistanceSquared = movement.lengthSquared() - verticalDistance*verticalDistance;
261 if (verticalDistanceSquared >= releaseHeightSquared && horizontalDistanceSquared < releaseHeightSquared) {
264 pressed = verticalDistance < 0;
266 }
else if (duration >= interval) {
268 if (horizontalDistanceSquared >= sidewaysMaxSquared) {
271 }
else if (pressed ? verticalDistance < smallDistance : verticalDistance > -smallDistance ) {
278 const qreal verticalSpeed = verticalDistance / duration;
279 const bool sufficientSpeed = pressed ? verticalSpeed > releaseSpeed : verticalSpeed < -releaseSpeed;
280 resetTimer = !sufficientSpeed;
285 pressed = closest.inside;
292 grab = closest.grab && closest.distance > 0 && closest.distance < hoverHeight;
298 *offset = closest.scenePosition - pos;
299 touchState->grabbed =
true;
300 touchState->target = target;
301 touchState->touchDistance = qMax(z, 0.0);
302 touchState->touchDepth = z;
303 touchState->pressed = pressed;
304 touchState->cursorPos = closest.point2D;
305 touchState->surfacePoint = closest.scenePosition;
306 touchState->normal = closest.sceneNormal;
307 touchState->uvPosition = closest.uvPosition;
309 touchState->previous = pos;
310 touchState->timer.start();
313 sendTouch(
this, closest, touchState->pointId, pressed);
318 touchState->grabbed =
false;
319 touchState->pressed =
false;
320 touchState->target = {};
321 sendTouch(
this, closest, touchState->pointId,
false);
328
329
330
331bool QQuick3DXrView::passthroughEnabled()
const
333 return m_xrManager.isPassthroughEnabled();
337
338
339
341QQuick3DXrRuntimeInfo *QQuick3DXrView::runtimeInfo()
const
343 return &m_xrRuntimeInfo;
346void QQuick3DXrView::setEnvironment(QQuick3DSceneEnvironment *environment)
348 QQuick3DViewport *view = m_xrManager.m_vrViewport;
353 m_pendingSceneEnvironment = environment;
357 auto oldEnvironment = view->environment();
358 if (oldEnvironment == environment)
362 disconnect(oldEnvironment);
364 view->setEnvironment(environment);
369 environment = view->environment();
371 handleClearColorChanged();
374 connect(environment, &QQuick3DSceneEnvironment::backgroundModeChanged,
this, &QQuick3DXrView::handleClearColorChanged);
375 connect(environment, &QQuick3DSceneEnvironment::clearColorChanged,
this, &QQuick3DXrView::handleClearColorChanged);
376 connect(environment, &QQuick3DSceneEnvironment::antialiasingModeChanged,
this, &QQuick3DXrView::handleAAChanged);
377 connect(environment, &QQuick3DSceneEnvironment::antialiasingQualityChanged,
this, &QQuick3DXrView::handleAAChanged);
379 emit environmentChanged(environment);
383
384
385
387bool QQuick3DXrView::passthroughSupported()
const
389 if (!m_xrManager.isValid())
392 return m_xrManager.supportsPassthrough();
395void QQuick3DXrView::setPassthroughEnabled(
bool enable)
397 if (!m_xrManager.isValid()) {
398 qWarning(
"Attempted to set passthrough mode without a valid XR manager");
402 const bool orgPassthroughEnabled = m_xrManager.isPassthroughEnabled();
403 if (!m_xrManager.setPassthroughEnabled(enable)) {
405 qWarning(
"Enabling passthrough is not supported.");
407 qWarning(
"Disabling passthrough is not supported.");
411 if (orgPassthroughEnabled != m_xrManager.isPassthroughEnabled())
412 emit passthroughEnabledChanged();
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
439QQuick3DXrView::FoveationLevel QQuick3DXrView::fixedFoveation()
const
441 return FoveationLevel(m_xrManager.getFixedFoveationLevel());
444void QQuick3DXrView::setFixedFoveation(FoveationLevel level)
446 const auto orgFoviationLevel = m_xrManager.getFixedFoveationLevel();
447 m_xrManager.setFixedFoveationLevel(QtQuick3DXr::FoveationLevel(level));
448 if (orgFoviationLevel != m_xrManager.getFixedFoveationLevel())
449 emit fixedFoveationChanged();
453
454
455
457bool QQuick3DXrView::isQuitOnSessionEndEnabled()
const
459 return m_quitOnSessionEnd;
462void QQuick3DXrView::setQuitOnSessionEnd(
bool enable)
464 if (m_quitOnSessionEnd == enable)
467 m_quitOnSessionEnd = enable;
468 emit quitOnSessionEndChanged();
471
472
473
475QQuick3DRenderStats *QQuick3DXrView::renderStats()
const
477 return m_xrManager.m_vrViewport ? m_xrManager.m_vrViewport->renderStats() :
nullptr;
480void QQuick3DXrView::updateViewportGeometry()
482 auto contentItem = m_xrManager.m_quickWindow->contentItem();
483 auto viewport = m_xrManager.m_vrViewport;
484 if (viewport->height() != contentItem->height())
485 viewport->setHeight(contentItem->height());
486 if (viewport->width() != contentItem->width())
487 viewport->setWidth(contentItem->width());
488 if (viewport->x() != contentItem->x())
489 viewport->setX(contentItem->x());
490 if (viewport->y() != contentItem->y())
491 viewport->setY(contentItem->y());
494void QQuick3DXrView::handleSessionEnded()
497 if (m_quitOnSessionEnd)
498 QCoreApplication::quit();
501void QQuick3DXrView::handleClearColorChanged()
503 auto env = environment();
506 if (env->backgroundMode() == QQuick3DSceneEnvironment::Color)
507 m_xrManager.m_quickWindow->setColor(env->clearColor());
508 else if (env->backgroundMode() == QQuick3DSceneEnvironment::Transparent)
509 m_xrManager.m_quickWindow->setColor(Qt::transparent);
513void QQuick3DXrView::handleAAChanged()
515 auto env = environment();
517 if (env && env->antialiasingMode() == QQuick3DSceneEnvironment::MSAA) {
518 switch (env->antialiasingQuality()) {
519 case QQuick3DSceneEnvironment::Medium:
522 case QQuick3DSceneEnvironment::High:
525 case QQuick3DSceneEnvironment::VeryHigh:
530 m_xrManager.setSamples(samples);
533bool QQuick3DXrView::init()
535 if (m_isInitialized) {
536 qWarning(
"Already initialized!");
540 connect(&m_xrManager, &QQuick3DXrManager::sessionEnded,
this, &QQuick3DXrView::handleSessionEnded);
541 connect(&m_xrManager, &QQuick3DXrManager::frameReady,
this, &QQuick3DXrView::frameReady);
542 connect(&m_xrManager, &QQuick3DXrManager::referenceSpaceChanged,
this, &QQuick3DXrView::referenceSpaceChanged);
543 connect(&m_xrManager, &QQuick3DXrManager::multiViewRenderingEnabledChanged,
this, &QQuick3DXrView::multiViewRenderingEnabledChanged);
544 connect(&m_xrManager, &QQuick3DXrManager::initialized,
this, &QQuick3DXrView::init, Qt::UniqueConnection);
546 if (!m_xrManager.isReady() && !m_xrManager.initialize()) {
547 qCDebug(lcQuick3DXr,
"Waiting for XR platform to be initialized");
552 if (!m_xrManager.initialize()) {
553 QString errorString = m_xrManager.errorString();
554 if (errorString.isEmpty())
555 errorString = tr(
"Failed to initialize XR platform");
556 qWarning(
"\n%s\n", qPrintable(errorString));
557 QMetaObject::invokeMethod(
this,
"initializeFailed", Qt::QueuedConnection, errorString);
562 QSSG_CHECK_X(m_xrManager.m_vrViewport ==
nullptr,
"View3D already created!");
563 auto viewport =
new QQuick3DViewport(QQuick3DViewport::PrivateInstanceType::XrViewInstance);
564 viewport->setRenderMode(QQuick3DViewport::Underlay);
565 auto contentItem = m_xrManager.m_quickWindow->contentItem();
566 viewport->setParentItem(contentItem);
567 m_xrManager.m_vrViewport = viewport;
568 viewport->setImportScene(
this);
570 contentItem->forceActiveFocus(Qt::MouseFocusReason);
572 connect(contentItem, &QQuickItem::heightChanged,
this, &QQuick3DXrView::updateViewportGeometry);
573 connect(contentItem, &QQuickItem::widthChanged,
this, &QQuick3DXrView::updateViewportGeometry);
574 connect(contentItem, &QQuickItem::xChanged,
this, &QQuick3DXrView::updateViewportGeometry);
575 connect(contentItem, &QQuickItem::yChanged,
this, &QQuick3DXrView::updateViewportGeometry);
577 QQuick3DSceneEnvironment *env = environment();
579 connect(env, &QQuick3DSceneEnvironment::backgroundModeChanged,
this, &QQuick3DXrView::handleClearColorChanged);
580 connect(env, &QQuick3DSceneEnvironment::clearColorChanged,
this, &QQuick3DXrView::handleClearColorChanged);
581 connect(env, &QQuick3DSceneEnvironment::antialiasingModeChanged,
this, &QQuick3DXrView::handleAAChanged);
582 connect(env, &QQuick3DSceneEnvironment::antialiasingQualityChanged,
this, &QQuick3DXrView::handleAAChanged);
586 setEnvironment(m_pendingSceneEnvironment);
587 m_pendingSceneEnvironment =
nullptr;
589 m_xrManager.update();
591 m_isInitialized =
true;
593 return m_isInitialized;
597
598
599
600
601
602
603
604
605
606
607QQuick3DPickResult QQuick3DXrView::rayPick(
const QVector3D &origin,
const QVector3D &direction)
const
609 if (!m_xrManager.m_vrViewport)
610 return QQuick3DPickResult();
612 return m_xrManager.m_vrViewport->rayPick(origin, direction);
616
617
618
619
620
621
622
623
624
625
626
627
628
629QList<QQuick3DPickResult> QQuick3DXrView::rayPickAll(
const QVector3D &origin,
const QVector3D &direction)
const
631 if (!m_xrManager.m_vrViewport)
634 return m_xrManager.m_vrViewport->rayPickAll(origin, direction);
638
639
640
641
642
643
644
645QQuick3DPickResult QQuick3DXrView::rayPick(
const QVector3D &origin,
const QVector3D &direction, QQuick3DModel *model)
const
647 if (!m_xrManager.m_vrViewport)
648 return QQuick3DPickResult();
650 return m_xrManager.m_vrViewport->rayPick(origin, direction, model);
654
655
656
657
658
659
660
661
662
663QQuick3DPickResult QQuick3DXrView::closestPointPick(
const QVector3D &origin,
float radius, QQuick3DModel *model)
const
665 if (!m_xrManager.m_vrViewport)
666 return QQuick3DPickResult();
668 return m_xrManager.m_vrViewport->closestPointPick(origin, radius, model);
672
673
674
675
676
677
678
680void QQuick3DXrView::setTouchpoint(QQuickItem *target,
const QPointF &position,
int pointId,
bool pressed)
682 view3d()->setTouchpoint(target, position, pointId, pressed);
686struct QQuick3DXrView::XrTouchStates
688 QHash<
int, TouchState> points;
692
693
694
695
696
697
698
699
700
701
702
703
704
705
707QVector3D QQuick3DXrView::processTouch(
const QVector3D &pos,
int pointId)
712 m_touchStates =
new XrTouchStates;
713 TouchState &state = m_touchStates->points[pointId];
714 state.pointId = pointId;
716 bool grabbed =
false;
717 const bool prevTarget = !std::holds_alternative<std::monostate>(state.target);
719 grabbed = handleVirtualTouch(state.target, pos, &state, &offset);
722 for (
auto *item : std::as_const(m_xrItems)) {
725 grabbed = handleVirtualTouch(item, pos, &state, &offset);
732 constexpr float pickRadius = 10.0;
733 auto pickResult = closestPointPick(pos, pickRadius);
735 if (QQuick3DModel *obj = pickResult.objectHit())
736 grabbed = handleVirtualTouch(obj, pos, &state, &offset);
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
792#define Q_TOUCHPOINT_STATE(prop) { QStringLiteral(#prop), QVariant::fromValue(it->prop) }
793QVariantMap QQuick3DXrView::touchpointState(
int pointId)
const
795 auto constexpr end = QHash<
int, TouchState>::const_iterator();
796 auto it = m_touchStates ? m_touchStates->points.constFind(pointId) : end;
799 return { { QStringLiteral(
"grabbed"), QVariant::fromValue(
false) } };
800 auto *itemPointer = std::get_if<QQuick3DXrItem *>(&it->target);
801 QQuick3DXrItem *xrItem = itemPointer ? *itemPointer :
nullptr;
802 auto *modelPointer = std::get_if<QQuick3DModel *>(&it->target);
803 QQuick3DModel *model = modelPointer ? *modelPointer :
nullptr;
804 return { { QStringLiteral(
"target"), QVariant::fromValue(xrItem) },
812 { QStringLiteral(
"model"), QVariant::fromValue(model) }};
814#undef Q_TOUCHPOINT_STATE
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
849QQuick3DXrView::ReferenceSpace QQuick3DXrView::referenceSpace()
const
851 return ReferenceSpace(m_xrManager.getReferenceSpace());
854void QQuick3DXrView::setReferenceSpace(ReferenceSpace newReferenceSpace)
856 m_xrManager.setReferenceSpace(QtQuick3DXr::ReferenceSpace(newReferenceSpace));
859bool QQuick3DXrView::depthSubmissionEnabled()
const
861 if (!m_xrManager.isValid()) {
862 qWarning(
"Attempted to check depth submission mode without a valid XR manager");
866 return m_xrManager.isDepthSubmissionEnabled();
870
871
872
873
874
875
876bool QQuick3DXrView::isMultiViewRenderingSupported()
const
878 if (!m_xrManager.isValid())
881 return m_xrManager.isMultiViewRenderingSupported();
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908bool QQuick3DXrView::multiViewRenderingEnabled()
const
910 if (!m_xrManager.isValid())
913 return m_xrManager.isMultiViewRenderingEnabled();
916void QQuick3DXrView::registerXrItem(QQuick3DXrItem *newXrItem)
918 m_xrItems.append(newXrItem);
921void QQuick3DXrView::unregisterXrItem(QQuick3DXrItem *xrItem)
923 m_xrItems.removeAll(xrItem);
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
977void QQuick3DXrView::setDepthSubmissionEnabled(
bool enable)
979 if (!m_xrManager.isValid()) {
980 qWarning(
"Attempted to set depth submission mode without a valid XR manager");
984 const bool orgDepthSubmission = m_xrManager.isDepthSubmissionEnabled();
986 m_xrManager.setDepthSubmissionEnabled(enable);
988 if (orgDepthSubmission != m_xrManager.isDepthSubmissionEnabled())
989 emit depthSubmissionEnabledChanged();
992void QQuick3DXrView::setXROrigin(QQuick3DXrOrigin *newXrOrigin)
994 if (m_xrOrigin == newXrOrigin)
997 QQuick3DObjectPrivate::attachWatcher(
this, &QQuick3DXrView::setXROrigin, newXrOrigin, m_xrOrigin);
999 m_xrOrigin = newXrOrigin;
1002 if (m_xrOrigin && !m_xrOrigin->parentItem())
1003 m_xrOrigin->setParentItem(
this);
1005 m_xrManager.setXROrigin(m_xrOrigin);
1007 emit xrOriginChanged();
1010QQuick3DViewport *QQuick3DXrViewPrivate::getView3d(QQuick3DXrView *view)
1012 QSSG_ASSERT(view !=
nullptr,
return nullptr);
1013 return view->view3d();
1017
1018
1019
1020
1021
1024
1025
1026
1027
1030
1031
1032
1033
1034
Combined button and popup list for selecting options.
#define Q_TOUCHPOINT_STATE(prop)
\qmlmethod object XrView::touchpointState(int pointId)