Allow scene environment as background for non-View3D scenes

For non-View3D scenes, the environment from the previous View3D
is used.

Fixes: QDS-12398
Change-Id: I6adc82cd0205ebe443b5834d9bb0f4906e2cd009
Reviewed-by: Qt CI Patch Build Bot <ci_patchbuild_bot@qt.io>
Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io>
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
This commit is contained in:
Miikka Heikkinen
2024-05-17 15:59:14 +03:00
parent cbd36f93fa
commit 73ce3a891f
9 changed files with 280 additions and 43 deletions

View File

@@ -54,7 +54,8 @@ enum class View3DActionType {
FlyModeToggle,
EditCameraRotation,
EditCameraMove,
EditCameraStopAllMoves
EditCameraStopAllMoves,
SetLastSceneEnvData
};
constexpr bool isNanotraceEnabled()

View File

@@ -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 <auxiliarydataproperties.h>
#include <model/modelutils.h>
@@ -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<AbstractProperty> &propertyList)
{
maybeStoreCurrentSceneEnvironment(propertyList);
}
void Edit3DView::bindingPropertiesChanged(const QList<BindingProperty> &propertyList, PropertyChangeFlags)
{
maybeStoreCurrentSceneEnvironment(propertyList);
}
void Edit3DView::variantPropertiesChanged(const QList<VariantProperty> &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<typename T, typename>
void Edit3DView::maybeStoreCurrentSceneEnvironment(const QList<T> &propertyList)
{
QSet<qint32> 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::SplitToolState> &Edit3DView::splitToolStates() const
{
return m_splitToolStates;

View File

@@ -8,6 +8,7 @@
#include <abstractview.h>
#include <modelcache.h>
#include <qmlobjectnode.h>
#include <QImage>
#include <QPointer>
@@ -59,6 +60,11 @@ public:
PropertyChangeFlags propertyChange) override;
void nodeRemoved(const ModelNode &removedNode, const NodeAbstractProperty &parentProperty,
PropertyChangeFlags propertyChange) override;
void propertiesRemoved(const QList<AbstractProperty> &propertyList) override;
void bindingPropertiesChanged(const QList<BindingProperty> &propertyList,
PropertyChangeFlags propertyChange) override;
void variantPropertiesChanged(const QList<VariantProperty> &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<typename T, typename = typename std::enable_if<std::is_base_of<AbstractProperty , T>::value>::type>
void maybeStoreCurrentSceneEnvironment(const QList<T> &propertyList);
QPointer<Edit3DWidget> m_edit3DWidget;
QVector<Edit3DAction *> m_leftActions;
QVector<Edit3DAction *> m_rightActions;

View File

@@ -1216,6 +1216,13 @@ CreateSceneCommand NodeInstanceView::createCreateSceneCommand()
if (stateNode.isValid() && stateNode.metaInfo().isQtQuickState())
stateInstanceId = stateNode.internalId();
QHash<QString, QVariantMap> sceneStates = m_edit3DToolStates[model()->fileUrl()];
QHash<QString, QVariantMap> 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<QVariantList>(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) {

View File

@@ -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;

View File

@@ -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 {

View File

@@ -6,6 +6,8 @@
#include "selectionboxgeometry.h"
#include <enumeration.h>
#include <QGuiApplication>
#include <QtQuick3D/qquick3dobject.h>
#include <QtQuick3D/private/qquick3dorthographiccamera_p.h>
@@ -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<float>::lowest();
static const float floatMax = std::numeric_limits<float>::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<Enumeration>();
QMetaEnum me = QMetaEnum::fromType<QQuick3DSceneEnvironment::QQuick3DEnvironmentBackgroundTypes>();
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<QColor>());
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;

View File

@@ -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<QQuick3DCubeMapTexture> skyBoxCubeMap;
};
QHash<QString, SceneEnvData> m_sceneEnvironmentData;
QVariantMap m_lastSceneEnvData;
struct MultiSelData {
QVector3D startScenePos;

View File

@@ -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<QmlDesigner::Internal::GeneralHelper *>(m_3dHelper);
if (helper)
helper->setLastSceneEnvironmentData(command.value().toMap());
break;
}
default:
break;