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
qquick3dviewport.cpp
Go to the documentation of this file.
1// Copyright (C) 2019 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
7#include "qquick3dtexture_p.h"
10#include "qquick3dcamera_p.h"
11#include "qquick3dmodel_p.h"
13#include "qquick3ditem2d_p.h"
18
19#include <QtQuick3DRuntimeRender/private/qssgrenderlayer_p.h>
20#include <QtQuick3DRuntimeRender/private/qssglayerrenderdata_p.h>
21
22#include <QtQuick3DUtils/private/qssgassert_p.h>
23
24#include <qsgtextureprovider.h>
25#include <QSGSimpleTextureNode>
26#include <QSGRendererInterface>
27#include <QQuickWindow>
28#include <QtQuick/private/qquickitem_p.h>
29#include <QtQuick/private/qquickpointerhandler_p.h>
30
31#include <QtQml>
32
33#include <QtGui/private/qeventpoint_p.h>
34
35#include <QtCore/private/qnumeric_p.h>
36#include <QtCore/qpointer.h>
37
38#include <optional>
39
41
42Q_LOGGING_CATEGORY(lcEv, "qt.quick3d.event")
43Q_LOGGING_CATEGORY(lcPick, "qt.quick3d.pick")
44Q_LOGGING_CATEGORY(lcHover, "qt.quick3d.hover")
45
47{
48 static const bool v = (qEnvironmentVariableIntValue("QT_QUICK3D_FORCE_INPUT_HANDLING") > 0);
49 return v;
50}
51
53{
54 static void removeAll() {
55 for (auto o : owners) {
56 if (!o.isNull())
57 o->setSceneTransform(nullptr);
58 }
59 owners.clear();
60 }
61
63 da->setSceneTransform(this);
64 owners.append(da);
65 }
66
67 /*
68 Transforms viewport coordinates to 2D scene coordinates.
69 Returns the point in targetItem corresponding to \a viewportPoint,
70 assuming that targetItem is mapped onto sceneParentNode.
71 If it's no longer a "hit" on sceneParentNode, returns the last-good point.
72 */
73 QPointF map(const QPointF &viewportPoint) override {
74 QPointF point = viewportPoint;
75 // Despite the name, the input coordinates are the window viewport coordinates
76 // so unless the View3D is the same size of the Window, we need to translate
77 // to the View3D coordinates before doing any picking.
78 if (viewport)
79 point = viewport->mapFromScene(viewportPoint);
80 point.rx() *= scaleX;
81 point.ry() *= scaleY;
82 std::optional<QSSGRenderRay> rayResult = renderer->getRayFromViewportPos(point);
83 if (rayResult.has_value()) {
84 const auto pickResults = renderer->syncPickOne(rayResult.value(), sceneParentNode);
85 if (!pickResults.isEmpty()) {
86 const auto pickResult = pickResults.first();
87 auto ret = pickResult.m_localUVCoords.toPointF();
88 if (!uvCoordsArePixels) {
89 ret = QPointF(targetItem->x() + ret.x() * targetItem->width(),
90 targetItem->y() - ret.y() * targetItem->height() + targetItem->height());
91 }
92 const bool outOfModel = pickResult.m_localUVCoords.isNull();
93 qCDebug(lcEv) << viewportPoint << "->" << (outOfModel ? "OOM" : "") << ret << "@" << pickResult.m_scenePosition
94 << "UV" << pickResult.m_localUVCoords << "dist" << qSqrt(pickResult.m_distanceSq);
95 if (outOfModel) {
96 return lastGoodMapping;
97 } else {
99 return ret;
100 }
101 }
102 }
103 return QPointF();
104 }
105
106 QPointer<QQuick3DViewport> viewport;
109 QPointer<QQuickItem> targetItem;
112 bool uvCoordsArePixels = false; // if false, they are in the range 0..1
114
115 static QList<QPointer<QQuickDeliveryAgent>> owners;
116};
117
118QList<QPointer<QQuickDeliveryAgent>> ViewportTransformHelper::owners;
119
121{
122 Q_DISABLE_COPY_MOVE(QQuick3DExtensionListHelper);
123public:
124 static void extensionAppend(QQmlListProperty<QQuick3DObject> *list, QQuick3DObject *extension)
125 {
126 QSSG_ASSERT(list && extension, return);
127
128 if (QQuick3DViewport *that = qobject_cast<QQuick3DViewport *>(list->object)) {
129 if (const auto idx = that->m_extensions.indexOf(extension); idx == -1) {
130 if (!extension->parentItem())
131 extension->setParentItem(that->m_sceneRoot);
132 that->m_extensions.push_back(extension);
133 that->m_extensionListDirty = true;
134 }
135 }
136 }
137 static QQuick3DObject *extensionAt(QQmlListProperty<QQuick3DObject> *list, qsizetype index)
138 {
139 QQuick3DObject *ret = nullptr;
140 QSSG_ASSERT(list, return ret);
141
142 if (QQuick3DViewport *that = qobject_cast<QQuick3DViewport *>(list->object)) {
143 if (that->m_extensions.size() > index)
144 ret = that->m_extensions.at(index);
145 }
146
147 return ret;
148 }
149 static qsizetype extensionCount(QQmlListProperty<QQuick3DObject> *list)
150 {
151 qsizetype ret = -1;
152 QSSG_ASSERT(list, return ret);
153
154 if (QQuick3DViewport *that = qobject_cast<QQuick3DViewport *>(list->object))
155 ret = that->m_extensions.size();
156
157 return ret;
158 }
159 static void extensionClear(QQmlListProperty<QQuick3DObject> *list)
160 {
161 QSSG_ASSERT(list, return);
162
163 if (QQuick3DViewport *that = qobject_cast<QQuick3DViewport *>(list->object)) {
164 that->m_extensions.clear();
165 that->m_extensionListDirty = true;
166 }
167 }
168 static void extensionReplace(QQmlListProperty<QQuick3DObject> *list, qsizetype idx, QQuick3DObject *o)
169 {
170 QSSG_ASSERT(list, return);
171
172 if (QQuick3DViewport *that = qobject_cast<QQuick3DViewport *>(list->object)) {
173 if (that->m_extensions.size() > idx && idx > -1) {
174 that->m_extensions.replace(idx, o);
175 that->m_extensionListDirty = true;
176 }
177 }
178 }
179 static void extensionRemoveLast(QQmlListProperty<QQuick3DObject> *list)
180 {
181 QSSG_ASSERT(list, return);
182
183 if (QQuick3DViewport *that = qobject_cast<QQuick3DViewport *>(list->object)) {
184 that->m_extensions.removeLast();
185 that->m_extensionListDirty = true;
186 }
187 }
188};
189
245 : QQuickItem(parent)
246{
248 m_camera = nullptr;
249 m_sceneRoot = new QQuick3DSceneRootNode(this);
250 m_environment = new QQuick3DSceneEnvironment(m_sceneRoot);
251 m_renderStats = new QQuick3DRenderStats();
252 QQuick3DSceneManager *sceneManager = new QQuick3DSceneManager();
253 QQuick3DObjectPrivate::get(m_sceneRoot)->refSceneManager(*sceneManager);
254 Q_ASSERT(sceneManager == QQuick3DObjectPrivate::get(m_sceneRoot)->sceneManager);
256 this, &QQuickItem::update);
257
258 // Overrides the internal input handling to always be true
259 // instead of potentially updated after a sync (see updatePaintNode)
261 m_enableInputProcessing = true;
262 updateInputProcessing();
264 }
265}
266
268{
269 // With the threaded render loop m_directRenderer must be destroyed on the
270 // render thread at the proper time, not here. That's handled in
271 // releaseResources() + upon sceneGraphInvalidated. However with a
272 // QQuickRenderControl-based window on the main thread there is a good
273 // chance that this viewport (and so our sceneGraphInvalidated signal
274 // connection) is destroyed before the window and the rendercontrol. So act
275 // here then.
276 if (m_directRenderer && m_directRenderer->thread() == thread()) {
277 delete m_directRenderer;
278 m_directRenderer = nullptr;
279 }
280
281 // If the quick window still exists, make sure to disconnect any of the direct
282 // connections to this View3D
283 if (auto qw = window())
284 disconnect(qw, nullptr, this, nullptr);
285
286 auto sceneManager = QQuick3DObjectPrivate::get(m_sceneRoot)->sceneManager;
287 if (sceneManager) {
288 sceneManager->setParent(nullptr);
289 if (auto wa = sceneManager->wattached)
290 wa->queueForCleanup(sceneManager);
291 }
292
293 delete m_sceneRoot;
294 m_sceneRoot = nullptr;
295
296 // m_renderStats is tightly coupled with the render thread, so can't delete while we
297 // might still be rendering.
298 m_renderStats->deleteLater();
299
300 if (!window() && sceneManager && sceneManager->wattached)
302}
303
304static void ssgn_append(QQmlListProperty<QObject> *property, QObject *obj)
305{
306 if (!obj)
307 return;
308 QQuick3DViewport *view3d = static_cast<QQuick3DViewport *>(property->object);
309
310 if (QQuick3DObject *sceneObject = qmlobject_cast<QQuick3DObject *>(obj)) {
311 QQmlListProperty<QObject> itemProperty = QQuick3DObjectPrivate::get(view3d->scene())->data();
312 itemProperty.append(&itemProperty, sceneObject);
313 } else {
315 }
316}
317
318static qsizetype ssgn_count(QQmlListProperty<QObject> *property)
319{
320 QQuick3DViewport *view3d = static_cast<QQuick3DViewport *>(property->object);
321 if (!view3d || !view3d->scene() || !QQuick3DObjectPrivate::get(view3d->scene())->data().count)
322 return 0;
323 QQmlListProperty<QObject> itemProperty = QQuick3DObjectPrivate::get(view3d->scene())->data();
324 return itemProperty.count(&itemProperty);
325}
326
327static QObject *ssgn_at(QQmlListProperty<QObject> *property, qsizetype i)
328{
329 QQuick3DViewport *view3d = static_cast<QQuick3DViewport *>(property->object);
330 QQmlListProperty<QObject> itemProperty = QQuick3DObjectPrivate::get(view3d->scene())->data();
331 return itemProperty.at(&itemProperty, i);
332}
333
334static void ssgn_clear(QQmlListProperty<QObject> *property)
335{
336 QQuick3DViewport *view3d = static_cast<QQuick3DViewport *>(property->object);
337 QQmlListProperty<QObject> itemProperty = QQuick3DObjectPrivate::get(view3d->scene())->data();
338 return itemProperty.clear(&itemProperty);
339}
340
341
342QQmlListProperty<QObject> QQuick3DViewport::data()
343{
344 return QQmlListProperty<QObject>(this,
345 nullptr,
348 ssgn_at,
349 ssgn_clear);
350}
351
364{
365 return m_camera;
366}
367
376{
377 return m_environment;
378}
379
389{
390 return m_sceneRoot;
391}
392
406{
407 return m_importScene;
408}
409
454{
455 return m_renderMode;
456}
457
480{
481 return m_renderFormat;
482}
483
494{
495 return m_renderStats;
496}
497
499{
501
502 if (QQuickWindow *qw = window()) {
504 auto rci = wa->rci();
505 if (!rci) {
506 QSGRendererInterface *rif = qw->rendererInterface();
507 if (QSSG_GUARD(QSGRendererInterface::isApiRhiBased(rif->graphicsApi()))) {
508 QRhi *rhi = static_cast<QRhi *>(rif->getResource(qw, QSGRendererInterface::RhiResource));
509 QSSG_CHECK_X(rhi != nullptr, "No QRhi from QQuickWindow, this cannot happen");
510 // The RenderContextInterface, and the objects owned by it (such
511 // as, the BufferManager) are always per-QQuickWindow, and so per
512 // scenegraph render thread. Hence the association with window.
513 // Multiple View3Ds in the same window can use the same rendering
514 // infrastructure (so e.g. the same QSSGBufferManager), but two
515 // View3D objects in different windows must not, except for certain
516 // components that do not work with and own native graphics
517 // resources (most notably, QSSGShaderLibraryManager - but this
518 // distinction is handled internally by QSSGRenderContextInterface).
519 rci = std::make_shared<QSSGRenderContextInterface>(rhi);
520 wa->setRci(rci);
521
522 // Use DirectConnection to stay on the render thread, if there is one.
524 &QQuick3DViewport::onReleaseCachedResources, Qt::DirectConnection);
525
526 } else {
527 qWarning("The Qt Quick scene is using a rendering method that is not based on QRhi and a 3D graphics API. "
528 "Qt Quick 3D is not functional in such an environment. The View3D item is not going to display anything.");
529 }
530 }
531
532 if (rci)
534 }
535
536 return renderer;
537}
538
540{
541 // We can only be a texture provider if we are rendering to a texture first
542 if (m_renderMode == QQuick3DViewport::Offscreen)
543 return true;
544
545 return false;
546}
547
549{
550 // When Item::layer::enabled == true, QQuickItem will be a texture
551 // provider. In this case we should prefer to return the layer rather
552 // than the fbo texture.
555
556 // We can only be a texture provider if we are rendering to a texture first
557 if (m_renderMode != QQuick3DViewport::Offscreen)
558 return nullptr;
559
560 QQuickWindow *w = window();
561 if (!w) {
562 qWarning("QSSGView3D::textureProvider: can only be queried on the rendering thread of an exposed window");
563 return nullptr;
564 }
565
566 if (!m_node)
567 m_node = new SGFramebufferObjectNode;
568 return m_node;
569}
570
571class CleanupJob : public QRunnable
572{
573public:
575 void run() override { delete m_renderer; }
576private:
577 QQuick3DSGDirectRenderer *m_renderer;
578};
579
581{
582 if (m_directRenderer) {
583 window()->scheduleRenderJob(new CleanupJob(m_directRenderer), QQuickWindow::BeforeSynchronizingStage);
584 m_directRenderer = nullptr;
585 }
586
587 m_node = nullptr;
588}
589
591{
592 delete m_directRenderer;
593 m_directRenderer = nullptr;
594}
595
596void QQuick3DViewport::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
597{
598 QQuickItem::geometryChange(newGeometry, oldGeometry);
599
600 if (newGeometry.size() != oldGeometry.size())
601 update();
602}
603
605{
606 // When changing render modes
607 if (m_renderModeDirty) {
608 if (node) {
609 delete node;
610 node = nullptr;
611 m_node = nullptr;
612 m_renderNode = nullptr;
613 }
614 if (m_directRenderer) {
615 delete m_directRenderer;
616 m_directRenderer = nullptr;
617 }
618 }
619
620 m_renderModeDirty = false;
621
622 switch (m_renderMode) {
623 // Direct rendering
624 case Underlay:
626 case Overlay:
627 setupDirectRenderer(m_renderMode);
628 node = nullptr;
629 break;
630 case Offscreen:
631 node = setupOffscreenRenderer(node);
632 break;
633 case Inline:
634 // QSGRenderNode-based rendering
635 node = setupInlineRenderer(node);
636 break;
637 }
638
640 // Implicitly enable internal input processing if any item2ds are present.
641 const auto inputHandlingEnabled =
642 QQuick3DObjectPrivate::get(m_sceneRoot)->sceneManager->inputHandlingEnabled;
643 const auto enable = inputHandlingEnabled > 0;
644 if (m_enableInputProcessing != enable) {
645 m_enableInputProcessing = enable;
646 QMetaObject::invokeMethod(this, "updateInputProcessing", Qt::QueuedConnection);
647 }
648 }
649
650 return node;
651}
652
654{
655 if (change == ItemSceneChange) {
656 if (value.window) {
657 // TODO: if we want to support multiple windows, there has to be a scene manager for
658 // every window.
659 QQuick3DObjectPrivate::get(m_sceneRoot)->sceneManager->setWindow(value.window);
660 if (m_importScene)
661 QQuick3DObjectPrivate::get(m_importScene)->sceneManager->setWindow(value.window);
662 m_renderStats->setWindow(value.window);
663 }
664 } else if (change == ItemVisibleHasChanged && isVisible()) {
665 update();
666 }
667}
668
670{
671 if (m_enableInputProcessing && event->isPointerEvent())
672 return internalPick(static_cast<QPointerEvent *>(event));
673 else
674 return QQuickItem::event(event);
675}
676
682
684{
685 if (m_camera == camera)
686 return;
687
688 if (camera && !camera->parentItem())
689 camera->setParentItem(m_sceneRoot);
690 if (camera)
692
694
695 m_camera = camera;
697 update();
698}
699
700void QQuick3DViewport::setMultiViewCameras(QQuick3DCamera **firstCamera, int count)
701{
702 m_multiViewCameras.clear();
703 bool sendChangeSignal = false;
704 for (int i = 0; i < count; ++i) {
705 QQuick3DCamera *camera = *(firstCamera + i);
706 if (camera) {
707 if (!camera->parentItem())
708 camera->setParentItem(m_sceneRoot);
710 }
711 if (i == 0) {
712 if (m_camera != camera) {
713 m_camera = camera;
714 sendChangeSignal = true;
715 }
716 }
717
718 m_multiViewCameras.append(camera);
719
720 // ### do we need attachWatcher stuff? the Xr-provided cameras cannot disappear, although the XrActor (the owner) might
721 }
722
723 if (sendChangeSignal)
725
726 update();
727}
728
730{
731 if (m_environment == environment)
732 return;
733
734 m_environment = environment;
735 if (m_environment && !m_environment->parentItem())
736 m_environment->setParentItem(m_sceneRoot);
738 update();
739}
740
742{
743 // ### We may need consider the case where there is
744 // already a scene tree here
745 // FIXME : Only the first importScene is an effective one
746 if (m_importScene)
747 return;
748
749 // FIXME : Check self-import or cross-import
750 // Currently it does not work since importScene qml parsed in a reverse order.
751 QQuick3DNode *scene = inScene;
752 while (scene) {
753 if (m_sceneRoot == scene) {
754 qmlWarning(this) << "Cannot allow self-import or cross-import!";
755 return;
756 }
757
758 QQuick3DSceneRootNode *rn = qobject_cast<QQuick3DSceneRootNode *>(scene);
759 scene = rn ? rn->view3D()->importScene() : nullptr;
760 }
761
762 m_importScene = inScene;
763 if (m_importScene) {
764 auto privateObject = QQuick3DObjectPrivate::get(m_importScene);
765 if (!privateObject->sceneManager) {
766 // If object doesn't already have scene manager, check from its children
767 QQuick3DSceneManager *manager = findChildSceneManager(m_importScene);
768 // If still not found, use the one from the scene root (scenes defined outside of an view3d)
769 if (!manager)
770 manager = QQuick3DObjectPrivate::get(m_sceneRoot)->sceneManager;
771 if (manager) {
772 manager->setWindow(window());
773 privateObject->refSceneManager(*manager);
774 }
775 // At this point some manager will exist
776 Q_ASSERT(privateObject->sceneManager);
777 }
778
779 connect(privateObject->sceneManager, &QQuick3DSceneManager::needsUpdate,
780 this, &QQuickItem::update);
781
782 QQuick3DNode *scene = inScene;
783 while (scene) {
784 QQuick3DSceneRootNode *rn = qobject_cast<QQuick3DSceneRootNode *>(scene);
785 scene = rn ? rn->view3D()->importScene() : nullptr;
786
787 if (scene) {
790 this, &QQuickItem::update);
791 }
792 }
793 }
794
796 update();
797}
798
800{
801 if (m_renderMode == renderMode)
802 return;
803
804 m_renderMode = renderMode;
805 m_renderModeDirty = true;
807 update();
808}
809
810void QQuick3DViewport::setRenderFormat(QQuickShaderEffectSource::Format format)
811{
812 if (m_renderFormat == format)
813 return;
814
815 m_renderFormat = format;
816 m_renderModeDirty = true;
817 emit renderFormatChanged();
818 update();
819}
820
841{
842 return m_explicitTextureWidth;
843}
844
845void QQuick3DViewport::setExplicitTextureWidth(int width)
846{
847 if (m_explicitTextureWidth == width)
848 return;
849
850 m_explicitTextureWidth = width;
851 emit explicitTextureWidthChanged();
852 update();
853}
854
875{
876 return m_explicitTextureHeight;
877}
878
879void QQuick3DViewport::setExplicitTextureHeight(int height)
880{
881 if (m_explicitTextureHeight == height)
882 return;
883
884 m_explicitTextureHeight = height;
885 emit explicitTextureHeightChanged();
886 update();
887}
888
905{
906 return m_effectiveTextureSize;
907}
908
909
927{
928 if (!m_camera) {
929 qmlWarning(this) << "Cannot resolve view position without a camera assigned!";
930 return QVector3D(0, 0, 0);
931 }
932
933 qreal _width = width();
934 qreal _height = height();
935 if (_width == 0 || _height == 0)
936 return QVector3D(0, 0, 0);
937
938 const QVector3D normalizedPos = m_camera->mapToViewport(scenePos, _width, _height);
939 return normalizedPos * QVector3D(float(_width), float(_height), 1);
940}
941
960{
961 if (!m_camera) {
962 qmlWarning(this) << "Cannot resolve scene position without a camera assigned!";
963 return QVector3D(0, 0, 0);
964 }
965
966 qreal _width = width();
967 qreal _height = height();
968 if (_width == 0 || _height == 0)
969 return QVector3D(0, 0, 0);
970
971 const QVector3D normalizedPos = viewPos / QVector3D(float(_width), float(_height), 1);
972 return m_camera->mapFromViewport(normalizedPos, _width, _height);
973}
974
984{
985 QQuick3DSceneRenderer *renderer = getRenderer();
986 if (!renderer)
987 return QQuick3DPickResult();
988
989 const QPointF position(qreal(x) * window()->effectiveDevicePixelRatio() * m_widthMultiplier,
990 qreal(y) * window()->effectiveDevicePixelRatio() * m_heightMultiplier);
991 std::optional<QSSGRenderRay> rayResult = renderer->getRayFromViewportPos(position);
992 if (!rayResult.has_value())
993 return QQuick3DPickResult();
994
995 const auto resultList = renderer->syncPick(rayResult.value());
996 return getNearestPickResult(resultList);
997}
998
1010{
1011 QQuick3DSceneRenderer *renderer = getRenderer();
1012 if (!renderer)
1013 return QQuick3DPickResult();
1014
1015 const QPointF position(qreal(x) * window()->effectiveDevicePixelRatio(),
1016 qreal(y) * window()->effectiveDevicePixelRatio());
1017 std::optional<QSSGRenderRay> rayResult = renderer->getRayFromViewportPos(position);
1018
1019 if (!rayResult.has_value())
1020 return QQuick3DPickResult();
1021
1022 const auto renderNode = static_cast<QSSGRenderNode *>(QQuick3DObjectPrivate::get(model)->spatialNode);
1023 const auto resultList = renderer->syncPickOne(rayResult.value(), renderNode);
1024 return getNearestPickResult(resultList);
1025}
1026
1042QList<QQuick3DPickResult> QQuick3DViewport::pickSubset(float x, float y, const QJSValue &models) const
1043{
1044 QQuick3DSceneRenderer *renderer = getRenderer();
1045 if (!renderer)
1046 return {};
1047
1048 QVarLengthArray<QSSGRenderNode*> renderNodes;
1049 // Check for regular JavaScript array
1050 if (models.isArray()) {
1051 const auto length = models.property(QStringLiteral("length")).toInt();
1052 if (length == 0)
1053 return {};
1054
1055 for (int i = 0; i < length; ++i) {
1056 const auto isQObject = models.property(i).isQObject();
1057 if (!isQObject) {
1058 qmlWarning(this) << "Type provided for picking is not a QObject. Needs to be of type QQuick3DModel.";
1059 continue;
1060 }
1061 const auto obj = models.property(i).toQObject();
1062 const auto model = qobject_cast<QQuick3DModel *>(obj);
1063 if (!model) {
1064 qmlWarning(this) << "Type " << obj->metaObject()->className() << " is not supported for picking. Needs to be of type QQuick3DModel.";
1065 continue;
1066 }
1068 if (priv && priv->spatialNode) {
1069 renderNodes.push_back(static_cast<QSSGRenderNode*>(priv->spatialNode));
1070 }
1071 }
1072 } else {
1073 // Check for property list<Model>
1074 const auto subsetVariant = models.toVariant();
1075 if (!subsetVariant.isValid() || !subsetVariant.canConvert<QQmlListReference>())
1076 return {};
1077
1078 const auto list = subsetVariant.value<QQmlListReference>();
1079
1080 // Only support array of models
1081 if (list.listElementType()->className() != QQuick3DModel::staticMetaObject.className()) {
1082 qmlWarning(this) << "Type " << list.listElementType()->className() << " is not supported for picking. Needs to be of type QQuick3DModel.";
1083 return {};
1084 }
1085 for (int i = 0; i < list.count(); ++i) {
1086 auto model = static_cast<QQuick3DModel *>(list.at(i));
1087 if (!model)
1088 continue;
1090 if (priv && priv->spatialNode) {
1091 renderNodes.push_back(static_cast<QSSGRenderNode*>(priv->spatialNode));
1092 }
1093 }
1094 }
1095
1096 if (renderNodes.empty())
1097 return {};
1098
1099 const QPointF position(qreal(x) * window()->effectiveDevicePixelRatio(),
1100 qreal(y) * window()->effectiveDevicePixelRatio());
1101 std::optional<QSSGRenderRay> rayResult = renderer->getRayFromViewportPos(position);
1102 if (!rayResult.has_value())
1103 return {};
1104
1105 const auto resultList = renderer->syncPickSubset(rayResult.value(), renderNodes);
1106
1107 QList<QQuick3DPickResult> processedResultList;
1108 processedResultList.reserve(resultList.size());
1109 for (const auto &result : resultList)
1110 processedResultList.append(processPickResult(result));
1111
1112 return processedResultList;
1113}
1114
1127QList<QQuick3DPickResult> QQuick3DViewport::pickAll(float x, float y) const
1128{
1129 QQuick3DSceneRenderer *renderer = getRenderer();
1130 if (!renderer)
1131 return QList<QQuick3DPickResult>();
1132
1133 const QPointF position(qreal(x) * window()->effectiveDevicePixelRatio() * m_widthMultiplier,
1134 qreal(y) * window()->effectiveDevicePixelRatio() * m_heightMultiplier);
1135 std::optional<QSSGRenderRay> rayResult = renderer->getRayFromViewportPos(position);
1136 if (!rayResult.has_value())
1137 return QList<QQuick3DPickResult>();
1138
1139 const auto resultList = renderer->syncPickAll(rayResult.value());
1140 QList<QQuick3DPickResult> processedResultList;
1141 processedResultList.reserve(resultList.size());
1142 for (const auto &result : resultList)
1143 processedResultList.append(processPickResult(result));
1144
1145 return processedResultList;
1146}
1147
1161QQuick3DPickResult QQuick3DViewport::rayPick(const QVector3D &origin, const QVector3D &direction) const
1162{
1163 QQuick3DSceneRenderer *renderer = getRenderer();
1164 if (!renderer)
1165 return QQuick3DPickResult();
1166
1167 const QSSGRenderRay ray(origin, direction);
1168 const auto resultList = renderer->syncPick(ray);
1169 return getNearestPickResult(resultList);
1170}
1171
1188QList<QQuick3DPickResult> QQuick3DViewport::rayPickAll(const QVector3D &origin, const QVector3D &direction) const
1189{
1190 QQuick3DSceneRenderer *renderer = getRenderer();
1191 if (!renderer)
1192 return QList<QQuick3DPickResult>();
1193
1194 const QSSGRenderRay ray(origin, direction);
1195
1196 const auto resultList = renderer->syncPickAll(ray);
1197 QList<QQuick3DPickResult> processedResultList;
1198 processedResultList.reserve(resultList.size());
1199 for (const auto &result : resultList) {
1200 auto processedResult = processPickResult(result);
1201 if (processedResult.hitType() != QQuick3DPickResult::HitType::Null)
1202 processedResultList.append(processedResult);
1203 }
1204
1205 return processedResultList;
1206}
1207
1209{
1210 internalPick(event, origin, direction);
1211}
1212
1213// Note: we have enough information to implement Capability::Hover and Capability::ZPosition,
1214// but those properties are not currently available in QTouchEvent/QEventPoint
1215
1216namespace {
1217class SyntheticTouchDevice : public QPointingDevice
1218{
1219public:
1220 SyntheticTouchDevice(QObject *parent = nullptr)
1221 : QPointingDevice(QLatin1StringView("QtQuick3D Touch Synthesizer"),
1222 0,
1223 DeviceType::TouchScreen,
1224 PointerType::Finger,
1225 Capability::Position,
1226 10, 0,
1228 parent)
1229 {
1230 }
1231};
1232}
1233
1245void QQuick3DViewport::setTouchpoint(QQuickItem *target, const QPointF &position, int pointId, bool pressed)
1246{
1247 if (pointId >= m_touchState.size())
1248 m_touchState.resize(pointId + 1);
1249 auto prevState = m_touchState[pointId];
1250
1251 const bool sameTarget = prevState.target == target;
1252 const bool wasPressed = prevState.isPressed;
1253
1254 const bool isPress = pressed && (!sameTarget || !wasPressed);
1255 const bool isRelease = !pressed && wasPressed && sameTarget;
1256
1257 // Hover if we're not active, and we weren't previously active.
1258 // We assume that we always get a non-active for a target when we release.
1259 // This function sends a release events if the target is changed.
1260 if (!sameTarget && wasPressed)
1261 qWarning("QQuick3DViewport::setTouchpoint missing release event");
1262
1263 if (!pressed && !wasPressed) {
1264 // This would be a hover event: skipping
1265 return;
1266 }
1267
1268 m_touchState[pointId] = { target, position, pressed };
1269
1270 if (!m_syntheticTouchDevice)
1271 m_syntheticTouchDevice = new SyntheticTouchDevice(this);
1272
1273 QPointingDevicePrivate *devPriv = QPointingDevicePrivate::get(m_syntheticTouchDevice);
1274
1275 auto makePoint = [devPriv](int id, QEventPoint::State pointState, QPointF pos) -> QEventPoint {
1276 auto epd = devPriv->pointById(id);
1277 auto &ep = epd->eventPoint;
1278 if (pointState != QEventPoint::State::Stationary)
1279 ep.setAccepted(false);
1280
1281 auto res = QMutableEventPoint::withTimeStamp(0, id, pointState, pos, pos, pos);
1283 return res;
1284 };
1285
1286 auto sendTouchEvent = [&](QQuickItem *t, const QPointF &position, int pointId, QEventPoint::State pointState) -> void {
1287 QList<QEventPoint> points;
1288 bool otherPoint = false; // Does the event have another point already?
1289 for (int i = 0; i < m_touchState.size(); ++i) {
1290 const auto &ts = m_touchState[i];
1291 if (ts.target != t)
1292 continue;
1293 if (i == pointId) {
1294 auto newPoint = makePoint(i, pointState, position);
1295 points << newPoint;
1296 } else if (ts.isPressed) {
1297 otherPoint = true;
1298 points << makePoint(i, QEventPoint::Stationary, ts.position);
1299 }
1300 }
1301
1303 if (pointState == QEventPoint::Pressed && !otherPoint)
1304 type = QEvent::Type::TouchBegin;
1305 else if (pointState == QEventPoint::Released && !otherPoint)
1306 type = QEvent::Type::TouchEnd;
1307 else
1308 type = QEvent::Type::TouchUpdate;
1309
1310 QTouchEvent ev(type, m_syntheticTouchDevice, {}, points);
1311
1312 if (t) {
1313 // Actually send event:
1314 auto da = QQuickItemPrivate::get(t)->deliveryAgent();
1315 bool handled = da->event(&ev);
1316 Q_UNUSED(handled);
1317 }
1318
1319 // Duplicate logic from QQuickWindowPrivate::clearGrabbers
1320 if (ev.isEndEvent()) {
1321 for (auto &point : ev.points()) {
1322 if (point.state() == QEventPoint::State::Released) {
1323 ev.setExclusiveGrabber(point, nullptr);
1324 ev.clearPassiveGrabbers(point);
1325 }
1326 }
1327 }
1328 };
1329
1330 // Send a release event to the previous target
1331 if (prevState.target && !sameTarget)
1332 sendTouchEvent(prevState.target, prevState.position, pointId, QEventPoint::Released);
1333
1334 // Now send an event for the new state
1336 sendTouchEvent(target, position, pointId, newState);
1337}
1338
1340{
1341 return m_lightmapBaker;
1342}
1343
1345{
1346 if (!m_lightmapBaker)
1347 m_lightmapBaker= new QQuick3DLightmapBaker(this);
1348
1349 return m_lightmapBaker;
1350}
1351
1359
1361{
1362 QQuick3DSceneRenderer *renderer = getRenderer();
1363 if (!renderer)
1364 return;
1365
1366 renderer->setGlobalPickingEnabled(isEnabled);
1367}
1368
1369void QQuick3DViewport::invalidateSceneGraph()
1370{
1371 m_node = nullptr;
1372}
1373
1374QQuick3DSceneRenderer *QQuick3DViewport::getRenderer() const
1375{
1377 if (m_node) {
1378 renderer = m_node->renderer;
1379 } else if (m_renderNode) {
1380 renderer = m_renderNode->renderer;
1381 } else if (m_directRenderer) {
1382 renderer = m_directRenderer->renderer();
1383 }
1384 return renderer;
1385}
1386
1387void QQuick3DViewport::updateDynamicTextures()
1388{
1389 // Update QSGDynamicTextures that are used for source textures and Quick items
1390 // Must be called on the render thread.
1391
1392 const auto &sceneManager = QQuick3DObjectPrivate::get(m_sceneRoot)->sceneManager;
1393 for (auto *texture : std::as_const(sceneManager->qsgDynamicTextures))
1394 texture->updateTexture();
1395
1396 QQuick3DNode *scene = m_importScene;
1397 while (scene) {
1398 const auto &importSm = QQuick3DObjectPrivate::get(scene)->sceneManager;
1399 if (importSm != sceneManager) {
1400 for (auto *texture : std::as_const(importSm->qsgDynamicTextures))
1401 texture->updateTexture();
1402 }
1403
1404 // if importScene has another import
1405 QQuick3DSceneRootNode *rn = qobject_cast<QQuick3DSceneRootNode *>(scene);
1406 scene = rn ? rn->view3D()->importScene() : nullptr;
1407 }
1408}
1409
1410QSGNode *QQuick3DViewport::setupOffscreenRenderer(QSGNode *node)
1411{
1412 SGFramebufferObjectNode *n = static_cast<SGFramebufferObjectNode *>(node);
1413
1414 if (!n) {
1415 if (!m_node)
1416 m_node = new SGFramebufferObjectNode;
1417 n = m_node;
1418 }
1419
1420 if (!n->renderer) {
1421 n->window = window();
1422 n->renderer = createRenderer();
1423 if (!n->renderer)
1424 return nullptr;
1425 n->renderer->fboNode = n;
1426 n->quickFbo = this;
1427 connect(window(), SIGNAL(screenChanged(QScreen*)), n, SLOT(handleScreenChange()));
1428 }
1429
1430 const qreal dpr = window()->effectiveDevicePixelRatio();
1431 const QSize minFboSize = QQuickItemPrivate::get(this)->sceneGraphContext()->minimumFBOSize();
1432 QSize desiredFboSize = QSize(m_explicitTextureWidth, m_explicitTextureHeight);
1433 if (desiredFboSize.isEmpty()) {
1434 desiredFboSize = QSize(width(), height()) * dpr;
1435 n->devicePixelRatio = dpr;
1436 // 1:1 mapping between the backing texture and the on-screen quad
1437 m_widthMultiplier = 1.0f;
1438 m_heightMultiplier = 1.0f;
1439 } else {
1440 QSize itemPixelSize = QSize(width(), height()) * dpr;
1441 // not 1:1 maping between the backing texture and the on-screen quad
1442 m_widthMultiplier = desiredFboSize.width() / float(itemPixelSize.width());
1443 m_heightMultiplier = desiredFboSize.height() / float(itemPixelSize.height());
1444 n->devicePixelRatio = 1.0;
1445 }
1446 desiredFboSize.setWidth(qMax(minFboSize.width(), desiredFboSize.width()));
1447 desiredFboSize.setHeight(qMax(minFboSize.height(), desiredFboSize.height()));
1448
1449 if (desiredFboSize != m_effectiveTextureSize) {
1450 m_effectiveTextureSize = desiredFboSize;
1451 emit effectiveTextureSizeChanged();
1452 }
1453
1454 n->setFiltering(smooth() ? QSGTexture::Linear : QSGTexture::Nearest);
1455 n->setRect(0, 0, width(), height());
1456 if (checkIsVisible() && isComponentComplete()) {
1457 n->renderer->synchronize(this, desiredFboSize, n->devicePixelRatio);
1458 if (n->renderer->m_textureNeedsFlip)
1459 n->setTextureCoordinatesTransform(QSGSimpleTextureNode::MirrorVertically);
1460 updateDynamicTextures();
1461 n->scheduleRender();
1462 }
1463
1464 return n;
1465}
1466
1467QSGNode *QQuick3DViewport::setupInlineRenderer(QSGNode *node)
1468{
1469 QQuick3DSGRenderNode *n = static_cast<QQuick3DSGRenderNode *>(node);
1470 if (!n) {
1471 if (!m_renderNode)
1472 m_renderNode = new QQuick3DSGRenderNode;
1473 n = m_renderNode;
1474 }
1475
1476 if (!n->renderer) {
1477 n->window = window();
1478 n->renderer = createRenderer();
1479 if (!n->renderer)
1480 return nullptr;
1481 }
1482
1483 if (!m_effectiveTextureSize.isEmpty()) {
1484 m_effectiveTextureSize = QSize();
1485 emit effectiveTextureSizeChanged();
1486 }
1487
1488 const QSize targetSize = window()->effectiveDevicePixelRatio() * QSize(width(), height());
1489
1490 // checkIsVisible, not isVisible, because, for example, a
1491 // { visible: false; layer.enabled: true } item still needs
1492 // to function normally.
1493 if (checkIsVisible() && isComponentComplete()) {
1494 n->renderer->synchronize(this, targetSize, window()->effectiveDevicePixelRatio());
1495 updateDynamicTextures();
1496 n->markDirty(QSGNode::DirtyMaterial);
1497 }
1498
1499 return n;
1500}
1501
1502
1503void QQuick3DViewport::setupDirectRenderer(RenderMode mode)
1504{
1507 if (!m_directRenderer) {
1508 QQuick3DSceneRenderer *sceneRenderer = createRenderer();
1509 if (!sceneRenderer)
1510 return;
1511 m_directRenderer = new QQuick3DSGDirectRenderer(sceneRenderer, window(), renderMode);
1513 }
1514
1515 if (!m_effectiveTextureSize.isEmpty()) {
1516 m_effectiveTextureSize = QSize();
1517 emit effectiveTextureSizeChanged();
1518 }
1519
1520 const QSizeF targetSize = window()->effectiveDevicePixelRatio() * QSizeF(width(), height());
1521 m_directRenderer->setViewport(QRectF(window()->effectiveDevicePixelRatio() * mapToScene(QPointF(0, 0)), targetSize));
1522 m_directRenderer->setVisibility(isVisible());
1523 if (isVisible()) {
1524 m_directRenderer->preSynchronize();
1525 m_directRenderer->renderer()->synchronize(this, targetSize.toSize(), window()->effectiveDevicePixelRatio());
1526 updateDynamicTextures();
1527 m_directRenderer->requestRender();
1528 }
1529}
1530
1531// This is used for offscreen mode since we need to check if
1532// this item is used by an effect but hidden
1533bool QQuick3DViewport::checkIsVisible() const
1534{
1535 auto childPrivate = QQuickItemPrivate::get(this);
1536 return (childPrivate->explicitVisible ||
1537 (childPrivate->extra.isAllocated() && childPrivate->extra->effectRefCount));
1538
1539}
1540
1550void QQuick3DViewport::processPickedObject(const QSSGRenderGraphObject *backendObject,
1551 const QSSGRenderPickResult &pickResult,
1552 int pointIndex,
1554 QFlatMap<QQuickItem *, SubsceneInfo> &visitedSubscenes) const
1555{
1556 QQuickItem *subsceneRootItem = nullptr;
1557 QPointF subscenePosition;
1558 const auto frontendObject = findFrontendNode(backendObject);
1559 if (!frontendObject)
1560 return;
1561
1562 // Figure out if there are any QQuickItem based scenes we need to forward
1563 // the event to, and if the are found, determine how to translate the UV Coords
1564 // in the pickResult based on what type the object containing the scene is.
1565 auto frontendObjectPrivate = QQuick3DObjectPrivate::get(frontendObject);
1566 if (frontendObjectPrivate->type == QQuick3DObjectPrivate::Type::Item2D) {
1567 // Item2D, this is the case where there is just an embedded Qt Quick 2D Item
1568 // rendered directly to the scene.
1569 auto item2D = qobject_cast<QQuick3DItem2D *>(frontendObject);
1570 if (item2D)
1571 subsceneRootItem = item2D->contentItem();
1572 if (!subsceneRootItem || subsceneRootItem->childItems().isEmpty())
1573 return; // ignore empty 2D subscenes
1574
1575 // In this case the "UV" coordinates are in pixels in the subscene root item's coordinate system.
1576 subscenePosition = pickResult.m_localUVCoords.toPointF();
1577
1578 // The following code will account for custom input masking, as well any
1579 // transformations that might have been applied to the Item
1580 if (!subsceneRootItem->childAt(subscenePosition.x(), subscenePosition.y()))
1581 return;
1582 } else if (frontendObjectPrivate->type == QQuick3DObjectPrivate::Type::Model) {
1583 // Model
1584 int materialSubset = pickResult.m_subset;
1585 const auto backendModel = static_cast<const QSSGRenderModel *>(backendObject);
1586 // Get material
1587 if (backendModel->materials.size() < (pickResult.m_subset + 1))
1588 materialSubset = backendModel->materials.size() - 1;
1589 if (materialSubset < 0)
1590 return;
1591 const auto backendMaterial = backendModel->materials.at(materialSubset);
1592 const auto frontendMaterial = static_cast<QQuick3DMaterial*>(findFrontendNode(backendMaterial));
1593 subsceneRootItem = getSubSceneRootItem(frontendMaterial);
1594
1595 if (subsceneRootItem) {
1596 // In this case the pick result really is using UV coordinates.
1597 subscenePosition = QPointF(subsceneRootItem->x() + pickResult.m_localUVCoords.x() * subsceneRootItem->width(),
1598 subsceneRootItem->y() - pickResult.m_localUVCoords.y() * subsceneRootItem->height() + subsceneRootItem->height());
1599 }
1600 }
1601
1602 // Add the new event (item and position) to the visitedSubscene map.
1603 if (subsceneRootItem) {
1604 SubsceneInfo &subscene = visitedSubscenes[subsceneRootItem]; // create if not found
1605 subscene.obj = frontendObject;
1606 if (subscene.eventPointScenePositions.size() != event->pointCount()) {
1607 // ensure capacity, and use an out-of-scene position rather than 0,0 by default
1608 constexpr QPointF inf(-qt_inf(), -qt_inf());
1609 subscene.eventPointScenePositions.resize(event->pointCount(), inf);
1610 }
1611 subscene.eventPointScenePositions[pointIndex] = subscenePosition;
1612 }
1613}
1614
1625QQuickItem *QQuick3DViewport::getSubSceneRootItem(QQuick3DMaterial *material) const
1626{
1627 if (!material)
1628 return nullptr;
1629
1630 QQuickItem *subsceneRootItem = nullptr;
1631 const auto frontendMaterialPrivate = QQuick3DObjectPrivate::get(material);
1632
1633 if (frontendMaterialPrivate->type == QQuick3DObjectPrivate::Type::DefaultMaterial) {
1634 // Default Material
1635 const auto defaultMaterial = qobject_cast<QQuick3DDefaultMaterial *>(material);
1636 if (defaultMaterial) {
1637 // Just check for a diffuseMap for now
1638 if (defaultMaterial->diffuseMap() && defaultMaterial->diffuseMap()->sourceItem())
1639 subsceneRootItem = defaultMaterial->diffuseMap()->sourceItem();
1640 }
1641
1642 } else if (frontendMaterialPrivate->type == QQuick3DObjectPrivate::Type::PrincipledMaterial) {
1643 // Principled Material
1644 const auto principledMaterial = qobject_cast<QQuick3DPrincipledMaterial *>(material);
1645 if (principledMaterial) {
1646 // Just check for a baseColorMap for now
1647 if (principledMaterial->baseColorMap() && principledMaterial->baseColorMap()->sourceItem())
1648 subsceneRootItem = principledMaterial->baseColorMap()->sourceItem();
1649 }
1650 } else if (frontendMaterialPrivate->type == QQuick3DObjectPrivate::Type::SpecularGlossyMaterial) {
1651 // SpecularGlossy Material
1652 const auto specularGlossyMaterial = qobject_cast<QQuick3DSpecularGlossyMaterial *>(material);
1653 if (specularGlossyMaterial) {
1654 // Just check for a albedoMap for now
1655 if (specularGlossyMaterial->albedoMap() && specularGlossyMaterial->albedoMap()->sourceItem())
1656 subsceneRootItem = specularGlossyMaterial->albedoMap()->sourceItem();
1657 }
1658 } else if (frontendMaterialPrivate->type == QQuick3DObjectPrivate::Type::CustomMaterial) {
1659 // Custom Material
1660 const auto customMaterial = qobject_cast<QQuick3DCustomMaterial *>(material);
1661 if (customMaterial) {
1662 // This case is a bit harder because we can not know how the textures will be used
1663 const auto &texturesInputs = customMaterial->m_dynamicTextureMaps;
1664 for (const auto &textureInput : texturesInputs) {
1665 if (auto texture = textureInput->texture()) {
1666 if (texture->sourceItem()) {
1667 subsceneRootItem = texture->sourceItem();
1668 break;
1669 }
1670 }
1671 }
1672 }
1673 }
1674 return subsceneRootItem;
1675}
1676
1677
1681QQuick3DPickResult QQuick3DViewport::getNearestPickResult(const QVarLengthArray<QSSGRenderPickResult, 20> &pickResults) const
1682{
1683 for (const auto &result : pickResults) {
1684 auto pickResult = processPickResult(result);
1685 if (pickResult.hitType() != QQuick3DPickResult::HitType::Null)
1686 return pickResult;
1687 }
1688
1689 return QQuick3DPickResult();
1690}
1691
1698bool QQuick3DViewport::forwardEventToSubscenes(QPointerEvent *event,
1699 bool useRayPicking,
1701 const QFlatMap<QQuickItem *, SubsceneInfo> &visitedSubscenes) const
1702{
1703 // Now deliver the entire event (all points) to each relevant subscene.
1704 // Maybe only some points fall inside, but QQuickDeliveryAgentPrivate::deliverMatchingPointsToItem()
1705 // makes reduced-subset touch events that contain only the relevant points, when necessary.
1706 bool ret = false;
1707
1708 QVarLengthArray<QPointF, 16> originalScenePositions;
1709 originalScenePositions.resize(event->pointCount());
1710 for (int pointIndex = 0; pointIndex < event->pointCount(); ++pointIndex)
1711 originalScenePositions[pointIndex] = event->point(pointIndex).scenePosition();
1712 for (auto subscene : visitedSubscenes) {
1713 QQuickItem *subsceneRoot = subscene.first;
1714 auto &subsceneInfo = subscene.second;
1715 Q_ASSERT(subsceneInfo.eventPointScenePositions.size() == event->pointCount());
1716 auto da = QQuickItemPrivate::get(subsceneRoot)->deliveryAgent();
1717 for (int pointIndex = 0; pointIndex < event->pointCount(); ++pointIndex) {
1718 const auto &pt = subsceneInfo.eventPointScenePositions.at(pointIndex);
1719 // By tradition, QGuiApplicationPrivate::processTouchEvent() has set the local position to the scene position,
1720 // and Qt Quick expects it to arrive that way: then QQuickDeliveryAgentPrivate::translateTouchEvent()
1721 // copies it into the scene position before localizing.
1722 // That might be silly, we might change it eventually, but gotta stay consistent for now.
1723 QEventPoint &ep = event->point(pointIndex);
1724 QMutableEventPoint::setPosition(ep, pt);
1725 QMutableEventPoint::setScenePosition(ep, pt);
1726 }
1727
1728 if (event->isBeginEvent())
1729 da->setSceneTransform(nullptr);
1730 if (da->event(event)) {
1731 ret = true;
1732 if (QQuickDeliveryAgentPrivate::anyPointGrabbed(event) && !useRayPicking) {
1733 // In case any QEventPoint was grabbed, the relevant QQuickDeliveryAgent needs to know
1734 // how to repeat the picking/coordinate transformation for each update,
1735 // because delivery will bypass internalPick() due to the grab, and it's
1736 // more efficient to avoid whole-scene picking each time anyway.
1737 auto frontendObjectPrivate = QQuick3DObjectPrivate::get(subsceneInfo.obj);
1738 const bool item2Dcase = (frontendObjectPrivate->type == QQuick3DObjectPrivate::Type::Item2D);
1740 transform->viewport = const_cast<QQuick3DViewport *>(this);
1741 transform->renderer = renderer;
1742 transform->sceneParentNode = static_cast<QSSGRenderNode*>(frontendObjectPrivate->spatialNode);
1743 transform->targetItem = subsceneRoot;
1744 transform->scaleX = window()->effectiveDevicePixelRatio() * m_widthMultiplier;
1745 transform->scaleY = window()->effectiveDevicePixelRatio() * m_heightMultiplier;
1746 transform->uvCoordsArePixels = item2Dcase;
1747 transform->setOnDeliveryAgent(da);
1748 qCDebug(lcPick) << event->type() << "created ViewportTransformHelper on" << da;
1749 }
1750 } else if (event->type() != QEvent::HoverMove) {
1751 qCDebug(lcPick) << subsceneRoot << "didn't want" << event;
1752 }
1753 event->setAccepted(false); // reject implicit grab and let it keep propagating
1754 }
1755 if (visitedSubscenes.isEmpty()) {
1756 event->setAccepted(false);
1757 } else {
1758 for (int pointIndex = 0; pointIndex < event->pointCount(); ++pointIndex)
1759 QMutableEventPoint::setScenePosition(event->point(pointIndex), originalScenePositions.at(pointIndex));
1760 }
1761
1762 // Normally this would occur in QQuickWindowPrivate::clearGrabbers(...) but
1763 // for ray based input, input never goes through QQuickWindow (since events
1764 // are generated from within scene space and not window/screen space).
1765 if (event->isEndEvent() && useRayPicking) {
1766 if (event->isSinglePointEvent()) {
1767 if (static_cast<QSinglePointEvent *>(event)->buttons() == Qt::NoButton) {
1768 auto &firstPt = event->point(0);
1769 event->setExclusiveGrabber(firstPt, nullptr);
1770 event->clearPassiveGrabbers(firstPt);
1771 }
1772 } else {
1773 for (auto &point : event->points()) {
1774 if (point.state() == QEventPoint::State::Released) {
1775 event->setExclusiveGrabber(point, nullptr);
1776 event->clearPassiveGrabbers(point);
1777 }
1778 }
1779 }
1780 }
1781
1782 return ret;
1783}
1784
1785
1786bool QQuick3DViewport::internalPick(QPointerEvent *event, const QVector3D &origin, const QVector3D &direction) const
1787{
1788 QQuick3DSceneRenderer *renderer = getRenderer();
1789 if (!renderer || !event)
1790 return false;
1791
1792 QFlatMap<QQuickItem*, SubsceneInfo> visitedSubscenes;
1793 const bool useRayPicking = !direction.isNull();
1794
1795 for (int pointIndex = 0; pointIndex < event->pointCount(); ++pointIndex) {
1796 auto &eventPoint = event->point(pointIndex);
1797 QVarLengthArray<QSSGRenderPickResult, 20> pickResults;
1798 if (Q_UNLIKELY(useRayPicking))
1799 pickResults = getPickResults(renderer, origin, direction);
1800 else
1801 pickResults = getPickResults(renderer, eventPoint);
1802
1803 if (!pickResults.isEmpty())
1804 for (const auto &pickResult : pickResults)
1805 processPickedObject(pickResult.m_hitObject, pickResult, pointIndex, event, visitedSubscenes);
1806 else
1807 eventPoint.setAccepted(false); // let it fall through the viewport to Items underneath
1808 }
1809
1810 return forwardEventToSubscenes(event, useRayPicking, renderer, visitedSubscenes);
1811}
1812
1813QVarLengthArray<QSSGRenderPickResult, 20> QQuick3DViewport::getPickResults(QQuick3DSceneRenderer *renderer,
1814 const QVector3D &origin,
1815 const QVector3D &direction) const
1816{
1817 const QSSGRenderRay ray(origin, direction);
1818 return renderer->syncPickAll(ray);
1819}
1820
1821QVarLengthArray<QSSGRenderPickResult, 20> QQuick3DViewport::getPickResults(QQuick3DSceneRenderer *renderer, const QEventPoint &eventPoint) const
1822{
1823 QVarLengthArray<QSSGRenderPickResult, 20> pickResults;
1824 QPointF realPosition = eventPoint.position() * window()->effectiveDevicePixelRatio();
1825 // correct when mapping is not 1:1 due to explicit backing texture size
1826 realPosition.rx() *= m_widthMultiplier;
1827 realPosition.ry() *= m_heightMultiplier;
1828 std::optional<QSSGRenderRay> rayResult = renderer->getRayFromViewportPos(realPosition);
1829 if (rayResult.has_value())
1830 pickResults = renderer->syncPickAll(rayResult.value());
1831 return pickResults;
1832}
1833
1840QQuick3DObject *QQuick3DViewport::findFrontendNode(const QSSGRenderGraphObject *backendObject) const
1841{
1842 if (!backendObject)
1843 return nullptr;
1844
1845 const auto sceneManager = QQuick3DObjectPrivate::get(m_sceneRoot)->sceneManager;
1846 QQuick3DObject *frontendObject = sceneManager->lookUpNode(backendObject);
1847 if (!frontendObject && m_importScene) {
1848 const auto importSceneManager = QQuick3DObjectPrivate::get(m_importScene)->sceneManager;
1849 frontendObject = importSceneManager->lookUpNode(backendObject);
1850 }
1851 return frontendObject;
1852}
1853
1854QQuick3DPickResult QQuick3DViewport::processPickResult(const QSSGRenderPickResult &pickResult) const
1855{
1856 if (!pickResult.m_hitObject)
1857 return QQuick3DPickResult();
1858
1859 QQuick3DObject *frontendObject = findFrontendNode(pickResult.m_hitObject);
1860
1861 QQuick3DModel *model = qobject_cast<QQuick3DModel *>(frontendObject);
1862 if (model)
1864 ::sqrtf(pickResult.m_distanceSq),
1865 pickResult.m_localUVCoords,
1866 pickResult.m_scenePosition,
1867 pickResult.m_localPosition,
1868 pickResult.m_faceNormal,
1869 pickResult.m_instanceIndex);
1870
1871 QQuick3DItem2D *frontend2DItem = qobject_cast<QQuick3DItem2D *>(frontendObject);
1872 if (frontend2DItem && frontend2DItem->contentItem()) {
1873 // Check if the pick is inside the content item (since the ray just intersected on the items plane)
1874 const QPointF subscenePosition = pickResult.m_localUVCoords.toPointF();
1875 const auto child = frontend2DItem->contentItem()->childAt(subscenePosition.x(), subscenePosition.y());
1876 if (child) {
1878 ::sqrtf(pickResult.m_distanceSq),
1879 QVector2D(frontend2DItem->contentItem()->mapToItem(child, subscenePosition)),
1880 pickResult.m_scenePosition,
1881 pickResult.m_localPosition,
1882 pickResult.m_faceNormal);
1883 }
1884 }
1885
1886 return QQuick3DPickResult();
1887
1888}
1889
1890// Returns the first found scene manager of objects children
1891QQuick3DSceneManager *QQuick3DViewport::findChildSceneManager(QQuick3DObject *inObject, QQuick3DSceneManager *manager)
1892{
1893 if (manager)
1894 return manager;
1895
1896 auto children = QQuick3DObjectPrivate::get(inObject)->childItems;
1897 for (auto child : children) {
1898 if (auto m = QQuick3DObjectPrivate::get(child)->sceneManager) {
1899 manager = m;
1900 break;
1901 }
1902 manager = findChildSceneManager(child, manager);
1903 }
1904 return manager;
1905}
1906
1907void QQuick3DViewport::updateInputProcessing()
1908{
1909 // This should be called from the gui thread.
1910 setAcceptTouchEvents(m_enableInputProcessing);
1911 setAcceptHoverEvents(m_enableInputProcessing);
1912 setAcceptedMouseButtons(m_enableInputProcessing ? Qt::AllButtons : Qt::NoButton);
1913}
1914
1915void QQuick3DViewport::onReleaseCachedResources()
1916{
1917 if (auto renderer = getRenderer())
1918 renderer->releaseCachedResources();
1919}
1920
1928QQmlListProperty<QQuick3DObject> QQuick3DViewport::extensions()
1929{
1930 return QQmlListProperty<QQuick3DObject>{ this,
1931 &m_extensionListDirty,
1938}
1939
1943void QQuick3DViewport::rebuildExtensionList()
1944{
1945 m_extensionListDirty = true;
1946 update();
1947}
1948
void run() override
Implement this pure virtual function in your subclass.
CleanupJob(QQuick3DSGDirectRenderer *renderer)
The QEventPoint class provides information about a point in a QPointerEvent.
Definition qeventpoint.h:20
void setAccepted(bool accepted=true)
State
Specifies the state of this event point.
Definition qeventpoint.h:48
QPointF position
the position of this point.
Definition qeventpoint.h:35
\inmodule QtCore
Definition qcoreevent.h:45
Type
This enum type defines the valid event types in Qt.
Definition qcoreevent.h:51
@ HoverMove
Definition qcoreevent.h:177
The QJSValue class acts as a container for Qt/JavaScript data types.
Definition qjsvalue.h:31
qint32 toInt() const
Returns the signed 32-bit integer value of this QJSValue, using the conversion rules described in \l{...
Definition qjsvalue.cpp:566
bool isArray() const
Returns true if this QJSValue is an object of the Array class; otherwise returns false.
Definition qjsvalue.cpp:425
bool isQObject() const
Returns true if this QJSValue is a QObject; otherwise returns false.
QObject * toQObject() const
If this QJSValue is a QObject, returns the QObject pointer that the QJSValue represents; otherwise,...
QJSValue property(const QString &name) const
Returns the value of this QJSValue's property with the given name.
QVariant toVariant() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qjsvalue.cpp:601
const_reference at(qsizetype i) const noexcept
Definition qlist.h:446
T value(qsizetype i) const
Definition qlist.h:664
qsizetype count() const noexcept
Definition qlist.h:398
static Q_GUI_EXPORT void update(const QEventPoint &from, QEventPoint &to)
static QEventPoint withTimeStamp(ulong timestamp, int pointId, QEventPoint::State state, QPointF position, QPointF scenePosition, QPointF globalPosition)
\inmodule QtCore
Definition qobject.h:103
const QObjectList & children() const
Returns a list of child objects.
Definition qobject.h:201
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
QThread * thread() const
Returns the thread in which the object lives.
Definition qobject.cpp:1598
void deleteLater()
\threadsafe
Definition qobject.cpp:2435
\inmodule QtCore\reentrant
Definition qpoint.h:217
constexpr qreal & ry() noexcept
Returns a reference to the y coordinate of this point.
Definition qpoint.h:368
constexpr qreal & rx() noexcept
Returns a reference to the x coordinate of this point.
Definition qpoint.h:363
A base class for pointer events.
Definition qevent.h:73
static QPointingDevicePrivate * get(QPointingDevice *q)
QPointingDeviceUniqueId identifies a unique object, such as a tagged token or stylus,...
The QPointingDevice class describes a device from which mouse, touch or tablet events originate.
The QQmlListReference class allows the manipulation of QQmlListProperty properties.
Definition qqmllist.h:183
Q_INVOKABLE QVector3D mapFromViewport(const QVector3D &viewportPos) const
\qmlmethod vector3d Camera::mapFromViewport(vector3d viewportPos)
void updateGlobalVariables(const QRectF &inViewport)
Q_INVOKABLE QVector3D mapToViewport(const QVector3D &scenePos) const
\qmlmethod vector3d Camera::mapToViewport(vector3d scenePos)
static void extensionClear(QQmlListProperty< QQuick3DObject > *list)
static void extensionReplace(QQmlListProperty< QQuick3DObject > *list, qsizetype idx, QQuick3DObject *o)
static void extensionAppend(QQmlListProperty< QQuick3DObject > *list, QQuick3DObject *extension)
static QQuick3DObject * extensionAt(QQmlListProperty< QQuick3DObject > *list, qsizetype index)
static void extensionRemoveLast(QQmlListProperty< QQuick3DObject > *list)
static qsizetype extensionCount(QQmlListProperty< QQuick3DObject > *list)
void bake(Callback callback)
Triggers a new frame where lightmap baking will take place.
static void attachWatcherPriv(SceneContext *sceneContext, CallContext *callContext, Setter setter, QQuick3DObject *newO, QObject *oldO)
static QQuick3DObjectPrivate * get(QQuick3DObject *item)
\qmltype Object3D \inqmlmodule QtQuick3D \instantiates QQuick3DObject \inherits QtObject
void setParentItem(QQuick3DObject *parentItem)
void setWindow(QQuickWindow *window)
QQuick3DSceneRenderer * renderer()
void setViewport(const QRectF &viewport)
QQuick3DSceneRenderer * renderer
static QQuick3DWindowAttachment * getOrSetWindowAttachment(QQuickWindow &window)
void synchronize(QQuick3DViewport *view3D, const QSize &size, float dpr)
std::optional< QSSGRenderRay > getRayFromViewportPos(const QPointF &pos)
PickResultList syncPickOne(const QSSGRenderRay &ray, QSSGRenderNode *node)
void renderModeChanged()
QQuick3DNode * importScene
bool isTextureProvider() const override
Returns true if this item is a texture provider.
QQmlListProperty< QObject > data
void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override
int explicitTextureHeight
\qmlproperty int QtQuick3D::View3D::explicitTextureHeight
void releaseResources() override
This function is called when an item should release graphics resources which are not already managed ...
QQuick3DLightmapBaker * lightmapBaker()
void processPointerEventFromRay(const QVector3D &origin, const QVector3D &direction, QPointerEvent *event)
void setGlobalPickingEnabled(bool isEnabled)
void setCamera(QQuick3DCamera *camera)
QQuickShaderEffectSource::Format renderFormat
\qmlproperty enumeration QtQuick3D::View3D::renderFormat
const QPointF int pointId
void environmentChanged()
void setImportScene(QQuick3DNode *inScene)
void componentComplete() override
Invoked after the root component that caused this instantiation has completed construction.
void setEnvironment(QQuick3DSceneEnvironment *environment)
const QPointF & position
void itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value) override
Called when change occurs for this item.
int explicitTextureWidth
\qmlproperty int QtQuick3D::View3D::explicitTextureWidth
Q_INVOKABLE QQuick3DPickResult pick(float x, float y) const
\qmlmethod PickResult View3D::pick(float x, float y)
bool event(QEvent *) override
This virtual function receives events to an object and should return true if the event e was recogniz...
Q_INVOKABLE QVector3D mapTo3DScene(const QVector3D &viewPos) const
\qmlmethod vector3d View3D::mapTo3DScene(vector3d viewPos)
QQuick3DSceneRenderer * createRenderer() const
QSGNode * updatePaintNode(QSGNode *, UpdatePaintNodeData *) override
Called on the render thread when it is time to sync the state of the item with the scene graph.
QSGTextureProvider * textureProvider() const override
Returns the texture provider for an item.
void importSceneChanged()
QQuick3DViewport(QQuickItem *parent=nullptr)
\qmltype View3D \inherits QQuickItem \inqmlmodule QtQuick3D
QQuick3DLightmapBaker * maybeLightmapBaker()
Q_INVOKABLE QVector3D mapFrom3DScene(const QVector3D &scenePos) const
\qmlmethod vector3d View3D::mapFrom3DScene(vector3d scenePos)
void setRenderMode(QQuick3DViewport::RenderMode renderMode)
Q_INVOKABLE void bakeLightmap()
QQuick3DRenderStats * renderStats
QQuick3DNode * scene
QQmlListProperty< QQuick3DObject > extensions
\qmlproperty List<QtQuick3D::Object3D> View3D::extensions
QQuick3DSceneEnvironment * environment
QQuick3DCamera * camera
QSize effectiveTextureSize
\qmlproperty size QtQuick3D::View3D::effectiveTextureSize
static bool anyPointGrabbed(const QPointerEvent *ev)
static void data_append(QQmlListProperty< QObject > *, QObject *)
static QQuickItemPrivate * get(QQuickItem *item)
The QQuickItem class provides the most basic of all visual items in \l {Qt Quick}.
Definition qquickitem.h:63
QPointF mapToScene(const QPointF &point) const
Maps the given point in this item's coordinate system to the equivalent point within the scene's coor...
bool event(QEvent *) override
\reimp
void setFlag(Flag flag, bool enabled=true)
Enables the specified flag for this item if enabled is true; if enabled is false, the flag is disable...
virtual void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
qreal x
\qmlproperty real QtQuick::Item::x \qmlproperty real QtQuick::Item::y \qmlproperty real QtQuick::Item...
Definition qquickitem.h:72
void componentComplete() override
\reimp Derived classes should call the base class method before adding their own actions to perform a...
void setAcceptHoverEvents(bool enabled)
If enabled is true, this sets the item to accept hover events; otherwise, hover events are not accept...
QPointF mapFromScene(const QPointF &point) const
Maps the given point in the scene's coordinate system to the equivalent point within this item's coor...
qreal y
Defines the item's y position relative to its parent.
Definition qquickitem.h:73
void setAcceptTouchEvents(bool accept)
If enabled is true, this sets the item to accept touch events; otherwise, touch events are not accept...
bool isVisible() const
virtual QSGTextureProvider * textureProvider() const
Returns the texture provider for an item.
void setAcceptedMouseButtons(Qt::MouseButtons buttons)
Sets the mouse buttons accepted by this item to buttons.
QQuickWindow * window() const
Returns the window in which this item is rendered.
qreal width
This property holds the width of this item.
Definition qquickitem.h:75
bool isComponentComplete() const
Returns true if construction of the QML component is complete; otherwise returns false.
bool isEnabled() const
Q_INVOKABLE void forceActiveFocus()
\qmlmethod point QtQuick::Item::mapToItem(Item item, real x, real y) \qmlmethod point QtQuick::Item::...
const QSize & targetSize
Definition qquickitem.h:306
bool smooth
\qmlproperty bool QtQuick::Item::smooth
Definition qquickitem.h:112
qreal height
This property holds the height of this item.
Definition qquickitem.h:76
ItemChange
Used in conjunction with QQuickItem::itemChange() to notify the item about certain types of changes.
Definition qquickitem.h:144
@ ItemVisibleHasChanged
Definition qquickitem.h:148
void update()
Schedules a call to updatePaintNode() for this item.
virtual bool isTextureProvider() const
Returns true if this item is a texture provider.
\qmltype Window \instantiates QQuickWindow \inqmlmodule QtQuick
void sceneGraphInvalidated()
\qmlsignal QtQuick::Window::sceneGraphInitialized()
\inmodule QtCore\reentrant
Definition qrect.h:484
constexpr QSizeF size() const noexcept
Returns the size of the rectangle.
Definition qrect.h:735
\inmodule QtCore\reentrant
Definition qrect.h:30
\inmodule QtGuiPrivate \inheaderfile rhi/qrhi.h
Definition qrhi.h:1804
\inmodule QtCore
Definition qrunnable.h:18
\group qtquick-scenegraph-nodes \title Qt Quick Scene Graph Node classes
Definition qsgnode.h:37
@ DirtyMaterial
Definition qsgnode.h:75
An interface providing access to some of the graphics API specific internals of the scenegraph.
static bool isApiRhiBased(GraphicsApi api)
The QSGTextureProvider class encapsulates texture based entities in QML.
\inmodule QtQuick
Definition qsgtexture.h:20
The QScreen class is used to query screen properties. \inmodule QtGui.
Definition qscreen.h:32
A base class for pointer events containing a single point, such as mouse events.
Definition qevent.h:109
\inmodule QtCore
Definition qsize.h:208
\inmodule QtCore
Definition qsize.h:25
constexpr bool isEmpty() const noexcept
Returns true if either of the width and height is less than or equal to 0; otherwise returns false.
Definition qsize.h:124
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
The QTouchEvent class contains parameters that describe a touch event.
Definition qevent.h:917
constexpr size_type size() const noexcept
void resize(qsizetype sz)
void append(const T &t)
The QVector2D class represents a vector or vertex in 2D space.
Definition qvectornd.h:31
The QVector3D class represents a vector or vertex in 3D space.
Definition qvectornd.h:171
QQuick3DSceneRenderer * renderer
void extension()
[6]
Definition dialogs.cpp:230
QCamera * camera
Definition camera.cpp:19
list append(new Employee("Blackpool", "Stephen"))
direction
void newState(QList< State > &states, const char *token, const char *lexem, bool pre)
Combined button and popup list for selecting options.
Definition qcompare.h:63
@ AllButtons
Definition qnamespace.h:90
@ NoButton
Definition qnamespace.h:57
@ QueuedConnection
@ DirectConnection
#define Q_FALLTHROUGH()
#define Q_UNLIKELY(x)
static int pick(bool vertical, const QSize &size)
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
qfloat16 qSqrt(qfloat16 f)
Definition qfloat16.h:289
#define qWarning
Definition qlogging.h:166
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
return ret
static const QMetaObjectPrivate * priv(const uint *data)
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
constexpr static Q_DECL_CONST_FUNCTION double qt_inf() noexcept
Definition qnumeric_p.h:83
#define SLOT(a)
Definition qobjectdefs.h:52
#define SIGNAL(a)
Definition qobjectdefs.h:53
GLsizei const GLfloat * v
[13]
GLint GLint GLint GLint GLint x
[0]
GLenum mode
const GLfloat * m
GLfloat GLfloat GLfloat w
[0]
GLint GLsizei GLsizei height
GLuint index
[2]
GLenum GLuint GLenum GLsizei length
GLenum GLuint id
[7]
GLenum GLenum GLsizei count
GLint GLsizei width
GLenum type
GLenum target
GLboolean enable
GLenum GLuint texture
GLfloat n
GLint GLsizei GLsizei GLenum format
GLint y
struct _cl_event * event
GLuint GLenum GLenum transform
GLfixed GLfixed GLint GLint GLfixed points
GLhandleARB obj
[2]
GLuint res
GLdouble GLdouble t
Definition qopenglext.h:243
GLuint64EXT * result
[6]
Q_QML_EXPORT QQmlInfo qmlWarning(const QObject *me)
#define Q_QUICK3D_PROFILE_REGISTER(obj)
static QT_BEGIN_NAMESPACE bool isforceInputHandlingSet()
static qsizetype ssgn_count(QQmlListProperty< QObject > *property)
static QObject * ssgn_at(QQmlListProperty< QObject > *property, qsizetype i)
static void ssgn_append(QQmlListProperty< QObject > *property, QObject *obj)
static void ssgn_clear(QQmlListProperty< QObject > *property)
static qreal position(const QQuickItem *item, QQuickAnchors::Anchor anchorLine)
static QT_BEGIN_NAMESPACE qreal dpr(const QWindow *w)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define QSSG_CHECK_X(cond, msg)
#define QSSG_ASSERT(cond, action)
#define QSSG_GUARD(cond)
#define QStringLiteral(str)
Q_CORE_EXPORT int qEnvironmentVariableIntValue(const char *varName, bool *ok=nullptr) noexcept
#define emit
#define Q_UNUSED(x)
ptrdiff_t qsizetype
Definition qtypes.h:165
double qreal
Definition qtypes.h:187
PointerType
Definition qwasmevent.h:40
const char property[13]
Definition qwizard.cpp:101
QSqlQueryModel * model
[16]
QList< int > list
[14]
myObject disconnect()
[26]
QLayoutItem * child
[0]
QNetworkAccessManager manager
QSvgRenderer * renderer
[0]
static bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType, QGenericReturnArgument ret, QGenericArgument val0=QGenericArgument(nullptr), QGenericArgument val1=QGenericArgument(), QGenericArgument val2=QGenericArgument(), QGenericArgument val3=QGenericArgument(), QGenericArgument val4=QGenericArgument(), QGenericArgument val5=QGenericArgument(), QGenericArgument val6=QGenericArgument(), QGenericArgument val7=QGenericArgument(), QGenericArgument val8=QGenericArgument(), QGenericArgument val9=QGenericArgument())
\threadsafe This is an overloaded member function, provided for convenience. It differs from the abov...
QVector< QSSGRenderGraphObject * > materials
QPointF map(const QPointF &viewportPoint) override
void setOnDeliveryAgent(QQuickDeliveryAgent *da)
static QList< QPointer< QQuickDeliveryAgent > > owners
QSSGRenderNode * sceneParentNode
QQuick3DSceneRenderer * renderer
QPointer< QQuick3DViewport > viewport
QPointer< QQuickItem > targetItem
\inmodule QtQuick
Definition qquickitem.h:159