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
371QQuick3DCamera *QQuick3DViewport::camera()
const
377
378
379
380
381
382
383
384
385QQuick3DSceneEnvironment *QQuick3DViewport::environment()
const
387 if (!m_environment) {
388 if (!m_builtInEnvironment) {
389 m_builtInEnvironment =
new QQuick3DSceneEnvironment;
393 if (QThread::currentThread() != m_sceneRoot->thread())
394 m_builtInEnvironment->moveToThread(m_sceneRoot->thread());
395 m_builtInEnvironment->setParentItem(m_sceneRoot);
398 return m_builtInEnvironment;
401 return m_environment;
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424QQuick3DNode *QQuick3DViewport::scene()
const
430
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
456QQuick3DNode *QQuick3DViewport::importScene()
const
458 return m_importScene;
462
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
504QQuick3DViewport::RenderMode QQuick3DViewport::renderMode()
const
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530#if QT_CONFIG(quick_shadereffect)
531QQuickShaderEffectSource::Format QQuick3DViewport::renderFormat()
const
533 return m_renderFormat;
538
539
540
541
542
543
544
545
546QQuick3DRenderStats *QQuick3DViewport::renderStats()
const
548 return m_renderStats;
551QQuick3DSceneRenderer *QQuick3DViewport::createRenderer()
const
553 QQuick3DSceneRenderer *renderer =
nullptr;
555 if (QQuickWindow *qw = window()) {
556 auto wa = QQuick3DSceneManager::getOrSetWindowAttachment(*qw);
557 auto rci = wa->rci();
559 QSGRendererInterface *rif = qw->rendererInterface();
560 if (QSSG_GUARD(QSGRendererInterface::isApiRhiBased(rif->graphicsApi()))) {
561 QRhi *rhi =
static_cast<QRhi *>(rif->getResource(qw, QSGRendererInterface::RhiResource));
562 QSSG_CHECK_X(rhi !=
nullptr,
"No QRhi from QQuickWindow, this cannot happen");
572 rci = std::make_shared<QSSGRenderContextInterface>(rhi);
576 connect(wa, &QQuick3DWindowAttachment::releaseCachedResources,
this,
577 &QQuick3DViewport::onReleaseCachedResources, Qt::DirectConnection);
580 qWarning(
"The Qt Quick scene is using a rendering method that is not based on QRhi and a 3D graphics API. "
581 "Qt Quick 3D is not functional in such an environment. The View3D item is not going to display anything.");
586 renderer =
new QQuick3DSceneRenderer(rci);
587 Q_QUICK3D_PROFILE_ASSIGN_ID(
this, renderer);
594bool QQuick3DViewport::isTextureProvider()
const
597 if (m_renderMode == QQuick3DViewport::Offscreen)
603QSGTextureProvider *QQuick3DViewport::textureProvider()
const
608 if (QQuickItem::isTextureProvider())
609 return QQuickItem::textureProvider();
612 if (m_renderMode != QQuick3DViewport::Offscreen)
615 QQuickWindow *w = window();
617 qWarning(
"QSSGView3D::textureProvider: can only be queried on the rendering thread of an exposed window");
622 m_node =
new SGFramebufferObjectNode;
635void QQuick3DViewport::releaseResources()
637 if (m_directRenderer) {
638 window()->scheduleRenderJob(
new CleanupJob(m_directRenderer), QQuickWindow::BeforeSynchronizingStage);
639 m_directRenderer =
nullptr;
645void QQuick3DViewport::cleanupDirectRenderer()
647 delete m_directRenderer;
648 m_directRenderer =
nullptr;
651void QQuick3DViewport::geometryChange(
const QRectF &newGeometry,
const QRectF &oldGeometry)
653 QQuickItem::geometryChange(newGeometry, oldGeometry);
655 if (newGeometry.size() != oldGeometry.size())
659QSGNode *QQuick3DViewport::updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNodeData *)
662 if (m_renderModeDirty) {
667 m_renderNode =
nullptr;
669 if (m_directRenderer) {
670 delete m_directRenderer;
671 m_directRenderer =
nullptr;
675 m_renderModeDirty =
false;
677 switch (m_renderMode) {
682 setupDirectRenderer(m_renderMode);
686 node = setupOffscreenRenderer(node);
690 node = setupInlineRenderer(node);
694 if (!isforceInputHandlingSet()) {
696 const auto inputHandlingEnabled =
697 QQuick3DObjectPrivate::get(m_sceneRoot)->sceneManager->inputHandlingEnabled;
698 const auto enable = inputHandlingEnabled > 0;
699 if (m_enableInputProcessing != enable) {
700 m_enableInputProcessing = enable;
701 QMetaObject::invokeMethod(
this,
"updateInputProcessing", Qt::QueuedConnection);
708void QQuick3DViewport::itemChange(QQuickItem::ItemChange change,
const QQuickItem::ItemChangeData &value)
710 if (change == ItemSceneChange) {
714 QQuick3DObjectPrivate::get(m_sceneRoot)->sceneManager->setWindow(value.window);
716 QQuick3DObjectPrivate::get(m_importScene)->sceneManager->setWindow(value.window);
717 m_renderStats->setWindow(value.window);
719 }
else if (change == ItemVisibleHasChanged && isVisible()) {
724bool QQuick3DViewport::event(QEvent *event)
726 if (m_enableInputProcessing && event->isPointerEvent())
727 return internalPick(
static_cast<QPointerEvent *>(event));
729 return QQuickItem::event(event);
732void QQuick3DViewport::componentComplete()
734 QQuickItem::componentComplete();
735 Q_QUICK3D_PROFILE_REGISTER(
this);
738void QQuick3DViewport::setCamera(QQuick3DCamera *camera)
740 if (m_camera == camera)
743 if (camera && !camera->parentItem())
744 camera->setParentItem(m_sceneRoot);
746 camera->updateGlobalVariables(QRect(0, 0, width(), height()));
748 QQuick3DObjectPrivate::attachWatcherPriv(m_sceneRoot,
this, &QQuick3DViewport::setCamera, camera, m_camera);
751 emit cameraChanged();
755void QQuick3DViewport::setMultiViewCameras(QQuick3DCamera **firstCamera,
int count)
757 m_multiViewCameras.clear();
758 bool sendChangeSignal =
false;
759 for (
int i = 0; i < count; ++i) {
760 QQuick3DCamera *camera = *(firstCamera + i);
762 if (!camera->parentItem())
763 camera->setParentItem(m_sceneRoot);
764 camera->updateGlobalVariables(QRect(0, 0, width(), height()));
767 if (m_camera != camera) {
769 sendChangeSignal =
true;
773 m_multiViewCameras.append(camera);
778 if (sendChangeSignal)
779 emit cameraChanged();
784void QQuick3DViewport::setEnvironment(QQuick3DSceneEnvironment *environment)
786 if (m_environment == environment)
789 m_environment = environment;
790 if (m_environment && !m_environment->parentItem())
791 m_environment->setParentItem(m_sceneRoot);
793 QQuick3DObjectPrivate::attachWatcherPriv(m_sceneRoot,
this, &QQuick3DViewport::setEnvironment, environment, m_environment);
795 emit environmentChanged();
799void QQuick3DViewport::setImportScene(QQuick3DNode *inScene)
809 QQuick3DNode *scene = inScene;
811 if (m_sceneRoot == scene) {
812 qmlWarning(
this) <<
"Cannot allow self-import or cross-import!";
816 QQuick3DSceneRootNode *rn = qobject_cast<QQuick3DSceneRootNode *>(scene);
817 scene = rn ? rn->view3D()->importScene() :
nullptr;
820 m_importScene = inScene;
822 updateSceneManagerForImportScene();
824 emit importSceneChanged();
828void QQuick3DViewport::setRenderMode(QQuick3DViewport::RenderMode renderMode)
830 if (m_renderMode == renderMode)
833 m_renderMode = renderMode;
834 m_renderModeDirty =
true;
835 emit renderModeChanged();
839#if QT_CONFIG(quick_shadereffect)
840void QQuick3DViewport::setRenderFormat(QQuickShaderEffectSource::Format format)
842 if (m_renderFormat == format)
845 m_renderFormat = format;
846 m_renderModeDirty =
true;
847 emit renderFormatChanged();
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871int QQuick3DViewport::explicitTextureWidth()
const
873 return m_explicitTextureWidth;
876void QQuick3DViewport::setExplicitTextureWidth(
int width)
878 if (m_explicitTextureWidth == width)
881 m_explicitTextureWidth = width;
882 emit explicitTextureWidthChanged();
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905int QQuick3DViewport::explicitTextureHeight()
const
907 return m_explicitTextureHeight;
910void QQuick3DViewport::setExplicitTextureHeight(
int height)
912 if (m_explicitTextureHeight == height)
915 m_explicitTextureHeight = height;
916 emit explicitTextureHeightChanged();
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935QSize QQuick3DViewport::effectiveTextureSize()
const
937 return m_effectiveTextureSize;
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957QVector3D QQuick3DViewport::mapFrom3DScene(
const QVector3D &scenePos)
const
960 qmlWarning(
this) <<
"Cannot resolve view position without a camera assigned!";
961 return QVector3D(0, 0, 0);
964 qreal _width = width();
965 qreal _height = height();
966 if (_width == 0 || _height == 0)
967 return QVector3D(0, 0, 0);
969 const QVector3D normalizedPos = m_camera->mapToViewport(scenePos, _width, _height);
970 return normalizedPos * QVector3D(
float(_width),
float(_height), 1);
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990QVector3D QQuick3DViewport::mapTo3DScene(
const QVector3D &viewPos)
const
993 qmlWarning(
this) <<
"Cannot resolve scene position without a camera assigned!";
994 return QVector3D(0, 0, 0);
997 qreal _width = width();
998 qreal _height = height();
999 if (_width == 0 || _height == 0)
1000 return QVector3D(0, 0, 0);
1002 const QVector3D normalizedPos = viewPos / QVector3D(
float(_width),
float(_height), 1);
1003 return m_camera->mapFromViewport(normalizedPos, _width, _height);
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016QQuick3DPickResult QQuick3DViewport::pick(
float x,
float y)
const
1018 QQuick3DSceneRenderer *renderer = getRenderer();
1020 return QQuick3DPickResult();
1022 const QPointF position(qreal(x) * window()->effectiveDevicePixelRatio() * m_widthMultiplier,
1023 qreal(y) * window()->effectiveDevicePixelRatio() * m_heightMultiplier);
1024 std::optional<QSSGRenderRay> rayResult = renderer->getRayFromViewportPos(position);
1025 if (!rayResult.has_value())
1026 return QQuick3DPickResult();
1028 const auto resultList = renderer->syncPick(rayResult.value());
1029 return getNearestPickResult(resultList);
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044QQuick3DPickResult QQuick3DViewport::pick(
float x,
float y, QQuick3DModel *model)
const
1046 QQuick3DSceneRenderer *renderer = getRenderer();
1048 return QQuick3DPickResult();
1050 const QPointF position(qreal(x) * window()->effectiveDevicePixelRatio(),
1051 qreal(y) * window()->effectiveDevicePixelRatio());
1052 std::optional<QSSGRenderRay> rayResult = renderer->getRayFromViewportPos(position);
1054 if (!rayResult.has_value())
1055 return QQuick3DPickResult();
1057 const auto renderNode =
static_cast<QSSGRenderNode *>(QQuick3DObjectPrivate::get(model)->spatialNode);
1058 const auto resultList = renderer->syncPickOne(rayResult.value(), renderNode);
1059 return getNearestPickResult(resultList);
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079QList<QQuick3DPickResult> QQuick3DViewport::pickSubset(
float x,
float y,
const QJSValue &models)
const
1081 QQuick3DSceneRenderer *renderer = getRenderer();
1085 QVarLengthArray<QSSGRenderNode*> renderNodes;
1087 if (models.isArray()) {
1088 const auto length = models.property(QStringLiteral(
"length")).toInt();
1092 for (
int i = 0; i < length; ++i) {
1093 const auto isQObject = models.property(i).isQObject();
1095 qmlWarning(
this) <<
"Type provided for picking is not a QObject. Needs to be of type QQuick3DModel.";
1098 const auto obj = models.property(i).toQObject();
1099 const auto model = qobject_cast<QQuick3DModel *>(obj);
1101 qmlWarning(
this) <<
"Type " << obj->metaObject()->className() <<
" is not supported for picking. Needs to be of type QQuick3DModel.";
1104 const auto priv = QQuick3DObjectPrivate::get(model);
1105 if (priv && priv->spatialNode) {
1106 renderNodes.push_back(
static_cast<QSSGRenderNode*>(priv->spatialNode));
1111 const auto subsetVariant = models.toVariant();
1112 if (!subsetVariant.isValid() || !subsetVariant.canConvert<QQmlListReference>())
1115 const auto list = subsetVariant.value<QQmlListReference>();
1118 if (list.listElementType()->className() != QQuick3DModel::staticMetaObject.className()) {
1119 qmlWarning(
this) <<
"Type " << list.listElementType()->className() <<
" is not supported for picking. Needs to be of type QQuick3DModel.";
1122 for (
int i = 0; i < list.count(); ++i) {
1123 auto model =
static_cast<QQuick3DModel *>(list.at(i));
1126 auto priv = QQuick3DObjectPrivate::get(model);
1127 if (priv && priv->spatialNode) {
1128 renderNodes.push_back(
static_cast<QSSGRenderNode*>(priv->spatialNode));
1133 if (renderNodes.empty())
1136 const QPointF position(qreal(x) * window()->effectiveDevicePixelRatio(),
1137 qreal(y) * window()->effectiveDevicePixelRatio());
1138 std::optional<QSSGRenderRay> rayResult = renderer->getRayFromViewportPos(position);
1139 if (!rayResult.has_value())
1142 const auto resultList = renderer->syncPickSubset(rayResult.value(), renderNodes);
1144 QList<QQuick3DPickResult> processedResultList;
1145 processedResultList.reserve(resultList.size());
1146 for (
const auto &result : resultList)
1147 processedResultList.append(processPickResult(result));
1149 return processedResultList;
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166QList<QQuick3DPickResult> QQuick3DViewport::pickAll(
float x,
float y)
const
1168 QQuick3DSceneRenderer *renderer = getRenderer();
1170 return QList<QQuick3DPickResult>();
1172 const QPointF position(qreal(x) * window()->effectiveDevicePixelRatio() * m_widthMultiplier,
1173 qreal(y) * window()->effectiveDevicePixelRatio() * m_heightMultiplier);
1174 std::optional<QSSGRenderRay> rayResult = renderer->getRayFromViewportPos(position);
1175 if (!rayResult.has_value())
1176 return QList<QQuick3DPickResult>();
1178 const auto resultList = renderer->syncPickAll(rayResult.value());
1179 QList<QQuick3DPickResult> processedResultList;
1180 processedResultList.reserve(resultList.size());
1181 for (
const auto &result : resultList)
1182 processedResultList.append(processPickResult(result));
1184 return processedResultList;
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200QQuick3DPickResult QQuick3DViewport::rayPick(
const QVector3D &origin,
const QVector3D &direction)
const
1202 QQuick3DSceneRenderer *renderer = getRenderer();
1204 return QQuick3DPickResult();
1206 const QSSGRenderRay ray(origin, direction);
1207 const auto resultList = renderer->syncPick(ray);
1208 return getNearestPickResult(resultList);
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227QList<QQuick3DPickResult> QQuick3DViewport::rayPickAll(
const QVector3D &origin,
const QVector3D &direction)
const
1229 QQuick3DSceneRenderer *renderer = getRenderer();
1231 return QList<QQuick3DPickResult>();
1233 const QSSGRenderRay ray(origin, direction);
1235 const auto resultList = renderer->syncPickAll(ray);
1236 QList<QQuick3DPickResult> processedResultList;
1237 processedResultList.reserve(resultList.size());
1238 for (
const auto &result : resultList) {
1239 auto processedResult = processPickResult(result);
1240 if (processedResult.hitType() != QQuick3DPickResultEnums::HitType::Null)
1241 processedResultList.append(processedResult);
1244 return processedResultList;
1248
1249
1250
1251
1252
1253
1254
1255QQuick3DPickResult QQuick3DViewport::rayPick(
const QVector3D &origin,
const QVector3D &direction, QQuick3DModel *model)
const
1257 QQuick3DSceneRenderer *renderer = getRenderer();
1259 return QQuick3DPickResult();
1261 const QSSGRenderRay ray(origin, direction);
1263 const auto renderNode =
static_cast<QSSGRenderNode *>(QQuick3DObjectPrivate::get(model)->spatialNode);
1264 const auto resultList = renderer->syncPickOne(ray, renderNode);
1265 return getNearestPickResult(resultList);
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278QQuick3DPickResult QQuick3DViewport::closestPointPick(
const QVector3D &origin,
float radius, QQuick3DModel *model)
const
1280 QQuick3DSceneRenderer *renderer = getRenderer();
1283 return QQuick3DPickResult();
1284 QSSGRenderNode *renderNode =
nullptr;
1286 renderNode =
static_cast<QSSGRenderNode *>(QQuick3DObjectPrivate::get(model)->spatialNode);
1287 if (Q_UNLIKELY(!renderNode))
1288 return QQuick3DPickResult{};
1291 const auto pickResult = renderer->syncPickClosestPoint(origin, radius * radius, renderNode);
1292 if (!pickResult.has_value())
1293 return QQuick3DPickResult{};
1294 return processPickResult(pickResult.value());
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310QList<QQuick3DObject *> QQuick3DViewport::pickInRect(
const QPointF &start,
const QPointF &end)
const
1312 const qreal minX = qMin(start.x(), end.x());
1313 const qreal maxX = qMax(start.x(), end.x());
1314 const qreal minY = qMin(start.y(), end.y());
1315 const qreal maxY = qMax(start.y(), end.y());
1317 const qreal ndc[4] = {
1318 2.0f * minX / width() - 1.0f,
1319 2.0f * maxX / width() - 1.0f,
1321 1.0f - 2.0f * maxY / height(),
1322 1.0f - 2.0f * minY / height(),
1325 const float near = 0.0f;
1326 const float far = 1.0f;
1327 enum { L, R, B, T };
1328 const QVector4D ndcCorners[8] = {
1330 QVector4D(ndc[L], ndc[B], near, 1.0f),
1331 QVector4D(ndc[R], ndc[B], near, 1.0f),
1332 QVector4D(ndc[L], ndc[T], near, 1.0f),
1333 QVector4D(ndc[R], ndc[T], near, 1.0f),
1335 QVector4D(ndc[L], ndc[B], far, 1.0f),
1336 QVector4D(ndc[R], ndc[B], far, 1.0f),
1337 QVector4D(ndc[L], ndc[T], far, 1.0f),
1338 QVector4D(ndc[R], ndc[T], far, 1.0f),
1341 QMatrix4x4 viewProjection;
1342 if (
this->camera()) {
1343 if (
auto camera =
static_cast<QSSGRenderCamera *>(QQuick3DObjectPrivate::get(
this->camera())->spatialNode))
1344 camera->calculateViewProjectionMatrix(camera->localTransform, camera->projection, viewProjection);
1346 QMatrix4x4 viewProjectionInverted = viewProjection.inverted();
1348 QVector3D worldCorners[8];
1349 for (
int i = 0; i < 8; ++i) {
1350 QVector4D worldPoint = viewProjectionInverted * ndcCorners[i];
1351 worldCorners[i] = worldPoint.toVector3D() / worldPoint.w();
1370 const QSSGFrustum frustum {
1371 QSSGPlane(worldCorners[0], worldCorners[4], worldCorners[6]),
1372 QSSGPlane(worldCorners[1], worldCorners[3], worldCorners[7]),
1373 QSSGPlane(worldCorners[0], worldCorners[1], worldCorners[5]),
1374 QSSGPlane(worldCorners[2], worldCorners[6], worldCorners[7]),
1375 QSSGPlane(worldCorners[0], worldCorners[2], worldCorners[3]),
1376 QSSGPlane(worldCorners[5], worldCorners[7], worldCorners[6]),
1379 QList<QQuick3DObject *> ret;
1380 if (QQuick3DSceneRenderer *renderer = getRenderer()) {
1381 auto nodes = renderer->syncPickInFrustum(frustum);
1382 for (
auto node : nodes) {
1383 if (QQuick3DObject *m = findFrontendNode(node))
1391void QQuick3DViewport::processPointerEventFromRay(
const QVector3D &origin,
const QVector3D &direction, QPointerEvent *event)
const
1393 internalPick(event, origin, direction);
1400class SyntheticTouchDevice :
public QPointingDevice
1403 SyntheticTouchDevice(QObject *parent =
nullptr)
1404 : QPointingDevice(QLatin1StringView(
"QtQuick3D Touch Synthesizer"),
1406 DeviceType::TouchScreen,
1407 PointerType::Finger,
1408 Capability::Position,
1410 QString(), QPointingDeviceUniqueId(),
1418
1419
1420
1421
1422
1423
1424
1425
1426
1428void QQuick3DViewport::setTouchpoint(QQuickItem *target,
const QPointF &position,
int pointId,
bool pressed)
1430 if (pointId >= m_touchState.size())
1431 m_touchState.resize(pointId + 1);
1432 auto prevState = m_touchState[pointId];
1434 const bool sameTarget = prevState.target == target;
1435 const bool wasPressed = prevState.isPressed;
1437 const bool isPress = pressed && (!sameTarget || !wasPressed);
1438 const bool isRelease = !pressed && wasPressed && sameTarget;
1443 if (!sameTarget && wasPressed)
1444 qWarning(
"QQuick3DViewport::setTouchpoint missing release event");
1446 if (!pressed && !wasPressed) {
1451 m_touchState[pointId] = { target, position, pressed };
1453 if (!m_syntheticTouchDevice)
1454 m_syntheticTouchDevice =
new SyntheticTouchDevice(
this);
1456 QPointingDevicePrivate *devPriv = QPointingDevicePrivate::get(m_syntheticTouchDevice);
1458 auto makePoint = [devPriv](
int id, QEventPoint::State pointState, QPointF pos, quint64 timestamp) -> QEventPoint {
1459 auto epd = devPriv->pointById(id);
1460 auto &ep = epd->eventPoint;
1461 if (pointState != QEventPoint::State::Stationary)
1462 ep.setAccepted(
false);
1464 auto res = QMutableEventPoint::withTimeStamp(timestamp, id, pointState, pos, pos, pos);
1465 QMutableEventPoint::update(res, ep);
1467 if (pointState == QEventPoint::State::Pressed)
1468 QMutableEventPoint::setGlobalPressPosition(res, pos);
1469 else if (ep.state() != QEventPoint::State::Unknown)
1470 QMutableEventPoint::setGlobalPressPosition(res, ep.globalPressPosition());
1475 auto sendTouchEvent = [&](QQuickItem *t,
const QPointF &position,
int pointId, QEventPoint::State pointState, quint64 timestamp) ->
void {
1476 QList<QEventPoint> points;
1477 bool otherPoint =
false;
1478 for (
int i = 0; i < m_touchState.size(); ++i) {
1479 const auto &ts = m_touchState[i];
1483 auto newPoint = makePoint(i, pointState, position, timestamp);
1485 }
else if (ts.isPressed) {
1487 points << makePoint(i, QEventPoint::Stationary, ts.position, timestamp);
1492 if (pointState == QEventPoint::Pressed && !otherPoint)
1493 type = QEvent::Type::TouchBegin;
1494 else if (pointState == QEventPoint::Released && !otherPoint)
1495 type = QEvent::Type::TouchEnd;
1497 type = QEvent::Type::TouchUpdate;
1499 QTouchEvent ev(type, m_syntheticTouchDevice, {}, points);
1500 ev.setTimestamp(timestamp);
1504 auto da = QQuickItemPrivate::get(t)->deliveryAgent();
1505 bool handled = da->event(&ev);
1510 if (ev.isEndEvent()) {
1511 for (
auto &point : ev.points()) {
1512 if (point.state() == QEventPoint::State::Released) {
1513 ev.setExclusiveGrabber(point,
nullptr);
1514 ev.clearPassiveGrabbers(point);
1520 auto timestamp = QDateTime::currentMSecsSinceEpoch();
1523 if (prevState.target && !sameTarget)
1524 sendTouchEvent(prevState.target, prevState.position, pointId, QEventPoint::Released, timestamp);
1527 QEventPoint::State newState = isPress ? QEventPoint::Pressed : isRelease ? QEventPoint::Released : QEventPoint::Updated;
1528 sendTouchEvent(target, position, pointId, newState, timestamp);
1531QQuick3DLightmapBaker *QQuick3DViewport::maybeLightmapBaker()
1533 return m_lightmapBaker;
1536QQuick3DLightmapBaker *QQuick3DViewport::lightmapBaker()
1538 if (!m_lightmapBaker)
1539 m_lightmapBaker=
new QQuick3DLightmapBaker(
this);
1541 return m_lightmapBaker;
1545
1546
1547void QQuick3DViewport::bakeLightmap()
1549 QQuick3DSceneRenderer *renderer = getRenderer();
1550 if (!renderer || !renderer->m_layer->renderData)
1553 const bool currentlyBaking = renderer->m_layer->renderData->lightmapBaker !=
nullptr;
1555 if (!currentlyBaking)
1556 lightmapBaker()->bake();
1560
1561
1562void QQuick3DViewport::denoiseLightmap()
1564 QQuick3DSceneRenderer *renderer = getRenderer();
1565 if (!renderer || !renderer->m_layer->renderData)
1568 const bool currentlyBaking = renderer->m_layer->renderData->lightmapBaker !=
nullptr;
1570 if (!currentlyBaking)
1571 lightmapBaker()->denoise();
1575void QQuick3DViewport::setGlobalPickingEnabled(
bool isEnabled)
1577 QQuick3DSceneRenderer *renderer = getRenderer();
1581 renderer->setGlobalPickingEnabled(isEnabled);
1584void QQuick3DViewport::invalidateSceneGraph()
1589QQuick3DSceneRenderer *QQuick3DViewport::getRenderer()
const
1591 QQuick3DSceneRenderer *renderer =
nullptr;
1593 renderer = m_node->renderer;
1594 }
else if (m_renderNode) {
1595 renderer = m_renderNode->renderer;
1596 }
else if (m_directRenderer) {
1597 renderer = m_directRenderer->renderer();
1602void QQuick3DViewport::updateDynamicTextures()
1607 const auto &sceneManager = QQuick3DObjectPrivate::get(m_sceneRoot)->sceneManager;
1608 for (
auto *texture : std::as_const(sceneManager->qsgDynamicTextures))
1609 texture->updateTexture();
1611 QQuick3DNode *scene = m_importScene;
1613 const auto &importSm = QQuick3DObjectPrivate::get(scene)->sceneManager;
1614 if (importSm != sceneManager) {
1615 for (
auto *texture : std::as_const(importSm->qsgDynamicTextures))
1616 texture->updateTexture();
1620 QQuick3DSceneRootNode *rn = qobject_cast<QQuick3DSceneRootNode *>(scene);
1621 scene = rn ? rn->view3D()->importScene() :
nullptr;
1625QSGNode *QQuick3DViewport::setupOffscreenRenderer(QSGNode *node)
1627 SGFramebufferObjectNode *n =
static_cast<SGFramebufferObjectNode *>(node);
1631 m_node =
new SGFramebufferObjectNode;
1636 n->window = window();
1637 n->renderer = createRenderer();
1640 n->renderer->fboNode = n;
1642 connect(window(), SIGNAL(screenChanged(QScreen*)), n, SLOT(handleScreenChange()));
1645 const qreal dpr = window()->effectiveDevicePixelRatio();
1646 const QSize minFboSize = QQuickItemPrivate::get(
this)->sceneGraphContext()->minimumFBOSize();
1647 QSize desiredFboSize = QSize(m_explicitTextureWidth, m_explicitTextureHeight);
1648 if (desiredFboSize.isEmpty()) {
1649 desiredFboSize = QSize(width(), height()) * dpr;
1650 n->devicePixelRatio = dpr;
1652 m_widthMultiplier = 1.0f;
1653 m_heightMultiplier = 1.0f;
1655 QSize itemPixelSize = QSize(width(), height()) * dpr;
1657 m_widthMultiplier = desiredFboSize.width() /
float(itemPixelSize.width());
1658 m_heightMultiplier = desiredFboSize.height() /
float(itemPixelSize.height());
1659 n->devicePixelRatio = 1.0;
1661 desiredFboSize.setWidth(qMax(minFboSize.width(), desiredFboSize.width()));
1662 desiredFboSize.setHeight(qMax(minFboSize.height(), desiredFboSize.height()));
1664 if (desiredFboSize != m_effectiveTextureSize) {
1665 m_effectiveTextureSize = desiredFboSize;
1666 emit effectiveTextureSizeChanged();
1669 n->setFiltering(smooth() ? QSGTexture::Linear : QSGTexture::Nearest);
1670 n->setRect(0, 0, width(), height());
1671 if (checkIsVisible() && isComponentComplete()) {
1672 n->renderer->synchronize(
this, desiredFboSize, n->devicePixelRatio);
1673 if (n->renderer->m_textureNeedsFlip)
1674 n->setTextureCoordinatesTransform(QSGSimpleTextureNode::MirrorVertically);
1675 updateDynamicTextures();
1676 n->scheduleRender();
1682QSGNode *QQuick3DViewport::setupInlineRenderer(QSGNode *node)
1684 QQuick3DSGRenderNode *n =
static_cast<QQuick3DSGRenderNode *>(node);
1687 m_renderNode =
new QQuick3DSGRenderNode;
1692 n->window = window();
1693 n->renderer = createRenderer();
1698 if (!m_effectiveTextureSize.isEmpty()) {
1699 m_effectiveTextureSize = QSize();
1700 emit effectiveTextureSizeChanged();
1703 const QSize targetSize = window()->effectiveDevicePixelRatio() * QSize(width(), height());
1708 if (checkIsVisible() && isComponentComplete()) {
1709 n->renderer->synchronize(
this, targetSize, window()->effectiveDevicePixelRatio());
1710 updateDynamicTextures();
1711 n->markDirty(QSGNode::DirtyMaterial);
1718void QQuick3DViewport::setupDirectRenderer(RenderMode mode)
1720 auto renderMode = (mode == Underlay) ? QQuick3DSGDirectRenderer::Underlay
1721 : QQuick3DSGDirectRenderer::Overlay;
1722 if (!m_directRenderer) {
1723 QQuick3DSceneRenderer *sceneRenderer = createRenderer();
1726 m_directRenderer =
new QQuick3DSGDirectRenderer(sceneRenderer, window(), renderMode);
1727 connect(window(), &QQuickWindow::sceneGraphInvalidated,
this, &QQuick3DViewport::cleanupDirectRenderer, Qt::DirectConnection);
1730 if (!m_effectiveTextureSize.isEmpty()) {
1731 m_effectiveTextureSize = QSize();
1732 emit effectiveTextureSizeChanged();
1735 const QSizeF targetSize = window()->effectiveDevicePixelRatio() * QSizeF(width(), height());
1736 m_directRenderer->setViewport(QRectF(window()->effectiveDevicePixelRatio() * mapToScene(QPointF(0, 0)), targetSize));
1737 m_directRenderer->setVisibility(isVisible());
1739 m_directRenderer->preSynchronize();
1740 m_directRenderer->renderer()->synchronize(
this, targetSize.toSize(), window()->effectiveDevicePixelRatio());
1741 updateDynamicTextures();
1742 m_directRenderer->requestRender();
1748bool QQuick3DViewport::checkIsVisible()
const
1750 auto childPrivate = QQuickItemPrivate::get(
this);
1751 return (childPrivate->explicitVisible ||
1752 (childPrivate->extra.isAllocated() && childPrivate->extra->effectRefCount));
1757
1758
1759
1760
1761
1762
1763
1764
1765void QQuick3DViewport::processPickedObject(
const QSSGRenderPickResult &pickResult,
1767 QPointerEvent *event,
1768 QFlatMap<QQuickItem *, SubsceneInfo> &visitedSubscenes)
const
1770 QQuickItem *subsceneRootItem =
nullptr;
1771 QPointF subscenePosition;
1772 const auto backendObject = pickResult.m_hitObject;
1773 const auto frontendObject = findFrontendNode(backendObject);
1774 if (!frontendObject)
1780 auto frontendObjectPrivate = QQuick3DObjectPrivate::get(frontendObject);
1781 if (frontendObjectPrivate->type == QQuick3DObjectPrivate::Type::Item2D) {
1784 auto item2D = qobject_cast<QQuick3DItem2D *>(frontendObject);
1786 subsceneRootItem = item2D->contentItem();
1787 if (!subsceneRootItem || subsceneRootItem->childItems().isEmpty())
1791 subscenePosition = pickResult.m_localUVCoords.toPointF();
1795 if (!subsceneRootItem->childAt(subscenePosition.x(), subscenePosition.y()))
1797 }
else if (frontendObjectPrivate->type == QQuick3DObjectPrivate::Type::Model) {
1799 int materialSubset = pickResult.m_subset;
1800 const auto backendModel =
static_cast<
const QSSGRenderModel *>(backendObject);
1802 if (backendModel->materials.size() < (pickResult.m_subset + 1))
1803 materialSubset = backendModel->materials.size() - 1;
1804 if (materialSubset < 0)
1806 const auto backendMaterial = backendModel->materials.at(materialSubset);
1807 const auto frontendMaterial =
static_cast<QQuick3DMaterial*>(findFrontendNode(backendMaterial));
1808 subsceneRootItem = getSubSceneRootItem(frontendMaterial);
1810 if (subsceneRootItem) {
1812 subscenePosition = QPointF(subsceneRootItem->x() + pickResult.m_localUVCoords.x() * subsceneRootItem->width(),
1813 subsceneRootItem->y() - pickResult.m_localUVCoords.y() * subsceneRootItem->height() + subsceneRootItem->height());
1818 if (subsceneRootItem) {
1819 SubsceneInfo &subscene = visitedSubscenes[subsceneRootItem];
1820 subscene.obj = frontendObject;
1821 if (subscene.eventPointScenePositions.size() != event->pointCount()) {
1823 constexpr QPointF inf(-qt_inf(), -qt_inf());
1824 subscene.eventPointScenePositions.resize(event->pointCount(), inf);
1826 subscene.eventPointScenePositions[pointIndex] = subscenePosition;
1831
1832
1833
1834
1835
1836
1837
1838
1840QQuickItem *QQuick3DViewport::getSubSceneRootItem(QQuick3DMaterial *material)
const
1845 QQuickItem *subsceneRootItem =
nullptr;
1846 const auto frontendMaterialPrivate = QQuick3DObjectPrivate::get(material);
1848 if (frontendMaterialPrivate->type == QQuick3DObjectPrivate::Type::DefaultMaterial) {
1850 const auto defaultMaterial = qobject_cast<QQuick3DDefaultMaterial *>(material);
1851 if (defaultMaterial) {
1853 if (defaultMaterial->diffuseMap() && defaultMaterial->diffuseMap()->sourceItem())
1854 subsceneRootItem = defaultMaterial->diffuseMap()->sourceItem();
1857 }
else if (frontendMaterialPrivate->type == QQuick3DObjectPrivate::Type::PrincipledMaterial) {
1859 const auto principledMaterial = qobject_cast<QQuick3DPrincipledMaterial *>(material);
1860 if (principledMaterial) {
1862 if (principledMaterial->baseColorMap() && principledMaterial->baseColorMap()->sourceItem())
1863 subsceneRootItem = principledMaterial->baseColorMap()->sourceItem();
1865 }
else if (frontendMaterialPrivate->type == QQuick3DObjectPrivate::Type::SpecularGlossyMaterial) {
1867 const auto specularGlossyMaterial = qobject_cast<QQuick3DSpecularGlossyMaterial *>(material);
1868 if (specularGlossyMaterial) {
1870 if (specularGlossyMaterial->albedoMap() && specularGlossyMaterial->albedoMap()->sourceItem())
1871 subsceneRootItem = specularGlossyMaterial->albedoMap()->sourceItem();
1873 }
else if (frontendMaterialPrivate->type == QQuick3DObjectPrivate::Type::CustomMaterial) {
1875 const auto customMaterial = qobject_cast<QQuick3DCustomMaterial *>(material);
1876 if (customMaterial) {
1878 const auto &texturesInputs = customMaterial->m_dynamicTextureMaps;
1879 for (
const auto &textureInput : texturesInputs) {
1880 if (
auto texture = textureInput->texture()) {
1881 if (texture->sourceItem()) {
1882 subsceneRootItem = texture->sourceItem();
1889 return subsceneRootItem;
1894
1895
1896QQuick3DPickResult QQuick3DViewport::getNearestPickResult(
const QVarLengthArray<QSSGRenderPickResult, 20> &pickResults)
const
1898 for (
const auto &result : pickResults) {
1899 auto pickResult = processPickResult(result);
1900 if (pickResult.hitType() != QQuick3DPickResultEnums::HitType::Null)
1904 return QQuick3DPickResult();
1908
1909
1910
1911
1912
1913bool QQuick3DViewport::forwardEventToSubscenes(QPointerEvent *event,
1915 QQuick3DSceneRenderer *renderer,
1916 const QFlatMap<QQuickItem *, SubsceneInfo> &visitedSubscenes)
const
1923 QVarLengthArray<QPointF, 16> originalScenePositions;
1924 originalScenePositions.resize(event->pointCount());
1925 for (
int pointIndex = 0; pointIndex < event->pointCount(); ++pointIndex)
1926 originalScenePositions[pointIndex] = event->point(pointIndex).scenePosition();
1927 for (
auto subscene : visitedSubscenes) {
1928 QQuickItem *subsceneRoot = subscene.first;
1929 auto &subsceneInfo = subscene.second;
1930 Q_ASSERT(subsceneInfo.eventPointScenePositions.size() == event->pointCount());
1931 auto da = QQuickItemPrivate::get(subsceneRoot)->deliveryAgent();
1932 for (
int pointIndex = 0; pointIndex < event->pointCount(); ++pointIndex) {
1933 const auto &pt = subsceneInfo.eventPointScenePositions.at(pointIndex);
1938 QEventPoint &ep = event->point(pointIndex);
1939 QMutableEventPoint::setPosition(ep, pt);
1940 QMutableEventPoint::setScenePosition(ep, pt);
1943 if (event->isBeginEvent())
1944 da->setSceneTransform(
nullptr);
1945 if (da->event(event)) {
1947 if (QQuickDeliveryAgentPrivate::anyPointGrabbed(event) && !useRayPicking) {
1952 auto frontendObjectPrivate = QQuick3DObjectPrivate::get(subsceneInfo.obj);
1953 const bool item2Dcase = (frontendObjectPrivate->type == QQuick3DObjectPrivate::Type::Item2D);
1954 ViewportTransformHelper *transform =
new ViewportTransformHelper;
1955 transform->viewport =
const_cast<QQuick3DViewport *>(
this);
1956 transform->renderer = renderer;
1957 transform->sceneParentNode =
static_cast<QSSGRenderNode*>(frontendObjectPrivate->spatialNode);
1958 transform->targetItem = subsceneRoot;
1959 transform->scaleX = window()->effectiveDevicePixelRatio() * m_widthMultiplier;
1960 transform->scaleY = window()->effectiveDevicePixelRatio() * m_heightMultiplier;
1961 transform->uvCoordsArePixels = item2Dcase;
1962 transform->setOnDeliveryAgent(da);
1963 qCDebug(lcPick) << event->type() <<
"created ViewportTransformHelper on" << da;
1965 }
else if (event->type() != QEvent::HoverMove) {
1966 qCDebug(lcPick) << subsceneRoot <<
"didn't want" << event;
1968 event->setAccepted(
false);
1970 if (visitedSubscenes.isEmpty()) {
1971 event->setAccepted(
false);
1973 for (
int pointIndex = 0; pointIndex < event->pointCount(); ++pointIndex)
1974 QMutableEventPoint::setScenePosition(event->point(pointIndex), originalScenePositions.at(pointIndex));
1980 if (event->isEndEvent() && useRayPicking) {
1981 if (event->isSinglePointEvent()) {
1982 if (
static_cast<QSinglePointEvent *>(event)->buttons() == Qt::NoButton) {
1983 auto &firstPt = event->point(0);
1984 event->setExclusiveGrabber(firstPt,
nullptr);
1985 event->clearPassiveGrabbers(firstPt);
1988 for (
auto &point : event->points()) {
1989 if (point.state() == QEventPoint::State::Released) {
1990 event->setExclusiveGrabber(point,
nullptr);
1991 event->clearPassiveGrabbers(point);
2001bool QQuick3DViewport::internalPick(QPointerEvent *event,
const QVector3D &origin,
const QVector3D &direction)
const
2003 QQuick3DSceneRenderer *renderer = getRenderer();
2004 if (!renderer || !event)
2007 QFlatMap<QQuickItem*, SubsceneInfo> visitedSubscenes;
2008 const bool useRayPicking = !direction.isNull();
2010 for (
int pointIndex = 0; pointIndex < event->pointCount(); ++pointIndex) {
2011 auto &eventPoint = event->point(pointIndex);
2012 QVarLengthArray<QSSGRenderPickResult, 20> pickResults;
2013 if (Q_UNLIKELY(useRayPicking))
2014 pickResults = getPickResults(renderer, origin, direction);
2016 pickResults = getPickResults(renderer, eventPoint);
2018 if (!pickResults.isEmpty())
2019 for (
const auto &pickResult : pickResults)
2020 processPickedObject(pickResult, pointIndex, event, visitedSubscenes);
2022 eventPoint.setAccepted(
false);
2025 return forwardEventToSubscenes(event, useRayPicking, renderer, visitedSubscenes);
2028bool QQuick3DViewport::singlePointPick(QSinglePointEvent *event,
const QVector3D &origin,
const QVector3D &direction)
2030 QQuick3DSceneRenderer *renderer = getRenderer();
2031 if (!renderer || !event)
2034 QSSGRenderRay ray(origin, direction);
2036 Q_ASSERT(event->pointCount() == 1);
2037 QPointF originalPosition = event->point(0).scenePosition();
2039 auto pickResults = renderer->syncPickAll(ray);
2041 bool delivered =
false;
2043 constexpr float jitterLimit = 2.5;
2044 bool withinJitterLimit =
false;
2046 for (
const auto &pickResult : pickResults) {
2047 auto [item, position] = getItemAndPosition(pickResult);
2050 if (item == m_prevMouseItem && (position - m_prevMousePos).manhattanLength() < jitterLimit && !event->button()) {
2051 withinJitterLimit =
true;
2054 auto da = QQuickItemPrivate::get(item)->deliveryAgent();
2055 QEventPoint &ep = event->point(0);
2056 QMutableEventPoint::setPosition(ep, position);
2057 QMutableEventPoint::setScenePosition(ep, position);
2058 if (da->event(event)) {
2060 if (event->type() == QEvent::MouseButtonPress) {
2061 m_prevMouseItem = item;
2062 m_prevMousePos = position;
2063 withinJitterLimit =
true;
2069 QMutableEventPoint::setScenePosition(event->point(0), originalPosition);
2070 if (!withinJitterLimit)
2071 m_prevMouseItem =
nullptr;
2076 if (event->isEndEvent()) {
2077 if (event->buttons() == Qt::NoButton) {
2078 auto &firstPt = event->point(0);
2079 event->setExclusiveGrabber(firstPt,
nullptr);
2080 event->clearPassiveGrabbers(firstPt);
2087QPair<QQuickItem *, QPointF> QQuick3DViewport::getItemAndPosition(
const QSSGRenderPickResult &pickResult)
const
2089 QQuickItem *subsceneRootItem =
nullptr;
2090 QPointF subscenePosition;
2091 const auto backendObject = pickResult.m_hitObject;
2092 const auto frontendObject = findFrontendNode(backendObject);
2093 if (!frontendObject)
2095 auto frontendObjectPrivate = QQuick3DObjectPrivate::get(frontendObject);
2096 if (frontendObjectPrivate->type == QQuick3DObjectPrivate::Type::Item2D) {
2099 auto item2D = qobject_cast<QQuick3DItem2D *>(frontendObject);
2101 subsceneRootItem = item2D->contentItem();
2102 if (!subsceneRootItem || subsceneRootItem->childItems().isEmpty())
2106 subscenePosition = pickResult.m_localUVCoords.toPointF();
2110 if (!subsceneRootItem->childAt(subscenePosition.x(), subscenePosition.y()))
2112 }
else if (frontendObjectPrivate->type == QQuick3DObjectPrivate::Type::Model) {
2114 int materialSubset = pickResult.m_subset;
2115 const auto backendModel =
static_cast<
const QSSGRenderModel *>(backendObject);
2117 if (backendModel->materials.size() < (pickResult.m_subset + 1))
2118 materialSubset = backendModel->materials.size() - 1;
2119 if (materialSubset < 0)
2121 const auto backendMaterial = backendModel->materials.at(materialSubset);
2122 const auto frontendMaterial =
static_cast<QQuick3DMaterial *>(findFrontendNode(backendMaterial));
2123 subsceneRootItem = getSubSceneRootItem(frontendMaterial);
2125 if (subsceneRootItem) {
2127 subscenePosition = QPointF(subsceneRootItem->x() + pickResult.m_localUVCoords.x() * subsceneRootItem->width(),
2128 subsceneRootItem->y() - pickResult.m_localUVCoords.y() * subsceneRootItem->height() + subsceneRootItem->height());
2131 return {subsceneRootItem, subscenePosition};
2134QVarLengthArray<QSSGRenderPickResult, 20> QQuick3DViewport::getPickResults(QQuick3DSceneRenderer *renderer,
2135 const QVector3D &origin,
2136 const QVector3D &direction)
const
2138 const QSSGRenderRay ray(origin, direction);
2139 return renderer->syncPickAll(ray);
2142QVarLengthArray<QSSGRenderPickResult, 20> QQuick3DViewport::getPickResults(QQuick3DSceneRenderer *renderer,
const QEventPoint &eventPoint)
const
2144 QVarLengthArray<QSSGRenderPickResult, 20> pickResults;
2145 QPointF realPosition = eventPoint.position() * window()->effectiveDevicePixelRatio();
2147 realPosition.rx() *= m_widthMultiplier;
2148 realPosition.ry() *= m_heightMultiplier;
2149 std::optional<QSSGRenderRay> rayResult = renderer->getRayFromViewportPos(realPosition);
2150 if (rayResult.has_value())
2151 pickResults = renderer->syncPickAll(rayResult.value());
2156
2157
2158
2159
2160
2161QQuick3DObject *QQuick3DViewport::findFrontendNode(
const QSSGRenderGraphObject *backendObject)
const
2166 const auto sceneManager = QQuick3DObjectPrivate::get(m_sceneRoot)->sceneManager;
2167 QQuick3DObject *frontendObject = sceneManager->lookUpNode(backendObject);
2168 if (!frontendObject && m_importScene) {
2169 const auto importSceneManager = QQuick3DObjectPrivate::get(m_importScene)->sceneManager;
2170 frontendObject = importSceneManager->lookUpNode(backendObject);
2172 return frontendObject;
2175QQuick3DPickResult QQuick3DViewport::processPickResult(
const QSSGRenderPickResult &pickResult)
const
2177 if (!pickResult.m_hitObject)
2178 return QQuick3DPickResult();
2180 QQuick3DObject *frontendObject = findFrontendNode(pickResult.m_hitObject);
2182 QQuick3DModel *model = qobject_cast<QQuick3DModel *>(frontendObject);
2184 auto itemAndPosition = getItemAndPosition(pickResult);
2185 return QQuick3DPickResult(model,
2186 ::sqrtf(pickResult.m_distanceSq),
2187 pickResult.m_localUVCoords,
2188 pickResult.m_scenePosition,
2189 pickResult.m_localPosition,
2190 pickResult.m_faceNormal,
2191 pickResult.m_sceneNormal,
2192 pickResult.m_instanceIndex,
2193 itemAndPosition.first);
2196 QQuick3DItem2D *frontend2DItem = qobject_cast<QQuick3DItem2D *>(frontendObject);
2197 if (frontend2DItem && frontend2DItem->contentItem()) {
2199 const QPointF subscenePosition = pickResult.m_localUVCoords.toPointF();
2200 const auto child = frontend2DItem->contentItem()->childAt(subscenePosition.x(), subscenePosition.y());
2202 return QQuick3DPickResult(child,
2203 ::sqrtf(pickResult.m_distanceSq),
2204 QVector2D(frontend2DItem->contentItem()->mapToItem(child, subscenePosition)),
2205 pickResult.m_scenePosition,
2206 pickResult.m_localPosition,
2207 pickResult.m_faceNormal);
2211 return QQuick3DPickResult();
2216QQuick3DSceneManager *QQuick3DViewport::findChildSceneManager(QQuick3DObject *inObject, QQuick3DSceneManager *manager)
2221 auto children = QQuick3DObjectPrivate::get(inObject)->childItems;
2222 for (
auto child : children) {
2223 if (
auto m = QQuick3DObjectPrivate::get(child)->sceneManager) {
2227 manager = findChildSceneManager(child, manager);
2232void QQuick3DViewport::updateInputProcessing()
2235 setAcceptTouchEvents(m_enableInputProcessing);
2236 setAcceptHoverEvents(m_enableInputProcessing);
2237 setAcceptedMouseButtons(m_enableInputProcessing ? Qt::AllButtons : Qt::NoButton);
2240void QQuick3DViewport::onReleaseCachedResources()
2242 if (
auto renderer = getRenderer())
2243 renderer->releaseCachedResources();
2247
2248
2249
2250
2251
2252
2253QQmlListProperty<QQuick3DObject> QQuick3DViewport::extensions()
2255 return QQmlListProperty<QQuick3DObject>{
this,
2256 &m_extensionListDirty,
2257 &QQuick3DExtensionListHelper::extensionAppend,
2258 &QQuick3DExtensionListHelper::extensionCount,
2259 &QQuick3DExtensionListHelper::extensionAt,
2260 &QQuick3DExtensionListHelper::extensionClear,
2261 &QQuick3DExtensionListHelper::extensionReplace,
2262 &QQuick3DExtensionListHelper::extensionRemoveLast};
2266
2267
2268void QQuick3DViewport::rebuildExtensionList()
2270 m_extensionListDirty =
true;
2275
2276
2277
2278
2279
2280QQuick3DViewport::QQuick3DViewport(PrivateInstanceType type, QQuickItem *parent)
2281 : QQuick3DViewport(parent)
2283 m_isXrViewInstance = type == PrivateInstanceType::XrViewInstance;
2286void QQuick3DViewport::updateCameraForLayer(
const QQuick3DViewport &view3D, QSSGRenderLayer &layerNode)
2288 layerNode.explicitCameras.clear();
2289 if (!view3D.m_multiViewCameras.isEmpty()) {
2290 for (QQuick3DCamera *camera : std::as_const(view3D.m_multiViewCameras))
2291 layerNode.explicitCameras.append(
static_cast<QSSGRenderCamera *>(QQuick3DObjectPrivate::get(camera)->spatialNode));
2292 }
else if (view3D.camera()) {
2293 if (QSSGRenderCamera *camera =
static_cast<QSSGRenderCamera *>(QQuick3DObjectPrivate::get(view3D.camera())->spatialNode))
2294 layerNode.explicitCameras.append(camera);
2299 for (QSSGRenderCamera *camera : std::as_const(layerNode.explicitCameras)) {
2300 if (!camera->parent)
2301 layerNode.addChild(*camera);
2305void QQuick3DViewport::updateSceneManagerForImportScene()
2307 auto privateObject = QQuick3DObjectPrivate::get(m_importScene);
2308 if (!privateObject->sceneManager) {
2310 QQuick3DSceneManager *manager = findChildSceneManager(m_importScene);
2313 manager = QQuick3DObjectPrivate::get(m_sceneRoot)->sceneManager;
2315 manager->setWindow(window());
2316 privateObject->refSceneManager(*manager);
2321 if (!privateObject->sceneManager)
2325 connect(privateObject->sceneManager, &QQuick3DSceneManager::needsUpdate,
2326 this, &QQuickItem::update, Qt::UniqueConnection);
2327 connect(privateObject->sceneManager, &QObject::destroyed,
2328 this, [&](QObject *) {
2329 auto privateObject = QQuick3DObjectPrivate::get(m_importScene);
2330 privateObject->sceneManager =
nullptr;
2331 updateSceneManagerForImportScene();
2332 }, Qt::DirectConnection);
2334 QQuick3DNode *scene = m_importScene;
2336 QQuick3DSceneRootNode *rn = qobject_cast<QQuick3DSceneRootNode *>(scene);
2337 scene = rn ? rn->view3D()->importScene() :
nullptr;
2340 connect(QQuick3DObjectPrivate::get(scene)->sceneManager,
2341 &QQuick3DSceneManager::needsUpdate,
2342 this, &QQuickItem::update, Qt::UniqueConnection);
2347QQuick3DViewport::RenderOverrides QQuick3DViewport::renderOverrides()
const
2349 return m_renderOverrides;
2352void QQuick3DViewport::setRenderOverrides(RenderOverrides newRenderOverrides)
2354 if (m_renderOverrides == newRenderOverrides)
2356 m_renderOverrides = newRenderOverrides;
2357 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)