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
838QQuick3DXrView::ReferenceSpace QQuick3DXrView::referenceSpace()
const
840 return ReferenceSpace(m_xrManager.getReferenceSpace());
843void QQuick3DXrView::setReferenceSpace(ReferenceSpace newReferenceSpace)
845 m_xrManager.setReferenceSpace(QtQuick3DXr::ReferenceSpace(newReferenceSpace));
848bool QQuick3DXrView::depthSubmissionEnabled()
const
850 if (!m_xrManager.isValid()) {
851 qWarning(
"Attempted to check depth submission mode without a valid XR manager");
855 return m_xrManager.isDepthSubmissionEnabled();
859
860
861
862
863
864
865bool QQuick3DXrView::isMultiViewRenderingSupported()
const
867 if (!m_xrManager.isValid())
870 return m_xrManager.isMultiViewRenderingSupported();
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897bool QQuick3DXrView::multiViewRenderingEnabled()
const
899 if (!m_xrManager.isValid())
902 return m_xrManager.isMultiViewRenderingEnabled();
905void QQuick3DXrView::registerXrItem(QQuick3DXrItem *newXrItem)
907 m_xrItems.append(newXrItem);
910void QQuick3DXrView::unregisterXrItem(QQuick3DXrItem *xrItem)
912 m_xrItems.removeAll(xrItem);
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
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
966void QQuick3DXrView::setDepthSubmissionEnabled(
bool enable)
968 if (!m_xrManager.isValid()) {
969 qWarning(
"Attempted to set depth submission mode without a valid XR manager");
973 const bool orgDepthSubmission = m_xrManager.isDepthSubmissionEnabled();
975 m_xrManager.setDepthSubmissionEnabled(enable);
977 if (orgDepthSubmission != m_xrManager.isDepthSubmissionEnabled())
978 emit depthSubmissionEnabledChanged();
981void QQuick3DXrView::setXROrigin(QQuick3DXrOrigin *newXrOrigin)
983 if (m_xrOrigin == newXrOrigin)
986 QQuick3DObjectPrivate::attachWatcher(
this, &QQuick3DXrView::setXROrigin, newXrOrigin, m_xrOrigin);
988 m_xrOrigin = newXrOrigin;
991 if (m_xrOrigin && !m_xrOrigin->parentItem())
992 m_xrOrigin->setParentItem(
this);
994 m_xrManager.setXROrigin(m_xrOrigin);
996 emit xrOriginChanged();
999QQuick3DViewport *QQuick3DXrViewPrivate::getView3d(QQuick3DXrView *view)
1001 QSSG_ASSERT(view !=
nullptr,
return nullptr);
1002 return view->view3d();
1006
1007
1008
1009
1010
1013
1014
1015
1016
1019
1020
1021
1022
1023
Combined button and popup list for selecting options.
#define Q_TOUCHPOINT_STATE(prop)
\qmlmethod object XrView::touchpointState(int pointId)