diff --git a/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h b/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h index a3012c99f53..3fdd7bf6cc5 100644 --- a/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h +++ b/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h @@ -54,7 +54,8 @@ enum class View3DActionType { FlyModeToggle, EditCameraRotation, EditCameraMove, - EditCameraStopAllMoves + EditCameraStopAllMoves, + SetLastSceneEnvData }; constexpr bool isNanotraceEnabled() diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index 7b9966d8bae..a78ff758613 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -20,9 +20,11 @@ #include "nodeinstanceview.h" #include "qmldesignerconstants.h" #include "qmldesignerplugin.h" +#include "qmlitemnode.h" #include "qmlvisualnode.h" #include "seekerslider.h" #include "snapconfiguration.h" +#include "variantproperty.h" #include #include @@ -235,36 +237,10 @@ void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState) state.showWireframe = false; } - // Syncing background color only makes sense for children of View3D instances - bool syncValue = false; - bool syncEnabled = false; - bool desiredSyncValue = false; if (sceneState.contains(syncEnvBgKey)) - desiredSyncValue = sceneState[syncEnvBgKey].toBool(); - ModelNode checkNode = Utils3D::active3DSceneNode(this); - const bool activeSceneValid = checkNode.isValid(); - - while (checkNode.isValid()) { - if (checkNode.metaInfo().isQtQuick3DView3D()) { - syncValue = desiredSyncValue; - syncEnabled = true; - break; - } - if (checkNode.hasParentProperty()) - checkNode = checkNode.parentProperty().parentModelNode(); - else - break; - } - - if (activeSceneValid && syncValue != desiredSyncValue) { - // Update actual toolstate as well if we overrode it. - QTimer::singleShot(0, this, [this, syncValue]() { - emitView3DAction(View3DActionType::SyncEnvBackground, syncValue); - }); - } - - m_syncEnvBackgroundAction->action()->setChecked(syncValue); - m_syncEnvBackgroundAction->action()->setEnabled(syncEnabled); + m_syncEnvBackgroundAction->action()->setChecked(sceneState[syncEnvBgKey].toBool()); + else + m_syncEnvBackgroundAction->action()->setChecked(false); // Selection context change updates visible and enabled states SelectionContext selectionContext(this); @@ -273,6 +249,8 @@ void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState) m_bakeLightsAction->currentContextChanged(selectionContext); syncCameraSpeedToNewView(); + + storeCurrentSceneEnvironment(); } void Edit3DView::modelAttached(Model *model) @@ -542,6 +520,21 @@ void Edit3DView::nodeRemoved(const ModelNode &, updateAlignActionStates(); } +void Edit3DView::propertiesRemoved(const QList &propertyList) +{ + maybeStoreCurrentSceneEnvironment(propertyList); +} + +void Edit3DView::bindingPropertiesChanged(const QList &propertyList, PropertyChangeFlags) +{ + maybeStoreCurrentSceneEnvironment(propertyList); +} + +void Edit3DView::variantPropertiesChanged(const QList &propertyList, PropertyChangeFlags) +{ + maybeStoreCurrentSceneEnvironment(propertyList); +} + void Edit3DView::sendInputEvent(QEvent *e) const { if (nodeInstanceView()) @@ -718,6 +711,30 @@ QPoint Edit3DView::resolveToolbarPopupPos(Edit3DAction *action) const return pos; } +template +void Edit3DView::maybeStoreCurrentSceneEnvironment(const QList &propertyList) +{ + QSet handledNodes; + QmlObjectNode sceneEnv; + for (const AbstractProperty &prop : propertyList) { + ModelNode node = prop.parentModelNode(); + const qint32 id = node.internalId(); + if (handledNodes.contains(id)) + continue; + + handledNodes.insert(id); + if (!node.metaInfo().isQtQuick3DSceneEnvironment()) + continue; + + if (!sceneEnv.isValid()) + sceneEnv = currentSceneEnv(); + if (sceneEnv == node) { + storeCurrentSceneEnvironment(); + break; + } + } +} + void Edit3DView::showContextMenu() { // If request for context menu is still pending, skip for now @@ -807,6 +824,75 @@ void Edit3DView::syncCameraSpeedToNewView() setCameraSpeedAuxData(speed, multiplier); } +QmlObjectNode Edit3DView::currentSceneEnv() +{ + PropertyName envProp{"environment"}; + ModelNode checkNode = Utils3D::active3DSceneNode(this); + while (checkNode.isValid()) { + if (checkNode.metaInfo().isQtQuick3DView3D()) { + QmlObjectNode sceneEnvNode = QmlItemNode(checkNode).bindingProperty(envProp) + .resolveToModelNode(); + if (sceneEnvNode.isValid()) + return sceneEnvNode; + break; + } + if (checkNode.hasParentProperty()) + checkNode = checkNode.parentProperty().parentModelNode(); + else + break; + } + return {}; +} + +void Edit3DView::storeCurrentSceneEnvironment() +{ + // If current active scene has scene environment, store relevant properties + QmlObjectNode sceneEnvNode = currentSceneEnv(); + if (sceneEnvNode.isValid()) { + QVariantMap lastSceneEnvData; + + auto insertPropValue = [](const PropertyName prop, const QmlObjectNode &node, + QVariantMap &map) { + if (!node.hasProperty(prop)) + return; + + map.insert(QString::fromUtf8(prop), node.modelValue(prop)); + }; + + auto insertTextureProps = [&](const PropertyName prop) { + // For now we just grab the absolute path of texture source for simplicity + if (!sceneEnvNode.hasProperty(prop)) + return; + + QmlObjectNode bindNode = QmlItemNode(sceneEnvNode).bindingProperty(prop) + .resolveToModelNode(); + if (bindNode.isValid()) { + QVariantMap props; + const PropertyName sourceProp = "source"; + if (bindNode.hasProperty(sourceProp)) { + Utils::FilePath qmlPath = Utils::FilePath::fromUrl( + model()->fileUrl()).absolutePath(); + Utils::FilePath sourcePath = Utils::FilePath::fromUrl( + bindNode.modelValue(sourceProp).toUrl()); + + sourcePath = qmlPath.resolvePath(sourcePath); + + props.insert(QString::fromUtf8(sourceProp), + sourcePath.absoluteFilePath().toUrl()); + } + lastSceneEnvData.insert(QString::fromUtf8(prop), props); + } + }; + + insertPropValue("backgroundMode", sceneEnvNode, lastSceneEnvData); + insertPropValue("clearColor", sceneEnvNode, lastSceneEnvData); + insertTextureProps("lightProbe"); + insertTextureProps("skyBoxCubeMap"); + + emitView3DAction(View3DActionType::SetLastSceneEnvData, lastSceneEnvData); + } +} + const QList &Edit3DView::splitToolStates() const { return m_splitToolStates; diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.h b/src/plugins/qmldesigner/components/edit3d/edit3dview.h index 918df3c6f98..a76607f003e 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.h +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.h @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -59,6 +60,11 @@ public: PropertyChangeFlags propertyChange) override; void nodeRemoved(const ModelNode &removedNode, const NodeAbstractProperty &parentProperty, PropertyChangeFlags propertyChange) override; + void propertiesRemoved(const QList &propertyList) override; + void bindingPropertiesChanged(const QList &propertyList, + PropertyChangeFlags propertyChange) override; + void variantPropertiesChanged(const QList &propertyList, + PropertyChangeFlags propertyChange) override; void sendInputEvent(QEvent *e) const; void edit3DViewResized(const QSize &size) const; @@ -127,9 +133,14 @@ private: void createSyncEnvBackgroundAction(); void createSeekerSliderAction(); void syncCameraSpeedToNewView(); + QmlObjectNode currentSceneEnv(); + void storeCurrentSceneEnvironment(); QPoint resolveToolbarPopupPos(Edit3DAction *action) const; + template::value>::type> + void maybeStoreCurrentSceneEnvironment(const QList &propertyList); + QPointer m_edit3DWidget; QVector m_leftActions; QVector m_rightActions; diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp index fd8c92d04d3..c033c983316 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp @@ -1216,6 +1216,13 @@ CreateSceneCommand NodeInstanceView::createCreateSceneCommand() if (stateNode.isValid() && stateNode.metaInfo().isQtQuickState()) stateInstanceId = stateNode.internalId(); + QHash sceneStates = m_edit3DToolStates[model()->fileUrl()]; + QHash projectStates = m_edit3DToolStates[ + QUrl::fromLocalFile(m_externalDependencies.currentProjectDirPath())]; + const QString ptsId = "@PTS"; + if (projectStates.contains(ptsId)) + sceneStates.insert(ptsId, projectStates[ptsId]); + return CreateSceneCommand(instanceContainerList, reparentContainerList, idContainerList, @@ -1226,7 +1233,7 @@ CreateSceneCommand NodeInstanceView::createCreateSceneCommand() mockupTypesVector, model()->fileUrl(), m_externalDependencies.currentResourcePath(), - m_edit3DToolStates[model()->fileUrl()], + sceneStates, lastUsedLanguage, m_captureImageMinimumSize, m_captureImageMaximumSize, @@ -1744,7 +1751,12 @@ void NodeInstanceView::handlePuppetToCreatorCommand(const PuppetToCreatorCommand auto data = qvariant_cast(command.data()); if (data.size() == 3) { QString qmlId = data[0].toString(); - m_edit3DToolStates[model()->fileUrl()][qmlId].insert(data[1].toString(), data[2]); + QUrl mainKey; + if (qmlId == "@PTS") // Project tool state + mainKey = QUrl::fromLocalFile(m_externalDependencies.currentProjectDirPath()); + else + mainKey = model()->fileUrl(); + m_edit3DToolStates[mainKey][qmlId].insert(data[1].toString(), data[2]); } } } else if (command.type() == PuppetToCreatorCommand::Render3DView) { diff --git a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml index 0e876e0d7d0..c4dc15ab5eb 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/EditView3D.qml @@ -166,7 +166,7 @@ Item { break; } } - showEditLight = !hasSceneLight; + showEditLight = !hasSceneLight && !_generalHelper.sceneHasLightProbe(sceneId); // Don't inherit camera angles from the previous scene for (let i = 0; i < 4; ++i) @@ -265,16 +265,22 @@ Item { for (var i = 0; i < 4; ++i) { if (syncEnvBackground) { - let bgMode = _generalHelper.sceneEnvironmentBgMode(sceneId); - if ((!_generalHelper.sceneEnvironmentLightProbe(sceneId) && bgMode === SceneEnvironment.SkyBox) - || (!_generalHelper.sceneEnvironmentSkyBoxCubeMap(sceneId) && bgMode === SceneEnvironment.SkyBoxCubeMap)) { - editViews[i].sceneEnv.backgroundMode = SceneEnvironment.Color; - } else { - editViews[i].sceneEnv.backgroundMode = bgMode; + if (_generalHelper.hasSceneEnvironmentData(sceneId)) { + let bgMode = _generalHelper.sceneEnvironmentBgMode(sceneId); + if ((!_generalHelper.sceneEnvironmentLightProbe(sceneId) && bgMode === SceneEnvironment.SkyBox) + || (!_generalHelper.sceneEnvironmentSkyBoxCubeMap(sceneId) && bgMode === SceneEnvironment.SkyBoxCubeMap)) { + editViews[i].sceneEnv.backgroundMode = SceneEnvironment.Color; + } else { + editViews[i].sceneEnv.backgroundMode = bgMode; + } + editViews[i].sceneEnv.lightProbe = _generalHelper.sceneEnvironmentLightProbe(sceneId); + editViews[i].sceneEnv.skyBoxCubeMap = _generalHelper.sceneEnvironmentSkyBoxCubeMap(sceneId); + editViews[i].sceneEnv.clearColor = _generalHelper.sceneEnvironmentColor(sceneId); + } else if (activeScene) { + _generalHelper.updateSceneEnvToLast(editViews[i].sceneEnv, + editViews[i].defaultLightProbe, + editViews[i].defaultCubeMap); } - editViews[i].sceneEnv.lightProbe = _generalHelper.sceneEnvironmentLightProbe(sceneId); - editViews[i].sceneEnv.skyBoxCubeMap = _generalHelper.sceneEnvironmentSkyBoxCubeMap(sceneId); - editViews[i].sceneEnv.clearColor = _generalHelper.sceneEnvironmentColor(sceneId); } else { editViews[i].sceneEnv.backgroundMode = SceneEnvironment.Transparent; editViews[i].sceneEnv.lightProbe = null; diff --git a/src/tools/qml2puppet/mockfiles/qt6/SceneView3D.qml b/src/tools/qml2puppet/mockfiles/qt6/SceneView3D.qml index 1de1cebd14a..82c735467ec 100644 --- a/src/tools/qml2puppet/mockfiles/qt6/SceneView3D.qml +++ b/src/tools/qml2puppet/mockfiles/qt6/SceneView3D.qml @@ -15,6 +15,8 @@ View3D { property alias perspectiveCamera: scenePerspectiveCamera property alias orthoCamera: sceneOrthoCamera property alias sceneEnv: sceneEnv + property alias defaultLightProbe: defaultLightProbe + property alias defaultCubeMap: defaultCubeMap property vector3d cameraLookAt property var selectionBoxes: [] property Node selectedNode @@ -61,6 +63,14 @@ View3D { id: sceneEnv antialiasingMode: SceneEnvironment.MSAA antialiasingQuality: SceneEnvironment.High + + Texture { + id: defaultLightProbe + } + + CubeMapTexture { + id: defaultCubeMap + } } Node { diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp index f24bc342b91..d4f79fdabdd 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp +++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.cpp @@ -6,6 +6,8 @@ #include "selectionboxgeometry.h" +#include + #include #include #include @@ -42,9 +44,17 @@ namespace QmlDesigner { namespace Internal { -const QString _globalStateId = QStringLiteral("@GTS"); // global tool state +const QString _globalStateId = QStringLiteral("@GTS"); // global tool state (within document) +const QString _projectStateId = QStringLiteral("@PTS"); // project wide tool state const QString _lastSceneIdKey = QStringLiteral("lastSceneId"); const QString _rootSizeKey = QStringLiteral("rootSize"); +const QString _lastSceneEnvKey = QStringLiteral("lastSceneEnv"); + +const QString _lightProbeProp = QStringLiteral("lightProbe"); +const QString _sourceProp = QStringLiteral("source"); +const QString _cubeProp = QStringLiteral("skyBoxCubeMap"); +const QString _bgProp = QStringLiteral("backgroundMode"); +const QString _colorProp = QStringLiteral("clearColor"); static const float floatMin = std::numeric_limits::lowest(); static const float floatMax = std::numeric_limits::max(); @@ -739,6 +749,11 @@ void GeneralHelper::setSceneEnvironmentData(const QString &sceneId, } } +bool GeneralHelper::hasSceneEnvironmentData(const QString &sceneId) const +{ + return m_sceneEnvironmentData.contains(sceneId); +} + QQuick3DSceneEnvironment::QQuick3DEnvironmentBackgroundTypes GeneralHelper::sceneEnvironmentBgMode( const QString &sceneId) const { @@ -760,6 +775,69 @@ QQuick3DCubeMapTexture *GeneralHelper::sceneEnvironmentSkyBoxCubeMap(const QStri return m_sceneEnvironmentData[sceneId].skyBoxCubeMap.data(); } +void GeneralHelper::updateSceneEnvToLast(QQuick3DSceneEnvironment *env, QQuick3DTexture *lightProbe, + QQuick3DCubeMapTexture *cubeMap) +{ + if (!env) + return; + + if (m_lastSceneEnvData.contains(_bgProp)) { + Enumeration enumeration = m_lastSceneEnvData[_bgProp].value(); + QMetaEnum me = QMetaEnum::fromType(); + int intValue = me.keyToValue(enumeration.toName()); + env->setBackgroundMode(QQuick3DSceneEnvironment::QQuick3DEnvironmentBackgroundTypes(intValue)); + } else { + env->setBackgroundMode(QQuick3DSceneEnvironment::Transparent); + } + + if (m_lastSceneEnvData.contains(_colorProp)) + env->setClearColor(m_lastSceneEnvData[_colorProp].value()); + else + env->setClearColor(Qt::transparent); + + if (lightProbe) { + if (m_lastSceneEnvData.contains(_lightProbeProp)) { + QVariantMap props = m_lastSceneEnvData[_lightProbeProp].toMap(); + if (props.contains(_sourceProp)) + lightProbe->setSource(props[_sourceProp].toUrl()); + else + lightProbe->setSource({}); + env->setLightProbe(lightProbe); + } else { + env->setLightProbe(nullptr); + } + } + + if (cubeMap) { + if (m_lastSceneEnvData.contains(_cubeProp)) { + QVariantMap props = m_lastSceneEnvData[_cubeProp].toMap(); + if (props.contains(_sourceProp)) + cubeMap->setSource(props[_sourceProp].toUrl()); + else + cubeMap->setSource({}); + env->setSkyBoxCubeMap(cubeMap); + } else { + env->setSkyBoxCubeMap(nullptr); + } + } +} + +bool GeneralHelper::sceneHasLightProbe(const QString &sceneId) +{ + // From editor perspective, a scene is considered to have a light probe if scene itself + // has a light probe or scene has no env data and last scene had a light probe + if (m_sceneEnvironmentData.contains(sceneId)) { + return bool(m_sceneEnvironmentData[sceneId].lightProbe); + } else { + if (m_lastSceneEnvData.contains(_lightProbeProp)) { + QVariantMap props = m_lastSceneEnvData[_sourceProp].toMap(); + if (props.contains(_sourceProp)) + return !props[_sourceProp].toUrl().isEmpty(); + } + } + return false; +} + void GeneralHelper::clearSceneEnvironmentData() { for (const SceneEnvData &data : std::as_const(m_sceneEnvironmentData)) { @@ -773,6 +851,12 @@ void GeneralHelper::clearSceneEnvironmentData() emit sceneEnvDataChanged(); } +void GeneralHelper::setLastSceneEnvironmentData(const QVariantMap &data) +{ + m_lastSceneEnvData = data; + storeToolState(_projectStateId, _lastSceneEnvKey, m_lastSceneEnvData); +} + void GeneralHelper::initToolStates(const QString &sceneId, const QVariantMap &toolStates) { m_toolStates[sceneId] = toolStates; @@ -797,11 +881,21 @@ QString GeneralHelper::globalStateId() const return _globalStateId; } +QString GeneralHelper::projectStateId() const +{ + return _projectStateId; +} + QString GeneralHelper::lastSceneIdKey() const { return _lastSceneIdKey; } +QString GeneralHelper::lastSceneEnvKey() const +{ + return _lastSceneEnvKey; +} + QString GeneralHelper::rootSizeKey() const { return _rootSizeKey; diff --git a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h index 10ed28aa721..50eaba68ebb 100644 --- a/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h +++ b/src/tools/qml2puppet/qml2puppet/editor3d/generalhelper.h @@ -96,7 +96,9 @@ public: Q_INVOKABLE void enableItemUpdate(QQuickItem *item, bool enable); Q_INVOKABLE QVariantMap getToolStates(const QString &sceneId); QString globalStateId() const; + QString projectStateId() const; QString lastSceneIdKey() const; + QString lastSceneEnvKey() const; QString rootSizeKey() const; Q_INVOKABLE void setMultiSelectionTargets(QQuick3DNode *multiSelectRootNode, @@ -109,12 +111,18 @@ public: Q_INVOKABLE void rotateMultiSelection(bool commit); void setSceneEnvironmentData(const QString &sceneId, QQuick3DSceneEnvironment *env); + Q_INVOKABLE bool hasSceneEnvironmentData(const QString &sceneId) const; Q_INVOKABLE QQuick3DSceneEnvironment::QQuick3DEnvironmentBackgroundTypes sceneEnvironmentBgMode( const QString &sceneId) const; Q_INVOKABLE QColor sceneEnvironmentColor(const QString &sceneId) const; Q_INVOKABLE QQuick3DTexture *sceneEnvironmentLightProbe(const QString &sceneId) const; Q_INVOKABLE QQuick3DCubeMapTexture *sceneEnvironmentSkyBoxCubeMap(const QString &sceneId) const; + Q_INVOKABLE void updateSceneEnvToLast(QQuick3DSceneEnvironment *env, QQuick3DTexture *lightProbe, + QQuick3DCubeMapTexture *cubeMap); + Q_INVOKABLE bool sceneHasLightProbe(const QString &sceneId); + void clearSceneEnvironmentData(); + void setLastSceneEnvironmentData(const QVariantMap &data); bool isMacOS() const; @@ -202,6 +210,7 @@ private: QPointer skyBoxCubeMap; }; QHash m_sceneEnvironmentData; + QVariantMap m_lastSceneEnvData; struct MultiSelData { QVector3D startScenePos; diff --git a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp index 705efce2cf9..d4353fc8f23 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp @@ -1963,6 +1963,8 @@ void Qt5InformationNodeInstanceServer::setup3DEditView( if (toolStates[helper->globalStateId()].contains(helper->lastSceneIdKey())) lastSceneId = toolStates[helper->globalStateId()][helper->lastSceneIdKey()].toString(); } + if (toolStates.contains(helper->projectStateId())) + helper->setLastSceneEnvironmentData(toolStates[helper->projectStateId()][helper->lastSceneEnvKey()].toMap()); } // Find a scene to show @@ -2607,6 +2609,12 @@ void Qt5InformationNodeInstanceServer::view3DAction(const View3DActionCommand &c case View3DActionType::MaterialOverride: updatedToolState.insert("matOverride", command.value().toList()); break; + case View3DActionType::SetLastSceneEnvData: { + auto helper = qobject_cast(m_3dHelper); + if (helper) + helper->setLastSceneEnvironmentData(command.value().toMap()); + break; + } default: break;