9#include <QLoggingCategory>
11#include <QtQuick3DUtils/private/qssgassert_p.h>
17Q_DECLARE_LOGGING_CATEGORY(lcQuick3DXr);
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
45QQuick3DXrView::QQuick3DXrView()
46 : m_xrRuntimeInfo(&m_xrManager)
51QQuick3DXrView::~QQuick3DXrView()
53 m_inDestructor =
true;
57
58
59
60
61
62
63
64
65
66
67
68
70QQuick3DXrOrigin *QQuick3DXrView::xrOrigin()
const
76
77
78
80QQuick3DSceneEnvironment *QQuick3DXrView::environment()
const
82 return m_xrManager.m_vrViewport ? m_xrManager.m_vrViewport->environment() :
nullptr;
85QQuick3DViewport *QQuick3DXrView::view3d()
const
87 return m_xrManager.m_vrViewport;
92struct ClosestPointResult
98 QVector3D scenePosition;
100 QVector3D sceneNormal;
101 QVector2D uvPosition;
103 QQuickItem *quickItem =
nullptr;
109static ClosestPointResult closestPointOnXrItem(
const QQuick3DXrItem *xrItem,
const QVector3D &pos,
const float margin = 0)
111 const auto mappedPos = xrItem->mapPositionFromScene(pos);
112 const qreal width = xrItem->width();
113 const qreal height = xrItem->height();
115 const float x = mappedPos.x();
116 const float y = mappedPos.y();
117 const float z = mappedPos.z();
120 const float y1 = -height;
121 const float x2 = width;
124 const bool insideMargin = x1 - margin <= x && x <= x2 + margin && y1 - margin <= y && y <= y2 + margin;
127 const bool inside = x1 <= x && x <= x2 && y1 <= y && y <= y2 && z <= 0;
129 const float distance = mappedPos.z();
131 const QVector3D position{ mappedPos.x(), mappedPos.y(), 0 };
132 const QVector3D scenePosition = xrItem->mapPositionToScene(position);
133 const QVector3D normal{ 0, 0, 1 };
134 const QVector3D sceneNormal = xrItem->mapDirectionToScene(normal);
145 QPointF{mappedPos.x(), -mappedPos.y()},
146 xrItem->contentItem()
151static ClosestPointResult closestPointOnModel(QQuick3DXrView *view, QQuick3DModel *model,
const QVector3D &pos,
const float radius = 0)
153 auto pickResult = view->closestPointPick(pos, radius, model);
154 if (pickResult.objectHit() != model)
157 const QVector3D offset = pos - pickResult.scenePosition();
159 const float signedDistance = QVector3D::dotProduct(offset, pickResult.sceneNormal());
160 const bool inside = signedDistance < 0;
163 QQuickItem *quickItem = pickResult.itemHit();
164 auto uv = pickResult.uvPosition();
166 quickPoint = QPointF(quickItem->x() + uv.x() * quickItem->width(),
167 quickItem->y() - uv.y() * quickItem->height() + quickItem->height());
173 pickResult.position(),
174 pickResult.scenePosition(),
176 pickResult.sceneNormal().normalized(),
184static inline ClosestPointResult closestPointOnItem(QQuick3DXrView *view, QQuick3DXrView::TouchTarget target,
const QVector3D &pos,
const float margin)
186 if (
auto *asXrItem = std::get_if<QQuick3DXrItem*>(&target))
187 return closestPointOnXrItem(*asXrItem, pos, margin);
188 if (
auto *asModel =std::get_if<QQuick3DModel*>(&target))
189 return closestPointOnModel(view, *asModel, pos, margin);
193static inline void sendTouch(QQuick3DXrView *view,
const ClosestPointResult &point,
int pointId,
bool active)
196 view->setTouchpoint(point.quickItem, point.point2D, pointId, active);
205bool QQuick3DXrView::handleVirtualTouch(TouchTarget target,
const QVector3D &pos, TouchState *touchState, QVector3D *offset)
207 constexpr qreal sideMargin = 10;
208 constexpr qreal hoverHeight = 10;
209 constexpr qreal cancelDepth = 50;
211 constexpr qreal releaseHeight = 2;
212 constexpr qreal releaseHeightSquared = releaseHeight * releaseHeight;
213 constexpr qreal smallDistance = 0.5;
214 constexpr qreal sidewaysMax = 3;
215 constexpr qreal sidewaysMaxSquared = sidewaysMax * sidewaysMax;
216 constexpr qreal releaseSpeed = 5.0 / 1000;
217 constexpr int interval = 100;
219 const bool wasGrabbed = touchState->grabbed;
220 const bool wasPressed = touchState->pressed;
221 const qreal wasInside = wasGrabbed && touchState->touchDepth < 0.0;
223 const QVector3D oldScenePos = touchState->previous;
224 const QVector3D oldSceneNormal = touchState->normal;
231 auto closest = closestPointOnItem(
this, target, pos, sideMargin);
234 bool pressed =
false;
236 bool resetTimer =
false;
237 float z = closest.distance;
241 grab = closest.grab && z > -cancelDepth && z < hoverHeight;
250 if (wasInside && closest.inside) {
252 int duration = touchState->timer.elapsed();
253 pressed = wasPressed;
254 const QVector3D movement = pos - oldScenePos;
255 const qreal verticalDistance = QVector3D::dotProduct(movement, oldSceneNormal);
256 const qreal verticalDistanceSquared = verticalDistance * verticalDistance;
257 const qreal horizontalDistanceSquared = movement.lengthSquared() - verticalDistance*verticalDistance;
259 if (verticalDistanceSquared >= releaseHeightSquared && horizontalDistanceSquared < releaseHeightSquared) {
262 pressed = verticalDistance < 0;
264 }
else if (duration >= interval) {
266 if (horizontalDistanceSquared >= sidewaysMaxSquared) {
269 }
else if (pressed ? verticalDistance < smallDistance : verticalDistance > -smallDistance ) {
276 const qreal verticalSpeed = verticalDistance / duration;
277 const bool sufficientSpeed = pressed ? verticalSpeed > releaseSpeed : verticalSpeed < -releaseSpeed;
278 resetTimer = !sufficientSpeed;
283 pressed = closest.inside;
290 grab = closest.grab && closest.distance > 0 && closest.distance < hoverHeight;
296 *offset = closest.scenePosition - pos;
297 touchState->grabbed =
true;
298 touchState->target = target;
299 touchState->touchDistance = qMax(z, 0.0);
300 touchState->touchDepth = z;
301 touchState->pressed = pressed;
302 touchState->cursorPos = closest.point2D;
303 touchState->surfacePoint = closest.scenePosition;
304 touchState->normal = closest.sceneNormal;
305 touchState->uvPosition = closest.uvPosition;
307 touchState->previous = pos;
308 touchState->timer.start();
311 sendTouch(
this, closest, touchState->pointId, pressed);
316 touchState->grabbed =
false;
317 touchState->pressed =
false;
318 touchState->target = {};
319 sendTouch(
this, closest, touchState->pointId,
false);
326
327
328
329bool QQuick3DXrView::passthroughEnabled()
const
331 return m_xrManager.isPassthroughEnabled();
335
336
337
339QQuick3DXrRuntimeInfo *QQuick3DXrView::runtimeInfo()
const
341 return &m_xrRuntimeInfo;
344void QQuick3DXrView::setEnvironment(QQuick3DSceneEnvironment *environment)
346 QQuick3DViewport *view = m_xrManager.m_vrViewport;
351 m_pendingSceneEnvironment = environment;
355 auto oldEnvironment = view->environment();
356 if (oldEnvironment == environment)
360 disconnect(oldEnvironment);
362 view->setEnvironment(environment);
367 environment = view->environment();
369 handleClearColorChanged();
372 connect(environment, &QQuick3DSceneEnvironment::backgroundModeChanged,
this, &QQuick3DXrView::handleClearColorChanged);
373 connect(environment, &QQuick3DSceneEnvironment::clearColorChanged,
this, &QQuick3DXrView::handleClearColorChanged);
374 connect(environment, &QQuick3DSceneEnvironment::antialiasingModeChanged,
this, &QQuick3DXrView::handleAAChanged);
375 connect(environment, &QQuick3DSceneEnvironment::antialiasingQualityChanged,
this, &QQuick3DXrView::handleAAChanged);
377 emit environmentChanged(environment);
381
382
383
385bool QQuick3DXrView::passthroughSupported()
const
387 if (!m_xrManager.isValid())
390 return m_xrManager.supportsPassthrough();
393void QQuick3DXrView::setPassthroughEnabled(
bool enable)
395 if (!m_xrManager.isValid()) {
396 qWarning(
"Attempted to set passthrough mode without a valid XR manager");
400 const bool orgPassthroughEnabled = m_xrManager.isPassthroughEnabled();
402 if (!m_xrManager.setPassthroughEnabled(enable)) {
403 qWarning(
"Enabling Passthrough is not supported.");
407 if (orgPassthroughEnabled != m_xrManager.isPassthroughEnabled())
408 emit passthroughEnabledChanged();
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
435QQuick3DXrView::FoveationLevel QQuick3DXrView::fixedFoveation()
const
437 return FoveationLevel(m_xrManager.getFixedFoveationLevel());
440void QQuick3DXrView::setFixedFoveation(FoveationLevel level)
442 const auto orgFoviationLevel = m_xrManager.getFixedFoveationLevel();
443 m_xrManager.setFixedFoveationLevel(QtQuick3DXr::FoveationLevel(level));
444 if (orgFoviationLevel != m_xrManager.getFixedFoveationLevel())
445 emit fixedFoveationChanged();
449
450
451
453bool QQuick3DXrView::isQuitOnSessionEndEnabled()
const
455 return m_quitOnSessionEnd;
458void QQuick3DXrView::setQuitOnSessionEnd(
bool enable)
460 if (m_quitOnSessionEnd == enable)
463 m_quitOnSessionEnd = enable;
464 emit quitOnSessionEndChanged();
467
468
469
471QQuick3DRenderStats *QQuick3DXrView::renderStats()
const
473 return m_xrManager.m_vrViewport ? m_xrManager.m_vrViewport->renderStats() :
nullptr;
476void QQuick3DXrView::updateViewportGeometry()
478 auto contentItem = m_xrManager.m_quickWindow->contentItem();
479 auto viewport = m_xrManager.m_vrViewport;
480 if (viewport->height() != contentItem->height())
481 viewport->setHeight(contentItem->height());
482 if (viewport->width() != contentItem->width())
483 viewport->setWidth(contentItem->width());
484 if (viewport->x() != contentItem->x())
485 viewport->setX(contentItem->x());
486 if (viewport->y() != contentItem->y())
487 viewport->setY(contentItem->y());
490void QQuick3DXrView::handleSessionEnded()
493 if (m_quitOnSessionEnd)
494 QCoreApplication::quit();
497void QQuick3DXrView::handleClearColorChanged()
499 auto env = environment();
502 if (env->backgroundMode() == QQuick3DSceneEnvironment::Color)
503 m_xrManager.m_quickWindow->setColor(env->clearColor());
504 else if (env->backgroundMode() == QQuick3DSceneEnvironment::Transparent)
505 m_xrManager.m_quickWindow->setColor(Qt::transparent);
509void QQuick3DXrView::handleAAChanged()
511 auto env = environment();
513 if (env && env->antialiasingMode() == QQuick3DSceneEnvironment::MSAA) {
514 switch (env->antialiasingQuality()) {
515 case QQuick3DSceneEnvironment::Medium:
518 case QQuick3DSceneEnvironment::High:
521 case QQuick3DSceneEnvironment::VeryHigh:
526 m_xrManager.setSamples(samples);
529bool QQuick3DXrView::init()
531 if (m_isInitialized) {
532 qWarning(
"Already initialized!");
536 connect(&m_xrManager, &QQuick3DXrManager::sessionEnded,
this, &QQuick3DXrView::handleSessionEnded);
537 connect(&m_xrManager, &QQuick3DXrManager::frameReady,
this, &QQuick3DXrView::frameReady);
538 connect(&m_xrManager, &QQuick3DXrManager::referenceSpaceChanged,
this, &QQuick3DXrView::referenceSpaceChanged);
539 connect(&m_xrManager, &QQuick3DXrManager::multiViewRenderingEnabledChanged,
this, &QQuick3DXrView::multiViewRenderingEnabledChanged);
540 connect(&m_xrManager, &QQuick3DXrManager::initialized,
this, &QQuick3DXrView::init, Qt::UniqueConnection);
542 if (!m_xrManager.isReady() && !m_xrManager.initialize()) {
543 qCDebug(lcQuick3DXr,
"Waiting for XR platform to be initialized");
548 if (!m_xrManager.initialize()) {
549 QString errorString = m_xrManager.errorString();
550 if (errorString.isEmpty())
551 errorString = tr(
"Failed to initialize XR platform");
552 qWarning(
"\n%s\n", qPrintable(errorString));
553 QMetaObject::invokeMethod(
this,
"initializeFailed", Qt::QueuedConnection, errorString);
558 QSSG_CHECK_X(m_xrManager.m_vrViewport ==
nullptr,
"View3D already created!");
559 auto viewport =
new QQuick3DViewport(QQuick3DViewport::PrivateInstanceType::XrViewInstance);
560 viewport->setRenderMode(QQuick3DViewport::Underlay);
561 auto contentItem = m_xrManager.m_quickWindow->contentItem();
562 viewport->setParentItem(contentItem);
563 m_xrManager.m_vrViewport = viewport;
564 viewport->setImportScene(
this);
566 contentItem->forceActiveFocus(Qt::MouseFocusReason);
568 connect(contentItem, &QQuickItem::heightChanged,
this, &QQuick3DXrView::updateViewportGeometry);
569 connect(contentItem, &QQuickItem::widthChanged,
this, &QQuick3DXrView::updateViewportGeometry);
570 connect(contentItem, &QQuickItem::xChanged,
this, &QQuick3DXrView::updateViewportGeometry);
571 connect(contentItem, &QQuickItem::yChanged,
this, &QQuick3DXrView::updateViewportGeometry);
573 QQuick3DSceneEnvironment *env = environment();
575 connect(env, &QQuick3DSceneEnvironment::backgroundModeChanged,
this, &QQuick3DXrView::handleClearColorChanged);
576 connect(env, &QQuick3DSceneEnvironment::clearColorChanged,
this, &QQuick3DXrView::handleClearColorChanged);
577 connect(env, &QQuick3DSceneEnvironment::antialiasingModeChanged,
this, &QQuick3DXrView::handleAAChanged);
578 connect(env, &QQuick3DSceneEnvironment::antialiasingQualityChanged,
this, &QQuick3DXrView::handleAAChanged);
582 setEnvironment(m_pendingSceneEnvironment);
583 m_pendingSceneEnvironment =
nullptr;
585 m_xrManager.update();
587 m_isInitialized =
true;
589 return m_isInitialized;
593
594
595
596
597
598
599
600
601
602
603QQuick3DPickResult QQuick3DXrView::rayPick(
const QVector3D &origin,
const QVector3D &direction)
const
605 return m_xrManager.m_vrViewport->rayPick(origin, direction);
609
610
611
612
613
614
615
616
617
618
619
620
621
622QList<QQuick3DPickResult> QQuick3DXrView::rayPickAll(
const QVector3D &origin,
const QVector3D &direction)
const
624 return m_xrManager.m_vrViewport->rayPickAll(origin, direction);
628
629
630
631
632
633
634
635QQuick3DPickResult QQuick3DXrView::rayPick(
const QVector3D &origin,
const QVector3D &direction, QQuick3DModel *model)
const
637 return m_xrManager.m_vrViewport->rayPick(origin, direction, model);
641
642
643
644
645
646
647
648
649
650QQuick3DPickResult QQuick3DXrView::closestPointPick(
const QVector3D &origin,
float radius, QQuick3DModel *model)
const
652 return m_xrManager.m_vrViewport->closestPointPick(origin, radius, model);
656
657
658
659
660
661
662
664void QQuick3DXrView::setTouchpoint(QQuickItem *target,
const QPointF &position,
int pointId,
bool pressed)
666 view3d()->setTouchpoint(target, position, pointId, pressed);
670struct QQuick3DXrView::XrTouchStates
672 QHash<
int, TouchState> points;
676
677
678
679
680
681
682
683
684
685
686
687
688
689
691QVector3D QQuick3DXrView::processTouch(
const QVector3D &pos,
int pointId)
696 m_touchStates =
new XrTouchStates;
697 TouchState &state = m_touchStates->points[pointId];
698 state.pointId = pointId;
700 bool grabbed =
false;
701 const bool prevTarget = !std::holds_alternative<std::monostate>(state.target);
703 grabbed = handleVirtualTouch(state.target, pos, &state, &offset);
706 for (
auto *item : std::as_const(m_xrItems)) {
709 grabbed = handleVirtualTouch(item, pos, &state, &offset);
716 constexpr float pickRadius = 10.0;
717 auto pickResult = closestPointPick(pos, pickRadius);
719 if (QQuick3DModel *obj = pickResult.objectHit())
720 grabbed = handleVirtualTouch(obj, pos, &state, &offset);
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
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
776#define Q_TOUCHPOINT_STATE(prop) { QStringLiteral(#prop), QVariant::fromValue(it->prop) }
777QVariantMap QQuick3DXrView::touchpointState(
int pointId)
const
779 auto constexpr end = QHash<
int, TouchState>::const_iterator();
780 auto it = m_touchStates ? m_touchStates->points.constFind(pointId) : end;
783 return { { QStringLiteral(
"grabbed"), QVariant::fromValue(
false) } };
784 auto *itemPointer = std::get_if<QQuick3DXrItem *>(&it->target);
785 QQuick3DXrItem *xrItem = itemPointer ? *itemPointer :
nullptr;
786 auto *modelPointer = std::get_if<QQuick3DModel *>(&it->target);
787 QQuick3DModel *model = modelPointer ? *modelPointer :
nullptr;
788 return { { QStringLiteral(
"target"), QVariant::fromValue(xrItem) },
796 { QStringLiteral(
"model"), QVariant::fromValue(model) }};
798#undef Q_TOUCHPOINT_STATE
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
822QQuick3DXrView::ReferenceSpace QQuick3DXrView::referenceSpace()
const
824 return ReferenceSpace(m_xrManager.getReferenceSpace());
827void QQuick3DXrView::setReferenceSpace(ReferenceSpace newReferenceSpace)
829 m_xrManager.setReferenceSpace(QtQuick3DXr::ReferenceSpace(newReferenceSpace));
832bool QQuick3DXrView::depthSubmissionEnabled()
const
834 if (!m_xrManager.isValid()) {
835 qWarning(
"Attempted to check depth submission mode without a valid XR manager");
839 return m_xrManager.isDepthSubmissionEnabled();
843
844
845
846
847
848
849bool QQuick3DXrView::isMultiViewRenderingSupported()
const
851 if (!m_xrManager.isValid())
854 return m_xrManager.isMultiViewRenderingSupported();
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881bool QQuick3DXrView::multiViewRenderingEnabled()
const
883 if (!m_xrManager.isValid())
886 return m_xrManager.isMultiViewRenderingEnabled();
889void QQuick3DXrView::registerXrItem(QQuick3DXrItem *newXrItem)
891 m_xrItems.append(newXrItem);
894void QQuick3DXrView::unregisterXrItem(QQuick3DXrItem *xrItem)
896 m_xrItems.removeAll(xrItem);
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
950void QQuick3DXrView::setDepthSubmissionEnabled(
bool enable)
952 if (!m_xrManager.isValid()) {
953 qWarning(
"Attempted to set depth submission mode without a valid XR manager");
957 const bool orgDepthSubmission = m_xrManager.isDepthSubmissionEnabled();
959 m_xrManager.setDepthSubmissionEnabled(enable);
961 if (orgDepthSubmission != m_xrManager.isDepthSubmissionEnabled())
962 emit depthSubmissionEnabledChanged();
965void QQuick3DXrView::setXROrigin(QQuick3DXrOrigin *newXrOrigin)
967 if (m_xrOrigin == newXrOrigin)
970 QQuick3DObjectPrivate::attachWatcher(
this, &QQuick3DXrView::setXROrigin, newXrOrigin, m_xrOrigin);
972 m_xrOrigin = newXrOrigin;
975 if (m_xrOrigin && !m_xrOrigin->parentItem())
976 m_xrOrigin->setParentItem(
this);
978 m_xrManager.setXROrigin(m_xrOrigin);
980 emit xrOriginChanged();
983QQuick3DViewport *QQuick3DXrViewPrivate::getView3d(QQuick3DXrView *view)
985 QSSG_ASSERT(view !=
nullptr,
return nullptr);
986 return view->view3d();
990
991
992
993
994
997
998
999
1000
1003
1004
1005
1006
1007
Combined button and popup list for selecting options.
#define Q_TOUCHPOINT_STATE(prop)
\qmlmethod object XrView::touchpointState(int pointId)