aboutsummaryrefslogtreecommitdiffstats
path: root/share
diff options
context:
space:
mode:
authorMiikka Heikkinen <miikka.heikkinen@qt.io>2022-02-21 11:26:53 +0200
committerMiikka Heikkinen <miikka.heikkinen@qt.io>2022-03-03 09:34:57 +0000
commit98673190adb6e67f133741254753750dae82ee4b (patch)
treed7763926ffb6accd3503e39a6c62319bcfee1fa6 /share
parent1b22558857f4ee2bc23824339d91aaa8e50b7add (diff)
QmlDesigner: Add visualization gizmo for ParticleEmitter3D
Particle emitters are now visualized in 3D edit view either by a small sphere for point emitters or by a proper model for model based emitters. The visualization model is rendered transparent. The visualization models can be displayed either always or only when the parent particle system is active in editor. Trail emitters are not visualized, as any visualization would be misleading, since these emitters follow generated particles. Fixes: QDS-6189 Change-Id: Idb6f12cadd9cea8110e5290cc18443aeb62c38d6 Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io>
Diffstat (limited to 'share')
-rw-r--r--share/qtcreator/qml/qmlpuppet/commands/view3dactioncommand.h1
-rw-r--r--share/qtcreator/qml/qmlpuppet/editor3d_qt6.qrc3
-rw-r--r--share/qtcreator/qml/qmlpuppet/mockfiles/qt6/EditView3D.qml126
-rw-r--r--share/qtcreator/qml/qmlpuppet/mockfiles/qt6/IconGizmo.qml3
-rw-r--r--share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ParticleEmitterGizmo.qml127
-rw-r--r--share/qtcreator/qml/qmlpuppet/mockfiles/qt6/ParticleSystemGizmo.qml4
-rw-r--r--share/qtcreator/qml/qmlpuppet/mockfiles/qt6/RotateRing.qml1
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp31
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.h2
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp78
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.h2
11 files changed, 350 insertions, 28 deletions
diff --git a/share/qtcreator/qml/qmlpuppet/commands/view3dactioncommand.h b/share/qtcreator/qml/qmlpuppet/commands/view3dactioncommand.h
index cb23b2c9ad..cb34c253f9 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 065f9e32cf..9831bec4be 100644
--- a/share/qtcreator/qml/qmlpuppet/editor3d_qt6.qrc
+++ b/share/qtcreator/qml/qmlpuppet/editor3d_qt6.qrc
@@ -35,7 +35,6 @@
<file>mockfiles/qt6/LightGizmo.qml</file>
<file>mockfiles/qt6/LightIconGizmo.qml</file>
<file>mockfiles/qt6/LightModel.qml</file>
- <file>mockfiles/qt6/ParticleSystemGizmo.qml</file>
<file>mockfiles/qt6/Line3D.qml</file>
<file>mockfiles/qt6/MaterialNodeView.qml</file>
<file>mockfiles/qt6/ModelNode2DImageView.qml</file>
@@ -44,6 +43,8 @@
<file>mockfiles/qt6/MoveGizmo.qml</file>
<file>mockfiles/qt6/NodeNodeView.qml</file>
<file>mockfiles/qt6/Overlay2D.qml</file>
+ <file>mockfiles/qt6/ParticleSystemGizmo.qml</file>
+ <file>mockfiles/qt6/ParticleEmitterGizmo.qml</file>
<file>mockfiles/qt6/PlanarDraggable.qml</file>
<file>mockfiles/qt6/PlanarMoveHandle.qml</file>
<file>mockfiles/qt6/PlanarScaleHandle.qml</file>
diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/EditView3D.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/EditView3D.qml
index eb7d50d2e4..a8425fd6cb 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 7752693d55..2b78dd04aa 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 0000000000..a06a38a202
--- /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 de591fcd21..3d498ce335 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 8003839a89..79d31f4594 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 9d7d4b05ce..3c976ea0cb 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/generalhelper.cpp
@@ -46,6 +46,12 @@
#include <QtQuick/qquickitem.h>
#include <QtCore/qmath.h>
+#ifdef QUICK3D_PARTICLES_MODULE
+#include <QtQuick3DParticles/private/qquick3dparticlemodelshape_p.h>
+#include <QtQuick3DParticles/private/qquick3dparticleemitter_p.h>
+#include <QtQuick3DParticles/private/qquick3dparticletrailemitter_p.h>
+#endif
+
#include <limits>
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<QQuick3DParticleEmitter *>(emitter);
+ if (!e || qobject_cast<QQuick3DParticleTrailEmitter *>(e) || !material)
+ return nullptr;
+
+ auto shape = qobject_cast<QQuick3DParticleModelShape *>(e->shape());
+ if (shape && shape->delegate()) {
+ if (auto model = qobject_cast<QQuick3DModel *>(
+ 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 46e6019dd5..97ffa35adb 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 e2bd56562d..c1df9cc56e 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp
@@ -113,6 +113,7 @@
#include <QtQuick3DParticles/private/qquick3dparticle_p.h>
#include <QtQuick3DParticles/private/qquick3dparticleaffector_p.h>
#include <QtQuick3DParticles/private/qquick3dparticleemitter_p.h>
+#include <QtQuick3DParticles/private/qquick3dparticletrailemitter_p.h>
#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<ServerNodeInstance> instanceList;
const QVariantList varObjs = objs.value<QVariantList>();
@@ -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<QQuick3DParticleSystem *>(obj)) {
QMetaObject::invokeMethod(m_editView3DData.rootItem, "releaseParticleSystemGizmo",
Q_ARG(QVariant, objectToVariant(obj)));
+ } else if (qobject_cast<QQuick3DParticleEmitter *>(obj)
+ && !qobject_cast<QQuick3DParticleTrailEmitter *>(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<QQuick3DParticleEmitter *>(node)
+ && !qobject_cast<QQuick3DParticleTrailEmitter *>(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<QObject *, QObjectList> cameras;
QHash<QObject *, QObjectList> lights;
QHash<QObject *, QObjectList> particleSystems;
+ QHash<QObject *, QObjectList> 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<ServerNodeInstance> &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<QQuick3DAbstractLight *>(object)
#ifdef QUICK3D_PARTICLES_MODULE
|| qobject_cast<QQuick3DParticleSystem *>(object)
+ || qobject_cast<QQuick3DParticleEmitter *>(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 2f6331e8dd..8f08dec389 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;