diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/MaterialNodeView.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/MaterialNodeView.qml index f52bcf4bee9..48ed09cba70 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/MaterialNodeView.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/MaterialNodeView.qml @@ -32,6 +32,11 @@ View3D { property Material previewMaterial + function fitToViewPort() + { + // No need to zoom this view, this is here just to avoid runtime warnings + } + SceneEnvironment { id: sceneEnv antialiasingMode: SceneEnvironment.MSAA diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/ModelNode3DImageView.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/ModelNode3DImageView.qml index 324a020d6fa..de3e3ee431c 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/ModelNode3DImageView.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/ModelNode3DImageView.qml @@ -41,8 +41,6 @@ Item { property var modelViewComponent property var nodeViewComponent - property bool ready: false - function destroyView() { previewObject = null; @@ -58,8 +56,6 @@ Item { createViewForModel(obj); else if (obj instanceof Node) createViewForNode(obj); - - previewObject = obj; } function createViewForMaterial(material) @@ -70,6 +66,8 @@ Item { // Always recreate the view to ensure material is up to date if (materialViewComponent.status === Component.Ready) view = materialViewComponent.createObject(viewRect, {"previewMaterial": material}); + + previewObject = material; } function createViewForModel(model) @@ -80,6 +78,8 @@ Item { // Always recreate the view to ensure model is up to date if (modelViewComponent.status === Component.Ready) view = modelViewComponent.createObject(viewRect, {"sourceModel": model}); + + previewObject = model; } function createViewForNode(node) @@ -90,16 +90,13 @@ Item { // Always recreate the view to ensure node is up to date if (nodeViewComponent.status === Component.Ready) view = nodeViewComponent.createObject(viewRect, {"importScene": node}); + + previewObject = node; } - function afterRender() + function fitToViewPort() { - if (previewObject instanceof Node) { - view.fitToViewPort(); - ready = view.ready; - } else { - ready = true; - } + view.fitToViewPort(); } View3D { diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/ModelNodeView.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/ModelNodeView.qml index dc10f441e1c..e61a9a8fb25 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/ModelNodeView.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/ModelNodeView.qml @@ -32,24 +32,13 @@ View3D { environment: sceneEnv camera: theCamera - property bool ready: false - property real prevZoomFactor: -1 property Model sourceModel function fitToViewPort() { - cameraControl.focusObject(model, theCamera.eulerRotation, true, false); - - if (cameraControl._zoomFactor < 0.1) { - model.scale = model.scale.times(10); - } else if (cameraControl._zoomFactor > 10) { - model.scale = model.scale.times(0.1); - } else { - // We need one more render after zoom factor change, so only set ready when zoom factor - // or scaling hasn't changed from the previous frame - ready = _generalHelper.fuzzyCompare(cameraControl._zoomFactor, prevZoomFactor); - prevZoomFactor = cameraControl._zoomFactor; - } + // The magic number is the distance from camera default pos to origin + _generalHelper.calculateNodeBoundsAndFocusCamera(theCamera, importScene, root, + 1040); } SceneEnvironment { @@ -58,14 +47,6 @@ View3D { antialiasingQuality: SceneEnvironment.High } - EditCameraController { - id: cameraControl - camera: theCamera - anchors.fill: parent - view3d: root - ignoreToolState: true - } - DirectionalLight { eulerRotation.x: -30 eulerRotation.y: -30 @@ -75,15 +56,15 @@ View3D { id: theCamera z: 600 y: 600 + x: 600 eulerRotation.x: -45 + eulerRotation.y: -45 clipFar: 10000 clipNear: 1 } Model { id: model - eulerRotation.y: 45 - source: sourceModel.source geometry: sourceModel.geometry diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/NodeNodeView.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/NodeNodeView.qml index b41c74af54e..91008abb251 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/NodeNodeView.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/NodeNodeView.qml @@ -32,30 +32,11 @@ View3D { environment: sceneEnv camera: theCamera - property bool ready: false - property bool first: true - property real prevZoomFactor: -1 - function fitToViewPort() { - if (first) { - first = false; - selectionBox.targetNode = root.importScene; - } else { - cameraControl.focusObject(selectionBox.model, theCamera.eulerRotation, true, false); - - if (cameraControl._zoomFactor < 0.1) { - root.importScene.scale = root.importScene.scale.times(10); - } else if (cameraControl._zoomFactor > 10) { - root.importScene.scale = root.importScene.scale.times(0.1); - } else { - // We need one more render after zoom factor change, so only set ready when zoom factor - // or scaling hasn't changed from the previous frame - ready = _generalHelper.fuzzyCompare(cameraControl._zoomFactor, prevZoomFactor); - prevZoomFactor = cameraControl._zoomFactor; - selectionBox.visible = false; - } - } + // The magic number is the distance from camera default pos to origin + _generalHelper.calculateNodeBoundsAndFocusCamera(theCamera, importScene, root, + 1040); } SceneEnvironment { @@ -64,20 +45,6 @@ View3D { antialiasingQuality: SceneEnvironment.High } - SelectionBox { - id: selectionBox - view3D: root - geometryName: "NodeNodeViewSB" - } - - EditCameraController { - id: cameraControl - camera: theCamera - anchors.fill: parent - view3d: root - ignoreToolState: true - } - DirectionalLight { eulerRotation.x: -30 eulerRotation.y: -30 diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/MaterialNodeView.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/MaterialNodeView.qml index 6103df98c2b..0b6c7bd2144 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/MaterialNodeView.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/MaterialNodeView.qml @@ -32,6 +32,11 @@ View3D { property Material previewMaterial + function fitToViewPort() + { + // No need to zoom this view, this is here just to avoid runtime warnings + } + SceneEnvironment { id: sceneEnv antialiasingMode: SceneEnvironment.MSAA diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ModelNode3DImageView.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ModelNode3DImageView.qml index 36d4cea8553..4bd07e57d12 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ModelNode3DImageView.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ModelNode3DImageView.qml @@ -41,8 +41,6 @@ Item { property var modelViewComponent property var nodeViewComponent - property bool ready: false - function destroyView() { previewObject = null; @@ -58,8 +56,6 @@ Item { createViewForModel(obj); else if (obj instanceof Node) createViewForNode(obj); - - previewObject = obj; } function createViewForMaterial(material) @@ -70,6 +66,8 @@ Item { // Always recreate the view to ensure material is up to date if (materialViewComponent.status === Component.Ready) view = materialViewComponent.createObject(viewRect, {"previewMaterial": material}); + + previewObject = material; } function createViewForModel(model) @@ -80,6 +78,8 @@ Item { // Always recreate the view to ensure model is up to date if (modelViewComponent.status === Component.Ready) view = modelViewComponent.createObject(viewRect, {"sourceModel": model}); + + previewObject = model; } function createViewForNode(node) @@ -90,16 +90,13 @@ Item { // Always recreate the view to ensure node is up to date if (nodeViewComponent.status === Component.Ready) view = nodeViewComponent.createObject(viewRect, {"importScene": node}); + + previewObject = node; } - function afterRender() + function fitToViewPort() { - if (previewObject instanceof Node) { - view.fitToViewPort(); - ready = view.ready; - } else { - ready = true; - } + view.fitToViewPort(); } Item { diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ModelNodeView.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ModelNodeView.qml index 7fe6a114163..d6574f660ca 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ModelNodeView.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ModelNodeView.qml @@ -32,24 +32,13 @@ View3D { environment: sceneEnv camera: theCamera - property bool ready: false - property real prevZoomFactor: -1 property Model sourceModel function fitToViewPort() { - cameraControl.focusObject(model, theCamera.eulerRotation, true, false); - - if (cameraControl._zoomFactor < 0.1) { - model.scale = model.scale.times(10); - } else if (cameraControl._zoomFactor > 10) { - model.scale = model.scale.times(0.1); - } else { - // We need one more render after zoom factor change, so only set ready when zoom factor - // or scaling hasn't changed from the previous frame - ready = _generalHelper.fuzzyCompare(cameraControl._zoomFactor, prevZoomFactor); - prevZoomFactor = cameraControl._zoomFactor; - } + // The magic number is the distance from camera default pos to origin + _generalHelper.calculateNodeBoundsAndFocusCamera(theCamera, importScene, root, + 1040); } SceneEnvironment { @@ -58,14 +47,6 @@ View3D { antialiasingQuality: SceneEnvironment.High } - EditCameraController { - id: cameraControl - camera: theCamera - anchors.fill: parent - view3d: root - ignoreToolState: true - } - DirectionalLight { eulerRotation.x: -30 eulerRotation.y: -30 @@ -75,16 +56,15 @@ View3D { id: theCamera z: 600 y: 600 + x: 600 eulerRotation.x: -45 + eulerRotation.y: -45 clipFar: 10000 clipNear: 1 } Model { id: model - readonly property bool _edit3dLocked: true // Make this non-pickable - eulerRotation.y: 45 - source: sourceModel.source geometry: sourceModel.geometry diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/NodeNodeView.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/NodeNodeView.qml index 49d88d4c8a9..a48eb665191 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/NodeNodeView.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/NodeNodeView.qml @@ -32,30 +32,11 @@ View3D { environment: sceneEnv camera: theCamera - property bool ready: false - property bool first: true - property real prevZoomFactor: -1 - function fitToViewPort() { - if (first) { - first = false; - selectionBox.targetNode = root.importScene; - } else { - cameraControl.focusObject(selectionBox.model, theCamera.eulerRotation, true, false); - - if (cameraControl._zoomFactor < 0.1) { - root.importScene.scale = root.importScene.scale.times(10); - } else if (cameraControl._zoomFactor > 10) { - root.importScene.scale = root.importScene.scale.times(0.1); - } else { - // We need one more render after zoom factor change, so only set ready when zoom factor - // or scaling hasn't changed from the previous frame - ready = _generalHelper.fuzzyCompare(cameraControl._zoomFactor, prevZoomFactor); - prevZoomFactor = cameraControl._zoomFactor; - selectionBox.visible = false; - } - } + // The magic number is the distance from camera default pos to origin + _generalHelper.calculateNodeBoundsAndFocusCamera(theCamera, importScene, root, + 1040); } SceneEnvironment { @@ -64,20 +45,6 @@ View3D { antialiasingQuality: SceneEnvironment.High } - SelectionBox { - id: selectionBox - view3D: root - geometryName: "NodeNodeViewSB" - } - - EditCameraController { - id: cameraControl - camera: theCamera - anchors.fill: parent - view3d: root - ignoreToolState: true - } - DirectionalLight { eulerRotation.x: -30 eulerRotation.y: -30 diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp index f0131c80895..9d7d4b05ce9 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp @@ -55,6 +55,11 @@ const QString _globalStateId = QStringLiteral("@GTS"); // global tool state const QString _lastSceneIdKey = QStringLiteral("lastSceneId"); const QString _rootSizeKey = QStringLiteral("rootSize"); +static const float floatMin = std::numeric_limits::lowest(); +static const float floatMax = std::numeric_limits::max(); +static const QVector3D maxVec = QVector3D(floatMax, floatMax, floatMax); +static const QVector3D minVec = QVector3D(floatMin, floatMin, floatMin); + GeneralHelper::GeneralHelper() : QObject() { @@ -269,6 +274,37 @@ QVector4D GeneralHelper::focusNodesToCamera(QQuick3DCamera *camera, float defaul return QVector4D(lookAt, cameraZoomFactor); } +// This function can be used to synchronously focus camera on a node, which doesn't have to be +// a selection box for bound calculations to work. This is used to focus the view for +// various preview image generations, where doing things asynchronously is not good +// and recalculating bounds for every frame is not a problem. +void GeneralHelper::calculateNodeBoundsAndFocusCamera( + QQuick3DCamera *camera, QQuick3DNode *node, QQuick3DViewport *viewPort, + float defaultLookAtDistance) +{ + QVector3D minBounds; + QVector3D maxBounds; + + getBounds(viewPort, node, minBounds, maxBounds); + + QVector3D extents = maxBounds - minBounds; + QVector3D lookAt = minBounds + (extents / 2.f); + float maxExtent = qMax(extents.x(), qMax(extents.y(), extents.z())); + + // Reset camera position to default zoom + QMatrix4x4 m = camera->sceneTransform(); + const float *dataPtr(m.data()); + QVector3D newLookVector(dataPtr[8], dataPtr[9], dataPtr[10]); + newLookVector.normalize(); + newLookVector *= defaultLookAtDistance; + + camera->setPosition(lookAt + newLookVector); + + float newZoomFactor = maxExtent / 725.f; // Divisor taken from focusNodesToCamera function + + zoomCamera(viewPort, camera, 0, defaultLookAtDistance, lookAt, newZoomFactor, false); +} + // Aligns any cameras found in nodes list to a camera. // Only position and rotation are copied, rest of the camera properties stay the same. void GeneralHelper::alignCameras(QQuick3DCamera *camera, const QVariant &nodes) @@ -727,6 +763,129 @@ QVector3D GeneralHelper::pivotScenePosition(QQuick3DNode *node) const return mat44::getPosition(sceneTransform); } +// Calculate bounds for given node, including all child nodes. +// Returns true if the tree contains at least one Model node. +bool GeneralHelper::getBounds(QQuick3DViewport *view3D, QQuick3DNode *node, QVector3D &minBounds, + QVector3D &maxBounds, bool recursive) +{ + if (!node) { + const float halfExtent = 100.f; + minBounds = {-halfExtent, -halfExtent, -halfExtent}; + maxBounds = {halfExtent, halfExtent, halfExtent}; + return false; + } + + QMatrix4x4 localTransform; + auto nodePriv = QQuick3DObjectPrivate::get(node); + auto renderNode = static_cast(nodePriv->spatialNode); + + if (recursive && renderNode) { + if (renderNode->flags.testFlag(QSSGRenderNode::Flag::TransformDirty)) + renderNode->calculateLocalTransform(); + localTransform = renderNode->localTransform; + } + + QVector3D localMinBounds = maxVec; + QVector3D localMaxBounds = minVec; + + // Find bounds for children + QVector minBoundsVec; + QVector maxBoundsVec; + const auto children = node->childItems(); + bool hasModel = false; + for (const auto child : children) { + if (auto childNode = qobject_cast(child)) { + QVector3D newMinBounds = minBounds; + QVector3D newMaxBounds = maxBounds; + hasModel = getBounds(view3D, childNode, newMinBounds, newMaxBounds, true); + // Ignore any subtrees that do not have Model in them as we don't need those + // for visual bounds calculations + if (hasModel) { + minBoundsVec << newMinBounds; + maxBoundsVec << newMaxBounds; + } + } + } + + auto combineMinBounds = [](QVector3D &target, const QVector3D &source) { + target.setX(qMin(source.x(), target.x())); + target.setY(qMin(source.y(), target.y())); + target.setZ(qMin(source.z(), target.z())); + }; + auto combineMaxBounds = [](QVector3D &target, const QVector3D &source) { + target.setX(qMax(source.x(), target.x())); + target.setY(qMax(source.y(), target.y())); + target.setZ(qMax(source.z(), target.z())); + }; + auto transformCorner = [&](const QMatrix4x4 &m, QVector3D &minTarget, QVector3D &maxTarget, + const QVector3D &corner) { + QVector3D mappedCorner = m.map(corner); + combineMinBounds(minTarget, mappedCorner); + combineMaxBounds(maxTarget, mappedCorner); + }; + auto transformCorners = [&](const QMatrix4x4 &m, QVector3D &minTarget, QVector3D &maxTarget, + const QVector3D &minCorner, const QVector3D &maxCorner) { + transformCorner(m, minTarget, maxTarget, minCorner); + transformCorner(m, minTarget, maxTarget, maxCorner); + transformCorner(m, minTarget, maxTarget, QVector3D(minCorner.x(), minCorner.y(), maxCorner.z())); + transformCorner(m, minTarget, maxTarget, QVector3D(minCorner.x(), maxCorner.y(), minCorner.z())); + transformCorner(m, minTarget, maxTarget, QVector3D(maxCorner.x(), minCorner.y(), minCorner.z())); + transformCorner(m, minTarget, maxTarget, QVector3D(minCorner.x(), maxCorner.y(), maxCorner.z())); + transformCorner(m, minTarget, maxTarget, QVector3D(maxCorner.x(), maxCorner.y(), minCorner.z())); + transformCorner(m, minTarget, maxTarget, QVector3D(maxCorner.x(), minCorner.y(), maxCorner.z())); + }; + + // Combine all child bounds + for (const auto &newBounds : qAsConst(minBoundsVec)) + combineMinBounds(localMinBounds, newBounds); + for (const auto &newBounds : qAsConst(maxBoundsVec)) + combineMaxBounds(localMaxBounds, newBounds); + + if (qobject_cast(node)) { + if (auto renderModel = static_cast(renderNode)) { + QWindow *window = static_cast(view3D->window()); + if (window) { + QSSGRef context; +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + context = QSSGRenderContextInterface::getRenderContextInterface(quintptr(window)); +#else + context = QQuick3DObjectPrivate::get(node)->sceneManager->rci; +#endif + if (!context.isNull()) { + auto bufferManager = context->bufferManager(); +#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0) + QSSGBounds3 bounds = renderModel->getModelBounds(bufferManager); +#else + QSSGBounds3 bounds = bufferManager->getModelBounds(renderModel); +#endif + QVector3D center = bounds.center(); + QVector3D extents = bounds.extents(); + QVector3D localMin = center - extents; + QVector3D localMax = center + extents; + + combineMinBounds(localMinBounds, localMin); + combineMaxBounds(localMaxBounds, localMax); + + hasModel = true; + } + } + } + } else { + combineMinBounds(localMinBounds, {}); + combineMaxBounds(localMaxBounds, {}); + } + + if (localMaxBounds == minVec) { + localMinBounds = {}; + localMaxBounds = {}; + } + + // Transform local space bounding box to parent space + transformCorners(localTransform, minBounds, maxBounds, localMinBounds, localMaxBounds); + + return hasModel; +} + } } diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.h index 06068601537..46e6019dd58 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.h @@ -72,6 +72,9 @@ public: const QVariant &nodes, QQuick3DViewport *viewPort, float oldZoom, bool updateZoom = true, bool closeUp = false); + Q_INVOKABLE void calculateNodeBoundsAndFocusCamera(QQuick3DCamera *camera, QQuick3DNode *node, + QQuick3DViewport *viewPort, + float defaultLookAtDistance); Q_INVOKABLE void alignCameras(QQuick3DCamera *camera, const QVariant &nodes); Q_INVOKABLE QVector3D alignView(QQuick3DCamera *camera, const QVariant &nodes, const QVector3D &lookAtPoint); @@ -126,6 +129,8 @@ protected: private: void handlePendingToolStateUpdate(); QVector3D pivotScenePosition(QQuick3DNode *node) const; + bool getBounds(QQuick3DViewport *view3D, QQuick3DNode *node, QVector3D &minBounds, + QVector3D &maxBounds, bool recursive = false); QTimer m_overlayUpdateTimer; QTimer m_toolStateUpdateTimer; diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp index b37486f0481..d496aff3889 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp @@ -1588,6 +1588,11 @@ bool NodeInstanceServer::isInformationServer() const return false; } +bool NodeInstanceServer::isPreviewServer() const +{ + return false; +} + static QString baseProperty(const QString &property) { int index = property.indexOf('.'); diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.h index f2255bd891d..ba51f5e5888 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.h @@ -229,8 +229,10 @@ public: virtual QImage grabWindow() = 0; virtual QImage grabItem(QQuickItem *item) = 0; + virtual bool renderWindow() = 0; virtual bool isInformationServer() const; + virtual bool isPreviewServer() const; void addAnimation(QQuickAbstractAnimation *animation); QVector animations() const; QVariant animationDefaultValue(int index) const; diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.cpp index 4eb8cdd65f4..9f3a1d6b3bc 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.cpp @@ -180,6 +180,11 @@ bool ObjectNodeInstance::isLayoutable() const return false; } +bool ObjectNodeInstance::isRenderable() const +{ + return false; +} + bool ObjectNodeInstance::equalGraphicsItem(QGraphicsItem * /*item*/) const { return false; diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.h index 67e1663496c..88e2ca26770 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.h @@ -103,6 +103,7 @@ public: virtual bool isQuickItem() const; virtual bool isQuickWindow() const; virtual bool isLayoutable() const; + virtual bool isRenderable() const; virtual bool equalGraphicsItem(QGraphicsItem *item) const; diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5captureimagenodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5captureimagenodeinstanceserver.cpp index c0d763c906b..cff435bdd49 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5captureimagenodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5captureimagenodeinstanceserver.cpp @@ -44,10 +44,10 @@ QImage renderImage(ServerNodeInstance rootNodeInstance) QSize previewImageSize = rootNodeInstance.boundingRect().size().toSize(); if (previewImageSize.isEmpty()) - previewImageSize = {300, 300}; + previewImageSize = {150, 150}; - if (previewImageSize.width() > 300 || previewImageSize.height() > 300) - previewImageSize.scale({300, 300}, Qt::KeepAspectRatio); + if (previewImageSize.width() > 150 || previewImageSize.height() > 150) + previewImageSize.scale({150, 150}, Qt::KeepAspectRatio); QImage previewImage = rootNodeInstance.renderPreviewImage(previewImageSize); @@ -68,7 +68,8 @@ void Qt5CaptureImageNodeInstanceServer::collectItemChangesAndSendChangeCommands( inFunction = true; auto rooNodeInstance = rootNodeInstance(); - rooNodeInstance.rootQuickItem()->setClip(true); + if (QQuickItem *qitem = rooNodeInstance.rootQuickItem()) + qitem->setClip(true); DesignerSupport::polishItems(quickWindow()); diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp index afde832afcb..e2bd56562d5 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp @@ -1007,17 +1007,17 @@ void Qt5InformationNodeInstanceServer::doRenderModelNodeImageView() void Qt5InformationNodeInstanceServer::doRenderModelNode3DImageView() { #ifdef QUICK3D_MODULE - m_modelNode3DImageViewAsyncData.cleanup(); if (m_modelNode3DImageViewData.rootItem) { QMetaObject::invokeMethod(m_modelNode3DImageViewData.rootItem, "destroyView"); if (!m_modelNode3DImageViewData.contentItem) m_modelNode3DImageViewData.contentItem = getContentItemForRendering(m_modelNode3DImageViewData.rootItem); + QImage renderImage; if (m_modelNodePreviewImageCache.contains(m_modelNodePreviewImageCommand.componentPath())) { - m_modelNode3DImageViewAsyncData.renderImage - = m_modelNodePreviewImageCache[m_modelNodePreviewImageCommand.componentPath()]; - modelNode3DImageViewSendImageToCreator(); + renderImage = m_modelNodePreviewImageCache[m_modelNodePreviewImageCommand.componentPath()]; } else { + bool createdFromComponent = false; + QObject *instanceObj = nullptr; ServerNodeInstance instance = instanceForId(m_modelNodePreviewImageCommand.instanceId()); if (!m_modelNodePreviewImageCommand.componentPath().isEmpty() && instance.isSubclassOf("QQuick3DNode")) { @@ -1026,15 +1026,14 @@ void Qt5InformationNodeInstanceServer::doRenderModelNode3DImageView() // wouldn't want the children of the Node to appear in the preview. QQmlComponent component(engine()); component.loadUrl(QUrl::fromLocalFile(m_modelNodePreviewImageCommand.componentPath())); - m_modelNode3DImageViewAsyncData.instanceObj = qobject_cast(component.create()); - if (!m_modelNode3DImageViewAsyncData.instanceObj) { + instanceObj = qobject_cast(component.create()); + if (!instanceObj) { qWarning() << "Could not create preview component: " << component.errors(); - m_modelNode3DImageViewAsyncData.cleanup(); return; } - m_modelNode3DImageViewAsyncData.createdFromComponent = true; + createdFromComponent = true; } else { - m_modelNode3DImageViewAsyncData.instanceObj = instance.internalObject(); + instanceObj = instance.internalObject(); } QSize renderSize = m_modelNodePreviewImageCommand.size(); if (Internal::QuickItemNodeInstance::unifiedRenderPathOrQt6()) { @@ -1055,69 +1054,53 @@ void Qt5InformationNodeInstanceServer::doRenderModelNode3DImageView() QMetaObject::invokeMethod( m_modelNode3DImageViewData.rootItem, "createViewForObject", - Q_ARG(QVariant, objectToVariant(m_modelNode3DImageViewAsyncData.instanceObj))); + Q_ARG(QVariant, objectToVariant(instanceObj))); - // Selection box geometry updates have an asynchronous step, so we need to do rendering - // in asynchronous steps as well, since we are adjusting the selection box geometry - // while finding correct zoom level. - m_modelNode3DImageViewAsyncData.timer.start(); - } - } + // Need to render twice, first render updates spatial nodes + for (int i = 0; i < 2; ++i) { + if (i == 1) + QMetaObject::invokeMethod(m_modelNode3DImageViewData.rootItem, "fitToViewPort" + , Qt::DirectConnection); + + updateNodesRecursive(m_modelNode3DImageViewData.contentItem); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + if (Internal::QuickItemNodeInstance::unifiedRenderPath()) { + renderImage = m_modelNode3DImageViewData.window->grabWindow(); + } else { + // Fake render loop signaling to update things like QML items as 3D textures + m_modelNode3DImageViewData.window->beforeSynchronizing(); + m_modelNode3DImageViewData.window->beforeRendering(); + + QSizeF size = qobject_cast(m_modelNode3DImageViewData.contentItem)->size(); + QRectF renderRect(QPointF(0., 0.), size); + renderImage = designerSupport()->renderImageForItem(m_modelNode3DImageViewData.contentItem, + renderRect, size.toSize()); + m_modelNode3DImageViewData.window->afterRendering(); + } +#else + renderImage = grabRenderControl(m_modelNode3DImageViewData); #endif -} + } -void Qt5InformationNodeInstanceServer::modelNode3DImageViewSendImageToCreator() -{ - if (!m_modelNode3DImageViewAsyncData.renderImage.isNull()) { + QMetaObject::invokeMethod(m_modelNode3DImageViewData.rootItem, "destroyView"); + + if (createdFromComponent) { + // If component changes, puppet will need a reset anyway, so we can cache the image + m_modelNodePreviewImageCache.insert(m_modelNodePreviewImageCommand.componentPath(), + renderImage); + delete instanceObj; + } + } // Key number is selected so that it is unlikely to conflict other ImageContainer use. ImageContainer imgContainer(m_modelNodePreviewImageCommand.instanceId(), {}, 2100000001); - imgContainer.setImage(m_modelNode3DImageViewAsyncData.renderImage); + imgContainer.setImage(renderImage); // send the rendered image to creator process nodeInstanceClient()->handlePuppetToCreatorCommand( {PuppetToCreatorCommand::RenderModelNodePreviewImage, QVariant::fromValue(imgContainer)}); - - m_modelNode3DImageViewAsyncData.cleanup(); } -} - -void Qt5InformationNodeInstanceServer::modelNode3DImageViewRenderStep() -{ - ++m_modelNode3DImageViewAsyncData.count; - - updateNodesRecursive(m_modelNode3DImageViewData.contentItem); -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - if (Internal::QuickItemNodeInstance::unifiedRenderPath()) { - m_modelNode3DImageViewAsyncData.renderImage = m_modelNode3DImageViewData.window->grabWindow(); - } else { - // Fake render loop signaling to update things like QML items as 3D textures - m_modelNode3DImageViewData.window->beforeSynchronizing(); - m_modelNode3DImageViewData.window->beforeRendering(); - - QSizeF size = qobject_cast(m_modelNode3DImageViewData.contentItem)->size(); - QRectF renderRect(QPointF(0., 0.), size); - m_modelNode3DImageViewAsyncData.renderImage - = designerSupport()->renderImageForItem(m_modelNode3DImageViewData.contentItem, - renderRect, size.toSize()); - m_modelNode3DImageViewData.window->afterRendering(); - } -#else - m_modelNode3DImageViewAsyncData.renderImage = grabRenderControl(m_modelNode3DImageViewData); #endif - QMetaObject::invokeMethod(m_modelNode3DImageViewData.rootItem, "afterRender"); - const bool ready = QQmlProperty::read(m_modelNode3DImageViewData.rootItem, "ready").value(); - if (ready || m_modelNode3DImageViewAsyncData.count >= 10) { - QMetaObject::invokeMethod(m_modelNode3DImageViewData.rootItem, "destroyView"); - if (m_modelNode3DImageViewAsyncData.createdFromComponent) { - // If component changes, puppet will need a reset anyway, so we can cache the image - m_modelNodePreviewImageCache.insert(m_modelNodePreviewImageCommand.componentPath(), - m_modelNode3DImageViewAsyncData.renderImage); - } - modelNode3DImageViewSendImageToCreator(); - } else { - m_modelNode3DImageViewAsyncData.timer.start(); - } } static QRectF itemBoundingRect(QQuickItem *item) @@ -1234,7 +1217,6 @@ Qt5InformationNodeInstanceServer::Qt5InformationNodeInstanceServer(NodeInstanceC m_render3DEditViewTimer.setSingleShot(true); m_inputEventTimer.setSingleShot(true); m_renderModelNodeImageViewTimer.setSingleShot(true); - m_modelNode3DImageViewAsyncData.timer.setSingleShot(true); m_dynamicAddObjectTimer.setSingleShot(true); #ifdef FPS_COUNTER @@ -1260,7 +1242,6 @@ Qt5InformationNodeInstanceServer::~Qt5InformationNodeInstanceServer() m_render3DEditViewTimer.stop(); m_inputEventTimer.stop(); m_renderModelNodeImageViewTimer.stop(); - m_modelNode3DImageViewAsyncData.timer.stop(); m_dynamicAddObjectTimer.stop(); if (m_editView3DData.rootItem) @@ -1677,8 +1658,6 @@ void Qt5InformationNodeInstanceServer::setup3DEditView(const QList &instances); void handleInputEvents(); @@ -190,26 +188,6 @@ private: QObject *m_3dHelper = nullptr; int m_need3DEditViewRender = 0; QSet m_dynamicObjectConstructors; - - struct ModelNode3DImageViewAsyncData { - QTimer timer; - QImage renderImage; - int count = 0; - bool createdFromComponent = false; - QObject *instanceObj = nullptr; - - void cleanup() - { - timer.stop(); - count = 0; - renderImage = {}; - if (createdFromComponent) - delete instanceObj; - instanceObj = nullptr; - createdFromComponent = false; - } - }; - ModelNode3DImageViewAsyncData m_modelNode3DImageViewAsyncData; }; } // namespace QmlDesigner diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.h index 4af451b61ae..a0c79296033 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.h @@ -70,6 +70,7 @@ public: QImage grabWindow() override; QImage grabItem(QQuickItem *item) override; + bool renderWindow() override; static QQuickItem *parentEffectItem(QQuickItem *item); @@ -97,7 +98,6 @@ protected: virtual bool initRhi(RenderViewData &viewData); virtual QImage grabRenderControl(RenderViewData &viewData); - virtual bool renderWindow(); private: RenderViewData m_viewData; diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5previewnodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5previewnodeinstanceserver.cpp index fb3113bdcd1..f61ba1a3421 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5previewnodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5previewnodeinstanceserver.cpp @@ -133,4 +133,9 @@ void Qt5PreviewNodeInstanceServer::changePreviewImageSize( collectItemChangesAndSendChangeCommands(); } +bool Qt5PreviewNodeInstanceServer::isPreviewServer() const +{ + return true; +} + } // namespace QmlDesigner diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5previewnodeinstanceserver.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5previewnodeinstanceserver.h index 182db45d405..6c97658466f 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5previewnodeinstanceserver.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5previewnodeinstanceserver.h @@ -39,6 +39,7 @@ public: void changeState(const ChangeStateCommand &command) override; void removeSharedMemory(const RemoveSharedMemoryCommand &command) override; void changePreviewImageSize(const ChangePreviewImageSizeCommand &command) override; + bool isPreviewServer() const override; QImage renderPreviewImage(); diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3dnodeinstance.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3dnodeinstance.cpp index 980da0da791..65bda831a6e 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3dnodeinstance.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3dnodeinstance.cpp @@ -26,6 +26,8 @@ #include "quick3dnodeinstance.h" #include "qt5nodeinstanceserver.h" #include "qt5informationnodeinstanceserver.h" +#include "quickitemnodeinstance.h" +#include "../editor3d/generalhelper.h" #include @@ -37,6 +39,7 @@ #include #ifdef QUICK3D_MODULE +#include #include #include #include @@ -45,8 +48,10 @@ #if defined(QUICK3D_ASSET_UTILS_MODULE) && QT_VERSION > QT_VERSION_CHECK(6, 2, 0) #include #endif +#include #endif + namespace QmlDesigner { namespace Internal { @@ -57,6 +62,7 @@ Quick3DNodeInstance::Quick3DNodeInstance(QObject *node) Quick3DNodeInstance::~Quick3DNodeInstance() { + delete m_dummyRootView; } void Quick3DNodeInstance::initialize(const ObjectNodeInstance::Pointer &objectNodeInstance, @@ -87,10 +93,123 @@ void Quick3DNodeInstance::initialize(const ObjectNodeInstance::Pointer &objectNo } } } + +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + // In case this is the scene root, we need to create a dummy View3D for the scene + // in preview puppets + if (instanceId() == 0 && nodeInstanceServer()->isPreviewServer()) { + auto helper = new QmlDesigner::Internal::GeneralHelper(); + engine()->rootContext()->setContextProperty("_generalHelper", helper); + + QQmlComponent component(engine()); + component.loadUrl(QUrl("qrc:/qtquickplugin/mockfiles/qt6/ModelNode3DImageView.qml")); + m_dummyRootView = qobject_cast(component.create()); + + QMetaObject::invokeMethod( + m_dummyRootView, "createViewForNode", + Q_ARG(QVariant, QVariant::fromValue(object()))); + + nodeInstanceServer()->setRootItem(m_dummyRootView); + } +#endif #endif ObjectNodeInstance::initialize(objectNodeInstance, flags); } +QImage Quick3DNodeInstance::renderImage() const +{ +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + if (!isRootNodeInstance() || !m_dummyRootView) + return {}; + + QSize size(640, 480); + nodeInstanceServer()->quickWindow()->resize(size); + m_dummyRootView->setSize(size); + + // Just render the window once to update spatial nodes + nodeInstanceServer()->renderWindow(); + + QMetaObject::invokeMethod(m_dummyRootView, "fitToViewPort", Qt::DirectConnection); + + QRectF renderBoundingRect = m_dummyRootView->boundingRect(); + QImage renderImage; + + if (QuickItemNodeInstance::unifiedRenderPath()) { + renderImage = nodeInstanceServer()->grabWindow(); + renderImage = renderImage.copy(renderBoundingRect.toRect()); + } else { + renderImage = nodeInstanceServer()->grabItem(m_dummyRootView); + } + + // When grabbing an offscreen window the device pixel ratio is 1 + renderImage.setDevicePixelRatio(1); + + return renderImage; +#endif + return {}; +} + +QImage Quick3DNodeInstance::renderPreviewImage(const QSize &previewImageSize) const +{ +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + if (!isRootNodeInstance() || !m_dummyRootView) + return {}; + + nodeInstanceServer()->quickWindow()->resize(previewImageSize); + m_dummyRootView->setSize(previewImageSize); + + // Just render the window once to update spatial nodes + nodeInstanceServer()->renderWindow(); + + QMetaObject::invokeMethod(m_dummyRootView, "fitToViewPort", Qt::DirectConnection); + + QRectF previewItemBoundingRect = boundingRect(); + + if (previewItemBoundingRect.isValid()) { + const QSize size = previewImageSize; + if (m_dummyRootView->isVisible()) { + QImage image; + image = nodeInstanceServer()->grabWindow(); + image = image.copy(previewItemBoundingRect.toRect()); + image = image.scaledToWidth(size.width()); + return image; + } else { + QImage transparentImage(size, QImage::Format_ARGB32_Premultiplied); + transparentImage.fill(Qt::transparent); + return transparentImage; + } + } +#endif + return {}; +} + +bool Quick3DNodeInstance::isRenderable() const +{ + return m_dummyRootView; +} + +QRectF Quick3DNodeInstance::boundingRect() const +{ + if (m_dummyRootView) + return m_dummyRootView->boundingRect(); + return ObjectNodeInstance::boundingRect(); +} + +QList Quick3DNodeInstance::stateInstances() const +{ + QList instanceList; +#ifdef QUICK3D_MODULE + if (auto obj3D = quick3DNode()) { + const QList stateList = QQuick3DObjectPrivate::get(obj3D)->_states()->states(); + for (QQuickState *state : stateList) { + if (state && nodeInstanceServer()->hasInstanceForObject(state)) + instanceList.append(nodeInstanceServer()->instanceForObject(state)); + } + } +#endif + return instanceList; +} + Qt5NodeInstanceServer *Quick3DNodeInstance::qt5NodeInstanceServer() const { return qobject_cast(nodeInstanceServer()); diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3dnodeinstance.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3dnodeinstance.h index 892946ee1f6..322762af26a 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3dnodeinstance.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quick3dnodeinstance.h @@ -47,12 +47,22 @@ public: void initialize(const ObjectNodeInstance::Pointer &objectNodeInstance, InstanceContainer::NodeFlags flags) override; + QImage renderImage() const override; + QImage renderPreviewImage(const QSize &previewImageSize) const override; + + bool isRenderable() const override; + QRectF boundingRect() const override; + + QList stateInstances() const override; + protected: explicit Quick3DNodeInstance(QObject *node); private: Qt5NodeInstanceServer *qt5NodeInstanceServer() const; QQuick3DNode *quick3DNode() const; + + QQuickItem *m_dummyRootView = nullptr; }; } // namespace Internal diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.cpp index b78bf6328e7..41d0015ce56 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.cpp @@ -571,6 +571,11 @@ bool QuickItemNodeInstance::isQuickItem() const return true; } +bool QuickItemNodeInstance::isRenderable() const +{ + return quickItem() && (!s_unifiedRenderPath || isRootNodeInstance()); +} + QList QuickItemNodeInstance::stateInstances() const { QList instanceList; diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.h index 46b22b8c9a8..50cf494e8e2 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.h @@ -94,6 +94,7 @@ public: bool isResizable() const override; bool isMovable() const override; bool isQuickItem() const override; + bool isRenderable() const override; QList stateInstances() const override; diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.cpp index 6b2ba783402..9803f6d79e6 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.cpp @@ -154,7 +154,7 @@ void ServerNodeInstance::setNodeSource(const QString &source) bool ServerNodeInstance::holdsGraphical() const { - return m_nodeInstance->isQuickItem(); + return m_nodeInstance->isRenderable(); } bool ServerNodeInstance::isComponentWrap() const diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryitem.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryitem.cpp index 78e06f0b56b..c86e5360d1c 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryitem.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryitem.cpp @@ -48,7 +48,8 @@ QString ItemLibraryItem::typeName() const QString ItemLibraryItem::itemLibraryIconPath() const { - if (m_itemLibraryEntry.customComponentSource().isEmpty()) { + if (m_itemLibraryEntry.customComponentSource().isEmpty() + || !m_itemLibraryEntry.libraryEntryIconPath().isEmpty()) { return QStringLiteral("image://qmldesigner_itemlibrary/") + m_itemLibraryEntry.libraryEntryIconPath(); } else { diff --git a/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.ui b/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.ui index fec0ba8f384..6839c1b107d 100644 --- a/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.ui +++ b/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.ui @@ -6,8 +6,8 @@ 0 0 - 651 - 318 + 517 + 166 @@ -90,8 +90,8 @@ - 300 - 300 + 150 + 150 @@ -163,7 +163,7 @@ 0 - 1 + 3 diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp index b4f50848b9a..f5164a97213 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp @@ -90,7 +90,8 @@ void ImageCacheCollector::start(Utils::SmallStringView name, model->setRewriterView(&rewriterView); - if (rewriterView.inErrorState() || !rewriterView.rootModelNode().metaInfo().isGraphicalItem()) { + if (rewriterView.inErrorState() || (!rewriterView.rootModelNode().metaInfo().isGraphicalItem() + && !rewriterView.rootModelNode().isSubclassOf("Quick3D.Node") )) { if (abortCallback) abortCallback(ImageCache::AbortReason::Failed); return; diff --git a/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp index cede56f299d..a001a8b0c29 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/subcomponentmanager.cpp @@ -420,6 +420,7 @@ void SubComponentManager::parseQuick3DAssetsItem(const QString &importUrl, const itemLibraryEntry.setType(type.toUtf8(), 1, 0); itemLibraryEntry.setName(name); itemLibraryEntry.setCategory(::QmlDesigner::SubComponentManager::tr("My 3D Components")); + itemLibraryEntry.setCustomComponentSource(qmlIt.fileInfo().absoluteFilePath()); itemLibraryEntry.setRequiredImport(importUrl); QString iconPath = qmlIt.fileInfo().absolutePath() + '/' + Constants::QUICK_3D_ASSET_ICON_DIR + '/' + name