QmlDesigner: Implement proper preview for pure 3D scenes

Refactored the existing 3D node preview generation to be done
synchronously, which enables their use also for creating state
and item library previews for 3D nodes with ease.

Fixes: QDS-5785
Change-Id: Ib493eccbc239f33bcad3301673a865494616a901
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
Miikka Heikkinen
2022-02-02 17:56:16 +02:00
parent ab5fdd94f3
commit f146b846cd
29 changed files with 420 additions and 243 deletions

View File

@@ -32,6 +32,11 @@ View3D {
property Material previewMaterial property Material previewMaterial
function fitToViewPort()
{
// No need to zoom this view, this is here just to avoid runtime warnings
}
SceneEnvironment { SceneEnvironment {
id: sceneEnv id: sceneEnv
antialiasingMode: SceneEnvironment.MSAA antialiasingMode: SceneEnvironment.MSAA

View File

@@ -41,8 +41,6 @@ Item {
property var modelViewComponent property var modelViewComponent
property var nodeViewComponent property var nodeViewComponent
property bool ready: false
function destroyView() function destroyView()
{ {
previewObject = null; previewObject = null;
@@ -58,8 +56,6 @@ Item {
createViewForModel(obj); createViewForModel(obj);
else if (obj instanceof Node) else if (obj instanceof Node)
createViewForNode(obj); createViewForNode(obj);
previewObject = obj;
} }
function createViewForMaterial(material) function createViewForMaterial(material)
@@ -70,6 +66,8 @@ Item {
// Always recreate the view to ensure material is up to date // Always recreate the view to ensure material is up to date
if (materialViewComponent.status === Component.Ready) if (materialViewComponent.status === Component.Ready)
view = materialViewComponent.createObject(viewRect, {"previewMaterial": material}); view = materialViewComponent.createObject(viewRect, {"previewMaterial": material});
previewObject = material;
} }
function createViewForModel(model) function createViewForModel(model)
@@ -80,6 +78,8 @@ Item {
// Always recreate the view to ensure model is up to date // Always recreate the view to ensure model is up to date
if (modelViewComponent.status === Component.Ready) if (modelViewComponent.status === Component.Ready)
view = modelViewComponent.createObject(viewRect, {"sourceModel": model}); view = modelViewComponent.createObject(viewRect, {"sourceModel": model});
previewObject = model;
} }
function createViewForNode(node) function createViewForNode(node)
@@ -90,16 +90,13 @@ Item {
// Always recreate the view to ensure node is up to date // Always recreate the view to ensure node is up to date
if (nodeViewComponent.status === Component.Ready) if (nodeViewComponent.status === Component.Ready)
view = nodeViewComponent.createObject(viewRect, {"importScene": node}); view = nodeViewComponent.createObject(viewRect, {"importScene": node});
previewObject = node;
} }
function afterRender() function fitToViewPort()
{ {
if (previewObject instanceof Node) { view.fitToViewPort();
view.fitToViewPort();
ready = view.ready;
} else {
ready = true;
}
} }
View3D { View3D {

View File

@@ -32,24 +32,13 @@ View3D {
environment: sceneEnv environment: sceneEnv
camera: theCamera camera: theCamera
property bool ready: false
property real prevZoomFactor: -1
property Model sourceModel property Model sourceModel
function fitToViewPort() function fitToViewPort()
{ {
cameraControl.focusObject(model, theCamera.eulerRotation, true, false); // The magic number is the distance from camera default pos to origin
_generalHelper.calculateNodeBoundsAndFocusCamera(theCamera, importScene, root,
if (cameraControl._zoomFactor < 0.1) { 1040);
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;
}
} }
SceneEnvironment { SceneEnvironment {
@@ -58,14 +47,6 @@ View3D {
antialiasingQuality: SceneEnvironment.High antialiasingQuality: SceneEnvironment.High
} }
EditCameraController {
id: cameraControl
camera: theCamera
anchors.fill: parent
view3d: root
ignoreToolState: true
}
DirectionalLight { DirectionalLight {
eulerRotation.x: -30 eulerRotation.x: -30
eulerRotation.y: -30 eulerRotation.y: -30
@@ -75,15 +56,15 @@ View3D {
id: theCamera id: theCamera
z: 600 z: 600
y: 600 y: 600
x: 600
eulerRotation.x: -45 eulerRotation.x: -45
eulerRotation.y: -45
clipFar: 10000 clipFar: 10000
clipNear: 1 clipNear: 1
} }
Model { Model {
id: model id: model
eulerRotation.y: 45
source: sourceModel.source source: sourceModel.source
geometry: sourceModel.geometry geometry: sourceModel.geometry

View File

@@ -32,30 +32,11 @@ View3D {
environment: sceneEnv environment: sceneEnv
camera: theCamera camera: theCamera
property bool ready: false
property bool first: true
property real prevZoomFactor: -1
function fitToViewPort() function fitToViewPort()
{ {
if (first) { // The magic number is the distance from camera default pos to origin
first = false; _generalHelper.calculateNodeBoundsAndFocusCamera(theCamera, importScene, root,
selectionBox.targetNode = root.importScene; 1040);
} 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;
}
}
} }
SceneEnvironment { SceneEnvironment {
@@ -64,20 +45,6 @@ View3D {
antialiasingQuality: SceneEnvironment.High antialiasingQuality: SceneEnvironment.High
} }
SelectionBox {
id: selectionBox
view3D: root
geometryName: "NodeNodeViewSB"
}
EditCameraController {
id: cameraControl
camera: theCamera
anchors.fill: parent
view3d: root
ignoreToolState: true
}
DirectionalLight { DirectionalLight {
eulerRotation.x: -30 eulerRotation.x: -30
eulerRotation.y: -30 eulerRotation.y: -30

View File

@@ -32,6 +32,11 @@ View3D {
property Material previewMaterial property Material previewMaterial
function fitToViewPort()
{
// No need to zoom this view, this is here just to avoid runtime warnings
}
SceneEnvironment { SceneEnvironment {
id: sceneEnv id: sceneEnv
antialiasingMode: SceneEnvironment.MSAA antialiasingMode: SceneEnvironment.MSAA

View File

@@ -41,8 +41,6 @@ Item {
property var modelViewComponent property var modelViewComponent
property var nodeViewComponent property var nodeViewComponent
property bool ready: false
function destroyView() function destroyView()
{ {
previewObject = null; previewObject = null;
@@ -58,8 +56,6 @@ Item {
createViewForModel(obj); createViewForModel(obj);
else if (obj instanceof Node) else if (obj instanceof Node)
createViewForNode(obj); createViewForNode(obj);
previewObject = obj;
} }
function createViewForMaterial(material) function createViewForMaterial(material)
@@ -70,6 +66,8 @@ Item {
// Always recreate the view to ensure material is up to date // Always recreate the view to ensure material is up to date
if (materialViewComponent.status === Component.Ready) if (materialViewComponent.status === Component.Ready)
view = materialViewComponent.createObject(viewRect, {"previewMaterial": material}); view = materialViewComponent.createObject(viewRect, {"previewMaterial": material});
previewObject = material;
} }
function createViewForModel(model) function createViewForModel(model)
@@ -80,6 +78,8 @@ Item {
// Always recreate the view to ensure model is up to date // Always recreate the view to ensure model is up to date
if (modelViewComponent.status === Component.Ready) if (modelViewComponent.status === Component.Ready)
view = modelViewComponent.createObject(viewRect, {"sourceModel": model}); view = modelViewComponent.createObject(viewRect, {"sourceModel": model});
previewObject = model;
} }
function createViewForNode(node) function createViewForNode(node)
@@ -90,16 +90,13 @@ Item {
// Always recreate the view to ensure node is up to date // Always recreate the view to ensure node is up to date
if (nodeViewComponent.status === Component.Ready) if (nodeViewComponent.status === Component.Ready)
view = nodeViewComponent.createObject(viewRect, {"importScene": node}); view = nodeViewComponent.createObject(viewRect, {"importScene": node});
previewObject = node;
} }
function afterRender() function fitToViewPort()
{ {
if (previewObject instanceof Node) { view.fitToViewPort();
view.fitToViewPort();
ready = view.ready;
} else {
ready = true;
}
} }
Item { Item {

View File

@@ -32,24 +32,13 @@ View3D {
environment: sceneEnv environment: sceneEnv
camera: theCamera camera: theCamera
property bool ready: false
property real prevZoomFactor: -1
property Model sourceModel property Model sourceModel
function fitToViewPort() function fitToViewPort()
{ {
cameraControl.focusObject(model, theCamera.eulerRotation, true, false); // The magic number is the distance from camera default pos to origin
_generalHelper.calculateNodeBoundsAndFocusCamera(theCamera, importScene, root,
if (cameraControl._zoomFactor < 0.1) { 1040);
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;
}
} }
SceneEnvironment { SceneEnvironment {
@@ -58,14 +47,6 @@ View3D {
antialiasingQuality: SceneEnvironment.High antialiasingQuality: SceneEnvironment.High
} }
EditCameraController {
id: cameraControl
camera: theCamera
anchors.fill: parent
view3d: root
ignoreToolState: true
}
DirectionalLight { DirectionalLight {
eulerRotation.x: -30 eulerRotation.x: -30
eulerRotation.y: -30 eulerRotation.y: -30
@@ -75,16 +56,15 @@ View3D {
id: theCamera id: theCamera
z: 600 z: 600
y: 600 y: 600
x: 600
eulerRotation.x: -45 eulerRotation.x: -45
eulerRotation.y: -45
clipFar: 10000 clipFar: 10000
clipNear: 1 clipNear: 1
} }
Model { Model {
id: model id: model
readonly property bool _edit3dLocked: true // Make this non-pickable
eulerRotation.y: 45
source: sourceModel.source source: sourceModel.source
geometry: sourceModel.geometry geometry: sourceModel.geometry

View File

@@ -32,30 +32,11 @@ View3D {
environment: sceneEnv environment: sceneEnv
camera: theCamera camera: theCamera
property bool ready: false
property bool first: true
property real prevZoomFactor: -1
function fitToViewPort() function fitToViewPort()
{ {
if (first) { // The magic number is the distance from camera default pos to origin
first = false; _generalHelper.calculateNodeBoundsAndFocusCamera(theCamera, importScene, root,
selectionBox.targetNode = root.importScene; 1040);
} 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;
}
}
} }
SceneEnvironment { SceneEnvironment {
@@ -64,20 +45,6 @@ View3D {
antialiasingQuality: SceneEnvironment.High antialiasingQuality: SceneEnvironment.High
} }
SelectionBox {
id: selectionBox
view3D: root
geometryName: "NodeNodeViewSB"
}
EditCameraController {
id: cameraControl
camera: theCamera
anchors.fill: parent
view3d: root
ignoreToolState: true
}
DirectionalLight { DirectionalLight {
eulerRotation.x: -30 eulerRotation.x: -30
eulerRotation.y: -30 eulerRotation.y: -30

View File

@@ -55,6 +55,11 @@ const QString _globalStateId = QStringLiteral("@GTS"); // global tool state
const QString _lastSceneIdKey = QStringLiteral("lastSceneId"); const QString _lastSceneIdKey = QStringLiteral("lastSceneId");
const QString _rootSizeKey = QStringLiteral("rootSize"); const QString _rootSizeKey = QStringLiteral("rootSize");
static const float floatMin = std::numeric_limits<float>::lowest();
static const float floatMax = std::numeric_limits<float>::max();
static const QVector3D maxVec = QVector3D(floatMax, floatMax, floatMax);
static const QVector3D minVec = QVector3D(floatMin, floatMin, floatMin);
GeneralHelper::GeneralHelper() GeneralHelper::GeneralHelper()
: QObject() : QObject()
{ {
@@ -269,6 +274,37 @@ QVector4D GeneralHelper::focusNodesToCamera(QQuick3DCamera *camera, float defaul
return QVector4D(lookAt, cameraZoomFactor); 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. // Aligns any cameras found in nodes list to a camera.
// Only position and rotation are copied, rest of the camera properties stay the same. // Only position and rotation are copied, rest of the camera properties stay the same.
void GeneralHelper::alignCameras(QQuick3DCamera *camera, const QVariant &nodes) void GeneralHelper::alignCameras(QQuick3DCamera *camera, const QVariant &nodes)
@@ -727,6 +763,129 @@ QVector3D GeneralHelper::pivotScenePosition(QQuick3DNode *node) const
return mat44::getPosition(sceneTransform); 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<QSSGRenderNode *>(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<QVector3D> minBoundsVec;
QVector<QVector3D> maxBoundsVec;
const auto children = node->childItems();
bool hasModel = false;
for (const auto child : children) {
if (auto childNode = qobject_cast<QQuick3DNode *>(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<QQuick3DModel *>(node)) {
if (auto renderModel = static_cast<QSSGRenderModel *>(renderNode)) {
QWindow *window = static_cast<QWindow *>(view3D->window());
if (window) {
QSSGRef<QSSGRenderContextInterface> 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;
}
} }
} }

View File

@@ -72,6 +72,9 @@ public:
const QVariant &nodes, QQuick3DViewport *viewPort, const QVariant &nodes, QQuick3DViewport *viewPort,
float oldZoom, bool updateZoom = true, float oldZoom, bool updateZoom = true,
bool closeUp = false); 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 void alignCameras(QQuick3DCamera *camera, const QVariant &nodes);
Q_INVOKABLE QVector3D alignView(QQuick3DCamera *camera, const QVariant &nodes, Q_INVOKABLE QVector3D alignView(QQuick3DCamera *camera, const QVariant &nodes,
const QVector3D &lookAtPoint); const QVector3D &lookAtPoint);
@@ -126,6 +129,8 @@ protected:
private: private:
void handlePendingToolStateUpdate(); void handlePendingToolStateUpdate();
QVector3D pivotScenePosition(QQuick3DNode *node) const; QVector3D pivotScenePosition(QQuick3DNode *node) const;
bool getBounds(QQuick3DViewport *view3D, QQuick3DNode *node, QVector3D &minBounds,
QVector3D &maxBounds, bool recursive = false);
QTimer m_overlayUpdateTimer; QTimer m_overlayUpdateTimer;
QTimer m_toolStateUpdateTimer; QTimer m_toolStateUpdateTimer;

View File

@@ -1588,6 +1588,11 @@ bool NodeInstanceServer::isInformationServer() const
return false; return false;
} }
bool NodeInstanceServer::isPreviewServer() const
{
return false;
}
static QString baseProperty(const QString &property) static QString baseProperty(const QString &property)
{ {
int index = property.indexOf('.'); int index = property.indexOf('.');

View File

@@ -229,8 +229,10 @@ public:
virtual QImage grabWindow() = 0; virtual QImage grabWindow() = 0;
virtual QImage grabItem(QQuickItem *item) = 0; virtual QImage grabItem(QQuickItem *item) = 0;
virtual bool renderWindow() = 0;
virtual bool isInformationServer() const; virtual bool isInformationServer() const;
virtual bool isPreviewServer() const;
void addAnimation(QQuickAbstractAnimation *animation); void addAnimation(QQuickAbstractAnimation *animation);
QVector<QQuickAbstractAnimation *> animations() const; QVector<QQuickAbstractAnimation *> animations() const;
QVariant animationDefaultValue(int index) const; QVariant animationDefaultValue(int index) const;

View File

@@ -180,6 +180,11 @@ bool ObjectNodeInstance::isLayoutable() const
return false; return false;
} }
bool ObjectNodeInstance::isRenderable() const
{
return false;
}
bool ObjectNodeInstance::equalGraphicsItem(QGraphicsItem * /*item*/) const bool ObjectNodeInstance::equalGraphicsItem(QGraphicsItem * /*item*/) const
{ {
return false; return false;

View File

@@ -103,6 +103,7 @@ public:
virtual bool isQuickItem() const; virtual bool isQuickItem() const;
virtual bool isQuickWindow() const; virtual bool isQuickWindow() const;
virtual bool isLayoutable() const; virtual bool isLayoutable() const;
virtual bool isRenderable() const;
virtual bool equalGraphicsItem(QGraphicsItem *item) const; virtual bool equalGraphicsItem(QGraphicsItem *item) const;

View File

@@ -44,10 +44,10 @@ QImage renderImage(ServerNodeInstance rootNodeInstance)
QSize previewImageSize = rootNodeInstance.boundingRect().size().toSize(); QSize previewImageSize = rootNodeInstance.boundingRect().size().toSize();
if (previewImageSize.isEmpty()) if (previewImageSize.isEmpty())
previewImageSize = {300, 300}; previewImageSize = {150, 150};
if (previewImageSize.width() > 300 || previewImageSize.height() > 300) if (previewImageSize.width() > 150 || previewImageSize.height() > 150)
previewImageSize.scale({300, 300}, Qt::KeepAspectRatio); previewImageSize.scale({150, 150}, Qt::KeepAspectRatio);
QImage previewImage = rootNodeInstance.renderPreviewImage(previewImageSize); QImage previewImage = rootNodeInstance.renderPreviewImage(previewImageSize);
@@ -68,7 +68,8 @@ void Qt5CaptureImageNodeInstanceServer::collectItemChangesAndSendChangeCommands(
inFunction = true; inFunction = true;
auto rooNodeInstance = rootNodeInstance(); auto rooNodeInstance = rootNodeInstance();
rooNodeInstance.rootQuickItem()->setClip(true); if (QQuickItem *qitem = rooNodeInstance.rootQuickItem())
qitem->setClip(true);
DesignerSupport::polishItems(quickWindow()); DesignerSupport::polishItems(quickWindow());

View File

@@ -1007,17 +1007,17 @@ void Qt5InformationNodeInstanceServer::doRenderModelNodeImageView()
void Qt5InformationNodeInstanceServer::doRenderModelNode3DImageView() void Qt5InformationNodeInstanceServer::doRenderModelNode3DImageView()
{ {
#ifdef QUICK3D_MODULE #ifdef QUICK3D_MODULE
m_modelNode3DImageViewAsyncData.cleanup();
if (m_modelNode3DImageViewData.rootItem) { if (m_modelNode3DImageViewData.rootItem) {
QMetaObject::invokeMethod(m_modelNode3DImageViewData.rootItem, "destroyView"); QMetaObject::invokeMethod(m_modelNode3DImageViewData.rootItem, "destroyView");
if (!m_modelNode3DImageViewData.contentItem) if (!m_modelNode3DImageViewData.contentItem)
m_modelNode3DImageViewData.contentItem = getContentItemForRendering(m_modelNode3DImageViewData.rootItem); m_modelNode3DImageViewData.contentItem = getContentItemForRendering(m_modelNode3DImageViewData.rootItem);
QImage renderImage;
if (m_modelNodePreviewImageCache.contains(m_modelNodePreviewImageCommand.componentPath())) { if (m_modelNodePreviewImageCache.contains(m_modelNodePreviewImageCommand.componentPath())) {
m_modelNode3DImageViewAsyncData.renderImage renderImage = m_modelNodePreviewImageCache[m_modelNodePreviewImageCommand.componentPath()];
= m_modelNodePreviewImageCache[m_modelNodePreviewImageCommand.componentPath()];
modelNode3DImageViewSendImageToCreator();
} else { } else {
bool createdFromComponent = false;
QObject *instanceObj = nullptr;
ServerNodeInstance instance = instanceForId(m_modelNodePreviewImageCommand.instanceId()); ServerNodeInstance instance = instanceForId(m_modelNodePreviewImageCommand.instanceId());
if (!m_modelNodePreviewImageCommand.componentPath().isEmpty() if (!m_modelNodePreviewImageCommand.componentPath().isEmpty()
&& instance.isSubclassOf("QQuick3DNode")) { && instance.isSubclassOf("QQuick3DNode")) {
@@ -1026,15 +1026,14 @@ void Qt5InformationNodeInstanceServer::doRenderModelNode3DImageView()
// wouldn't want the children of the Node to appear in the preview. // wouldn't want the children of the Node to appear in the preview.
QQmlComponent component(engine()); QQmlComponent component(engine());
component.loadUrl(QUrl::fromLocalFile(m_modelNodePreviewImageCommand.componentPath())); component.loadUrl(QUrl::fromLocalFile(m_modelNodePreviewImageCommand.componentPath()));
m_modelNode3DImageViewAsyncData.instanceObj = qobject_cast<QQuick3DObject *>(component.create()); instanceObj = qobject_cast<QQuick3DObject *>(component.create());
if (!m_modelNode3DImageViewAsyncData.instanceObj) { if (!instanceObj) {
qWarning() << "Could not create preview component: " << component.errors(); qWarning() << "Could not create preview component: " << component.errors();
m_modelNode3DImageViewAsyncData.cleanup();
return; return;
} }
m_modelNode3DImageViewAsyncData.createdFromComponent = true; createdFromComponent = true;
} else { } else {
m_modelNode3DImageViewAsyncData.instanceObj = instance.internalObject(); instanceObj = instance.internalObject();
} }
QSize renderSize = m_modelNodePreviewImageCommand.size(); QSize renderSize = m_modelNodePreviewImageCommand.size();
if (Internal::QuickItemNodeInstance::unifiedRenderPathOrQt6()) { if (Internal::QuickItemNodeInstance::unifiedRenderPathOrQt6()) {
@@ -1055,69 +1054,53 @@ void Qt5InformationNodeInstanceServer::doRenderModelNode3DImageView()
QMetaObject::invokeMethod( QMetaObject::invokeMethod(
m_modelNode3DImageViewData.rootItem, "createViewForObject", 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 // Need to render twice, first render updates spatial nodes
// in asynchronous steps as well, since we are adjusting the selection box geometry for (int i = 0; i < 2; ++i) {
// while finding correct zoom level. if (i == 1)
m_modelNode3DImageViewAsyncData.timer.start(); 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<QQuickItem *>(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 #endif
} }
void Qt5InformationNodeInstanceServer::modelNode3DImageViewSendImageToCreator() QMetaObject::invokeMethod(m_modelNode3DImageViewData.rootItem, "destroyView");
{
if (!m_modelNode3DImageViewAsyncData.renderImage.isNull()) { 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. // Key number is selected so that it is unlikely to conflict other ImageContainer use.
ImageContainer imgContainer(m_modelNodePreviewImageCommand.instanceId(), {}, 2100000001); ImageContainer imgContainer(m_modelNodePreviewImageCommand.instanceId(), {}, 2100000001);
imgContainer.setImage(m_modelNode3DImageViewAsyncData.renderImage); imgContainer.setImage(renderImage);
// send the rendered image to creator process // send the rendered image to creator process
nodeInstanceClient()->handlePuppetToCreatorCommand( nodeInstanceClient()->handlePuppetToCreatorCommand(
{PuppetToCreatorCommand::RenderModelNodePreviewImage, {PuppetToCreatorCommand::RenderModelNodePreviewImage,
QVariant::fromValue(imgContainer)}); 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<QQuickItem *>(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 #endif
QMetaObject::invokeMethod(m_modelNode3DImageViewData.rootItem, "afterRender");
const bool ready = QQmlProperty::read(m_modelNode3DImageViewData.rootItem, "ready").value<bool>();
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) static QRectF itemBoundingRect(QQuickItem *item)
@@ -1234,7 +1217,6 @@ Qt5InformationNodeInstanceServer::Qt5InformationNodeInstanceServer(NodeInstanceC
m_render3DEditViewTimer.setSingleShot(true); m_render3DEditViewTimer.setSingleShot(true);
m_inputEventTimer.setSingleShot(true); m_inputEventTimer.setSingleShot(true);
m_renderModelNodeImageViewTimer.setSingleShot(true); m_renderModelNodeImageViewTimer.setSingleShot(true);
m_modelNode3DImageViewAsyncData.timer.setSingleShot(true);
m_dynamicAddObjectTimer.setSingleShot(true); m_dynamicAddObjectTimer.setSingleShot(true);
#ifdef FPS_COUNTER #ifdef FPS_COUNTER
@@ -1260,7 +1242,6 @@ Qt5InformationNodeInstanceServer::~Qt5InformationNodeInstanceServer()
m_render3DEditViewTimer.stop(); m_render3DEditViewTimer.stop();
m_inputEventTimer.stop(); m_inputEventTimer.stop();
m_renderModelNodeImageViewTimer.stop(); m_renderModelNodeImageViewTimer.stop();
m_modelNode3DImageViewAsyncData.timer.stop();
m_dynamicAddObjectTimer.stop(); m_dynamicAddObjectTimer.stop();
if (m_editView3DData.rootItem) if (m_editView3DData.rootItem)
@@ -1677,8 +1658,6 @@ void Qt5InformationNodeInstanceServer::setup3DEditView(const QList<ServerNodeIns
this, &Qt5InformationNodeInstanceServer::doRender3DEditView); this, &Qt5InformationNodeInstanceServer::doRender3DEditView);
QObject::connect(&m_inputEventTimer, &QTimer::timeout, QObject::connect(&m_inputEventTimer, &QTimer::timeout,
this, &Qt5InformationNodeInstanceServer::handleInputEvents); this, &Qt5InformationNodeInstanceServer::handleInputEvents);
QObject::connect(&m_modelNode3DImageViewAsyncData.timer, &QTimer::timeout,
this, &Qt5InformationNodeInstanceServer::modelNode3DImageViewRenderStep);
QObject::connect(&m_dynamicAddObjectTimer, &QTimer::timeout, QObject::connect(&m_dynamicAddObjectTimer, &QTimer::timeout,
this, &Qt5InformationNodeInstanceServer::handleDynamicAddObjectTimeout); this, &Qt5InformationNodeInstanceServer::handleDynamicAddObjectTimeout);
@@ -2165,7 +2144,6 @@ void Qt5InformationNodeInstanceServer::view3DAction(const View3DActionCommand &c
void Qt5InformationNodeInstanceServer::requestModelNodePreviewImage(const RequestModelNodePreviewImageCommand &command) void Qt5InformationNodeInstanceServer::requestModelNodePreviewImage(const RequestModelNodePreviewImageCommand &command)
{ {
m_modelNode3DImageViewAsyncData.timer.stop();
m_modelNodePreviewImageCommand = command; m_modelNodePreviewImageCommand = command;
renderModelNodeImageView(); renderModelNodeImageView();
} }

View File

@@ -140,8 +140,6 @@ private:
void renderModelNodeImageView(); void renderModelNodeImageView();
void doRenderModelNodeImageView(); void doRenderModelNodeImageView();
void doRenderModelNode3DImageView(); void doRenderModelNode3DImageView();
void modelNode3DImageViewSendImageToCreator();
void modelNode3DImageViewRenderStep();
void doRenderModelNode2DImageView(); void doRenderModelNode2DImageView();
void updateLockedAndHiddenStates(const QSet<ServerNodeInstance> &instances); void updateLockedAndHiddenStates(const QSet<ServerNodeInstance> &instances);
void handleInputEvents(); void handleInputEvents();
@@ -190,26 +188,6 @@ private:
QObject *m_3dHelper = nullptr; QObject *m_3dHelper = nullptr;
int m_need3DEditViewRender = 0; int m_need3DEditViewRender = 0;
QSet<QObject *> m_dynamicObjectConstructors; QSet<QObject *> 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 } // namespace QmlDesigner

View File

@@ -70,6 +70,7 @@ public:
QImage grabWindow() override; QImage grabWindow() override;
QImage grabItem(QQuickItem *item) override; QImage grabItem(QQuickItem *item) override;
bool renderWindow() override;
static QQuickItem *parentEffectItem(QQuickItem *item); static QQuickItem *parentEffectItem(QQuickItem *item);
@@ -97,7 +98,6 @@ protected:
virtual bool initRhi(RenderViewData &viewData); virtual bool initRhi(RenderViewData &viewData);
virtual QImage grabRenderControl(RenderViewData &viewData); virtual QImage grabRenderControl(RenderViewData &viewData);
virtual bool renderWindow();
private: private:
RenderViewData m_viewData; RenderViewData m_viewData;

View File

@@ -133,4 +133,9 @@ void Qt5PreviewNodeInstanceServer::changePreviewImageSize(
collectItemChangesAndSendChangeCommands(); collectItemChangesAndSendChangeCommands();
} }
bool Qt5PreviewNodeInstanceServer::isPreviewServer() const
{
return true;
}
} // namespace QmlDesigner } // namespace QmlDesigner

View File

@@ -39,6 +39,7 @@ public:
void changeState(const ChangeStateCommand &command) override; void changeState(const ChangeStateCommand &command) override;
void removeSharedMemory(const RemoveSharedMemoryCommand &command) override; void removeSharedMemory(const RemoveSharedMemoryCommand &command) override;
void changePreviewImageSize(const ChangePreviewImageSizeCommand &command) override; void changePreviewImageSize(const ChangePreviewImageSizeCommand &command) override;
bool isPreviewServer() const override;
QImage renderPreviewImage(); QImage renderPreviewImage();

View File

@@ -26,6 +26,8 @@
#include "quick3dnodeinstance.h" #include "quick3dnodeinstance.h"
#include "qt5nodeinstanceserver.h" #include "qt5nodeinstanceserver.h"
#include "qt5informationnodeinstanceserver.h" #include "qt5informationnodeinstanceserver.h"
#include "quickitemnodeinstance.h"
#include "../editor3d/generalhelper.h"
#include <qmlprivategate.h> #include <qmlprivategate.h>
@@ -37,6 +39,7 @@
#include <cmath> #include <cmath>
#ifdef QUICK3D_MODULE #ifdef QUICK3D_MODULE
#include <private/qquick3dobject_p.h>
#include <private/qquick3dnode_p.h> #include <private/qquick3dnode_p.h>
#include <private/qquick3dmodel_p.h> #include <private/qquick3dmodel_p.h>
#include <private/qquick3dnode_p_p.h> #include <private/qquick3dnode_p_p.h>
@@ -45,8 +48,10 @@
#if defined(QUICK3D_ASSET_UTILS_MODULE) && QT_VERSION > QT_VERSION_CHECK(6, 2, 0) #if defined(QUICK3D_ASSET_UTILS_MODULE) && QT_VERSION > QT_VERSION_CHECK(6, 2, 0)
#include <private/qquick3druntimeloader_p.h> #include <private/qquick3druntimeloader_p.h>
#endif #endif
#include <private/qquickstategroup_p.h>
#endif #endif
namespace QmlDesigner { namespace QmlDesigner {
namespace Internal { namespace Internal {
@@ -57,6 +62,7 @@ Quick3DNodeInstance::Quick3DNodeInstance(QObject *node)
Quick3DNodeInstance::~Quick3DNodeInstance() Quick3DNodeInstance::~Quick3DNodeInstance()
{ {
delete m_dummyRootView;
} }
void Quick3DNodeInstance::initialize(const ObjectNodeInstance::Pointer &objectNodeInstance, 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<QQuickItem *>(component.create());
QMetaObject::invokeMethod(
m_dummyRootView, "createViewForNode",
Q_ARG(QVariant, QVariant::fromValue(object())));
nodeInstanceServer()->setRootItem(m_dummyRootView);
}
#endif
#endif #endif
ObjectNodeInstance::initialize(objectNodeInstance, flags); 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<ServerNodeInstance> Quick3DNodeInstance::stateInstances() const
{
QList<ServerNodeInstance> instanceList;
#ifdef QUICK3D_MODULE
if (auto obj3D = quick3DNode()) {
const QList<QQuickState *> 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 Qt5NodeInstanceServer *Quick3DNodeInstance::qt5NodeInstanceServer() const
{ {
return qobject_cast<Qt5NodeInstanceServer *>(nodeInstanceServer()); return qobject_cast<Qt5NodeInstanceServer *>(nodeInstanceServer());

View File

@@ -47,12 +47,22 @@ public:
void initialize(const ObjectNodeInstance::Pointer &objectNodeInstance, void initialize(const ObjectNodeInstance::Pointer &objectNodeInstance,
InstanceContainer::NodeFlags flags) override; InstanceContainer::NodeFlags flags) override;
QImage renderImage() const override;
QImage renderPreviewImage(const QSize &previewImageSize) const override;
bool isRenderable() const override;
QRectF boundingRect() const override;
QList<ServerNodeInstance> stateInstances() const override;
protected: protected:
explicit Quick3DNodeInstance(QObject *node); explicit Quick3DNodeInstance(QObject *node);
private: private:
Qt5NodeInstanceServer *qt5NodeInstanceServer() const; Qt5NodeInstanceServer *qt5NodeInstanceServer() const;
QQuick3DNode *quick3DNode() const; QQuick3DNode *quick3DNode() const;
QQuickItem *m_dummyRootView = nullptr;
}; };
} // namespace Internal } // namespace Internal

View File

@@ -571,6 +571,11 @@ bool QuickItemNodeInstance::isQuickItem() const
return true; return true;
} }
bool QuickItemNodeInstance::isRenderable() const
{
return quickItem() && (!s_unifiedRenderPath || isRootNodeInstance());
}
QList<ServerNodeInstance> QuickItemNodeInstance::stateInstances() const QList<ServerNodeInstance> QuickItemNodeInstance::stateInstances() const
{ {
QList<ServerNodeInstance> instanceList; QList<ServerNodeInstance> instanceList;

View File

@@ -94,6 +94,7 @@ public:
bool isResizable() const override; bool isResizable() const override;
bool isMovable() const override; bool isMovable() const override;
bool isQuickItem() const override; bool isQuickItem() const override;
bool isRenderable() const override;
QList<ServerNodeInstance> stateInstances() const override; QList<ServerNodeInstance> stateInstances() const override;

View File

@@ -154,7 +154,7 @@ void ServerNodeInstance::setNodeSource(const QString &source)
bool ServerNodeInstance::holdsGraphical() const bool ServerNodeInstance::holdsGraphical() const
{ {
return m_nodeInstance->isQuickItem(); return m_nodeInstance->isRenderable();
} }
bool ServerNodeInstance::isComponentWrap() const bool ServerNodeInstance::isComponentWrap() const

View File

@@ -48,7 +48,8 @@ QString ItemLibraryItem::typeName() const
QString ItemLibraryItem::itemLibraryIconPath() const QString ItemLibraryItem::itemLibraryIconPath() const
{ {
if (m_itemLibraryEntry.customComponentSource().isEmpty()) { if (m_itemLibraryEntry.customComponentSource().isEmpty()
|| !m_itemLibraryEntry.libraryEntryIconPath().isEmpty()) {
return QStringLiteral("image://qmldesigner_itemlibrary/") return QStringLiteral("image://qmldesigner_itemlibrary/")
+ m_itemLibraryEntry.libraryEntryIconPath(); + m_itemLibraryEntry.libraryEntryIconPath();
} else { } else {

View File

@@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>651</width> <width>517</width>
<height>318</height> <height>166</height>
</rect> </rect>
</property> </property>
<property name="sizePolicy"> <property name="sizePolicy">
@@ -90,8 +90,8 @@
</property> </property>
<property name="minimumSize"> <property name="minimumSize">
<size> <size>
<width>300</width> <width>150</width>
<height>300</height> <height>150</height>
</size> </size>
</property> </property>
<property name="frameShape"> <property name="frameShape">
@@ -163,7 +163,7 @@
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred"> <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch> <horstretch>0</horstretch>
<verstretch>1</verstretch> <verstretch>3</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
<property name="minimumSize"> <property name="minimumSize">

View File

@@ -90,7 +90,8 @@ void ImageCacheCollector::start(Utils::SmallStringView name,
model->setRewriterView(&rewriterView); model->setRewriterView(&rewriterView);
if (rewriterView.inErrorState() || !rewriterView.rootModelNode().metaInfo().isGraphicalItem()) { if (rewriterView.inErrorState() || (!rewriterView.rootModelNode().metaInfo().isGraphicalItem()
&& !rewriterView.rootModelNode().isSubclassOf("Quick3D.Node") )) {
if (abortCallback) if (abortCallback)
abortCallback(ImageCache::AbortReason::Failed); abortCallback(ImageCache::AbortReason::Failed);
return; return;

View File

@@ -420,6 +420,7 @@ void SubComponentManager::parseQuick3DAssetsItem(const QString &importUrl, const
itemLibraryEntry.setType(type.toUtf8(), 1, 0); itemLibraryEntry.setType(type.toUtf8(), 1, 0);
itemLibraryEntry.setName(name); itemLibraryEntry.setName(name);
itemLibraryEntry.setCategory(::QmlDesigner::SubComponentManager::tr("My 3D Components")); itemLibraryEntry.setCategory(::QmlDesigner::SubComponentManager::tr("My 3D Components"));
itemLibraryEntry.setCustomComponentSource(qmlIt.fileInfo().absoluteFilePath());
itemLibraryEntry.setRequiredImport(importUrl); itemLibraryEntry.setRequiredImport(importUrl);
QString iconPath = qmlIt.fileInfo().absolutePath() + '/' QString iconPath = qmlIt.fileInfo().absolutePath() + '/'
+ Constants::QUICK_3D_ASSET_ICON_DIR + '/' + name + Constants::QUICK_3D_ASSET_ICON_DIR + '/' + name