forked from qt-creator/qt-creator
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>
This commit is contained in:
@@ -50,6 +50,7 @@ public:
|
||||
ShowSelectionBox,
|
||||
ShowIconGizmo,
|
||||
ShowCameraFrustum,
|
||||
ShowParticleEmitter,
|
||||
Edit3DParticleModeToggle,
|
||||
ParticlesPlay,
|
||||
ParticlesRestart,
|
||||
|
@@ -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>
|
||||
|
@@ -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)
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
}
|
||||
}
|
@@ -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
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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)
|
||||
{
|
||||
|
@@ -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);
|
||||
|
@@ -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());
|
||||
|
@@ -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;
|
||||
|
@@ -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<qint32>();
|
||||
@@ -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<Edit3DAction *> Edit3DView::leftActions() const
|
||||
|
@@ -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;
|
||||
|
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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";
|
||||
|
Reference in New Issue
Block a user