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
1014QQuick3DPickResult QQuick3DViewport::pick(
float x,
float y)
const
1016 QQuick3DSceneRenderer *renderer = getRenderer();
1018 return QQuick3DPickResult();
1020 const QPointF position(qreal(x) * window()->effectiveDevicePixelRatio() * m_widthMultiplier,
1021 qreal(y) * window()->effectiveDevicePixelRatio() * m_heightMultiplier);
1022 std::optional<QSSGRenderRay> rayResult = renderer->getRayFromViewportPos(position);
1023 if (!rayResult.has_value())
1024 return QQuick3DPickResult();
1026 const auto resultList = renderer->syncPick(rayResult.value());
1027 return getNearestPickResult(resultList);
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040QQuick3DPickResult QQuick3DViewport::pick(
float x,
float y, QQuick3DModel *model)
const
1042 QQuick3DSceneRenderer *renderer = getRenderer();
1044 return QQuick3DPickResult();
1046 const QPointF position(qreal(x) * window()->effectiveDevicePixelRatio(),
1047 qreal(y) * window()->effectiveDevicePixelRatio());
1048 std::optional<QSSGRenderRay> rayResult = renderer->getRayFromViewportPos(position);
1050 if (!rayResult.has_value())
1051 return QQuick3DPickResult();
1053 const auto renderNode =
static_cast<QSSGRenderNode *>(QQuick3DObjectPrivate::get(model)->spatialNode);
1054 const auto resultList = renderer->syncPickOne(rayResult.value(), renderNode);
1055 return getNearestPickResult(resultList);
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073QList<QQuick3DPickResult> QQuick3DViewport::pickSubset(
float x,
float y,
const QJSValue &models)
const
1075 QQuick3DSceneRenderer *renderer = getRenderer();
1079 QVarLengthArray<QSSGRenderNode*> renderNodes;
1081 if (models.isArray()) {
1082 const auto length = models.property(QStringLiteral(
"length")).toInt();
1086 for (
int i = 0; i < length; ++i) {
1087 const auto isQObject = models.property(i).isQObject();
1089 qmlWarning(
this) <<
"Type provided for picking is not a QObject. Needs to be of type QQuick3DModel.";
1092 const auto obj = models.property(i).toQObject();
1093 const auto model = qobject_cast<QQuick3DModel *>(obj);
1095 qmlWarning(
this) <<
"Type " << obj->metaObject()->className() <<
" is not supported for picking. Needs to be of type QQuick3DModel.";
1098 const auto priv = QQuick3DObjectPrivate::get(model);
1099 if (priv && priv->spatialNode) {
1100 renderNodes.push_back(
static_cast<QSSGRenderNode*>(priv->spatialNode));
1105 const auto subsetVariant = models.toVariant();
1106 if (!subsetVariant.isValid() || !subsetVariant.canConvert<QQmlListReference>())
1109 const auto list = subsetVariant.value<QQmlListReference>();
1112 if (list.listElementType()->className() != QQuick3DModel::staticMetaObject.className()) {
1113 qmlWarning(
this) <<
"Type " << list.listElementType()->className() <<
" is not supported for picking. Needs to be of type QQuick3DModel.";
1116 for (
int i = 0; i < list.count(); ++i) {
1117 auto model =
static_cast<QQuick3DModel *>(list.at(i));
1120 auto priv = QQuick3DObjectPrivate::get(model);
1121 if (priv && priv->spatialNode) {
1122 renderNodes.push_back(
static_cast<QSSGRenderNode*>(priv->spatialNode));
1127 if (renderNodes.empty())
1130 const QPointF position(qreal(x) * window()->effectiveDevicePixelRatio(),
1131 qreal(y) * window()->effectiveDevicePixelRatio());
1132 std::optional<QSSGRenderRay> rayResult = renderer->getRayFromViewportPos(position);
1133 if (!rayResult.has_value())
1136 const auto resultList = renderer->syncPickSubset(rayResult.value(), renderNodes);
1138 QList<QQuick3DPickResult> processedResultList;
1139 processedResultList.reserve(resultList.size());
1140 for (
const auto &result : resultList)
1141 processedResultList.append(processPickResult(result));
1143 return processedResultList;
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158QList<QQuick3DPickResult> QQuick3DViewport::pickAll(
float x,
float y)
const
1160 QQuick3DSceneRenderer *renderer = getRenderer();
1162 return QList<QQuick3DPickResult>();
1164 const QPointF position(qreal(x) * window()->effectiveDevicePixelRatio() * m_widthMultiplier,
1165 qreal(y) * window()->effectiveDevicePixelRatio() * m_heightMultiplier);
1166 std::optional<QSSGRenderRay> rayResult = renderer->getRayFromViewportPos(position);
1167 if (!rayResult.has_value())
1168 return QList<QQuick3DPickResult>();
1170 const auto resultList = renderer->syncPickAll(rayResult.value());
1171 QList<QQuick3DPickResult> processedResultList;
1172 processedResultList.reserve(resultList.size());
1173 for (
const auto &result : resultList)
1174 processedResultList.append(processPickResult(result));
1176 return processedResultList;
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192QQuick3DPickResult QQuick3DViewport::rayPick(
const QVector3D &origin,
const QVector3D &direction)
const
1194 QQuick3DSceneRenderer *renderer = getRenderer();
1196 return QQuick3DPickResult();
1198 const QSSGRenderRay ray(origin, direction);
1199 const auto resultList = renderer->syncPick(ray);
1200 return getNearestPickResult(resultList);
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219QList<QQuick3DPickResult> QQuick3DViewport::rayPickAll(
const QVector3D &origin,
const QVector3D &direction)
const
1221 QQuick3DSceneRenderer *renderer = getRenderer();
1223 return QList<QQuick3DPickResult>();
1225 const QSSGRenderRay ray(origin, direction);
1227 const auto resultList = renderer->syncPickAll(ray);
1228 QList<QQuick3DPickResult> processedResultList;
1229 processedResultList.reserve(resultList.size());
1230 for (
const auto &result : resultList) {
1231 auto processedResult = processPickResult(result);
1232 if (processedResult.hitType() != QQuick3DPickResultEnums::HitType::Null)
1233 processedResultList.append(processedResult);
1236 return processedResultList;
1240
1241
1242
1243
1244
1245
1246
1247QQuick3DPickResult QQuick3DViewport::rayPick(
const QVector3D &origin,
const QVector3D &direction, QQuick3DModel *model)
const
1249 QQuick3DSceneRenderer *renderer = getRenderer();
1251 return QQuick3DPickResult();
1253 const QSSGRenderRay ray(origin, direction);
1255 const auto renderNode =
static_cast<QSSGRenderNode *>(QQuick3DObjectPrivate::get(model)->spatialNode);
1256 const auto resultList = renderer->syncPickOne(ray, renderNode);
1257 return getNearestPickResult(resultList);
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270QQuick3DPickResult QQuick3DViewport::closestPointPick(
const QVector3D &origin,
float radius, QQuick3DModel *model)
const
1272 QQuick3DSceneRenderer *renderer = getRenderer();
1275 return QQuick3DPickResult();
1276 QSSGRenderNode *renderNode =
nullptr;
1278 renderNode =
static_cast<QSSGRenderNode *>(QQuick3DObjectPrivate::get(model)->spatialNode);
1279 if (Q_UNLIKELY(!renderNode))
1280 return QQuick3DPickResult{};
1283 const auto pickResult = renderer->syncPickClosestPoint(origin, radius * radius, renderNode);
1284 if (!pickResult.has_value())
1285 return QQuick3DPickResult{};
1286 return processPickResult(pickResult.value());
1289QList<QQuick3DObject *> QQuick3DViewport::pickInRect(
const QPointF &start,
const QPointF &end)
const
1291 const qreal minX = qMin(start.x(), end.x());
1292 const qreal maxX = qMax(start.x(), end.x());
1293 const qreal minY = qMin(start.y(), end.y());
1294 const qreal maxY = qMax(start.y(), end.y());
1296 const qreal ndc[4] = {
1297 2.0f * minX / width() - 1.0f,
1298 2.0f * maxX / width() - 1.0f,
1300 1.0f - 2.0f * maxY / height(),
1301 1.0f - 2.0f * minY / height(),
1304 const float near = 0.0f;
1305 const float far = 1.0f;
1306 enum { L, R, B, T };
1307 const QVector4D ndcCorners[8] = {
1309 QVector4D(ndc[L], ndc[B], near, 1.0f),
1310 QVector4D(ndc[R], ndc[B], near, 1.0f),
1311 QVector4D(ndc[L], ndc[T], near, 1.0f),
1312 QVector4D(ndc[R], ndc[T], near, 1.0f),
1314 QVector4D(ndc[L], ndc[B], far, 1.0f),
1315 QVector4D(ndc[R], ndc[B], far, 1.0f),
1316 QVector4D(ndc[L], ndc[T], far, 1.0f),
1317 QVector4D(ndc[R], ndc[T], far, 1.0f),
1320 QMatrix4x4 viewProjection;
1321 if (
this->camera()) {
1322 if (
auto camera =
static_cast<QSSGRenderCamera *>(QQuick3DObjectPrivate::get(
this->camera())->spatialNode))
1323 camera->calculateViewProjectionMatrix(camera->localTransform, camera->projection, viewProjection);
1325 QMatrix4x4 viewProjectionInverted = viewProjection.inverted();
1327 QVector3D worldCorners[8];
1328 for (
int i = 0; i < 8; ++i) {
1329 QVector4D worldPoint = viewProjectionInverted * ndcCorners[i];
1330 worldCorners[i] = worldPoint.toVector3D() / worldPoint.w();
1349 const QSSGFrustum frustum {
1350 QSSGPlane(worldCorners[0], worldCorners[4], worldCorners[6]),
1351 QSSGPlane(worldCorners[1], worldCorners[3], worldCorners[7]),
1352 QSSGPlane(worldCorners[0], worldCorners[1], worldCorners[5]),
1353 QSSGPlane(worldCorners[2], worldCorners[6], worldCorners[7]),
1354 QSSGPlane(worldCorners[0], worldCorners[2], worldCorners[3]),
1355 QSSGPlane(worldCorners[5], worldCorners[7], worldCorners[6]),
1358 QList<QQuick3DObject *> ret;
1359 if (QQuick3DSceneRenderer *renderer = getRenderer()) {
1360 auto nodes = renderer->syncPickInFrustum(frustum);
1361 for (
auto node : nodes) {
1362 if (QQuick3DObject *m = findFrontendNode(node))
1370void QQuick3DViewport::processPointerEventFromRay(
const QVector3D &origin,
const QVector3D &direction, QPointerEvent *event)
const
1372 internalPick(event, origin, direction);
1379class SyntheticTouchDevice :
public QPointingDevice
1382 SyntheticTouchDevice(QObject *parent =
nullptr)
1383 : QPointingDevice(QLatin1StringView(
"QtQuick3D Touch Synthesizer"),
1385 DeviceType::TouchScreen,
1386 PointerType::Finger,
1387 Capability::Position,
1389 QString(), QPointingDeviceUniqueId(),
1397
1398
1399
1400
1401
1402
1403
1404
1405
1407void QQuick3DViewport::setTouchpoint(QQuickItem *target,
const QPointF &position,
int pointId,
bool pressed)
1409 if (pointId >= m_touchState.size())
1410 m_touchState.resize(pointId + 1);
1411 auto prevState = m_touchState[pointId];
1413 const bool sameTarget = prevState.target == target;
1414 const bool wasPressed = prevState.isPressed;
1416 const bool isPress = pressed && (!sameTarget || !wasPressed);
1417 const bool isRelease = !pressed && wasPressed && sameTarget;
1422 if (!sameTarget && wasPressed)
1423 qWarning(
"QQuick3DViewport::setTouchpoint missing release event");
1425 if (!pressed && !wasPressed) {
1430 m_touchState[pointId] = { target, position, pressed };
1432 if (!m_syntheticTouchDevice)
1433 m_syntheticTouchDevice =
new SyntheticTouchDevice(
this);
1435 QPointingDevicePrivate *devPriv = QPointingDevicePrivate::get(m_syntheticTouchDevice);
1437 auto makePoint = [devPriv](
int id, QEventPoint::State pointState, QPointF pos, quint64 timestamp) -> QEventPoint {
1438 auto epd = devPriv->pointById(id);
1439 auto &ep = epd->eventPoint;
1440 if (pointState != QEventPoint::State::Stationary)
1441 ep.setAccepted(
false);
1443 auto res = QMutableEventPoint::withTimeStamp(timestamp, id, pointState, pos, pos, pos);
1444 QMutableEventPoint::update(res, ep);
1446 if (pointState == QEventPoint::State::Pressed)
1447 QMutableEventPoint::setGlobalPressPosition(res, pos);
1448 else if (ep.state() != QEventPoint::State::Unknown)
1449 QMutableEventPoint::setGlobalPressPosition(res, ep.globalPressPosition());
1454 auto sendTouchEvent = [&](QQuickItem *t,
const QPointF &position,
int pointId, QEventPoint::State pointState, quint64 timestamp) ->
void {
1455 QList<QEventPoint> points;
1456 bool otherPoint =
false;
1457 for (
int i = 0; i < m_touchState.size(); ++i) {
1458 const auto &ts = m_touchState[i];
1462 auto newPoint = makePoint(i, pointState, position, timestamp);
1464 }
else if (ts.isPressed) {
1466 points << makePoint(i, QEventPoint::Stationary, ts.position, timestamp);
1471 if (pointState == QEventPoint::Pressed && !otherPoint)
1472 type = QEvent::Type::TouchBegin;
1473 else if (pointState == QEventPoint::Released && !otherPoint)
1474 type = QEvent::Type::TouchEnd;
1476 type = QEvent::Type::TouchUpdate;
1478 QTouchEvent ev(type, m_syntheticTouchDevice, {}, points);
1479 ev.setTimestamp(timestamp);
1483 auto da = QQuickItemPrivate::get(t)->deliveryAgent();
1484 bool handled = da->event(&ev);
1489 if (ev.isEndEvent()) {
1490 for (
auto &point : ev.points()) {
1491 if (point.state() == QEventPoint::State::Released) {
1492 ev.setExclusiveGrabber(point,
nullptr);
1493 ev.clearPassiveGrabbers(point);
1499 auto timestamp = QDateTime::currentMSecsSinceEpoch();
1502 if (prevState.target && !sameTarget)
1503 sendTouchEvent(prevState.target, prevState.position, pointId, QEventPoint::Released, timestamp);
1506 QEventPoint::State newState = isPress ? QEventPoint::Pressed : isRelease ? QEventPoint::Released : QEventPoint::Updated;
1507 sendTouchEvent(target, position, pointId, newState, timestamp);
1510QQuick3DLightmapBaker *QQuick3DViewport::maybeLightmapBaker()
1512 return m_lightmapBaker;
1515QQuick3DLightmapBaker *QQuick3DViewport::lightmapBaker()
1517 if (!m_lightmapBaker)
1518 m_lightmapBaker=
new QQuick3DLightmapBaker(
this);
1520 return m_lightmapBaker;
1524
1525
1526void QQuick3DViewport::bakeLightmap()
1528 QQuick3DSceneRenderer *renderer = getRenderer();
1529 if (!renderer || !renderer->m_layer->renderData)
1532 const bool currentlyBaking = renderer->m_layer->renderData->lightmapBaker !=
nullptr;
1534 if (!currentlyBaking)
1535 lightmapBaker()->bake();
1539
1540
1541void QQuick3DViewport::denoiseLightmap()
1543 QQuick3DSceneRenderer *renderer = getRenderer();
1544 if (!renderer || !renderer->m_layer->renderData)
1547 const bool currentlyBaking = renderer->m_layer->renderData->lightmapBaker !=
nullptr;
1549 if (!currentlyBaking)
1550 lightmapBaker()->denoise();
1554void QQuick3DViewport::setGlobalPickingEnabled(
bool isEnabled)
1556 QQuick3DSceneRenderer *renderer = getRenderer();
1560 renderer->setGlobalPickingEnabled(isEnabled);
1563void QQuick3DViewport::invalidateSceneGraph()
1568QQuick3DSceneRenderer *QQuick3DViewport::getRenderer()
const
1570 QQuick3DSceneRenderer *renderer =
nullptr;
1572 renderer = m_node->renderer;
1573 }
else if (m_renderNode) {
1574 renderer = m_renderNode->renderer;
1575 }
else if (m_directRenderer) {
1576 renderer = m_directRenderer->renderer();
1581void QQuick3DViewport::updateDynamicTextures()
1586 const auto &sceneManager = QQuick3DObjectPrivate::get(m_sceneRoot)->sceneManager;
1587 for (
auto *texture : std::as_const(sceneManager->qsgDynamicTextures))
1588 texture->updateTexture();
1590 QQuick3DNode *scene = m_importScene;
1592 const auto &importSm = QQuick3DObjectPrivate::get(scene)->sceneManager;
1593 if (importSm != sceneManager) {
1594 for (
auto *texture : std::as_const(importSm->qsgDynamicTextures))
1595 texture->updateTexture();
1599 QQuick3DSceneRootNode *rn = qobject_cast<QQuick3DSceneRootNode *>(scene);
1600 scene = rn ? rn->view3D()->importScene() :
nullptr;
1604QSGNode *QQuick3DViewport::setupOffscreenRenderer(QSGNode *node)
1606 SGFramebufferObjectNode *n =
static_cast<SGFramebufferObjectNode *>(node);
1610 m_node =
new SGFramebufferObjectNode;
1615 n->window = window();
1616 n->renderer = createRenderer();
1619 n->renderer->fboNode = n;
1621 connect(window(), SIGNAL(screenChanged(QScreen*)), n, SLOT(handleScreenChange()));
1624 const qreal dpr = window()->effectiveDevicePixelRatio();
1625 const QSize minFboSize = QQuickItemPrivate::get(
this)->sceneGraphContext()->minimumFBOSize();
1626 QSize desiredFboSize = QSize(m_explicitTextureWidth, m_explicitTextureHeight);
1627 if (desiredFboSize.isEmpty()) {
1628 desiredFboSize = QSize(width(), height()) * dpr;
1629 n->devicePixelRatio = dpr;
1631 m_widthMultiplier = 1.0f;
1632 m_heightMultiplier = 1.0f;
1634 QSize itemPixelSize = QSize(width(), height()) * dpr;
1636 m_widthMultiplier = desiredFboSize.width() /
float(itemPixelSize.width());
1637 m_heightMultiplier = desiredFboSize.height() /
float(itemPixelSize.height());
1638 n->devicePixelRatio = 1.0;
1640 desiredFboSize.setWidth(qMax(minFboSize.width(), desiredFboSize.width()));
1641 desiredFboSize.setHeight(qMax(minFboSize.height(), desiredFboSize.height()));
1643 if (desiredFboSize != m_effectiveTextureSize) {
1644 m_effectiveTextureSize = desiredFboSize;
1645 emit effectiveTextureSizeChanged();
1648 n->setFiltering(smooth() ? QSGTexture::Linear : QSGTexture::Nearest);
1649 n->setRect(0, 0, width(), height());
1650 if (checkIsVisible() && isComponentComplete()) {
1651 n->renderer->synchronize(
this, desiredFboSize, n->devicePixelRatio);
1652 if (n->renderer->m_textureNeedsFlip)
1653 n->setTextureCoordinatesTransform(QSGSimpleTextureNode::MirrorVertically);
1654 updateDynamicTextures();
1655 n->scheduleRender();
1661QSGNode *QQuick3DViewport::setupInlineRenderer(QSGNode *node)
1663 QQuick3DSGRenderNode *n =
static_cast<QQuick3DSGRenderNode *>(node);
1666 m_renderNode =
new QQuick3DSGRenderNode;
1671 n->window = window();
1672 n->renderer = createRenderer();
1677 if (!m_effectiveTextureSize.isEmpty()) {
1678 m_effectiveTextureSize = QSize();
1679 emit effectiveTextureSizeChanged();
1682 const QSize targetSize = window()->effectiveDevicePixelRatio() * QSize(width(), height());
1687 if (checkIsVisible() && isComponentComplete()) {
1688 n->renderer->synchronize(
this, targetSize, window()->effectiveDevicePixelRatio());
1689 updateDynamicTextures();
1690 n->markDirty(QSGNode::DirtyMaterial);
1697void QQuick3DViewport::setupDirectRenderer(RenderMode mode)
1699 auto renderMode = (mode == Underlay) ? QQuick3DSGDirectRenderer::Underlay
1700 : QQuick3DSGDirectRenderer::Overlay;
1701 if (!m_directRenderer) {
1702 QQuick3DSceneRenderer *sceneRenderer = createRenderer();
1705 m_directRenderer =
new QQuick3DSGDirectRenderer(sceneRenderer, window(), renderMode);
1706 connect(window(), &QQuickWindow::sceneGraphInvalidated,
this, &QQuick3DViewport::cleanupDirectRenderer, Qt::DirectConnection);
1709 if (!m_effectiveTextureSize.isEmpty()) {
1710 m_effectiveTextureSize = QSize();
1711 emit effectiveTextureSizeChanged();
1714 const QSizeF targetSize = window()->effectiveDevicePixelRatio() * QSizeF(width(), height());
1715 m_directRenderer->setViewport(QRectF(window()->effectiveDevicePixelRatio() * mapToScene(QPointF(0, 0)), targetSize));
1716 m_directRenderer->setVisibility(isVisible());
1718 m_directRenderer->preSynchronize();
1719 m_directRenderer->renderer()->synchronize(
this, targetSize.toSize(), window()->effectiveDevicePixelRatio());
1720 updateDynamicTextures();
1721 m_directRenderer->requestRender();
1727bool QQuick3DViewport::checkIsVisible()
const
1729 auto childPrivate = QQuickItemPrivate::get(
this);
1730 return (childPrivate->explicitVisible ||
1731 (childPrivate->extra.isAllocated() && childPrivate->extra->effectRefCount));
1736
1737
1738
1739
1740
1741
1742
1743
1744void QQuick3DViewport::processPickedObject(
const QSSGRenderPickResult &pickResult,
1746 QPointerEvent *event,
1747 QFlatMap<QQuickItem *, SubsceneInfo> &visitedSubscenes)
const
1749 QQuickItem *subsceneRootItem =
nullptr;
1750 QPointF subscenePosition;
1751 const auto backendObject = pickResult.m_hitObject;
1752 const auto frontendObject = findFrontendNode(backendObject);
1753 if (!frontendObject)
1759 auto frontendObjectPrivate = QQuick3DObjectPrivate::get(frontendObject);
1760 if (frontendObjectPrivate->type == QQuick3DObjectPrivate::Type::Item2D) {
1763 auto item2D = qobject_cast<QQuick3DItem2D *>(frontendObject);
1765 subsceneRootItem = item2D->contentItem();
1766 if (!subsceneRootItem || subsceneRootItem->childItems().isEmpty())
1770 subscenePosition = pickResult.m_localUVCoords.toPointF();
1774 if (!subsceneRootItem->childAt(subscenePosition.x(), subscenePosition.y()))
1776 }
else if (frontendObjectPrivate->type == QQuick3DObjectPrivate::Type::Model) {
1778 int materialSubset = pickResult.m_subset;
1779 const auto backendModel =
static_cast<
const QSSGRenderModel *>(backendObject);
1781 if (backendModel->materials.size() < (pickResult.m_subset + 1))
1782 materialSubset = backendModel->materials.size() - 1;
1783 if (materialSubset < 0)
1785 const auto backendMaterial = backendModel->materials.at(materialSubset);
1786 const auto frontendMaterial =
static_cast<QQuick3DMaterial*>(findFrontendNode(backendMaterial));
1787 subsceneRootItem = getSubSceneRootItem(frontendMaterial);
1789 if (subsceneRootItem) {
1791 subscenePosition = QPointF(subsceneRootItem->x() + pickResult.m_localUVCoords.x() * subsceneRootItem->width(),
1792 subsceneRootItem->y() - pickResult.m_localUVCoords.y() * subsceneRootItem->height() + subsceneRootItem->height());
1797 if (subsceneRootItem) {
1798 SubsceneInfo &subscene = visitedSubscenes[subsceneRootItem];
1799 subscene.obj = frontendObject;
1800 if (subscene.eventPointScenePositions.size() != event->pointCount()) {
1802 constexpr QPointF inf(-qt_inf(), -qt_inf());
1803 subscene.eventPointScenePositions.resize(event->pointCount(), inf);
1805 subscene.eventPointScenePositions[pointIndex] = subscenePosition;
1810
1811
1812
1813
1814
1815
1816
1817
1819QQuickItem *QQuick3DViewport::getSubSceneRootItem(QQuick3DMaterial *material)
const
1824 QQuickItem *subsceneRootItem =
nullptr;
1825 const auto frontendMaterialPrivate = QQuick3DObjectPrivate::get(material);
1827 if (frontendMaterialPrivate->type == QQuick3DObjectPrivate::Type::DefaultMaterial) {
1829 const auto defaultMaterial = qobject_cast<QQuick3DDefaultMaterial *>(material);
1830 if (defaultMaterial) {
1832 if (defaultMaterial->diffuseMap() && defaultMaterial->diffuseMap()->sourceItem())
1833 subsceneRootItem = defaultMaterial->diffuseMap()->sourceItem();
1836 }
else if (frontendMaterialPrivate->type == QQuick3DObjectPrivate::Type::PrincipledMaterial) {
1838 const auto principledMaterial = qobject_cast<QQuick3DPrincipledMaterial *>(material);
1839 if (principledMaterial) {
1841 if (principledMaterial->baseColorMap() && principledMaterial->baseColorMap()->sourceItem())
1842 subsceneRootItem = principledMaterial->baseColorMap()->sourceItem();
1844 }
else if (frontendMaterialPrivate->type == QQuick3DObjectPrivate::Type::SpecularGlossyMaterial) {
1846 const auto specularGlossyMaterial = qobject_cast<QQuick3DSpecularGlossyMaterial *>(material);
1847 if (specularGlossyMaterial) {
1849 if (specularGlossyMaterial->albedoMap() && specularGlossyMaterial->albedoMap()->sourceItem())
1850 subsceneRootItem = specularGlossyMaterial->albedoMap()->sourceItem();
1852 }
else if (frontendMaterialPrivate->type == QQuick3DObjectPrivate::Type::CustomMaterial) {
1854 const auto customMaterial = qobject_cast<QQuick3DCustomMaterial *>(material);
1855 if (customMaterial) {
1857 const auto &texturesInputs = customMaterial->m_dynamicTextureMaps;
1858 for (
const auto &textureInput : texturesInputs) {
1859 if (
auto texture = textureInput->texture()) {
1860 if (texture->sourceItem()) {
1861 subsceneRootItem = texture->sourceItem();
1868 return subsceneRootItem;
1873
1874
1875QQuick3DPickResult QQuick3DViewport::getNearestPickResult(
const QVarLengthArray<QSSGRenderPickResult, 20> &pickResults)
const
1877 for (
const auto &result : pickResults) {
1878 auto pickResult = processPickResult(result);
1879 if (pickResult.hitType() != QQuick3DPickResultEnums::HitType::Null)
1883 return QQuick3DPickResult();
1887
1888
1889
1890
1891
1892bool QQuick3DViewport::forwardEventToSubscenes(QPointerEvent *event,
1894 QQuick3DSceneRenderer *renderer,
1895 const QFlatMap<QQuickItem *, SubsceneInfo> &visitedSubscenes)
const
1902 QVarLengthArray<QPointF, 16> originalScenePositions;
1903 originalScenePositions.resize(event->pointCount());
1904 for (
int pointIndex = 0; pointIndex < event->pointCount(); ++pointIndex)
1905 originalScenePositions[pointIndex] = event->point(pointIndex).scenePosition();
1906 for (
auto subscene : visitedSubscenes) {
1907 QQuickItem *subsceneRoot = subscene.first;
1908 auto &subsceneInfo = subscene.second;
1909 Q_ASSERT(subsceneInfo.eventPointScenePositions.size() == event->pointCount());
1910 auto da = QQuickItemPrivate::get(subsceneRoot)->deliveryAgent();
1911 for (
int pointIndex = 0; pointIndex < event->pointCount(); ++pointIndex) {
1912 const auto &pt = subsceneInfo.eventPointScenePositions.at(pointIndex);
1917 QEventPoint &ep = event->point(pointIndex);
1918 QMutableEventPoint::setPosition(ep, pt);
1919 QMutableEventPoint::setScenePosition(ep, pt);
1922 if (event->isBeginEvent())
1923 da->setSceneTransform(
nullptr);
1924 if (da->event(event)) {
1926 if (QQuickDeliveryAgentPrivate::anyPointGrabbed(event) && !useRayPicking) {
1931 auto frontendObjectPrivate = QQuick3DObjectPrivate::get(subsceneInfo.obj);
1932 const bool item2Dcase = (frontendObjectPrivate->type == QQuick3DObjectPrivate::Type::Item2D);
1933 ViewportTransformHelper *transform =
new ViewportTransformHelper;
1934 transform->viewport =
const_cast<QQuick3DViewport *>(
this);
1935 transform->renderer = renderer;
1936 transform->sceneParentNode =
static_cast<QSSGRenderNode*>(frontendObjectPrivate->spatialNode);
1937 transform->targetItem = subsceneRoot;
1938 transform->scaleX = window()->effectiveDevicePixelRatio() * m_widthMultiplier;
1939 transform->scaleY = window()->effectiveDevicePixelRatio() * m_heightMultiplier;
1940 transform->uvCoordsArePixels = item2Dcase;
1941 transform->setOnDeliveryAgent(da);
1942 qCDebug(lcPick) << event->type() <<
"created ViewportTransformHelper on" << da;
1944 }
else if (event->type() != QEvent::HoverMove) {
1945 qCDebug(lcPick) << subsceneRoot <<
"didn't want" << event;
1947 event->setAccepted(
false);
1949 if (visitedSubscenes.isEmpty()) {
1950 event->setAccepted(
false);
1952 for (
int pointIndex = 0; pointIndex < event->pointCount(); ++pointIndex)
1953 QMutableEventPoint::setScenePosition(event->point(pointIndex), originalScenePositions.at(pointIndex));
1959 if (event->isEndEvent() && useRayPicking) {
1960 if (event->isSinglePointEvent()) {
1961 if (
static_cast<QSinglePointEvent *>(event)->buttons() == Qt::NoButton) {
1962 auto &firstPt = event->point(0);
1963 event->setExclusiveGrabber(firstPt,
nullptr);
1964 event->clearPassiveGrabbers(firstPt);
1967 for (
auto &point : event->points()) {
1968 if (point.state() == QEventPoint::State::Released) {
1969 event->setExclusiveGrabber(point,
nullptr);
1970 event->clearPassiveGrabbers(point);
1980bool QQuick3DViewport::internalPick(QPointerEvent *event,
const QVector3D &origin,
const QVector3D &direction)
const
1982 QQuick3DSceneRenderer *renderer = getRenderer();
1983 if (!renderer || !event)
1986 QFlatMap<QQuickItem*, SubsceneInfo> visitedSubscenes;
1987 const bool useRayPicking = !direction.isNull();
1989 for (
int pointIndex = 0; pointIndex < event->pointCount(); ++pointIndex) {
1990 auto &eventPoint = event->point(pointIndex);
1991 QVarLengthArray<QSSGRenderPickResult, 20> pickResults;
1992 if (Q_UNLIKELY(useRayPicking))
1993 pickResults = getPickResults(renderer, origin, direction);
1995 pickResults = getPickResults(renderer, eventPoint);
1997 if (!pickResults.isEmpty())
1998 for (
const auto &pickResult : pickResults)
1999 processPickedObject(pickResult, pointIndex, event, visitedSubscenes);
2001 eventPoint.setAccepted(
false);
2004 return forwardEventToSubscenes(event, useRayPicking, renderer, visitedSubscenes);
2007bool QQuick3DViewport::singlePointPick(QSinglePointEvent *event,
const QVector3D &origin,
const QVector3D &direction)
2009 QQuick3DSceneRenderer *renderer = getRenderer();
2010 if (!renderer || !event)
2013 QSSGRenderRay ray(origin, direction);
2015 Q_ASSERT(event->pointCount() == 1);
2016 QPointF originalPosition = event->point(0).scenePosition();
2018 auto pickResults = renderer->syncPickAll(ray);
2020 bool delivered =
false;
2022 constexpr float jitterLimit = 2.5;
2023 bool withinJitterLimit =
false;
2025 for (
const auto &pickResult : pickResults) {
2026 auto [item, position] = getItemAndPosition(pickResult);
2029 if (item == m_prevMouseItem && (position - m_prevMousePos).manhattanLength() < jitterLimit && !event->button()) {
2030 withinJitterLimit =
true;
2033 auto da = QQuickItemPrivate::get(item)->deliveryAgent();
2034 QEventPoint &ep = event->point(0);
2035 QMutableEventPoint::setPosition(ep, position);
2036 QMutableEventPoint::setScenePosition(ep, position);
2037 if (da->event(event)) {
2039 if (event->type() == QEvent::MouseButtonPress) {
2040 m_prevMouseItem = item;
2041 m_prevMousePos = position;
2042 withinJitterLimit =
true;
2048 QMutableEventPoint::setScenePosition(event->point(0), originalPosition);
2049 if (!withinJitterLimit)
2050 m_prevMouseItem =
nullptr;
2055 if (event->isEndEvent()) {
2056 if (event->buttons() == Qt::NoButton) {
2057 auto &firstPt = event->point(0);
2058 event->setExclusiveGrabber(firstPt,
nullptr);
2059 event->clearPassiveGrabbers(firstPt);
2066QPair<QQuickItem *, QPointF> QQuick3DViewport::getItemAndPosition(
const QSSGRenderPickResult &pickResult)
const
2068 QQuickItem *subsceneRootItem =
nullptr;
2069 QPointF subscenePosition;
2070 const auto backendObject = pickResult.m_hitObject;
2071 const auto frontendObject = findFrontendNode(backendObject);
2072 if (!frontendObject)
2074 auto frontendObjectPrivate = QQuick3DObjectPrivate::get(frontendObject);
2075 if (frontendObjectPrivate->type == QQuick3DObjectPrivate::Type::Item2D) {
2078 auto item2D = qobject_cast<QQuick3DItem2D *>(frontendObject);
2080 subsceneRootItem = item2D->contentItem();
2081 if (!subsceneRootItem || subsceneRootItem->childItems().isEmpty())
2085 subscenePosition = pickResult.m_localUVCoords.toPointF();
2089 if (!subsceneRootItem->childAt(subscenePosition.x(), subscenePosition.y()))
2091 }
else if (frontendObjectPrivate->type == QQuick3DObjectPrivate::Type::Model) {
2093 int materialSubset = pickResult.m_subset;
2094 const auto backendModel =
static_cast<
const QSSGRenderModel *>(backendObject);
2096 if (backendModel->materials.size() < (pickResult.m_subset + 1))
2097 materialSubset = backendModel->materials.size() - 1;
2098 if (materialSubset < 0)
2100 const auto backendMaterial = backendModel->materials.at(materialSubset);
2101 const auto frontendMaterial =
static_cast<QQuick3DMaterial *>(findFrontendNode(backendMaterial));
2102 subsceneRootItem = getSubSceneRootItem(frontendMaterial);
2104 if (subsceneRootItem) {
2106 subscenePosition = QPointF(subsceneRootItem->x() + pickResult.m_localUVCoords.x() * subsceneRootItem->width(),
2107 subsceneRootItem->y() - pickResult.m_localUVCoords.y() * subsceneRootItem->height() + subsceneRootItem->height());
2110 return {subsceneRootItem, subscenePosition};
2113QVarLengthArray<QSSGRenderPickResult, 20> QQuick3DViewport::getPickResults(QQuick3DSceneRenderer *renderer,
2114 const QVector3D &origin,
2115 const QVector3D &direction)
const
2117 const QSSGRenderRay ray(origin, direction);
2118 return renderer->syncPickAll(ray);
2121QVarLengthArray<QSSGRenderPickResult, 20> QQuick3DViewport::getPickResults(QQuick3DSceneRenderer *renderer,
const QEventPoint &eventPoint)
const
2123 QVarLengthArray<QSSGRenderPickResult, 20> pickResults;
2124 QPointF realPosition = eventPoint.position() * window()->effectiveDevicePixelRatio();
2126 realPosition.rx() *= m_widthMultiplier;
2127 realPosition.ry() *= m_heightMultiplier;
2128 std::optional<QSSGRenderRay> rayResult = renderer->getRayFromViewportPos(realPosition);
2129 if (rayResult.has_value())
2130 pickResults = renderer->syncPickAll(rayResult.value());
2135
2136
2137
2138
2139
2140QQuick3DObject *QQuick3DViewport::findFrontendNode(
const QSSGRenderGraphObject *backendObject)
const
2145 const auto sceneManager = QQuick3DObjectPrivate::get(m_sceneRoot)->sceneManager;
2146 QQuick3DObject *frontendObject = sceneManager->lookUpNode(backendObject);
2147 if (!frontendObject && m_importScene) {
2148 const auto importSceneManager = QQuick3DObjectPrivate::get(m_importScene)->sceneManager;
2149 frontendObject = importSceneManager->lookUpNode(backendObject);
2151 return frontendObject;
2154QQuick3DPickResult QQuick3DViewport::processPickResult(
const QSSGRenderPickResult &pickResult)
const
2156 if (!pickResult.m_hitObject)
2157 return QQuick3DPickResult();
2159 QQuick3DObject *frontendObject = findFrontendNode(pickResult.m_hitObject);
2161 QQuick3DModel *model = qobject_cast<QQuick3DModel *>(frontendObject);
2163 auto itemAndPosition = getItemAndPosition(pickResult);
2164 return QQuick3DPickResult(model,
2165 ::sqrtf(pickResult.m_distanceSq),
2166 pickResult.m_localUVCoords,
2167 pickResult.m_scenePosition,
2168 pickResult.m_localPosition,
2169 pickResult.m_faceNormal,
2170 pickResult.m_sceneNormal,
2171 pickResult.m_instanceIndex,
2172 itemAndPosition.first);
2175 QQuick3DItem2D *frontend2DItem = qobject_cast<QQuick3DItem2D *>(frontendObject);
2176 if (frontend2DItem && frontend2DItem->contentItem()) {
2178 const QPointF subscenePosition = pickResult.m_localUVCoords.toPointF();
2179 const auto child = frontend2DItem->contentItem()->childAt(subscenePosition.x(), subscenePosition.y());
2181 return QQuick3DPickResult(child,
2182 ::sqrtf(pickResult.m_distanceSq),
2183 QVector2D(frontend2DItem->contentItem()->mapToItem(child, subscenePosition)),
2184 pickResult.m_scenePosition,
2185 pickResult.m_localPosition,
2186 pickResult.m_faceNormal);
2190 return QQuick3DPickResult();
2195QQuick3DSceneManager *QQuick3DViewport::findChildSceneManager(QQuick3DObject *inObject, QQuick3DSceneManager *manager)
2200 auto children = QQuick3DObjectPrivate::get(inObject)->childItems;
2201 for (
auto child : children) {
2202 if (
auto m = QQuick3DObjectPrivate::get(child)->sceneManager) {
2206 manager = findChildSceneManager(child, manager);
2211void QQuick3DViewport::updateInputProcessing()
2214 setAcceptTouchEvents(m_enableInputProcessing);
2215 setAcceptHoverEvents(m_enableInputProcessing);
2216 setAcceptedMouseButtons(m_enableInputProcessing ? Qt::AllButtons : Qt::NoButton);
2219void QQuick3DViewport::onReleaseCachedResources()
2221 if (
auto renderer = getRenderer())
2222 renderer->releaseCachedResources();
2226
2227
2228
2229
2230
2231
2232QQmlListProperty<QQuick3DObject> QQuick3DViewport::extensions()
2234 return QQmlListProperty<QQuick3DObject>{
this,
2235 &m_extensionListDirty,
2236 &QQuick3DExtensionListHelper::extensionAppend,
2237 &QQuick3DExtensionListHelper::extensionCount,
2238 &QQuick3DExtensionListHelper::extensionAt,
2239 &QQuick3DExtensionListHelper::extensionClear,
2240 &QQuick3DExtensionListHelper::extensionReplace,
2241 &QQuick3DExtensionListHelper::extensionRemoveLast};
2245
2246
2247void QQuick3DViewport::rebuildExtensionList()
2249 m_extensionListDirty =
true;
2254
2255
2256
2257
2258
2259QQuick3DViewport::QQuick3DViewport(PrivateInstanceType type, QQuickItem *parent)
2260 : QQuick3DViewport(parent)
2262 m_isXrViewInstance = type == PrivateInstanceType::XrViewInstance;
2265void QQuick3DViewport::updateCameraForLayer(
const QQuick3DViewport &view3D, QSSGRenderLayer &layerNode)
2267 layerNode.explicitCameras.clear();
2268 if (!view3D.m_multiViewCameras.isEmpty()) {
2269 for (QQuick3DCamera *camera : std::as_const(view3D.m_multiViewCameras))
2270 layerNode.explicitCameras.append(
static_cast<QSSGRenderCamera *>(QQuick3DObjectPrivate::get(camera)->spatialNode));
2271 }
else if (view3D.camera()) {
2272 if (QSSGRenderCamera *camera =
static_cast<QSSGRenderCamera *>(QQuick3DObjectPrivate::get(view3D.camera())->spatialNode))
2273 layerNode.explicitCameras.append(camera);
2278 for (QSSGRenderCamera *camera : std::as_const(layerNode.explicitCameras)) {
2279 if (!camera->parent)
2280 layerNode.addChild(*camera);
2284void QQuick3DViewport::updateSceneManagerForImportScene()
2286 auto privateObject = QQuick3DObjectPrivate::get(m_importScene);
2287 if (!privateObject->sceneManager) {
2289 QQuick3DSceneManager *manager = findChildSceneManager(m_importScene);
2292 manager = QQuick3DObjectPrivate::get(m_sceneRoot)->sceneManager;
2294 manager->setWindow(window());
2295 privateObject->refSceneManager(*manager);
2300 if (!privateObject->sceneManager)
2304 connect(privateObject->sceneManager, &QQuick3DSceneManager::needsUpdate,
2305 this, &QQuickItem::update, Qt::UniqueConnection);
2306 connect(privateObject->sceneManager, &QObject::destroyed,
2307 this, [&](QObject *) {
2308 auto privateObject = QQuick3DObjectPrivate::get(m_importScene);
2309 privateObject->sceneManager =
nullptr;
2310 updateSceneManagerForImportScene();
2311 }, Qt::DirectConnection);
2313 QQuick3DNode *scene = m_importScene;
2315 QQuick3DSceneRootNode *rn = qobject_cast<QQuick3DSceneRootNode *>(scene);
2316 scene = rn ? rn->view3D()->importScene() :
nullptr;
2319 connect(QQuick3DObjectPrivate::get(scene)->sceneManager,
2320 &QQuick3DSceneManager::needsUpdate,
2321 this, &QQuickItem::update, Qt::UniqueConnection);
2326QQuick3DViewport::RenderOverrides QQuick3DViewport::renderOverrides()
const
2328 return m_renderOverrides;
2331void QQuick3DViewport::setRenderOverrides(RenderOverrides newRenderOverrides)
2333 if (m_renderOverrides == newRenderOverrides)
2335 m_renderOverrides = newRenderOverrides;
2336 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)