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
46
47
48
49
51QQuick3DXrView::QQuick3DXrView()
52 : m_xrRuntimeInfo(&m_xrManager)
57QQuick3DXrView::~QQuick3DXrView()
59 m_inDestructor =
true;
63
64
65
66
67
68
69
70
71
72
73
74
76QQuick3DXrOrigin *QQuick3DXrView::xrOrigin()
const
82
83
84
86QQuick3DSceneEnvironment *QQuick3DXrView::environment()
const
88 return m_xrManager.m_vrViewport ? m_xrManager.m_vrViewport->environment() :
nullptr;
91QQuick3DViewport *QQuick3DXrView::view3d()
const
93 return m_xrManager.m_vrViewport;
98struct ClosestPointResult
104 QVector3D scenePosition;
106 QVector3D sceneNormal;
107 QVector2D uvPosition;
109 QQuickItem *quickItem =
nullptr;
115static ClosestPointResult closestPointOnXrItem(
const QQuick3DXrItem *xrItem,
const QVector3D &pos,
const float margin = 0)
117 const auto mappedPos = xrItem->mapPositionFromScene(pos);
118 const qreal width = xrItem->width();
119 const qreal height = xrItem->height();
121 const float x = mappedPos.x();
122 const float y = mappedPos.y();
123 const float z = mappedPos.z();
126 const float y1 = -height;
127 const float x2 = width;
130 const bool insideMargin = x1 - margin <= x && x <= x2 + margin && y1 - margin <= y && y <= y2 + margin;
133 const bool inside = x1 <= x && x <= x2 && y1 <= y && y <= y2 && z <= 0;
135 const float distance = mappedPos.z();
137 const QVector3D position{ mappedPos.x(), mappedPos.y(), 0 };
138 const QVector3D scenePosition = xrItem->mapPositionToScene(position);
139 const QVector3D normal{ 0, 0, 1 };
140 const QVector3D sceneNormal = xrItem->mapDirectionToScene(normal);
151 QPointF{mappedPos.x(), -mappedPos.y()},
152 xrItem->contentItem()
157static ClosestPointResult closestPointOnModel(QQuick3DXrView *view, QQuick3DModel *model,
const QVector3D &pos,
const float radius = 0)
159 auto pickResult = view->closestPointPick(pos, radius, model);
160 if (pickResult.objectHit() != model)
163 const QVector3D offset = pos - pickResult.scenePosition();
165 const float signedDistance = QVector3D::dotProduct(offset, pickResult.sceneNormal());
166 const bool inside = signedDistance < 0;
169 QQuickItem *quickItem = pickResult.itemHit();
170 auto uv = pickResult.uvPosition();
172 quickPoint = QPointF(quickItem->x() + uv.x() * quickItem->width(),
173 quickItem->y() - uv.y() * quickItem->height() + quickItem->height());
179 pickResult.position(),
180 pickResult.scenePosition(),
182 pickResult.sceneNormal().normalized(),
190static inline ClosestPointResult closestPointOnItem(QQuick3DXrView *view, QQuick3DXrView::TouchTarget target,
const QVector3D &pos,
const float margin)
192 if (
auto *asXrItem = std::get_if<QQuick3DXrItem*>(&target))
193 return closestPointOnXrItem(*asXrItem, pos, margin);
194 if (
auto *asModel =std::get_if<QQuick3DModel*>(&target))
195 return closestPointOnModel(view, *asModel, pos, margin);
199static inline void sendTouch(QQuick3DXrView *view,
const ClosestPointResult &point,
int pointId,
bool active)
202 view->setTouchpoint(point.quickItem, point.point2D, pointId, active);
211bool QQuick3DXrView::handleVirtualTouch(TouchTarget target,
const QVector3D &pos, TouchState *touchState, QVector3D *offset)
213 constexpr qreal sideMargin = 10;
214 constexpr qreal hoverHeight = 10;
215 constexpr qreal cancelDepth = 50;
217 constexpr qreal releaseHeight = 2;
218 constexpr qreal releaseHeightSquared = releaseHeight * releaseHeight;
219 constexpr qreal smallDistance = 0.5;
220 constexpr qreal sidewaysMax = 3;
221 constexpr qreal sidewaysMaxSquared = sidewaysMax * sidewaysMax;
222 constexpr qreal releaseSpeed = 5.0 / 1000;
223 constexpr int interval = 100;
225 const bool wasGrabbed = touchState->grabbed;
226 const bool wasPressed = touchState->pressed;
227 const qreal wasInside = wasGrabbed && touchState->touchDepth < 0.0;
229 const QVector3D oldScenePos = touchState->previous;
230 const QVector3D oldSceneNormal = touchState->normal;
237 auto closest = closestPointOnItem(
this, target, pos, sideMargin);
240 bool pressed =
false;
242 bool resetTimer =
false;
243 float z = closest.distance;
247 grab = closest.grab && z > -cancelDepth && z < hoverHeight;
256 if (wasInside && closest.inside) {
258 int duration = touchState->timer.elapsed();
259 pressed = wasPressed;
260 const QVector3D movement = pos - oldScenePos;
261 const qreal verticalDistance = QVector3D::dotProduct(movement, oldSceneNormal);
262 const qreal verticalDistanceSquared = verticalDistance * verticalDistance;
263 const qreal horizontalDistanceSquared = movement.lengthSquared() - verticalDistance*verticalDistance;
265 if (verticalDistanceSquared >= releaseHeightSquared && horizontalDistanceSquared < releaseHeightSquared) {
268 pressed = verticalDistance < 0;
270 }
else if (duration >= interval) {
272 if (horizontalDistanceSquared >= sidewaysMaxSquared) {
275 }
else if (pressed ? verticalDistance < smallDistance : verticalDistance > -smallDistance ) {
282 const qreal verticalSpeed = verticalDistance / duration;
283 const bool sufficientSpeed = pressed ? verticalSpeed > releaseSpeed : verticalSpeed < -releaseSpeed;
284 resetTimer = !sufficientSpeed;
289 pressed = closest.inside;
296 grab = closest.grab && closest.distance > 0 && closest.distance < hoverHeight;
302 *offset = closest.scenePosition - pos;
303 touchState->grabbed =
true;
304 touchState->target = target;
305 touchState->touchDistance = qMax(z, 0.0);
306 touchState->touchDepth = z;
307 touchState->pressed = pressed;
308 touchState->cursorPos = closest.point2D;
309 touchState->surfacePoint = closest.scenePosition;
310 touchState->normal = closest.sceneNormal;
311 touchState->uvPosition = closest.uvPosition;
313 touchState->previous = pos;
314 touchState->timer.start();
317 sendTouch(
this, closest, touchState->pointId, pressed);
322 touchState->grabbed =
false;
323 touchState->pressed =
false;
324 touchState->target = {};
325 sendTouch(
this, closest, touchState->pointId,
false);
332
333
334
335bool QQuick3DXrView::passthroughEnabled()
const
337 return m_xrManager.isPassthroughEnabled();
341
342
343
345QQuick3DXrRuntimeInfo *QQuick3DXrView::runtimeInfo()
const
347 return &m_xrRuntimeInfo;
350void QQuick3DXrView::setEnvironment(QQuick3DSceneEnvironment *environment)
352 QQuick3DViewport *view = m_xrManager.m_vrViewport;
357 m_pendingSceneEnvironment = environment;
361 auto oldEnvironment = view->environment();
362 if (oldEnvironment == environment)
366 disconnect(oldEnvironment);
368 view->setEnvironment(environment);
373 environment = view->environment();
375 handleClearColorChanged();
378 connect(environment, &QQuick3DSceneEnvironment::backgroundModeChanged,
this, &QQuick3DXrView::handleClearColorChanged);
379 connect(environment, &QQuick3DSceneEnvironment::clearColorChanged,
this, &QQuick3DXrView::handleClearColorChanged);
380 connect(environment, &QQuick3DSceneEnvironment::antialiasingModeChanged,
this, &QQuick3DXrView::handleAAChanged);
381 connect(environment, &QQuick3DSceneEnvironment::antialiasingQualityChanged,
this, &QQuick3DXrView::handleAAChanged);
383 emit environmentChanged(environment);
387
388
389
391bool QQuick3DXrView::passthroughSupported()
const
393 if (!m_xrManager.isValid())
396 return m_xrManager.supportsPassthrough();
399void QQuick3DXrView::setPassthroughEnabled(
bool enable)
401 if (!m_xrManager.isValid()) {
402 qWarning(
"Attempted to set passthrough mode without a valid XR manager");
406 const bool orgPassthroughEnabled = m_xrManager.isPassthroughEnabled();
407 if (!m_xrManager.setPassthroughEnabled(enable)) {
409 qWarning(
"Enabling passthrough is not supported.");
411 qWarning(
"Disabling passthrough is not supported.");
415 if (orgPassthroughEnabled != m_xrManager.isPassthroughEnabled())
416 emit passthroughEnabledChanged();
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
443QQuick3DXrView::FoveationLevel QQuick3DXrView::fixedFoveation()
const
445 return FoveationLevel(m_xrManager.getFixedFoveationLevel());
448void QQuick3DXrView::setFixedFoveation(FoveationLevel level)
450 const auto orgFoviationLevel = m_xrManager.getFixedFoveationLevel();
451 m_xrManager.setFixedFoveationLevel(QtQuick3DXr::FoveationLevel(level));
452 if (orgFoviationLevel != m_xrManager.getFixedFoveationLevel())
453 emit fixedFoveationChanged();
457
458
459
461bool QQuick3DXrView::isQuitOnSessionEndEnabled()
const
463 return m_quitOnSessionEnd;
466void QQuick3DXrView::setQuitOnSessionEnd(
bool enable)
468 if (m_quitOnSessionEnd == enable)
471 m_quitOnSessionEnd = enable;
472 emit quitOnSessionEndChanged();
475
476
477
479QQuick3DRenderStats *QQuick3DXrView::renderStats()
const
481 return m_xrManager.m_vrViewport ? m_xrManager.m_vrViewport->renderStats() :
nullptr;
484void QQuick3DXrView::updateViewportGeometry()
486 auto contentItem = m_xrManager.m_quickWindow->contentItem();
487 auto viewport = m_xrManager.m_vrViewport;
488 if (viewport->height() != contentItem->height())
489 viewport->setHeight(contentItem->height());
490 if (viewport->width() != contentItem->width())
491 viewport->setWidth(contentItem->width());
492 if (viewport->x() != contentItem->x())
493 viewport->setX(contentItem->x());
494 if (viewport->y() != contentItem->y())
495 viewport->setY(contentItem->y());
498void QQuick3DXrView::handleSessionEnded()
501 if (m_quitOnSessionEnd)
502 QCoreApplication::quit();
505void QQuick3DXrView::handleClearColorChanged()
507 auto env = environment();
510 if (env->backgroundMode() == QQuick3DSceneEnvironment::Color)
511 m_xrManager.m_quickWindow->setColor(env->clearColor());
512 else if (env->backgroundMode() == QQuick3DSceneEnvironment::Transparent)
513 m_xrManager.m_quickWindow->setColor(Qt::transparent);
517void QQuick3DXrView::handleAAChanged()
519 auto env = environment();
521 if (env && env->antialiasingMode() == QQuick3DSceneEnvironment::MSAA) {
522 switch (env->antialiasingQuality()) {
523 case QQuick3DSceneEnvironment::Medium:
526 case QQuick3DSceneEnvironment::High:
529 case QQuick3DSceneEnvironment::VeryHigh:
534 m_xrManager.setSamples(samples);
537bool QQuick3DXrView::init()
539 if (m_isInitialized) {
540 qWarning(
"Already initialized!");
544 connect(&m_xrManager, &QQuick3DXrManager::sessionEnded,
this, &QQuick3DXrView::handleSessionEnded);
545 connect(&m_xrManager, &QQuick3DXrManager::frameReady,
this, &QQuick3DXrView::frameReady);
546 connect(&m_xrManager, &QQuick3DXrManager::referenceSpaceChanged,
this, &QQuick3DXrView::referenceSpaceChanged);
547 connect(&m_xrManager, &QQuick3DXrManager::multiViewRenderingEnabledChanged,
this, &QQuick3DXrView::multiViewRenderingEnabledChanged);
548 connect(&m_xrManager, &QQuick3DXrManager::initialized,
this, &QQuick3DXrView::init, Qt::UniqueConnection);
550 if (!m_xrManager.isReady() && !m_xrManager.initialize()) {
551 qCDebug(lcQuick3DXr,
"Waiting for XR platform to be initialized");
556 if (!m_xrManager.initialize()) {
557 QString errorString = m_xrManager.errorString();
558 if (errorString.isEmpty())
559 errorString = tr(
"Failed to initialize XR platform");
560 qWarning(
"\n%s\n", qPrintable(errorString));
561 QMetaObject::invokeMethod(
this,
"initializeFailed", Qt::QueuedConnection, errorString);
566 QSSG_CHECK_X(m_xrManager.m_vrViewport ==
nullptr,
"View3D already created!");
567 auto viewport =
new QQuick3DViewport(QQuick3DViewport::PrivateInstanceType::XrViewInstance);
568 viewport->setRenderMode(QQuick3DViewport::Underlay);
569 auto contentItem = m_xrManager.m_quickWindow->contentItem();
570 viewport->setParentItem(contentItem);
571 m_xrManager.m_vrViewport = viewport;
572 viewport->setImportScene(
this);
574 contentItem->forceActiveFocus(Qt::MouseFocusReason);
576 connect(contentItem, &QQuickItem::heightChanged,
this, &QQuick3DXrView::updateViewportGeometry);
577 connect(contentItem, &QQuickItem::widthChanged,
this, &QQuick3DXrView::updateViewportGeometry);
578 connect(contentItem, &QQuickItem::xChanged,
this, &QQuick3DXrView::updateViewportGeometry);
579 connect(contentItem, &QQuickItem::yChanged,
this, &QQuick3DXrView::updateViewportGeometry);
581 QQuick3DSceneEnvironment *env = environment();
583 connect(env, &QQuick3DSceneEnvironment::backgroundModeChanged,
this, &QQuick3DXrView::handleClearColorChanged);
584 connect(env, &QQuick3DSceneEnvironment::clearColorChanged,
this, &QQuick3DXrView::handleClearColorChanged);
585 connect(env, &QQuick3DSceneEnvironment::antialiasingModeChanged,
this, &QQuick3DXrView::handleAAChanged);
586 connect(env, &QQuick3DSceneEnvironment::antialiasingQualityChanged,
this, &QQuick3DXrView::handleAAChanged);
590 setEnvironment(m_pendingSceneEnvironment);
591 m_pendingSceneEnvironment =
nullptr;
593 m_xrManager.update();
595 m_isInitialized =
true;
597 return m_isInitialized;
601
602
603
604
605
606
607
608
609
610
611QQuick3DPickResult QQuick3DXrView::rayPick(
const QVector3D &origin,
const QVector3D &direction)
const
613 if (!m_xrManager.m_vrViewport)
614 return QQuick3DPickResult();
616 return m_xrManager.m_vrViewport->rayPick(origin, direction);
620
621
622
623
624
625
626
627
628
629
630
631
632
633QList<QQuick3DPickResult> QQuick3DXrView::rayPickAll(
const QVector3D &origin,
const QVector3D &direction)
const
635 if (!m_xrManager.m_vrViewport)
638 return m_xrManager.m_vrViewport->rayPickAll(origin, direction);
642
643
644
645
646
647
648
649QQuick3DPickResult QQuick3DXrView::rayPick(
const QVector3D &origin,
const QVector3D &direction, QQuick3DModel *model)
const
651 if (!m_xrManager.m_vrViewport)
652 return QQuick3DPickResult();
654 return m_xrManager.m_vrViewport->rayPick(origin, direction, model);
658
659
660
661
662
663
664
665
666
667QQuick3DPickResult QQuick3DXrView::closestPointPick(
const QVector3D &origin,
float radius, QQuick3DModel *model)
const
669 if (!m_xrManager.m_vrViewport)
670 return QQuick3DPickResult();
672 return m_xrManager.m_vrViewport->closestPointPick(origin, radius, model);
676
677
678
679
680
681
682
684void QQuick3DXrView::setTouchpoint(QQuickItem *target,
const QPointF &position,
int pointId,
bool pressed)
686 view3d()->setTouchpoint(target, position, pointId, pressed);
690struct QQuick3DXrView::XrTouchStates
692 QHash<
int, TouchState> points;
696
697
698
699
700
701
702
703
704
705
706
707
708
709
711QVector3D QQuick3DXrView::processTouch(
const QVector3D &pos,
int pointId)
716 m_touchStates =
new XrTouchStates;
717 TouchState &state = m_touchStates->points[pointId];
718 state.pointId = pointId;
720 bool grabbed =
false;
721 const bool prevTarget = !std::holds_alternative<std::monostate>(state.target);
723 grabbed = handleVirtualTouch(state.target, pos, &state, &offset);
726 for (
auto *item : std::as_const(m_xrItems)) {
729 grabbed = handleVirtualTouch(item, pos, &state, &offset);
736 constexpr float pickRadius = 10.0;
737 auto pickResult = closestPointPick(pos, pickRadius);
739 if (QQuick3DModel *obj = pickResult.objectHit())
740 grabbed = handleVirtualTouch(obj, pos, &state, &offset);
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
791
792
793
794
796#define Q_TOUCHPOINT_STATE(prop) { QStringLiteral(#prop), QVariant::fromValue(it->prop) }
797QVariantMap QQuick3DXrView::touchpointState(
int pointId)
const
799 auto constexpr end = QHash<
int, TouchState>::const_iterator();
800 auto it = m_touchStates ? m_touchStates->points.constFind(pointId) : end;
803 return { { QStringLiteral(
"grabbed"), QVariant::fromValue(
false) } };
804 auto *itemPointer = std::get_if<QQuick3DXrItem *>(&it->target);
805 QQuick3DXrItem *xrItem = itemPointer ? *itemPointer :
nullptr;
806 auto *modelPointer = std::get_if<QQuick3DModel *>(&it->target);
807 QQuick3DModel *model = modelPointer ? *modelPointer :
nullptr;
808 return { { QStringLiteral(
"target"), QVariant::fromValue(xrItem) },
816 { QStringLiteral(
"model"), QVariant::fromValue(model) }};
818#undef Q_TOUCHPOINT_STATE
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
848
849
850
851
852
854QQuick3DXrView::ReferenceSpace QQuick3DXrView::referenceSpace()
const
856 return ReferenceSpace(m_xrManager.getReferenceSpace());
859void QQuick3DXrView::setReferenceSpace(ReferenceSpace newReferenceSpace)
861 m_xrManager.setReferenceSpace(QtQuick3DXr::ReferenceSpace(newReferenceSpace));
864bool QQuick3DXrView::depthSubmissionEnabled()
const
866 if (!m_xrManager.isValid()) {
867 qWarning(
"Attempted to check depth submission mode without a valid XR manager");
871 return m_xrManager.isDepthSubmissionEnabled();
875
876
877
878
879
880
881bool QQuick3DXrView::isMultiViewRenderingSupported()
const
883 if (!m_xrManager.isValid())
886 return m_xrManager.isMultiViewRenderingSupported();
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913bool QQuick3DXrView::multiViewRenderingEnabled()
const
915 if (!m_xrManager.isValid())
918 return m_xrManager.isMultiViewRenderingEnabled();
921void QQuick3DXrView::registerXrItem(QQuick3DXrItem *newXrItem)
923 m_xrItems.append(newXrItem);
926void QQuick3DXrView::unregisterXrItem(QQuick3DXrItem *xrItem)
928 m_xrItems.removeAll(xrItem);
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
976
977
978
979
980
982void QQuick3DXrView::setDepthSubmissionEnabled(
bool enable)
984 if (!m_xrManager.isValid()) {
985 qWarning(
"Attempted to set depth submission mode without a valid XR manager");
989 const bool orgDepthSubmission = m_xrManager.isDepthSubmissionEnabled();
991 m_xrManager.setDepthSubmissionEnabled(enable);
993 if (orgDepthSubmission != m_xrManager.isDepthSubmissionEnabled())
994 emit depthSubmissionEnabledChanged();
997void QQuick3DXrView::setXROrigin(QQuick3DXrOrigin *newXrOrigin)
999 if (m_xrOrigin == newXrOrigin)
1002 QQuick3DObjectPrivate::attachWatcher(
this, &QQuick3DXrView::setXROrigin, newXrOrigin, m_xrOrigin);
1004 m_xrOrigin = newXrOrigin;
1007 if (m_xrOrigin && !m_xrOrigin->parentItem())
1008 m_xrOrigin->setParentItem(
this);
1010 m_xrManager.setXROrigin(m_xrOrigin);
1012 emit xrOriginChanged();
1015QQuick3DViewport *QQuick3DXrViewPrivate::getView3d(QQuick3DXrView *view)
1017 QSSG_ASSERT(view !=
nullptr,
return nullptr);
1018 return view->view3d();
1022
1023
1024
1025
1026
1029
1030
1031
1032
1035
1036
1037
1038
1039
Combined button and popup list for selecting options.
#define Q_TOUCHPOINT_STATE(prop)
\qmlmethod object XrView::touchpointState(int pointId)