diff --git a/share/qtcreator/qml/qmlpuppet/commands/view3dactioncommand.h b/share/qtcreator/qml/qmlpuppet/commands/view3dactioncommand.h
index cb23b2c9ad5..cb34c253f9e 100644
--- a/share/qtcreator/qml/qmlpuppet/commands/view3dactioncommand.h
+++ b/share/qtcreator/qml/qmlpuppet/commands/view3dactioncommand.h
@@ -50,6 +50,7 @@ public:
ShowSelectionBox,
ShowIconGizmo,
ShowCameraFrustum,
+ ShowParticleEmitter,
Edit3DParticleModeToggle,
ParticlesPlay,
ParticlesRestart,
diff --git a/share/qtcreator/qml/qmlpuppet/editor3d_qt6.qrc b/share/qtcreator/qml/qmlpuppet/editor3d_qt6.qrc
index 065f9e32cf3..9831bec4be2 100644
--- a/share/qtcreator/qml/qmlpuppet/editor3d_qt6.qrc
+++ b/share/qtcreator/qml/qmlpuppet/editor3d_qt6.qrc
@@ -35,7 +35,6 @@
mockfiles/qt6/LightGizmo.qml
mockfiles/qt6/LightIconGizmo.qml
mockfiles/qt6/LightModel.qml
- mockfiles/qt6/ParticleSystemGizmo.qml
mockfiles/qt6/Line3D.qml
mockfiles/qt6/MaterialNodeView.qml
mockfiles/qt6/ModelNode2DImageView.qml
@@ -44,6 +43,8 @@
mockfiles/qt6/MoveGizmo.qml
mockfiles/qt6/NodeNodeView.qml
mockfiles/qt6/Overlay2D.qml
+ mockfiles/qt6/ParticleSystemGizmo.qml
+ mockfiles/qt6/ParticleEmitterGizmo.qml
mockfiles/qt6/PlanarDraggable.qml
mockfiles/qt6/PlanarMoveHandle.qml
mockfiles/qt6/PlanarScaleHandle.qml
diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/EditView3D.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/EditView3D.qml
index eb7d50d2e4f..a8425fd6cb7 100644
--- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/EditView3D.qml
+++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/EditView3D.qml
@@ -42,6 +42,7 @@ Item {
property bool showSelectionBox: true
property bool showIconGizmo: true
property bool showCameraFrustum: false
+ property bool showParticleEmitter: false
property bool usePerspective: true
property bool globalOrientation: false
property alias contentItem: contentItem
@@ -58,9 +59,10 @@ Item {
property var lightIconGizmos: []
property var cameraGizmos: []
property var particleSystemIconGizmos: []
+ property var particleEmitterGizmos: []
property var selectionBoxes: []
property rect viewPortRect: Qt.rect(0, 0, 1000, 1000)
-
+ property Node activeParticleSystem: null
property bool shuttingDown: false
property real fps: 0
@@ -70,15 +72,16 @@ Item {
signal changeObjectProperty(var objects, var propNames)
signal notifyActiveSceneChange()
- onUsePerspectiveChanged: _generalHelper.storeToolState(sceneId, "usePerspective", usePerspective)
- onShowEditLightChanged: _generalHelper.storeToolState(sceneId, "showEditLight", showEditLight)
- onGlobalOrientationChanged: _generalHelper.storeToolState(sceneId, "globalOrientation", globalOrientation)
- onShowGridChanged: _generalHelper.storeToolState(sceneId, "showGrid", showGrid);
- onShowSelectionBoxChanged: _generalHelper.storeToolState(sceneId, "showSelectionBox", showSelectionBox);
- onShowIconGizmoChanged: _generalHelper.storeToolState(sceneId, "showIconGizmo", showIconGizmo);
- onShowCameraFrustumChanged: _generalHelper.storeToolState(sceneId, "showCameraFrustum", showCameraFrustum);
- onSelectionModeChanged: _generalHelper.storeToolState(sceneId, "selectionMode", selectionMode);
- onTransformModeChanged: _generalHelper.storeToolState(sceneId, "transformMode", transformMode);
+ onUsePerspectiveChanged: _generalHelper.storeToolState(sceneId, "usePerspective", usePerspective)
+ onShowEditLightChanged: _generalHelper.storeToolState(sceneId, "showEditLight", showEditLight)
+ onGlobalOrientationChanged: _generalHelper.storeToolState(sceneId, "globalOrientation", globalOrientation)
+ onShowGridChanged: _generalHelper.storeToolState(sceneId, "showGrid", showGrid);
+ onShowSelectionBoxChanged: _generalHelper.storeToolState(sceneId, "showSelectionBox", showSelectionBox);
+ onShowIconGizmoChanged: _generalHelper.storeToolState(sceneId, "showIconGizmo", showIconGizmo);
+ onShowCameraFrustumChanged: _generalHelper.storeToolState(sceneId, "showCameraFrustum", showCameraFrustum);
+ onShowParticleEmitterChanged: _generalHelper.storeToolState(sceneId, "showParticleEmitter", showParticleEmitter);
+ onSelectionModeChanged: _generalHelper.storeToolState(sceneId, "selectionMode", selectionMode);
+ onTransformModeChanged: _generalHelper.storeToolState(sceneId, "transformMode", transformMode);
onActiveSceneChanged: updateActiveScene()
@@ -232,6 +235,11 @@ Item {
else if (resetToDefault)
showCameraFrustum = false;
+ if ("showParticleEmitter" in toolStates)
+ showParticleEmitter = toolStates.showParticleEmitter;
+ else if (resetToDefault)
+ showParticleEmitter = false;
+
if ("usePerspective" in toolStates)
usePerspective = toolStates.usePerspective;
else if (resetToDefault)
@@ -265,6 +273,7 @@ Item {
_generalHelper.storeToolState(sceneId, "showSelectionBox", showSelectionBox)
_generalHelper.storeToolState(sceneId, "showIconGizmo", showIconGizmo)
_generalHelper.storeToolState(sceneId, "showCameraFrustum", showCameraFrustum)
+ _generalHelper.storeToolState(sceneId, "showParticleEmitter", showParticleEmitter)
_generalHelper.storeToolState(sceneId, "usePerspective", usePerspective)
_generalHelper.storeToolState(sceneId, "globalOrientation", globalOrientation)
_generalHelper.storeToolState(sceneId, "selectionMode", selectionMode);
@@ -480,12 +489,56 @@ Item {
"activeScene": activeScene,
"locked": _generalHelper.isLocked(obj),
"hidden": _generalHelper.isHidden(obj),
- "globalShow": showIconGizmo});
+ "globalShow": showIconGizmo,
+ "activeParticleSystem": activeParticleSystem});
particleSystemIconGizmos[particleSystemIconGizmos.length] = gizmo;
gizmo.clicked.connect(handleObjectClicked);
gizmo.selectedNodes = Qt.binding(function() {return selectedNodes;});
gizmo.activeScene = Qt.binding(function() {return activeScene;});
gizmo.globalShow = Qt.binding(function() {return showIconGizmo;});
+ gizmo.activeParticleSystem = Qt.binding(function() {return activeParticleSystem;});
+ }
+ }
+
+ function addParticleEmitterGizmo(scene, obj)
+ {
+ // Insert into first available gizmo if we don't already have gizmo for this object
+ var slotFound = -1;
+ for (var i = 0; i < particleEmitterGizmos.length; ++i) {
+ if (!particleEmitterGizmos[i].targetNode) {
+ slotFound = i;
+ } else if (particleEmitterGizmos[i].targetNode === obj) {
+ particleEmitterGizmos[i].scene = scene;
+ return;
+ }
+ }
+
+ if (slotFound !== -1) {
+ particleEmitterGizmos[slotFound].scene = scene;
+ particleEmitterGizmos[slotFound].targetNode = obj;
+ particleEmitterGizmos[slotFound].hidden = _generalHelper.isHidden(obj);
+ particleEmitterGizmos[slotFound].systemHidden = _generalHelper.isHidden(obj.system);
+ _generalHelper.registerGizmoTarget(obj);
+ return;
+ }
+
+ // No free gizmos available, create a new one
+ var gizmoComponent = Qt.createComponent("ParticleEmitterGizmo.qml");
+ if (gizmoComponent.status === Component.Ready) {
+ _generalHelper.registerGizmoTarget(obj);
+ var gizmo = gizmoComponent.createObject(
+ overlayScene,
+ {"targetNode": obj, "selectedNodes": selectedNodes,
+ "activeParticleSystem": activeParticleSystem, "scene": scene,
+ "activeScene": activeScene, "hidden": _generalHelper.isHidden(obj),
+ "systemHidden": _generalHelper.isHidden(obj.system),
+ "globalShow": showParticleEmitter});
+
+ particleEmitterGizmos[particleEmitterGizmos.length] = gizmo;
+ gizmo.selectedNodes = Qt.binding(function() {return selectedNodes;});
+ gizmo.activeParticleSystem = Qt.binding(function() {return activeParticleSystem;});
+ gizmo.globalShow = Qt.binding(function() {return showParticleEmitter;});
+ gizmo.activeScene = Qt.binding(function() {return activeScene;});
}
}
@@ -525,6 +578,18 @@ Item {
}
}
+ function releaseParticleEmitterGizmo(obj)
+ {
+ for (var i = 0; i < particleEmitterGizmos.length; ++i) {
+ if (particleEmitterGizmos[i].targetNode === obj) {
+ particleEmitterGizmos[i].scene = null;
+ particleEmitterGizmos[i].targetNode = null;
+ _generalHelper.unregisterGizmoTarget(obj);
+ return;
+ }
+ }
+ }
+
function updateLightGizmoScene(scene, obj)
{
for (var i = 0; i < lightIconGizmos.length; ++i) {
@@ -555,6 +620,16 @@ Item {
}
}
+ function updateParticleEmitterGizmoScene(scene, obj)
+ {
+ for (var i = 0; i < particleEmitterGizmos.length; ++i) {
+ if (particleEmitterGizmos[i].targetNode === obj) {
+ particleEmitterGizmos[i].scene = scene;
+ return;
+ }
+ }
+ }
+
Component.onCompleted: {
createEditView();
selectObjects([]);
@@ -588,7 +663,12 @@ Item {
return;
}
}
-
+ for (var i = 0; i < particleEmitterGizmos.length; ++i) {
+ if (particleEmitterGizmos[i].targetNode === node) {
+ particleEmitterGizmos[i].locked = _generalHelper.isLocked(node);
+ return;
+ }
+ }
}
function onHiddenStateChanged(node)
{
@@ -610,6 +690,15 @@ Item {
return;
}
}
+ for (var i = 0; i < particleEmitterGizmos.length; ++i) {
+ if (particleEmitterGizmos[i].targetNode === node) {
+ particleEmitterGizmos[i].hidden = _generalHelper.isHidden(node);
+ return;
+ } else if (particleEmitterGizmos[i].targetNode && particleEmitterGizmos[i].targetNode.system === node) {
+ particleEmitterGizmos[i].systemHidden = _generalHelper.isHidden(node);
+ return;
+ }
+ }
}
}
@@ -810,9 +899,16 @@ Item {
onPressed: (mouse)=> {
if (viewRoot.editView) {
- var pickResult = _generalHelper.pickViewAt(viewRoot.editView, mouse.x, mouse.y);
- handleObjectClicked(_generalHelper.resolvePick(pickResult.objectHit),
- mouse.modifiers & Qt.ControlModifier);
+ // First pick overlay to check for hits there
+ var pickResult = _generalHelper.pickViewAt(overlayView, mouse.x, mouse.y);
+ var resolvedResult = _generalHelper.resolvePick(pickResult.objectHit);
+ if (!resolvedResult) {
+ // No hits from overlay view, pick the main scene
+ pickResult = _generalHelper.pickViewAt(viewRoot.editView, mouse.x, mouse.y);
+ resolvedResult = _generalHelper.resolvePick(pickResult.objectHit);
+ }
+
+ handleObjectClicked(resolvedResult, mouse.modifiers & Qt.ControlModifier);
if (pickResult.objectHit) {
if (transformMode === EditView3D.TransformMode.Move)
diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/IconGizmo.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/IconGizmo.qml
index 7752693d559..2b78dd04aac 100644
--- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/IconGizmo.qml
+++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/IconGizmo.qml
@@ -47,6 +47,7 @@ Item {
property bool locked: false
property bool globalShow: true
property bool canBeVisible: activeScene === scene && !hidden && (targetNode ? targetNode.visible : false)
+ property real iconOpacity: selected ? 0.2 : 1
property alias iconSource: iconImage.source
@@ -76,7 +77,7 @@ Item {
border.color: "#7777ff"
border.width: !iconGizmo.locked && iconGizmo.highlightOnHover && iconGizmo.hasMouse ? 2 : 0
radius: 5
- opacity: iconGizmo.selected ? 0.2 : 1
+ opacity: iconGizmo.iconOpacity
Image {
id: iconImage
fillMode: Image.Pad
diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ParticleEmitterGizmo.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ParticleEmitterGizmo.qml
new file mode 100644
index 00000000000..a06a38a2024
--- /dev/null
+++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ParticleEmitterGizmo.qml
@@ -0,0 +1,127 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+import QtQuick
+import QtQuick3D
+import QtQuick3D.Particles3D
+
+Node {
+ id: root
+
+ property Node targetNode: null
+ property var selectedNodes: []
+ property Node activeParticleSystem: null
+ property Node scene: null
+ property Node activeScene: null
+ property bool hidden: false
+ property bool systemHidden: false
+ property Node shapeModel: null
+ property bool globalShow: false
+ property bool canBeVisible: activeScene === scene && targetNode && !hidden && !systemHidden
+ property bool partOfActiveSystem: root.targetNode && root.targetNode.system === activeParticleSystem
+
+ opacity: 0.15
+
+ readonly property bool selected: selectedNodes.includes(targetNode)
+
+ visible: canBeVisible && (globalShow || selected)
+
+ position: targetNode ? targetNode.scenePosition : Qt.vector3d(0, 0, 0)
+ rotation: targetNode ? targetNode.sceneRotation : Qt.quaternion(1, 0, 0, 0)
+ scale: targetNode ? targetNode.sceneScale : Qt.vector3d(1, 1, 1)
+
+ function basicShape()
+ {
+ if (targetNode && targetNode.shape && targetNode.shape instanceof ParticleShape3D) {
+ if (targetNode.shape.type === ParticleShape3D.Cube)
+ return "#Cube";
+ else if (targetNode.shape.type === ParticleShape3D.Cylinder)
+ return "#Cylinder";
+ }
+ return "#Sphere";
+ }
+
+ function updateShape()
+ {
+ if (shapeModel)
+ shapeModel.destroy();
+
+ if (!targetNode)
+ return;
+
+ if (targetNode.shape instanceof ParticleModelShape3D) {
+ shapeModel = _generalHelper.createParticleEmitterGizmoModel(targetNode, defaultMaterial);
+ shapeModel.parent = root;
+ }
+ }
+
+ Component.onCompleted: {
+ updateShape();
+ }
+
+ Connections {
+ target: targetNode
+ function onSystemChanged() { systemHidden = _generalHelper.isHidden(system); }
+ }
+
+ Connections {
+ target: targetNode
+ function onShapeChanged() { updateShape(); }
+ }
+
+ Connections {
+ target: targetNode.shape instanceof ParticleModelShape3D ? targetNode.shape
+ :null
+ function onDelegateChanged() { updateShape(); }
+ }
+
+ Connections {
+ target: targetNode.shape instanceof ParticleModelShape3D ? targetNode.shape.delegate
+ : null
+ function onSourceChanged() { updateShape(); }
+ }
+
+ Model {
+ readonly property Node _pickTarget: root.targetNode
+ materials: [defaultMaterial]
+ source: basicShape()
+ scale: root.targetNode && root.targetNode.shape && targetNode.shape instanceof ParticleShape3D
+ ? root.targetNode.shape.extents.times(0.02) // default extent is 50
+ : autoScale.getScale(Qt.vector3d(0.1, 0.1, 0.1))
+ visible: !shapeModel
+ }
+
+ AutoScaleHelper {
+ id: autoScale
+ view3D: overlayView
+ }
+
+ DefaultMaterial {
+ id: defaultMaterial
+ diffuseColor: root.selected ? "#FF0000" : partOfActiveSystem ? "#FFFF00" : "#AAAAAA"
+ lighting: DefaultMaterial.NoLighting
+ cullMode: Material.NoCulling
+ }
+}
diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ParticleSystemGizmo.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ParticleSystemGizmo.qml
index de591fcd217..3d498ce335d 100644
--- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ParticleSystemGizmo.qml
+++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ParticleSystemGizmo.qml
@@ -28,5 +28,9 @@ import QtQuick3D 6.0
IconGizmo {
id: particleSystemGizmo
+
+ property Node activeParticleSystem: null
+
iconSource: "qrc:///qtquickplugin/mockfiles/images/editor_particlesystem.png"
+ iconOpacity: selected || activeParticleSystem == targetNode ? 0.2 : 1
}
diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/RotateRing.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/RotateRing.qml
index 8003839a894..79d31f45943 100644
--- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/RotateRing.qml
+++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/RotateRing.qml
@@ -54,6 +54,7 @@ Model {
Model {
id: pickModel
+ readonly property bool _edit3dLocked: true // Make this non-pickable in main picking handling
objectName: "PickModel for " + rotateRing.objectName
source: "../meshes/ringselect.mesh"
pickable: true
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp
index 9d7d4b05ce9..3c976ea0cbe 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp
@@ -46,6 +46,12 @@
#include
#include
+#ifdef QUICK3D_PARTICLES_MODULE
+#include
+#include
+#include
+#endif
+
#include
namespace QmlDesigner {
@@ -460,6 +466,31 @@ bool GeneralHelper::isPickable(QQuick3DNode *node) const
return true;
}
+// Emitter gizmo model creation is done in C++ as creating dynamic properties and
+// assigning materials to dynamically created models is lot simpler in C++
+QQuick3DNode *GeneralHelper::createParticleEmitterGizmoModel(QQuick3DNode *emitter,
+ QQuick3DMaterial *material) const
+{
+#ifdef QUICK3D_PARTICLES_MODULE
+ auto e = qobject_cast(emitter);
+ if (!e || qobject_cast(e) || !material)
+ return nullptr;
+
+ auto shape = qobject_cast(e->shape());
+ if (shape && shape->delegate()) {
+ if (auto model = qobject_cast(
+ shape->delegate()->create(shape->delegate()->creationContext()))) {
+ QQmlEngine::setObjectOwnership(model, QQmlEngine::JavaScriptOwnership);
+ model->setProperty("_pickTarget", QVariant::fromValue(emitter));
+ QQmlListReference matRef(model, "materials");
+ matRef.append(material);
+ return model;
+ }
+ }
+#endif
+ return nullptr;
+}
+
void GeneralHelper::storeToolState(const QString &sceneId, const QString &tool, const QVariant &state,
int delay)
{
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.h
index 46e6019dd58..97ffa35adb4 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.h
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.h
@@ -89,6 +89,8 @@ public:
Q_INVOKABLE bool isLocked(QQuick3DNode *node) const;
Q_INVOKABLE bool isHidden(QQuick3DNode *node) const;
Q_INVOKABLE bool isPickable(QQuick3DNode *node) const;
+ Q_INVOKABLE QQuick3DNode *createParticleEmitterGizmoModel(QQuick3DNode *emitter,
+ QQuick3DMaterial *material) const;
Q_INVOKABLE void storeToolState(const QString &sceneId, const QString &tool,
const QVariant &state, int delayEmit = 0);
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp
index e2bd56562d5..c1df9cc56ee 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp
@@ -113,6 +113,7 @@
#include
#include
#include
+#include
#endif
#ifdef IMPORT_QUICK3D_ASSETS
@@ -427,15 +428,23 @@ void Qt5InformationNodeInstanceServer::resetParticleSystem()
void Qt5InformationNodeInstanceServer::handleParticleSystemSelected(QQuick3DParticleSystem* targetParticleSystem)
{
- if (!m_particleAnimationDriver || targetParticleSystem == m_targetParticleSystem)
+ if (targetParticleSystem == m_targetParticleSystem)
+ return;
+
+ m_targetParticleSystem = targetParticleSystem;
+
+ if (m_editView3DData.rootItem) {
+ QQmlProperty systemProperty(m_editView3DData.rootItem, "activeParticleSystem", context());
+ systemProperty.write(objectToVariant(m_targetParticleSystem));
+ }
+
+ if (!m_particleAnimationDriver)
return;
m_particleAnimationDriver->reset();
// stop the previously selected from animating
resetParticleSystem();
- m_targetParticleSystem = targetParticleSystem;
-
resetParticleSystem();
#if QT_VERSION >= QT_VERSION_CHECK(6, 2, 2)
QObject::disconnect(m_particleAnimationConnection);
@@ -507,9 +516,17 @@ static QQuick3DParticleSystem *parentParticleSystem(QObject *selectedObject)
return nullptr;
}
-void Qt5InformationNodeInstanceServer::handleParticleSystemDeselected(QObject *selectedObject)
+void Qt5InformationNodeInstanceServer::handleParticleSystemDeselected()
{
+ resetParticleSystem();
+
m_targetParticleSystem = nullptr;
+
+ if (m_editView3DData.rootItem) {
+ QQmlProperty systemProperty(m_editView3DData.rootItem, "activeParticleSystem", context());
+ systemProperty.write(objectToVariant(nullptr));
+ }
+
const auto anim = animations();
int i = 0;
for (auto a : anim) {
@@ -526,7 +543,7 @@ void Qt5InformationNodeInstanceServer::handleParticleSystemDeselected(QObject *s
void Qt5InformationNodeInstanceServer::handleSelectionChanged(const QVariant &objs)
{
#ifdef QUICK3D_PARTICLES_MODULE
- resetParticleSystem();
+ bool skipSystemDeselect = m_targetParticleSystem == nullptr;
#endif
QList instanceList;
const QVariantList varObjs = objs.value();
@@ -535,8 +552,20 @@ void Qt5InformationNodeInstanceServer::handleSelectionChanged(const QVariant &ob
if (obj) {
ServerNodeInstance instance = instanceForObject(obj);
instanceList << instance;
+#ifdef QUICK3D_PARTICLES_MODULE
+ if (!skipSystemDeselect) {
+ auto particleSystem = parentParticleSystem(instance.internalObject());
+ skipSystemDeselect = particleSystem == m_targetParticleSystem;
+ }
+#endif
}
}
+
+#ifdef QUICK3D_PARTICLES_MODULE
+ if (m_targetParticleSystem && !skipSystemDeselect)
+ handleParticleSystemDeselected();
+#endif
+
selectInstances(instanceList);
// Hold selection changes reflected back from designer for a bit
m_selectionChangeTimer.start(500);
@@ -745,6 +774,10 @@ void Qt5InformationNodeInstanceServer::handleNode3DDestroyed(QObject *obj)
} else if (qobject_cast(obj)) {
QMetaObject::invokeMethod(m_editView3DData.rootItem, "releaseParticleSystemGizmo",
Q_ARG(QVariant, objectToVariant(obj)));
+ } else if (qobject_cast(obj)
+ && !qobject_cast(obj)) {
+ QMetaObject::invokeMethod(m_editView3DData.rootItem, "releaseParticleEmitterGizmo",
+ Q_ARG(QVariant, objectToVariant(obj)));
#endif
}
removeNode3D(obj);
@@ -853,6 +886,11 @@ void Qt5InformationNodeInstanceServer::resolveSceneRoots()
QMetaObject::invokeMethod(m_editView3DData.rootItem, "updateParticleSystemGizmoScene",
Q_ARG(QVariant, objectToVariant(newRoot)),
Q_ARG(QVariant, objectToVariant(node)));
+ } else if (qobject_cast(node)
+ && !qobject_cast(node)) {
+ QMetaObject::invokeMethod(m_editView3DData.rootItem, "updateParticleEmitterGizmoScene",
+ Q_ARG(QVariant, objectToVariant(newRoot)),
+ Q_ARG(QVariant, objectToVariant(node)));
#endif
}
}
@@ -1415,15 +1453,19 @@ void Qt5InformationNodeInstanceServer::createCameraAndLightGizmos(
QHash cameras;
QHash lights;
QHash particleSystems;
+ QHash particleEmitters;
for (const ServerNodeInstance &instance : instanceList) {
- if (instance.isSubclassOf("QQuick3DCamera"))
+ if (instance.isSubclassOf("QQuick3DCamera")) {
cameras[find3DSceneRoot(instance)] << instance.internalObject();
- else if (instance.isSubclassOf("QQuick3DAbstractLight"))
+ } else if (instance.isSubclassOf("QQuick3DAbstractLight")) {
lights[find3DSceneRoot(instance)] << instance.internalObject();
- else if (instance.isSubclassOf("QQuick3DParticleSystem"))
+ } else if (instance.isSubclassOf("QQuick3DParticleSystem")) {
particleSystems[find3DSceneRoot(instance)] << instance.internalObject();
-
+ } else if (instance.isSubclassOf("QQuick3DParticleEmitter")
+ && !instance.isSubclassOf("QQuick3DParticleTrailEmitter")) {
+ particleEmitters[find3DSceneRoot(instance)] << instance.internalObject();
+ }
}
auto cameraIt = cameras.constBegin();
@@ -1456,6 +1498,17 @@ void Qt5InformationNodeInstanceServer::createCameraAndLightGizmos(
}
++particleIt;
}
+
+ auto emitterIt = particleEmitters.constBegin();
+ while (emitterIt != particleEmitters.constEnd()) {
+ const auto emitterObjs = emitterIt.value();
+ for (auto &obj : emitterObjs) {
+ QMetaObject::invokeMethod(m_editView3DData.rootItem, "addParticleEmitterGizmo",
+ Q_ARG(QVariant, objectToVariant(emitterIt.key())),
+ Q_ARG(QVariant, objectToVariant(obj)));
+ }
+ ++emitterIt;
+ }
}
void Qt5InformationNodeInstanceServer::add3DViewPorts(const QList &instanceList)
@@ -1946,7 +1999,7 @@ void Qt5InformationNodeInstanceServer::changeSelection(const ChangeSelectionComm
if (particlesystem != m_targetParticleSystem)
handleParticleSystemSelected(particlesystem);
} else {
- handleParticleSystemDeselected(instance.internalObject());
+ handleParticleSystemDeselected();
}
}
#endif
@@ -1957,6 +2010,8 @@ void Qt5InformationNodeInstanceServer::changeSelection(const ChangeSelectionComm
|| qobject_cast(object)
#ifdef QUICK3D_PARTICLES_MODULE
|| qobject_cast(object)
+ || qobject_cast(object)
+
#endif
) {
return true;
@@ -2105,6 +2160,9 @@ void Qt5InformationNodeInstanceServer::view3DAction(const View3DActionCommand &c
updatedState.insert("showCameraFrustum", command.isEnabled());
break;
#ifdef QUICK3D_PARTICLES_MODULE
+ case View3DActionCommand::ShowParticleEmitter:
+ updatedState.insert("showParticleEmitter", command.isEnabled());
+ break;
case View3DActionCommand::ParticlesPlay:
m_particleAnimationPlaying = command.isEnabled();
updatedState.insert("particlePlay", command.isEnabled());
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.h
index 2f6331e8dd4..8f08dec3892 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.h
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.h
@@ -151,7 +151,7 @@ private:
#ifdef QUICK3D_PARTICLES_MODULE
void handleParticleSystemSelected(QQuick3DParticleSystem* targetParticleSystem);
void resetParticleSystem();
- void handleParticleSystemDeselected(QObject *selectedObject);
+ void handleParticleSystemDeselected();
#endif
RenderViewData m_editView3DData;
diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp
index 082c7d3eb55..dc4af0255fd 100644
--- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp
+++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp
@@ -118,17 +118,18 @@ void Edit3DView::renderImage3DChanged(const QImage &img)
void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState)
{
- const QString sceneKey = QStringLiteral("sceneInstanceId");
- const QString selectKey = QStringLiteral("selectionMode");
- const QString transformKey = QStringLiteral("transformMode");
- const QString perspectiveKey = QStringLiteral("usePerspective");
- const QString orientationKey = QStringLiteral("globalOrientation");
- const QString editLightKey = QStringLiteral("showEditLight");
- const QString gridKey = QStringLiteral("showGrid");
- const QString selectionBoxKey = QStringLiteral("showSelectionBox");
- const QString iconGizmoKey = QStringLiteral("showIconGizmo");
- const QString cameraFrustumKey = QStringLiteral("showCameraFrustum");
- const QString particlesPlayKey = QStringLiteral("particlePlay");
+ const QString sceneKey = QStringLiteral("sceneInstanceId");
+ const QString selectKey = QStringLiteral("selectionMode");
+ const QString transformKey = QStringLiteral("transformMode");
+ const QString perspectiveKey = QStringLiteral("usePerspective");
+ const QString orientationKey = QStringLiteral("globalOrientation");
+ const QString editLightKey = QStringLiteral("showEditLight");
+ const QString gridKey = QStringLiteral("showGrid");
+ const QString selectionBoxKey = QStringLiteral("showSelectionBox");
+ const QString iconGizmoKey = QStringLiteral("showIconGizmo");
+ const QString cameraFrustumKey = QStringLiteral("showCameraFrustum");
+ const QString particleEmitterKey = QStringLiteral("showParticleEmitter");
+ const QString particlesPlayKey = QStringLiteral("particlePlay");
if (sceneState.contains(sceneKey)) {
qint32 newActiveScene = sceneState[sceneKey].value();
@@ -188,6 +189,11 @@ void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState)
else
m_showCameraFrustumAction->action()->setChecked(false);
+ if (sceneState.contains(particleEmitterKey))
+ m_showParticleEmitterAction->action()->setChecked(sceneState[particleEmitterKey].toBool());
+ else
+ m_showParticleEmitterAction->action()->setChecked(false);
+
if (sceneState.contains(particlesPlayKey))
m_particlesPlayAction->action()->setChecked(sceneState[particlesPlayKey].toBool());
else
@@ -352,6 +358,12 @@ void Edit3DView::createEdit3DActions()
QKeySequence(Qt::Key_C), true, false, {}, {}, nullptr,
QCoreApplication::translate("ShowCameraFrustumAction", "Toggle between always showing the camera frustum visualization and only showing it when the camera is selected."));
+ m_showParticleEmitterAction = new Edit3DAction(
+ QmlDesigner::Constants::EDIT3D_EDIT_SHOW_PARTICLE_EMITTER, View3DActionCommand::ShowParticleEmitter,
+ QCoreApplication::translate("ShowParticleEmitterAction", "Always Show Particle Emitters"),
+ QKeySequence(Qt::Key_M), true, false, {}, {}, nullptr,
+ QCoreApplication::translate("ShowParticleEmitterAction", "Toggle between always showing the particle emitter visualization and only showing it when the emitter is selected."));
+
SelectionContextOperation resetTrigger = [this](const SelectionContext &) {
m_particlesPlayAction->action()->setEnabled(particlemode);
m_particlesRestartAction->action()->setEnabled(particlemode);
@@ -458,6 +470,7 @@ void Edit3DView::createEdit3DActions()
m_visibilityToggleActions << m_showSelectionBoxAction;
m_visibilityToggleActions << m_showIconGizmoAction;
m_visibilityToggleActions << m_showCameraFrustumAction;
+ m_visibilityToggleActions << m_showParticleEmitterAction;
}
QVector Edit3DView::leftActions() const
diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.h b/src/plugins/qmldesigner/components/edit3d/edit3dview.h
index daddb84d6c8..40e21708734 100644
--- a/src/plugins/qmldesigner/components/edit3d/edit3dview.h
+++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.h
@@ -102,6 +102,7 @@ private:
Edit3DAction *m_showSelectionBoxAction = nullptr;
Edit3DAction *m_showIconGizmoAction = nullptr;
Edit3DAction *m_showCameraFrustumAction = nullptr;
+ Edit3DAction *m_showParticleEmitterAction = nullptr;
Edit3DAction *m_resetAction = nullptr;
Edit3DAction *m_particleViewModeAction = nullptr;
Edit3DAction *m_particlesPlayAction = nullptr;
diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp
index 48391fe4b9c..547d7ad8a60 100644
--- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp
+++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp
@@ -555,6 +555,13 @@ void NodeInstanceView::nodeReparented(const ModelNode &node, const NodeAbstractP
updateChildren(newPropertyParent);
m_nodeInstanceServer->reparentInstances(
createReparentInstancesCommand(node, newPropertyParent, oldPropertyParent));
+
+ // Reset puppet when particle emitter is reparented to work around issue in
+ // autodetecting the particle system it belongs to. QTBUG-101157
+ if (node.isSubclassOf("QtQuick.Particles3D.ParticleEmitter3D")
+ && node.property("system").toBindingProperty().expression().isEmpty()) {
+ resetPuppet();
+ }
}
}
diff --git a/src/plugins/qmldesigner/qmldesignerconstants.h b/src/plugins/qmldesigner/qmldesignerconstants.h
index 03c2f42cca3..fc394ae089c 100644
--- a/src/plugins/qmldesigner/qmldesignerconstants.h
+++ b/src/plugins/qmldesigner/qmldesignerconstants.h
@@ -68,6 +68,7 @@ const char EDIT3D_EDIT_SHOW_GRID[] = "QmlDesigner.Editor3D.ToggleGrid";
const char EDIT3D_EDIT_SHOW_SELECTION_BOX[] = "QmlDesigner.Editor3D.ToggleSelectionBox";
const char EDIT3D_EDIT_SHOW_ICON_GIZMO[] = "QmlDesigner.Editor3D.ToggleIconGizmo";
const char EDIT3D_EDIT_SHOW_CAMERA_FRUSTUM[] = "QmlDesigner.Editor3D.ToggleCameraFrustum";
+const char EDIT3D_EDIT_SHOW_PARTICLE_EMITTER[] = "QmlDesigner.Editor3D.ToggleParticleEmitter";
const char EDIT3D_RESET_VIEW[] = "QmlDesigner.Editor3D.ResetView";
const char EDIT3D_PARTICLE_MODE[] = "QmlDesigner.Editor3D.ParticleViewModeToggle";
const char EDIT3D_PARTICLES_PLAY[] = "QmlDesigner.Editor3D.ParticlesPlay";