21#include <QtQuick3DRuntimeRender/private/qssgrenderlayer_p.h>
22#include <QtQuick3DRuntimeRender/private/qssglayerrenderdata_p.h>
24#include <QtQuick3DUtils/private/qssgassert_p.h>
25#include <QtQuick3DUtils/private/qssgfrustum_p.h>
27#include <qsgtextureprovider.h>
28#include <QSGSimpleTextureNode>
29#include <QSGRendererInterface>
30#include <QQuickWindow>
31#include <QtQuick/private/qquickitem_p.h>
32#include <QtQuick/private/qquickpointerhandler_p.h>
36#include <QtGui/private/qeventpoint_p.h>
38#include <QtCore/private/qnumeric_p.h>
39#include <QtCore/qpointer.h>
45Q_STATIC_LOGGING_CATEGORY(lcEv,
"qt.quick3d.event")
46Q_STATIC_LOGGING_CATEGORY(lcPick,
"qt.quick3d.pick")
48static bool isforceInputHandlingSet()
50 static const bool v = (qEnvironmentVariableIntValue(
"QT_QUICK3D_FORCE_INPUT_HANDLING") > 0);
57 for (
auto o : owners) {
59 o->setSceneTransform(
nullptr);
65 da->setSceneTransform(
this);
70
71
72
73
74
76 QPointF point = viewportPoint;
81 point = viewport->mapFromScene(viewportPoint);
84 std::optional<QSSGRenderRay> rayResult =
renderer->getRayFromViewportPos(point);
85 if (rayResult.has_value()) {
87 if (!pickResults.isEmpty()) {
88 const auto pickResult = pickResults.first();
89 auto ret = pickResult.m_localUVCoords.toPointF();
91 ret = QPointF(targetItem->x() + ret.x() * targetItem->width(),
92 targetItem->y() - ret.y() * targetItem->height() + targetItem->height());
94 const bool outOfModel = pickResult.m_localUVCoords.isNull();
95 qCDebug(lcEv) << viewportPoint <<
"->" << (outOfModel ?
"OOM" :
"") << ret <<
"@" << pickResult.m_scenePosition
96 <<
"UV" << pickResult.m_localUVCoords <<
"dist" << qSqrt(pickResult.m_distanceSq);
98 return lastGoodMapping;
100 lastGoodMapping = ret;
126 static void extensionAppend(QQmlListProperty<QQuick3DObject> *list, QQuick3DObject *extension)
128 QSSG_ASSERT(list && extension,
return);
130 if (QQuick3DViewport *that = qobject_cast<QQuick3DViewport *>(list->object)) {
131 if (
const auto idx = that->m_extensions.indexOf(extension); idx == -1) {
132 if (!extension->parentItem())
133 extension->setParentItem(that->m_sceneRoot);
134 that->m_extensions.push_back(extension);
135 that->m_extensionListDirty =
true;
141 QQuick3DObject *ret =
nullptr;
142 QSSG_ASSERT(list,
return ret);
144 if (QQuick3DViewport *that = qobject_cast<QQuick3DViewport *>(list->object)) {
145 if (that->m_extensions.size() > index)
146 ret = that->m_extensions.at(index);
154 QSSG_ASSERT(list,
return ret);
156 if (QQuick3DViewport *that = qobject_cast<QQuick3DViewport *>(list->object))
157 ret = that->m_extensions.size();
163 QSSG_ASSERT(list,
return);
165 if (QQuick3DViewport *that = qobject_cast<QQuick3DViewport *>(list->object)) {
166 that->m_extensions.clear();
167 that->m_extensionListDirty =
true;
170 static void extensionReplace(QQmlListProperty<QQuick3DObject> *list, qsizetype idx, QQuick3DObject *o)
172 QSSG_ASSERT(list,
return);
174 if (QQuick3DViewport *that = qobject_cast<QQuick3DViewport *>(list->object)) {
175 if (that->m_extensions.size() > idx && idx > -1) {
176 that->m_extensions.replace(idx, o);
177 that->m_extensionListDirty =
true;
183 QSSG_ASSERT(list,
return);
185 if (QQuick3DViewport *that = qobject_cast<QQuick3DViewport *>(list->object)) {
186 that->m_extensions.removeLast();
187 that->m_extensionListDirty =
true;
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
246QQuick3DViewport::QQuick3DViewport(QQuickItem *parent)
249 setFlag(ItemHasContents);
251 m_sceneRoot =
new QQuick3DSceneRootNode(
this);
252 m_renderStats =
new QQuick3DRenderStats();
253 QQuick3DSceneManager *sceneManager =
new QQuick3DSceneManager();
254 QQuick3DObjectPrivate::get(m_sceneRoot)->refSceneManager(*sceneManager);
255 Q_ASSERT(sceneManager == QQuick3DObjectPrivate::get(m_sceneRoot)->sceneManager);
256 connect(sceneManager, &QQuick3DSceneManager::needsUpdate,
257 this, &QQuickItem::update);
261 if (isforceInputHandlingSet()) {
262 m_enableInputProcessing =
true;
263 updateInputProcessing();
268QQuick3DViewport::~QQuick3DViewport()
277 if (m_directRenderer && m_directRenderer->thread() == thread()) {
278 delete m_directRenderer;
279 m_directRenderer =
nullptr;
284 if (
auto qw = window())
285 disconnect(qw,
nullptr,
this,
nullptr);
287 auto sceneManager = QQuick3DObjectPrivate::get(m_sceneRoot)->sceneManager;
289 sceneManager->setParent(
nullptr);
290 if (
auto wa = sceneManager->wattached)
291 wa->queueForCleanup(sceneManager);
295 m_sceneRoot =
nullptr;
297 delete m_builtInEnvironment;
301 m_renderStats->deleteLater();
303 if (!window() && sceneManager && sceneManager->wattached)
304 QMetaObject::invokeMethod(sceneManager->wattached, &QQuick3DWindowAttachment::evaluateEol, Qt::QueuedConnection);
307static void ssgn_append(QQmlListProperty<QObject> *property, QObject *obj)
311 QQuick3DViewport *view3d =
static_cast<QQuick3DViewport *>(property->object);
313 if (QQuick3DObject *sceneObject = qmlobject_cast<QQuick3DObject *>(obj)) {
314 QQmlListProperty<QObject> itemProperty = QQuick3DObjectPrivate::get(view3d->scene())->data();
315 itemProperty.append(&itemProperty, sceneObject);
317 QQuickItemPrivate::data_append(property, obj);
323 QQuick3DViewport *view3d =
static_cast<QQuick3DViewport *>(property->object);
324 if (!view3d || !view3d->scene() || !QQuick3DObjectPrivate::get(view3d->scene())->data().count)
326 QQmlListProperty<QObject> itemProperty = QQuick3DObjectPrivate::get(view3d->scene())->data();
327 return itemProperty.count(&itemProperty);
332 QQuick3DViewport *view3d =
static_cast<QQuick3DViewport *>(property->object);
333 QQmlListProperty<QObject> itemProperty = QQuick3DObjectPrivate::get(view3d->scene())->data();
334 return itemProperty.at(&itemProperty, i);
339 QQuick3DViewport *view3d =
static_cast<QQuick3DViewport *>(property->object);
340 QQmlListProperty<QObject> itemProperty = QQuick3DObjectPrivate::get(view3d->scene())->data();
341 return itemProperty.clear(&itemProperty);
345QQmlListProperty<QObject> QQuick3DViewport::data()
347 return QQmlListProperty<QObject>(
this,
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372QQuick3DCamera *QQuick3DViewport::camera()
const
378
379
380
381
382
383
384
385
386QQuick3DSceneEnvironment *QQuick3DViewport::environment()
const
388 if (!m_environment) {
389 if (!m_builtInEnvironment) {
390 m_builtInEnvironment =
new QQuick3DSceneEnvironment;
394 if (QThread::currentThread() != m_sceneRoot->thread())
395 m_builtInEnvironment->moveToThread(m_sceneRoot->thread());
396 m_builtInEnvironment->setParentItem(m_sceneRoot);
399 return m_builtInEnvironment;
402 return m_environment;
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425QQuick3DNode *QQuick3DViewport::scene()
const
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457QQuick3DNode *QQuick3DViewport::importScene()
const
459 return m_importScene;
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505QQuick3DViewport::RenderMode QQuick3DViewport::renderMode()
const
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531#if QT_CONFIG(quick_shadereffect)
532QQuickShaderEffectSource::Format QQuick3DViewport::renderFormat()
const
534 return m_renderFormat;
539
540
541
542
543
544
545
546
547QQuick3DRenderStats *QQuick3DViewport::renderStats()
const
549 return m_renderStats;
552QQuick3DSceneRenderer *QQuick3DViewport::createRenderer()
const
554 QQuick3DSceneRenderer *renderer =
nullptr;
556 if (QQuickWindow *qw = window()) {
557 auto wa = QQuick3DSceneManager::getOrSetWindowAttachment(*qw);
558 auto rci = wa->rci();
560 QSGRendererInterface *rif = qw->rendererInterface();
561 if (QSSG_GUARD(QSGRendererInterface::isApiRhiBased(rif->graphicsApi()))) {
562 QRhi *rhi =
static_cast<QRhi *>(rif->getResource(qw, QSGRendererInterface::RhiResource));
563 QSSG_CHECK_X(rhi !=
nullptr,
"No QRhi from QQuickWindow, this cannot happen");
573 rci = std::make_shared<QSSGRenderContextInterface>(rhi);
577 connect(wa, &QQuick3DWindowAttachment::releaseCachedResources,
this,
578 &QQuick3DViewport::onReleaseCachedResources, Qt::DirectConnection);
581 qWarning(
"The Qt Quick scene is using a rendering method that is not based on QRhi and a 3D graphics API. "
582 "Qt Quick 3D is not functional in such an environment. The View3D item is not going to display anything.");
587 renderer =
new QQuick3DSceneRenderer(rci);
588 Q_QUICK3D_PROFILE_ASSIGN_ID(
this, renderer);
595bool QQuick3DViewport::isTextureProvider()
const
598 if (m_renderMode == QQuick3DViewport::Offscreen)
604QSGTextureProvider *QQuick3DViewport::textureProvider()
const
609 if (QQuickItem::isTextureProvider())
610 return QQuickItem::textureProvider();
613 if (m_renderMode != QQuick3DViewport::Offscreen)
616 QQuickWindow *w = window();
618 qWarning(
"QSSGView3D::textureProvider: can only be queried on the rendering thread of an exposed window");
623 m_node =
new SGFramebufferObjectNode;
636void QQuick3DViewport::releaseResources()
638 if (m_directRenderer) {
639 window()->scheduleRenderJob(
new CleanupJob(m_directRenderer), QQuickWindow::BeforeSynchronizingStage);
640 m_directRenderer =
nullptr;
646void QQuick3DViewport::cleanupDirectRenderer()
648 delete m_directRenderer;
649 m_directRenderer =
nullptr;
652void QQuick3DViewport::geometryChange(
const QRectF &newGeometry,
const QRectF &oldGeometry)
654 QQuickItem::geometryChange(newGeometry, oldGeometry);
656 if (newGeometry.size() != oldGeometry.size())
660QSGNode *QQuick3DViewport::updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNodeData *)
663 if (m_renderModeDirty) {
668 m_renderNode =
nullptr;
670 if (m_directRenderer) {
671 delete m_directRenderer;
672 m_directRenderer =
nullptr;
676 m_renderModeDirty =
false;
678 switch (m_renderMode) {
683 setupDirectRenderer(m_renderMode);
687 node = setupOffscreenRenderer(node);
691 node = setupInlineRenderer(node);
695 if (!isforceInputHandlingSet()) {
697 const auto inputHandlingEnabled =
698 QQuick3DObjectPrivate::get(m_sceneRoot)->sceneManager->inputHandlingEnabled;
699 const auto enable = inputHandlingEnabled > 0;
700 if (m_enableInputProcessing != enable) {
701 m_enableInputProcessing = enable;
702 QMetaObject::invokeMethod(
this,
"updateInputProcessing", Qt::QueuedConnection);
709void QQuick3DViewport::itemChange(QQuickItem::ItemChange change,
const QQuickItem::ItemChangeData &value)
711 if (change == ItemSceneChange) {
715 QQuick3DObjectPrivate::get(m_sceneRoot)->sceneManager->setWindow(value.window);
717 QQuick3DObjectPrivate::get(m_importScene)->sceneManager->setWindow(value.window);
718 m_renderStats->setWindow(value.window);
720 }
else if (change == ItemVisibleHasChanged && isVisible()) {
725bool QQuick3DViewport::event(QEvent *event)
727 if (m_enableInputProcessing && event->isPointerEvent())
728 return internalPick(
static_cast<QPointerEvent *>(event));
730 return QQuickItem::event(event);
733void QQuick3DViewport::componentComplete()
735 QQuickItem::componentComplete();
736 Q_QUICK3D_PROFILE_REGISTER(
this);
739void QQuick3DViewport::setCamera(QQuick3DCamera *camera)
741 if (m_camera == camera)
744 if (camera && !camera->parentItem())
745 camera->setParentItem(m_sceneRoot);
747 camera->updateGlobalVariables(QRect(0, 0, width(), height()));
749 QQuick3DObjectPrivate::attachWatcherPriv(m_sceneRoot,
this, &QQuick3DViewport::setCamera, camera, m_camera);
752 emit cameraChanged();
756void QQuick3DViewport::setMultiViewCameras(QQuick3DCamera **firstCamera,
int count)
758 m_multiViewCameras.clear();
759 bool sendChangeSignal =
false;
760 for (
int i = 0; i < count; ++i) {
761 QQuick3DCamera *camera = *(firstCamera + i);
763 if (!camera->parentItem())
764 camera->setParentItem(m_sceneRoot);
765 camera->updateGlobalVariables(QRect(0, 0, width(), height()));
768 if (m_camera != camera) {
770 sendChangeSignal =
true;
774 m_multiViewCameras.append(camera);
779 if (sendChangeSignal)
780 emit cameraChanged();
785void QQuick3DViewport::setEnvironment(QQuick3DSceneEnvironment *environment)
787 if (m_environment == environment)
790 m_environment = environment;
791 if (m_environment && !m_environment->parentItem())
792 m_environment->setParentItem(m_sceneRoot);
794 QQuick3DObjectPrivate::attachWatcherPriv(m_sceneRoot,
this, &QQuick3DViewport::setEnvironment, environment, m_environment);
796 emit environmentChanged();
800void QQuick3DViewport::setImportScene(QQuick3DNode *inScene)
810 QQuick3DNode *scene = inScene;
812 if (m_sceneRoot == scene) {
813 qmlWarning(
this) <<
"Cannot allow self-import or cross-import!";
817 QQuick3DSceneRootNode *rn = qobject_cast<QQuick3DSceneRootNode *>(scene);
818 scene = rn ? rn->view3D()->importScene() :
nullptr;
821 m_importScene = inScene;
823 updateSceneManagerForImportScene();
825 emit importSceneChanged();
829void QQuick3DViewport::setRenderMode(QQuick3DViewport::RenderMode renderMode)
831 if (m_renderMode == renderMode)
834 m_renderMode = renderMode;
835 m_renderModeDirty =
true;
836 emit renderModeChanged();
840#if QT_CONFIG(quick_shadereffect)
841void QQuick3DViewport::setRenderFormat(QQuickShaderEffectSource::Format format)
843 if (m_renderFormat == format)
846 m_renderFormat = format;
847 m_renderModeDirty =
true;
848 emit renderFormatChanged();
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872int QQuick3DViewport::explicitTextureWidth()
const
874 return m_explicitTextureWidth;
877void QQuick3DViewport::setExplicitTextureWidth(
int width)
879 if (m_explicitTextureWidth == width)
882 m_explicitTextureWidth = width;
883 emit explicitTextureWidthChanged();
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906int QQuick3DViewport::explicitTextureHeight()
const
908 return m_explicitTextureHeight;
911void QQuick3DViewport::setExplicitTextureHeight(
int height)
913 if (m_explicitTextureHeight == height)
916 m_explicitTextureHeight = height;
917 emit explicitTextureHeightChanged();
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936QSize QQuick3DViewport::effectiveTextureSize()
const
938 return m_effectiveTextureSize;
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958QVector3D QQuick3DViewport::mapFrom3DScene(
const QVector3D &scenePos)
const
961 qmlWarning(
this) <<
"Cannot resolve view position without a camera assigned!";
962 return QVector3D(0, 0, 0);
965 qreal _width = width();
966 qreal _height = height();
967 if (_width == 0 || _height == 0)
968 return QVector3D(0, 0, 0);
970 const QVector3D normalizedPos = m_camera->mapToViewport(scenePos, _width, _height);
971 return normalizedPos * QVector3D(
float(_width),
float(_height), 1);
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991QVector3D QQuick3DViewport::mapTo3DScene(
const QVector3D &viewPos)
const
994 qmlWarning(
this) <<
"Cannot resolve scene position without a camera assigned!";
995 return QVector3D(0, 0, 0);
998 qreal _width = width();
999 qreal _height = height();
1000 if (_width == 0 || _height == 0)
1001 return QVector3D(0, 0, 0);
1003 const QVector3D normalizedPos = viewPos / QVector3D(
float(_width),
float(_height), 1);
1004 return m_camera->mapFromViewport(normalizedPos, _width, _height);
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017QQuick3DPickResult QQuick3DViewport::pick(
float x,
float y)
const
1019 QQuick3DSceneRenderer *renderer = getRenderer();
1021 return QQuick3DPickResult();
1023 const QPointF position(qreal(x) * window()->effectiveDevicePixelRatio() * m_widthMultiplier,
1024 qreal(y) * window()->effectiveDevicePixelRatio() * m_heightMultiplier);
1025 std::optional<QSSGRenderRay> rayResult = renderer->getRayFromViewportPos(position);
1026 if (!rayResult.has_value())
1027 return QQuick3DPickResult();
1029 const auto resultList = renderer->syncPick(rayResult.value());
1030 return getNearestPickResult(resultList);
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045QQuick3DPickResult QQuick3DViewport::pick(
float x,
float y, QQuick3DModel *model)
const
1047 QQuick3DSceneRenderer *renderer = getRenderer();
1049 return QQuick3DPickResult();
1051 const QPointF position(qreal(x) * window()->effectiveDevicePixelRatio(),
1052 qreal(y) * window()->effectiveDevicePixelRatio());
1053 std::optional<QSSGRenderRay> rayResult = renderer->getRayFromViewportPos(position);
1055 if (!rayResult.has_value())
1056 return QQuick3DPickResult();
1058 const auto renderNode =
static_cast<QSSGRenderNode *>(QQuick3DObjectPrivate::get(model)->spatialNode);
1059 const auto resultList = renderer->syncPickOne(rayResult.value(), renderNode);
1060 return getNearestPickResult(resultList);
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080QList<QQuick3DPickResult> QQuick3DViewport::pickSubset(
float x,
float y,
const QJSValue &models)
const
1082 QQuick3DSceneRenderer *renderer = getRenderer();
1086 QVarLengthArray<QSSGRenderNode*> renderNodes;
1088 if (models.isArray()) {
1089 const auto length = models.property(QStringLiteral(
"length")).toInt();
1093 for (
int i = 0; i < length; ++i) {
1094 const auto isQObject = models.property(i).isQObject();
1096 qmlWarning(
this) <<
"Type provided for picking is not a QObject. Needs to be of type QQuick3DModel.";
1099 const auto obj = models.property(i).toQObject();
1100 const auto model = qobject_cast<QQuick3DModel *>(obj);
1102 qmlWarning(
this) <<
"Type " << obj->metaObject()->className() <<
" is not supported for picking. Needs to be of type QQuick3DModel.";
1105 const auto priv = QQuick3DObjectPrivate::get(model);
1106 if (priv && priv->spatialNode) {
1107 renderNodes.push_back(
static_cast<QSSGRenderNode*>(priv->spatialNode));
1112 const auto subsetVariant = models.toVariant();
1113 if (!subsetVariant.isValid() || !subsetVariant.canConvert<QQmlListReference>())
1116 const auto list = subsetVariant.value<QQmlListReference>();
1119 if (list.listElementType()->className() != QQuick3DModel::staticMetaObject.className()) {
1120 qmlWarning(
this) <<
"Type " << list.listElementType()->className() <<
" is not supported for picking. Needs to be of type QQuick3DModel.";
1123 for (
int i = 0; i < list.count(); ++i) {
1124 auto model =
static_cast<QQuick3DModel *>(list.at(i));
1127 auto priv = QQuick3DObjectPrivate::get(model);
1128 if (priv && priv->spatialNode) {
1129 renderNodes.push_back(
static_cast<QSSGRenderNode*>(priv->spatialNode));
1134 if (renderNodes.empty())
1137 const QPointF position(qreal(x) * window()->effectiveDevicePixelRatio(),
1138 qreal(y) * window()->effectiveDevicePixelRatio());
1139 std::optional<QSSGRenderRay> rayResult = renderer->getRayFromViewportPos(position);
1140 if (!rayResult.has_value())
1143 const auto resultList = renderer->syncPickSubset(rayResult.value(), renderNodes);
1145 QList<QQuick3DPickResult> processedResultList;
1146 processedResultList.reserve(resultList.size());
1147 for (
const auto &result : resultList)
1148 processedResultList.append(processPickResult(result));
1150 return processedResultList;
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167QList<QQuick3DPickResult> QQuick3DViewport::pickAll(
float x,
float y)
const
1169 QQuick3DSceneRenderer *renderer = getRenderer();
1171 return QList<QQuick3DPickResult>();
1173 const QPointF position(qreal(x) * window()->effectiveDevicePixelRatio() * m_widthMultiplier,
1174 qreal(y) * window()->effectiveDevicePixelRatio() * m_heightMultiplier);
1175 std::optional<QSSGRenderRay> rayResult = renderer->getRayFromViewportPos(position);
1176 if (!rayResult.has_value())
1177 return QList<QQuick3DPickResult>();
1179 const auto resultList = renderer->syncPickAll(rayResult.value());
1180 QList<QQuick3DPickResult> processedResultList;
1181 processedResultList.reserve(resultList.size());
1182 for (
const auto &result : resultList)
1183 processedResultList.append(processPickResult(result));
1185 return processedResultList;
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201QQuick3DPickResult QQuick3DViewport::rayPick(
const QVector3D &origin,
const QVector3D &direction)
const
1203 QQuick3DSceneRenderer *renderer = getRenderer();
1205 return QQuick3DPickResult();
1207 const QSSGRenderRay ray(origin, direction);
1208 const auto resultList = renderer->syncPick(ray);
1209 return getNearestPickResult(resultList);
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228QList<QQuick3DPickResult> QQuick3DViewport::rayPickAll(
const QVector3D &origin,
const QVector3D &direction)
const
1230 QQuick3DSceneRenderer *renderer = getRenderer();
1232 return QList<QQuick3DPickResult>();
1234 const QSSGRenderRay ray(origin, direction);
1236 const auto resultList = renderer->syncPickAll(ray);
1237 QList<QQuick3DPickResult> processedResultList;
1238 processedResultList.reserve(resultList.size());
1239 for (
const auto &result : resultList) {
1240 auto processedResult = processPickResult(result);
1241 if (processedResult.hitType() != QQuick3DPickResultEnums::HitType::Null)
1242 processedResultList.append(processedResult);
1245 return processedResultList;
1249
1250
1251
1252
1253
1254
1255
1256QQuick3DPickResult QQuick3DViewport::rayPick(
const QVector3D &origin,
const QVector3D &direction, QQuick3DModel *model)
const
1258 QQuick3DSceneRenderer *renderer = getRenderer();
1260 return QQuick3DPickResult();
1262 const QSSGRenderRay ray(origin, direction);
1264 const auto renderNode =
static_cast<QSSGRenderNode *>(QQuick3DObjectPrivate::get(model)->spatialNode);
1265 const auto resultList = renderer->syncPickOne(ray, renderNode);
1266 return getNearestPickResult(resultList);
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279QQuick3DPickResult QQuick3DViewport::closestPointPick(
const QVector3D &origin,
float radius, QQuick3DModel *model)
const
1281 QQuick3DSceneRenderer *renderer = getRenderer();
1284 return QQuick3DPickResult();
1285 QSSGRenderNode *renderNode =
nullptr;
1287 renderNode =
static_cast<QSSGRenderNode *>(QQuick3DObjectPrivate::get(model)->spatialNode);
1288 if (Q_UNLIKELY(!renderNode))
1289 return QQuick3DPickResult{};
1292 const auto pickResult = renderer->syncPickClosestPoint(origin, radius * radius, renderNode);
1293 if (!pickResult.has_value())
1294 return QQuick3DPickResult{};
1295 return processPickResult(pickResult.value());
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311QList<QQuick3DObject *> QQuick3DViewport::pickInRect(
const QPointF &start,
const QPointF &end)
const
1313 const qreal minX = qMin(start.x(), end.x());
1314 const qreal maxX = qMax(start.x(), end.x());
1315 const qreal minY = qMin(start.y(), end.y());
1316 const qreal maxY = qMax(start.y(), end.y());
1318 const qreal ndc[4] = {
1319 2.0f * minX / width() - 1.0f,
1320 2.0f * maxX / width() - 1.0f,
1322 1.0f - 2.0f * maxY / height(),
1323 1.0f - 2.0f * minY / height(),
1326 const float near = 0.0f;
1327 const float far = 1.0f;
1328 enum { L, R, B, T };
1329 const QVector4D ndcCorners[8] = {
1331 QVector4D(ndc[L], ndc[B], near, 1.0f),
1332 QVector4D(ndc[R], ndc[B], near, 1.0f),
1333 QVector4D(ndc[L], ndc[T], near, 1.0f),
1334 QVector4D(ndc[R], ndc[T], near, 1.0f),
1336 QVector4D(ndc[L], ndc[B], far, 1.0f),
1337 QVector4D(ndc[R], ndc[B], far, 1.0f),
1338 QVector4D(ndc[L], ndc[T], far, 1.0f),
1339 QVector4D(ndc[R], ndc[T], far, 1.0f),
1342 QMatrix4x4 viewProjection;
1343 if (
this->camera()) {
1344 if (
auto camera =
static_cast<QSSGRenderCamera *>(QQuick3DObjectPrivate::get(
this->camera())->spatialNode))
1345 camera->calculateViewProjectionMatrix(camera->localTransform, camera->projection, viewProjection);
1347 QMatrix4x4 viewProjectionInverted = viewProjection.inverted();
1349 QVector3D worldCorners[8];
1350 for (
int i = 0; i < 8; ++i) {
1351 QVector4D worldPoint = viewProjectionInverted * ndcCorners[i];
1352 worldCorners[i] = worldPoint.toVector3D() / worldPoint.w();
1371 const QSSGFrustum frustum {
1372 QSSGPlane(worldCorners[0], worldCorners[4], worldCorners[6]),
1373 QSSGPlane(worldCorners[1], worldCorners[3], worldCorners[7]),
1374 QSSGPlane(worldCorners[0], worldCorners[1], worldCorners[5]),
1375 QSSGPlane(worldCorners[2], worldCorners[6], worldCorners[7]),
1376 QSSGPlane(worldCorners[0], worldCorners[2], worldCorners[3]),
1377 QSSGPlane(worldCorners[5], worldCorners[7], worldCorners[6]),
1380 QList<QQuick3DObject *> ret;
1381 if (QQuick3DSceneRenderer *renderer = getRenderer()) {
1382 auto nodes = renderer->syncPickInFrustum(frustum);
1383 for (
auto node : nodes) {
1384 if (QQuick3DObject *m = findFrontendNode(node))
1392void QQuick3DViewport::processPointerEventFromRay(
const QVector3D &origin,
const QVector3D &direction, QPointerEvent *event)
const
1394 internalPick(event, origin, direction);
1401class SyntheticTouchDevice :
public QPointingDevice
1404 SyntheticTouchDevice(QObject *parent =
nullptr)
1405 : QPointingDevice(QLatin1StringView(
"QtQuick3D Touch Synthesizer"),
1407 DeviceType::TouchScreen,
1408 PointerType::Finger,
1409 Capability::Position,
1411 QString(), QPointingDeviceUniqueId(),
1419
1420
1421
1422
1423
1424
1425
1426
1427
1429void QQuick3DViewport::setTouchpoint(QQuickItem *target,
const QPointF &position,
int pointId,
bool pressed)
1431 if (pointId >= m_touchState.size())
1432 m_touchState.resize(pointId + 1);
1433 auto prevState = m_touchState[pointId];
1435 const bool sameTarget = prevState.target == target;
1436 const bool wasPressed = prevState.isPressed;
1438 const bool isPress = pressed && (!sameTarget || !wasPressed);
1439 const bool isRelease = !pressed && wasPressed && sameTarget;
1444 if (!sameTarget && wasPressed)
1445 qWarning(
"QQuick3DViewport::setTouchpoint missing release event");
1447 if (!pressed && !wasPressed) {
1452 m_touchState[pointId] = { target, position, pressed };
1454 if (!m_syntheticTouchDevice)
1455 m_syntheticTouchDevice =
new SyntheticTouchDevice(
this);
1457 QPointingDevicePrivate *devPriv = QPointingDevicePrivate::get(m_syntheticTouchDevice);
1459 auto makePoint = [devPriv](
int id, QEventPoint::State pointState, QPointF pos, quint64 timestamp) -> QEventPoint {
1460 auto epd = devPriv->pointById(id);
1461 auto &ep = epd->eventPoint;
1462 if (pointState != QEventPoint::State::Stationary)
1463 ep.setAccepted(
false);
1465 auto res = QMutableEventPoint::withTimeStamp(timestamp, id, pointState, pos, pos, pos);
1466 QMutableEventPoint::update(res, ep);
1468 if (pointState == QEventPoint::State::Pressed)
1469 QMutableEventPoint::setGlobalPressPosition(res, pos);
1470 else if (ep.state() != QEventPoint::State::Unknown)
1471 QMutableEventPoint::setGlobalPressPosition(res, ep.globalPressPosition());
1476 auto sendTouchEvent = [&](QQuickItem *t,
const QPointF &position,
int pointId, QEventPoint::State pointState, quint64 timestamp) ->
void {
1477 QList<QEventPoint> points;
1478 bool otherPoint =
false;
1479 for (
int i = 0; i < m_touchState.size(); ++i) {
1480 const auto &ts = m_touchState[i];
1484 auto newPoint = makePoint(i, pointState, position, timestamp);
1486 }
else if (ts.isPressed) {
1488 points << makePoint(i, QEventPoint::Stationary, ts.position, timestamp);
1493 if (pointState == QEventPoint::Pressed && !otherPoint)
1494 type = QEvent::Type::TouchBegin;
1495 else if (pointState == QEventPoint::Released && !otherPoint)
1496 type = QEvent::Type::TouchEnd;
1498 type = QEvent::Type::TouchUpdate;
1500 QTouchEvent ev(type, m_syntheticTouchDevice, {}, points);
1501 ev.setTimestamp(timestamp);
1505 auto da = QQuickItemPrivate::get(t)->deliveryAgent();
1506 bool handled = da->event(&ev);
1511 if (ev.isEndEvent()) {
1512 for (
auto &point : ev.points()) {
1513 if (point.state() == QEventPoint::State::Released) {
1514 ev.setExclusiveGrabber(point,
nullptr);
1515 ev.clearPassiveGrabbers(point);
1521 auto timestamp = QDateTime::currentMSecsSinceEpoch();
1524 if (prevState.target && !sameTarget)
1525 sendTouchEvent(prevState.target, prevState.position, pointId, QEventPoint::Released, timestamp);
1528 QEventPoint::State newState = isPress ? QEventPoint::Pressed : isRelease ? QEventPoint::Released : QEventPoint::Updated;
1529 sendTouchEvent(target, position, pointId, newState, timestamp);
1532QQuick3DLightmapBaker *QQuick3DViewport::maybeLightmapBaker()
1534 return m_lightmapBaker;
1537QQuick3DLightmapBaker *QQuick3DViewport::lightmapBaker()
1539 if (!m_lightmapBaker)
1540 m_lightmapBaker=
new QQuick3DLightmapBaker(
this);
1542 return m_lightmapBaker;
1546
1547
1548void QQuick3DViewport::bakeLightmap()
1550 QQuick3DSceneRenderer *renderer = getRenderer();
1551 if (!renderer || !renderer->m_layer->renderData)
1554 const bool currentlyBaking = renderer->m_layer->renderData->lightmapBaker !=
nullptr;
1556 if (!currentlyBaking)
1557 lightmapBaker()->bake();
1561
1562
1563void QQuick3DViewport::denoiseLightmap()
1565 QQuick3DSceneRenderer *renderer = getRenderer();
1566 if (!renderer || !renderer->m_layer->renderData)
1569 const bool currentlyBaking = renderer->m_layer->renderData->lightmapBaker !=
nullptr;
1571 if (!currentlyBaking)
1572 lightmapBaker()->denoise();
1576void QQuick3DViewport::setGlobalPickingEnabled(
bool isEnabled)
1578 QQuick3DSceneRenderer *renderer = getRenderer();
1582 renderer->setGlobalPickingEnabled(isEnabled);
1585void QQuick3DViewport::invalidateSceneGraph()
1590QQuick3DSceneRenderer *QQuick3DViewport::getRenderer()
const
1592 QQuick3DSceneRenderer *renderer =
nullptr;
1594 renderer = m_node->renderer;
1595 }
else if (m_renderNode) {
1596 renderer = m_renderNode->renderer;
1597 }
else if (m_directRenderer) {
1598 renderer = m_directRenderer->renderer();
1603void QQuick3DViewport::updateDynamicTextures()
1608 const auto &sceneManager = QQuick3DObjectPrivate::get(m_sceneRoot)->sceneManager;
1609 for (
auto *texture : std::as_const(sceneManager->qsgDynamicTextures))
1610 texture->updateTexture();
1612 QQuick3DNode *scene = m_importScene;
1614 const auto &importSm = QQuick3DObjectPrivate::get(scene)->sceneManager;
1615 if (importSm != sceneManager) {
1616 for (
auto *texture : std::as_const(importSm->qsgDynamicTextures))
1617 texture->updateTexture();
1621 QQuick3DSceneRootNode *rn = qobject_cast<QQuick3DSceneRootNode *>(scene);
1622 scene = rn ? rn->view3D()->importScene() :
nullptr;
1626QSGNode *QQuick3DViewport::setupOffscreenRenderer(QSGNode *node)
1628 SGFramebufferObjectNode *n =
static_cast<SGFramebufferObjectNode *>(node);
1632 m_node =
new SGFramebufferObjectNode;
1637 n->window = window();
1638 n->renderer = createRenderer();
1641 n->renderer->fboNode = n;
1643 connect(window(), SIGNAL(screenChanged(QScreen*)), n, SLOT(handleScreenChange()));
1646 const qreal dpr = window()->effectiveDevicePixelRatio();
1647 const QSize minFboSize = QQuickItemPrivate::get(
this)->sceneGraphContext()->minimumFBOSize();
1648 QSize desiredFboSize = QSize(m_explicitTextureWidth, m_explicitTextureHeight);
1649 if (desiredFboSize.isEmpty()) {
1650 desiredFboSize = QSize(width(), height()) * dpr;
1651 n->devicePixelRatio = dpr;
1653 m_widthMultiplier = 1.0f;
1654 m_heightMultiplier = 1.0f;
1656 QSize itemPixelSize = QSize(width(), height()) * dpr;
1658 m_widthMultiplier = desiredFboSize.width() /
float(itemPixelSize.width());
1659 m_heightMultiplier = desiredFboSize.height() /
float(itemPixelSize.height());
1660 n->devicePixelRatio = 1.0;
1662 desiredFboSize.setWidth(qMax(minFboSize.width(), desiredFboSize.width()));
1663 desiredFboSize.setHeight(qMax(minFboSize.height(), desiredFboSize.height()));
1665 if (desiredFboSize != m_effectiveTextureSize) {
1666 m_effectiveTextureSize = desiredFboSize;
1667 emit effectiveTextureSizeChanged();
1670 n->setFiltering(smooth() ? QSGTexture::Linear : QSGTexture::Nearest);
1671 n->setRect(0, 0, width(), height());
1672 if (checkIsVisible() && isComponentComplete()) {
1673 n->renderer->synchronize(
this, desiredFboSize, n->devicePixelRatio);
1674 if (n->renderer->m_textureNeedsFlip)
1675 n->setTextureCoordinatesTransform(QSGSimpleTextureNode::MirrorVertically);
1676 updateDynamicTextures();
1677 n->scheduleRender();
1683QSGNode *QQuick3DViewport::setupInlineRenderer(QSGNode *node)
1685 QQuick3DSGRenderNode *n =
static_cast<QQuick3DSGRenderNode *>(node);
1688 m_renderNode =
new QQuick3DSGRenderNode;
1693 n->window = window();
1694 n->renderer = createRenderer();
1699 if (!m_effectiveTextureSize.isEmpty()) {
1700 m_effectiveTextureSize = QSize();
1701 emit effectiveTextureSizeChanged();
1704 const QSize targetSize = window()->effectiveDevicePixelRatio() * QSize(width(), height());
1709 if (checkIsVisible() && isComponentComplete()) {
1710 n->renderer->synchronize(
this, targetSize, window()->effectiveDevicePixelRatio());
1711 updateDynamicTextures();
1712 n->markDirty(QSGNode::DirtyMaterial);
1719void QQuick3DViewport::setupDirectRenderer(RenderMode mode)
1721 auto renderMode = (mode == Underlay) ? QQuick3DSGDirectRenderer::Underlay
1722 : QQuick3DSGDirectRenderer::Overlay;
1723 if (!m_directRenderer) {
1724 QQuick3DSceneRenderer *sceneRenderer = createRenderer();
1727 m_directRenderer =
new QQuick3DSGDirectRenderer(sceneRenderer, window(), renderMode);
1728 connect(window(), &QQuickWindow::sceneGraphInvalidated,
this, &QQuick3DViewport::cleanupDirectRenderer, Qt::DirectConnection);
1731 if (!m_effectiveTextureSize.isEmpty()) {
1732 m_effectiveTextureSize = QSize();
1733 emit effectiveTextureSizeChanged();
1736 const QSizeF targetSize = window()->effectiveDevicePixelRatio() * QSizeF(width(), height());
1737 m_directRenderer->setViewport(QRectF(window()->effectiveDevicePixelRatio() * mapToScene(QPointF(0, 0)), targetSize));
1738 m_directRenderer->setVisibility(isVisible());
1740 m_directRenderer->preSynchronize();
1741 m_directRenderer->renderer()->synchronize(
this, targetSize.toSize(), window()->effectiveDevicePixelRatio());
1742 updateDynamicTextures();
1743 m_directRenderer->requestRender();
1749bool QQuick3DViewport::checkIsVisible()
const
1751 auto childPrivate = QQuickItemPrivate::get(
this);
1752 return (childPrivate->explicitVisible ||
1753 (childPrivate->extra.isAllocated() && childPrivate->extra->effectRefCount));
1758
1759
1760
1761
1762
1763
1764
1765
1766void QQuick3DViewport::processPickedObject(
const QSSGRenderPickResult &pickResult,
1768 QPointerEvent *event,
1769 QFlatMap<QQuickItem *, SubsceneInfo> &visitedSubscenes)
const
1771 QQuickItem *subsceneRootItem =
nullptr;
1772 QPointF subscenePosition;
1773 const auto backendObject = pickResult.m_hitObject;
1774 const auto frontendObject = findFrontendNode(backendObject);
1775 if (!frontendObject)
1781 auto frontendObjectPrivate = QQuick3DObjectPrivate::get(frontendObject);
1782 if (frontendObjectPrivate->type == QQuick3DObjectPrivate::Type::Item2D) {
1785 auto item2D = qobject_cast<QQuick3DItem2D *>(frontendObject);
1787 subsceneRootItem = item2D->contentItem();
1788 if (!subsceneRootItem || subsceneRootItem->childItems().isEmpty())
1792 subscenePosition = pickResult.m_localUVCoords.toPointF();
1796 if (!subsceneRootItem->childAt(subscenePosition.x(), subscenePosition.y()))
1798 }
else if (frontendObjectPrivate->type == QQuick3DObjectPrivate::Type::Model) {
1800 int materialSubset = pickResult.m_subset;
1801 const auto backendModel =
static_cast<
const QSSGRenderModel *>(backendObject);
1803 if (backendModel->materials.size() < (pickResult.m_subset + 1))
1804 materialSubset = backendModel->materials.size() - 1;
1805 if (materialSubset < 0)
1807 const auto backendMaterial = backendModel->materials.at(materialSubset);
1808 const auto frontendMaterial =
static_cast<QQuick3DMaterial*>(findFrontendNode(backendMaterial));
1809 subsceneRootItem = getSubSceneRootItem(frontendMaterial);
1811 if (subsceneRootItem) {
1813 subscenePosition = QPointF(subsceneRootItem->x() + pickResult.m_localUVCoords.x() * subsceneRootItem->width(),
1814 subsceneRootItem->y() - pickResult.m_localUVCoords.y() * subsceneRootItem->height() + subsceneRootItem->height());
1819 if (subsceneRootItem) {
1820 SubsceneInfo &subscene = visitedSubscenes[subsceneRootItem];
1821 subscene.obj = frontendObject;
1822 if (subscene.eventPointScenePositions.size() != event->pointCount()) {
1824 constexpr QPointF inf(-qt_inf(), -qt_inf());
1825 subscene.eventPointScenePositions.resize(event->pointCount(), inf);
1827 subscene.eventPointScenePositions[pointIndex] = subscenePosition;
1832
1833
1834
1835
1836
1837
1838
1839
1841QQuickItem *QQuick3DViewport::getSubSceneRootItem(QQuick3DMaterial *material)
const
1846 QQuickItem *subsceneRootItem =
nullptr;
1847 const auto frontendMaterialPrivate = QQuick3DObjectPrivate::get(material);
1849 if (frontendMaterialPrivate->type == QQuick3DObjectPrivate::Type::DefaultMaterial) {
1851 const auto defaultMaterial = qobject_cast<QQuick3DDefaultMaterial *>(material);
1852 if (defaultMaterial) {
1854 if (defaultMaterial->diffuseMap() && defaultMaterial->diffuseMap()->sourceItem())
1855 subsceneRootItem = defaultMaterial->diffuseMap()->sourceItem();
1858 }
else if (frontendMaterialPrivate->type == QQuick3DObjectPrivate::Type::PrincipledMaterial) {
1860 const auto principledMaterial = qobject_cast<QQuick3DPrincipledMaterial *>(material);
1861 if (principledMaterial) {
1863 if (principledMaterial->baseColorMap() && principledMaterial->baseColorMap()->sourceItem())
1864 subsceneRootItem = principledMaterial->baseColorMap()->sourceItem();
1866 }
else if (frontendMaterialPrivate->type == QQuick3DObjectPrivate::Type::SpecularGlossyMaterial) {
1868 const auto specularGlossyMaterial = qobject_cast<QQuick3DSpecularGlossyMaterial *>(material);
1869 if (specularGlossyMaterial) {
1871 if (specularGlossyMaterial->albedoMap() && specularGlossyMaterial->albedoMap()->sourceItem())
1872 subsceneRootItem = specularGlossyMaterial->albedoMap()->sourceItem();
1874 }
else if (frontendMaterialPrivate->type == QQuick3DObjectPrivate::Type::CustomMaterial) {
1876 const auto customMaterial = qobject_cast<QQuick3DCustomMaterial *>(material);
1877 if (customMaterial) {
1879 const auto &texturesInputs = customMaterial->m_dynamicTextureMaps;
1880 for (
const auto &textureInput : texturesInputs) {
1881 if (
auto texture = textureInput->texture()) {
1882 if (texture->sourceItem()) {
1883 subsceneRootItem = texture->sourceItem();
1890 return subsceneRootItem;
1895
1896
1897QQuick3DPickResult QQuick3DViewport::getNearestPickResult(
const QVarLengthArray<QSSGRenderPickResult, 20> &pickResults)
const
1899 for (
const auto &result : pickResults) {
1900 auto pickResult = processPickResult(result);
1901 if (pickResult.hitType() != QQuick3DPickResultEnums::HitType::Null)
1905 return QQuick3DPickResult();
1909
1910
1911
1912
1913
1914bool QQuick3DViewport::forwardEventToSubscenes(QPointerEvent *event,
1916 QQuick3DSceneRenderer *renderer,
1917 const QFlatMap<QQuickItem *, SubsceneInfo> &visitedSubscenes)
const
1924 QVarLengthArray<QPointF, 16> originalScenePositions;
1925 originalScenePositions.resize(event->pointCount());
1926 for (
int pointIndex = 0; pointIndex < event->pointCount(); ++pointIndex)
1927 originalScenePositions[pointIndex] = event->point(pointIndex).scenePosition();
1928 for (
auto subscene : visitedSubscenes) {
1929 QQuickItem *subsceneRoot = subscene.first;
1930 auto &subsceneInfo = subscene.second;
1931 Q_ASSERT(subsceneInfo.eventPointScenePositions.size() == event->pointCount());
1932 auto da = QQuickItemPrivate::get(subsceneRoot)->deliveryAgent();
1933 for (
int pointIndex = 0; pointIndex < event->pointCount(); ++pointIndex) {
1934 const auto &pt = subsceneInfo.eventPointScenePositions.at(pointIndex);
1939 QEventPoint &ep = event->point(pointIndex);
1940 QMutableEventPoint::setPosition(ep, pt);
1941 QMutableEventPoint::setScenePosition(ep, pt);
1944 if (event->isBeginEvent())
1945 da->setSceneTransform(
nullptr);
1946 if (da->event(event)) {
1948 if (QQuickDeliveryAgentPrivate::anyPointGrabbed(event) && !useRayPicking) {
1953 auto frontendObjectPrivate = QQuick3DObjectPrivate::get(subsceneInfo.obj);
1954 const bool item2Dcase = (frontendObjectPrivate->type == QQuick3DObjectPrivate::Type::Item2D);
1955 ViewportTransformHelper *transform =
new ViewportTransformHelper;
1956 transform->viewport =
const_cast<QQuick3DViewport *>(
this);
1957 transform->renderer = renderer;
1958 transform->sceneParentNode =
static_cast<QSSGRenderNode*>(frontendObjectPrivate->spatialNode);
1959 transform->targetItem = subsceneRoot;
1960 transform->scaleX = window()->effectiveDevicePixelRatio() * m_widthMultiplier;
1961 transform->scaleY = window()->effectiveDevicePixelRatio() * m_heightMultiplier;
1962 transform->uvCoordsArePixels = item2Dcase;
1963 transform->setOnDeliveryAgent(da);
1964 qCDebug(lcPick) << event->type() <<
"created ViewportTransformHelper on" << da;
1966 }
else if (event->type() != QEvent::HoverMove) {
1967 qCDebug(lcPick) << subsceneRoot <<
"didn't want" << event;
1969 event->setAccepted(
false);
1971 if (visitedSubscenes.isEmpty()) {
1972 event->setAccepted(
false);
1974 for (
int pointIndex = 0; pointIndex < event->pointCount(); ++pointIndex)
1975 QMutableEventPoint::setScenePosition(event->point(pointIndex), originalScenePositions.at(pointIndex));
1981 if (event->isEndEvent() && useRayPicking) {
1982 if (event->isSinglePointEvent()) {
1983 if (
static_cast<QSinglePointEvent *>(event)->buttons() == Qt::NoButton) {
1984 auto &firstPt = event->point(0);
1985 event->setExclusiveGrabber(firstPt,
nullptr);
1986 event->clearPassiveGrabbers(firstPt);
1989 for (
auto &point : event->points()) {
1990 if (point.state() == QEventPoint::State::Released) {
1991 event->setExclusiveGrabber(point,
nullptr);
1992 event->clearPassiveGrabbers(point);
2002bool QQuick3DViewport::internalPick(QPointerEvent *event,
const QVector3D &origin,
const QVector3D &direction)
const
2004 QQuick3DSceneRenderer *renderer = getRenderer();
2005 if (!renderer || !event)
2008 QFlatMap<QQuickItem*, SubsceneInfo> visitedSubscenes;
2009 const bool useRayPicking = !direction.isNull();
2011 for (
int pointIndex = 0; pointIndex < event->pointCount(); ++pointIndex) {
2012 auto &eventPoint = event->point(pointIndex);
2013 QVarLengthArray<QSSGRenderPickResult, 20> pickResults;
2014 if (Q_UNLIKELY(useRayPicking))
2015 pickResults = getPickResults(renderer, origin, direction);
2017 pickResults = getPickResults(renderer, eventPoint);
2019 if (!pickResults.isEmpty())
2020 for (
const auto &pickResult : pickResults)
2021 processPickedObject(pickResult, pointIndex, event, visitedSubscenes);
2023 eventPoint.setAccepted(
false);
2026 return forwardEventToSubscenes(event, useRayPicking, renderer, visitedSubscenes);
2029bool QQuick3DViewport::singlePointPick(QSinglePointEvent *event,
const QVector3D &origin,
const QVector3D &direction)
2031 QQuick3DSceneRenderer *renderer = getRenderer();
2032 if (!renderer || !event)
2035 QSSGRenderRay ray(origin, direction);
2037 Q_ASSERT(event->pointCount() == 1);
2038 QPointF originalPosition = event->point(0).scenePosition();
2040 auto pickResults = renderer->syncPickAll(ray);
2042 bool delivered =
false;
2044 constexpr float jitterLimit = 2.5;
2045 bool withinJitterLimit =
false;
2047 for (
const auto &pickResult : pickResults) {
2048 auto [item, position] = getItemAndPosition(pickResult);
2051 if (item == m_prevMouseItem && (position - m_prevMousePos).manhattanLength() < jitterLimit && !event->button()) {
2052 withinJitterLimit =
true;
2055 auto da = QQuickItemPrivate::get(item)->deliveryAgent();
2056 QEventPoint &ep = event->point(0);
2057 QMutableEventPoint::setPosition(ep, position);
2058 QMutableEventPoint::setScenePosition(ep, position);
2059 if (da->event(event)) {
2061 if (event->type() == QEvent::MouseButtonPress) {
2062 m_prevMouseItem = item;
2063 m_prevMousePos = position;
2064 withinJitterLimit =
true;
2070 QMutableEventPoint::setScenePosition(event->point(0), originalPosition);
2071 if (!withinJitterLimit)
2072 m_prevMouseItem =
nullptr;
2077 if (event->isEndEvent()) {
2078 if (event->buttons() == Qt::NoButton) {
2079 auto &firstPt = event->point(0);
2080 event->setExclusiveGrabber(firstPt,
nullptr);
2081 event->clearPassiveGrabbers(firstPt);
2088QPair<QQuickItem *, QPointF> QQuick3DViewport::getItemAndPosition(
const QSSGRenderPickResult &pickResult)
const
2090 QQuickItem *subsceneRootItem =
nullptr;
2091 QPointF subscenePosition;
2092 const auto backendObject = pickResult.m_hitObject;
2093 const auto frontendObject = findFrontendNode(backendObject);
2094 if (!frontendObject)
2096 auto frontendObjectPrivate = QQuick3DObjectPrivate::get(frontendObject);
2097 if (frontendObjectPrivate->type == QQuick3DObjectPrivate::Type::Item2D) {
2100 auto item2D = qobject_cast<QQuick3DItem2D *>(frontendObject);
2102 subsceneRootItem = item2D->contentItem();
2103 if (!subsceneRootItem || subsceneRootItem->childItems().isEmpty())
2107 subscenePosition = pickResult.m_localUVCoords.toPointF();
2111 if (!subsceneRootItem->childAt(subscenePosition.x(), subscenePosition.y()))
2113 }
else if (frontendObjectPrivate->type == QQuick3DObjectPrivate::Type::Model) {
2115 int materialSubset = pickResult.m_subset;
2116 const auto backendModel =
static_cast<
const QSSGRenderModel *>(backendObject);
2118 if (backendModel->materials.size() < (pickResult.m_subset + 1))
2119 materialSubset = backendModel->materials.size() - 1;
2120 if (materialSubset < 0)
2122 const auto backendMaterial = backendModel->materials.at(materialSubset);
2123 const auto frontendMaterial =
static_cast<QQuick3DMaterial *>(findFrontendNode(backendMaterial));
2124 subsceneRootItem = getSubSceneRootItem(frontendMaterial);
2126 if (subsceneRootItem) {
2128 subscenePosition = QPointF(subsceneRootItem->x() + pickResult.m_localUVCoords.x() * subsceneRootItem->width(),
2129 subsceneRootItem->y() - pickResult.m_localUVCoords.y() * subsceneRootItem->height() + subsceneRootItem->height());
2132 return {subsceneRootItem, subscenePosition};
2135QVarLengthArray<QSSGRenderPickResult, 20> QQuick3DViewport::getPickResults(QQuick3DSceneRenderer *renderer,
2136 const QVector3D &origin,
2137 const QVector3D &direction)
const
2139 const QSSGRenderRay ray(origin, direction);
2140 return renderer->syncPickAll(ray);
2143QVarLengthArray<QSSGRenderPickResult, 20> QQuick3DViewport::getPickResults(QQuick3DSceneRenderer *renderer,
const QEventPoint &eventPoint)
const
2145 QVarLengthArray<QSSGRenderPickResult, 20> pickResults;
2146 QPointF realPosition = eventPoint.position() * window()->effectiveDevicePixelRatio();
2148 realPosition.rx() *= m_widthMultiplier;
2149 realPosition.ry() *= m_heightMultiplier;
2150 std::optional<QSSGRenderRay> rayResult = renderer->getRayFromViewportPos(realPosition);
2151 if (rayResult.has_value())
2152 pickResults = renderer->syncPickAll(rayResult.value());
2157
2158
2159
2160
2161
2162QQuick3DObject *QQuick3DViewport::findFrontendNode(
const QSSGRenderGraphObject *backendObject)
const
2167 const auto sceneManager = QQuick3DObjectPrivate::get(m_sceneRoot)->sceneManager;
2168 QQuick3DObject *frontendObject = sceneManager->lookUpNode(backendObject);
2169 if (!frontendObject && m_importScene) {
2170 const auto importSceneManager = QQuick3DObjectPrivate::get(m_importScene)->sceneManager;
2171 frontendObject = importSceneManager->lookUpNode(backendObject);
2173 return frontendObject;
2176QQuick3DPickResult QQuick3DViewport::processPickResult(
const QSSGRenderPickResult &pickResult)
const
2178 if (!pickResult.m_hitObject)
2179 return QQuick3DPickResult();
2181 QQuick3DObject *frontendObject = findFrontendNode(pickResult.m_hitObject);
2183 QQuick3DModel *model = qobject_cast<QQuick3DModel *>(frontendObject);
2185 auto itemAndPosition = getItemAndPosition(pickResult);
2186 return QQuick3DPickResult(model,
2187 ::sqrtf(pickResult.m_distanceSq),
2188 pickResult.m_localUVCoords,
2189 pickResult.m_scenePosition,
2190 pickResult.m_localPosition,
2191 pickResult.m_faceNormal,
2192 pickResult.m_sceneNormal,
2193 pickResult.m_instanceIndex,
2194 itemAndPosition.first);
2197 QQuick3DItem2D *frontend2DItem = qobject_cast<QQuick3DItem2D *>(frontendObject);
2198 if (frontend2DItem && frontend2DItem->contentItem()) {
2200 const QPointF subscenePosition = pickResult.m_localUVCoords.toPointF();
2201 const auto child = frontend2DItem->contentItem()->childAt(subscenePosition.x(), subscenePosition.y());
2203 return QQuick3DPickResult(child,
2204 ::sqrtf(pickResult.m_distanceSq),
2205 QVector2D(frontend2DItem->contentItem()->mapToItem(child, subscenePosition)),
2206 pickResult.m_scenePosition,
2207 pickResult.m_localPosition,
2208 pickResult.m_faceNormal);
2212 return QQuick3DPickResult();
2217QQuick3DSceneManager *QQuick3DViewport::findChildSceneManager(QQuick3DObject *inObject, QQuick3DSceneManager *manager)
2222 auto children = QQuick3DObjectPrivate::get(inObject)->childItems;
2223 for (
auto child : children) {
2224 if (
auto m = QQuick3DObjectPrivate::get(child)->sceneManager) {
2228 manager = findChildSceneManager(child, manager);
2233void QQuick3DViewport::updateInputProcessing()
2236 setAcceptTouchEvents(m_enableInputProcessing);
2237 setAcceptHoverEvents(m_enableInputProcessing);
2238 setAcceptedMouseButtons(m_enableInputProcessing ? Qt::AllButtons : Qt::NoButton);
2241void QQuick3DViewport::onReleaseCachedResources()
2243 if (
auto renderer = getRenderer())
2244 renderer->releaseCachedResources();
2248
2249
2250
2251
2252
2253
2254QQmlListProperty<QQuick3DObject> QQuick3DViewport::extensions()
2256 return QQmlListProperty<QQuick3DObject>{
this,
2257 &m_extensionListDirty,
2258 &QQuick3DExtensionListHelper::extensionAppend,
2259 &QQuick3DExtensionListHelper::extensionCount,
2260 &QQuick3DExtensionListHelper::extensionAt,
2261 &QQuick3DExtensionListHelper::extensionClear,
2262 &QQuick3DExtensionListHelper::extensionReplace,
2263 &QQuick3DExtensionListHelper::extensionRemoveLast};
2267
2268
2269void QQuick3DViewport::rebuildExtensionList()
2271 m_extensionListDirty =
true;
2276
2277
2278
2279
2280
2281QQuick3DViewport::QQuick3DViewport(PrivateInstanceType type, QQuickItem *parent)
2282 : QQuick3DViewport(parent)
2284 m_isXrViewInstance = type == PrivateInstanceType::XrViewInstance;
2287void QQuick3DViewport::updateCameraForLayer(
const QQuick3DViewport &view3D, QSSGRenderLayer &layerNode)
2289 layerNode.explicitCameras.clear();
2290 if (!view3D.m_multiViewCameras.isEmpty()) {
2291 for (QQuick3DCamera *camera : std::as_const(view3D.m_multiViewCameras))
2292 layerNode.explicitCameras.append(
static_cast<QSSGRenderCamera *>(QQuick3DObjectPrivate::get(camera)->spatialNode));
2293 }
else if (view3D.camera()) {
2294 if (QSSGRenderCamera *camera =
static_cast<QSSGRenderCamera *>(QQuick3DObjectPrivate::get(view3D.camera())->spatialNode))
2295 layerNode.explicitCameras.append(camera);
2300 for (QSSGRenderCamera *camera : std::as_const(layerNode.explicitCameras)) {
2301 if (!camera->parent)
2302 layerNode.addChild(*camera);
2306void QQuick3DViewport::updateSceneManagerForImportScene()
2308 auto privateObject = QQuick3DObjectPrivate::get(m_importScene);
2309 if (!privateObject->sceneManager) {
2311 QQuick3DSceneManager *manager = findChildSceneManager(m_importScene);
2314 manager = QQuick3DObjectPrivate::get(m_sceneRoot)->sceneManager;
2316 manager->setWindow(window());
2317 privateObject->refSceneManager(*manager);
2322 if (!privateObject->sceneManager)
2326 connect(privateObject->sceneManager, &QQuick3DSceneManager::needsUpdate,
2327 this, &QQuickItem::update, Qt::UniqueConnection);
2328 connect(privateObject->sceneManager, &QObject::destroyed,
2329 this, [&](QObject *) {
2330 auto privateObject = QQuick3DObjectPrivate::get(m_importScene);
2331 privateObject->sceneManager =
nullptr;
2332 updateSceneManagerForImportScene();
2333 }, Qt::DirectConnection);
2335 QQuick3DNode *scene = m_importScene;
2337 QQuick3DSceneRootNode *rn = qobject_cast<QQuick3DSceneRootNode *>(scene);
2338 scene = rn ? rn->view3D()->importScene() :
nullptr;
2341 connect(QQuick3DObjectPrivate::get(scene)->sceneManager,
2342 &QQuick3DSceneManager::needsUpdate,
2343 this, &QQuickItem::update, Qt::UniqueConnection);
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2362QQuick3DViewport::RenderOverrides QQuick3DViewport::renderOverrides()
const
2364 return m_renderOverrides;
2367void QQuick3DViewport::setRenderOverrides(RenderOverrides newRenderOverrides)
2369 if (m_renderOverrides == newRenderOverrides)
2371 m_renderOverrides = newRenderOverrides;
2372 emit renderOverridesChanged();
void run() override
Implement this pure virtual function in your subclass.
CleanupJob(QQuick3DSGDirectRenderer *renderer)
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)
Combined button and popup list for selecting options.
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)