summaryrefslogtreecommitdiffstats
path: root/src/Authoring/Qt3DStudio/Palettes/Inspector/InspectorControlModel.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/Authoring/Qt3DStudio/Palettes/Inspector/InspectorControlModel.cpp')
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/Inspector/InspectorControlModel.cpp2134
1 files changed, 2134 insertions, 0 deletions
diff --git a/src/Authoring/Qt3DStudio/Palettes/Inspector/InspectorControlModel.cpp b/src/Authoring/Qt3DStudio/Palettes/Inspector/InspectorControlModel.cpp
new file mode 100644
index 00000000..4542f72d
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/Inspector/InspectorControlModel.cpp
@@ -0,0 +1,2134 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "InspectorControlModel.h"
+#include "StudioApp.h"
+#include "MainFrm.h"
+#include "Core.h"
+#include "Doc.h"
+#include "InspectorGroup.h"
+#include "Qt3DSDMInspectorGroup.h"
+#include "Qt3DSDMInspectorRow.h"
+#include "Qt3DSDMStudioSystem.h"
+#include "Qt3DSDMInspectable.h"
+#include "Qt3DSDMDataCore.h"
+#include "IDocumentEditor.h"
+#include "Qt3DSDMMetaData.h"
+#include "Qt3DSDMSignals.h"
+#include "CmdDataModelDeanimate.h"
+#include "GuideInspectable.h"
+#include "Qt3DSDMDataTypes.h"
+#include "IObjectReferenceHelper.h"
+#include "SlideSystem.h"
+#include "ClientDataModelBridge.h"
+#include "IDocumentReader.h"
+#include "IStudioRenderer.h"
+#include "Dialogs.h"
+#include "Dispatch.h"
+#include "VariantsGroupModel.h"
+#include "StudioProjectSettings.h"
+#include "Literals.h"
+
+#include <QtCore/qfileinfo.h>
+
+static QStringList renderableItems()
+{
+ QStringList renderables;
+ renderables.push_back(QObject::tr("No renderable item"));
+ const CDoc *doc = g_StudioApp.GetCore()->GetDoc();
+ Q3DStudio::CString docDir = Q3DStudio::CString::fromQString(doc->GetDocumentDirectory());
+
+ for (SubPresentationRecord r : qAsConst(g_StudioApp.m_subpresentations))
+ renderables.push_back(r.m_id);
+
+ // second step, find the renderable plugins.
+ {
+ Q3DStudio::CFilePath pluginDir
+ = Q3DStudio::CFilePath::CombineBaseAndRelative(docDir, "plugins");
+ if (pluginDir.Exists() && pluginDir.IsDirectory()) {
+ std::vector<Q3DStudio::CFilePath> dirFiles;
+ pluginDir.ListFilesAndDirectories(dirFiles);
+ for (size_t idx = 0, end = dirFiles.size(); idx < end; ++idx) {
+ if (dirFiles[idx].IsFile()) {
+ Q3DStudio::CFilePath relPath =
+ Q3DStudio::CFilePath::GetRelativePathFromBase(docDir, dirFiles[idx]);
+ renderables.push_back(relPath.toQString());
+ }
+ }
+ }
+ }
+ std::sort(renderables.begin() + 1, renderables.end());
+ return renderables;
+}
+
+static std::pair<bool, bool> getSlideCharacteristics(qt3dsdm::Qt3DSDMInstanceHandle instance,
+ const qt3dsdm::ISlideCore &slideCore,
+ const qt3dsdm::ISlideSystem &slideSystem)
+{
+ // Get the slide from the instance.
+ qt3dsdm::Qt3DSDMSlideHandle slide = slideCore.GetSlideByInstance(instance);
+ qt3dsdm::Qt3DSDMSlideHandle master = slideSystem.GetMasterSlide(slide);
+ int index = int(slideSystem.GetSlideIndex(slide));
+ int count = int(slideSystem.GetSlideCount(master));
+ bool hasNextSlide = index > 0 && index < count - 1;
+ bool hasPreviousSlide = index > 1;
+ return std::make_pair(hasNextSlide, hasPreviousSlide);
+}
+
+InspectorControlModel::InspectorControlModel(VariantsGroupModel *variantsModel, QObject *parent)
+ : QAbstractListModel(parent)
+ , m_UpdatableEditor(*g_StudioApp.GetCore()->GetDoc())
+ , m_variantsModel(variantsModel)
+{
+ m_modifiedProperty.first = 0;
+ m_modifiedProperty.second = 0;
+}
+
+void InspectorControlModel::setInspectable(CInspectableBase *inInspectable)
+{
+ // Commit any pending transactions on selection change
+ m_UpdatableEditor.CommitEditor();
+ m_modifiedProperty.first = 0;
+ m_modifiedProperty.second = 0;
+ m_previouslyCommittedValue = {};
+
+ if (m_inspectableBase != inInspectable) {
+ m_inspectableBase = inInspectable;
+ rebuildTree();
+ }
+}
+
+CInspectableBase *InspectorControlModel::inspectable() const
+{
+ return m_inspectableBase;
+}
+
+qt3dsdm::Qt3DSDMInstanceHandle InspectorControlModel::getReferenceMaterial(
+ CInspectableBase *inspectable) const
+{
+ if (inspectable)
+ return getBridge()->getMaterialReference(inspectable->getInstance());
+
+ return 0;
+}
+
+qt3dsdm::Qt3DSDMInstanceHandle InspectorControlModel::getReferenceMaterialRecursively(
+ CInspectableBase *inspectable) const
+{
+ if (inspectable) {
+ auto bridge = getBridge();
+ auto refMaterial = bridge->getMaterialReference(inspectable->getInstance());
+ auto type = bridge->GetObjectType(refMaterial);
+ while (type == OBJTYPE_REFERENCEDMATERIAL) {
+ refMaterial = bridge->getMaterialReference(refMaterial);
+ type = bridge->GetObjectType(refMaterial);
+ }
+ return refMaterial;
+ }
+
+ return 0;
+}
+
+void InspectorControlModel::notifyPropertyChanged(qt3dsdm::Qt3DSDMInstanceHandle inInstance,
+ qt3dsdm::Qt3DSDMPropertyHandle inProperty)
+{
+ auto doc = g_StudioApp.GetCore()->GetDoc();
+ if (!getBridge()->IsSceneGraphInstance(inInstance))
+ return;
+
+ if (inProperty == getBridge()->getVariantsProperty(inInstance)) {
+ // variants model is updated upon edit but this is needed to handle undoing
+ m_variantsModel->refresh();
+ return;
+ }
+
+ bool changed = false;
+ for (int row = 0; row < m_groupElements.count(); ++row) {
+ auto group = m_groupElements[row];
+ for (int p = 0; p < group.controlElements.count(); ++p) {
+ QVariant& element = group.controlElements[p];
+ InspectorControlBase *property = element.value<InspectorControlBase *>();
+ qt3dsdm::Qt3DSDMInstanceHandle imageInstance;
+ if (property->m_dataType == qt3dsdm::DataModelDataType::Long4
+ && property->m_property.Valid()) {
+ imageInstance = doc->GetDocumentReader().GetImageInstanceForProperty(
+ property->m_instance, property->m_property);
+ }
+ if (property->m_property == inProperty || imageInstance == inInstance) {
+ updatePropertyValue(property);
+ changed = true;
+ updateValidState(property);
+ }
+ }
+ }
+ if (changed)
+ Q_EMIT dataChanged(index(0), index(rowCount() - 1));
+}
+
+bool InspectorControlModel::hasInstanceProperty(qt3dsdm::Qt3DSDMInstanceHandle instance,
+ qt3dsdm::Qt3DSDMPropertyHandle handle) const
+{
+ for (const auto &group : qAsConst(m_groupElements)) {
+ for (const auto &element : qAsConst(group.controlElements)) {
+ InspectorControlBase *property = element.value<InspectorControlBase *>();
+ if (property->m_property == handle) {
+ auto refInstance = getBridge()->getMaterialReference(property->m_instance);
+ return property->m_instance == instance || refInstance == instance;
+ }
+ }
+ }
+
+ return false;
+}
+
+bool InspectorControlModel::isInsideMaterialContainer() const
+{
+ return isInsideMaterialContainer(m_inspectableBase);
+}
+
+bool InspectorControlModel::isInsideMaterialContainer(CInspectableBase *inspectable) const
+{
+ if (!inspectable || !inspectable->getInstance())
+ return false;
+
+ return getBridge()->isInsideMaterialContainer(inspectable->getInstance());
+}
+
+bool InspectorControlModel::isDefaultMaterial() const
+{
+ if (m_inspectableBase)
+ return getBridge()->isDefaultMaterial(m_inspectableBase->getInstance());
+
+ return false;
+}
+
+bool InspectorControlModel::isAnimatableMaterial() const
+{
+ return isAnimatableMaterial(m_inspectableBase);
+}
+
+bool InspectorControlModel::isAnimatableMaterial(CInspectableBase *inspectable) const
+{
+ if (!inspectable)
+ return false;
+
+ return inspectable->getObjectType() & (OBJTYPE_CUSTOMMATERIAL | OBJTYPE_MATERIAL);
+}
+
+bool InspectorControlModel::isBasicMaterial() const
+{
+ return isBasicMaterial(m_inspectableBase);
+}
+
+bool InspectorControlModel::isBasicMaterial(CInspectableBase *inspectable) const
+{
+ if (!inspectable)
+ return false;
+
+ return getBridge()->isBasicMaterial(inspectable->getInstance());
+}
+
+bool InspectorControlModel::isMaterial() const
+{
+ if (m_inspectableBase)
+ return m_inspectableBase->getObjectType() & OBJTYPE_IS_MATERIAL;
+
+ return false;
+}
+
+void InspectorControlModel::addMaterial()
+{
+ const auto doc = g_StudioApp.GetCore()->GetDoc();
+ qt3dsdm::Qt3DSDMInstanceHandle instance = m_inspectableBase->getInstance();
+ if (!instance.Valid())
+ return;
+
+ QString path = doc->getSceneEditor()->getMaterialDirectoryPath() + QStringLiteral("/Material");
+ QString extension = QStringLiteral(".materialdef");
+
+ auto absPath = path + extension;
+ int i = 1;
+ while (QFileInfo(absPath).exists()) {
+ i++;
+ absPath = path + QString::number(i) + extension;
+ }
+ const auto relPath = doc->GetRelativePathToDoc(absPath);
+
+ qt3dsdm::Qt3DSDMInstanceHandle newMaterial;
+ {
+ newMaterial = Q3DStudio::SCOPED_DOCUMENT_EDITOR(*doc, {})
+ ->getOrCreateMaterial(absPath, false);
+ }
+ // Several aspects of the editor are not updated correctly
+ // if the data core is changed without a transaction
+ // The above scope completes the transaction for creating a new material
+ // Next the added undo has to be popped from the stack
+ // TODO: Find a way to update the editor fully without a transaction
+ g_StudioApp.GetCore()->GetCmdStack()->RemoveLastUndo();
+
+ saveIfMaterial(newMaterial);
+
+ Q3DStudio::ScopedDocumentEditor sceneEditor(
+ Q3DStudio::SCOPED_DOCUMENT_EDITOR(*doc, tr("Set Material Type")));
+ doc->SelectDataModelObject(newMaterial);
+
+ const auto type = getBridge()->GetObjectType(instance);
+ if (type != OBJTYPE_REFERENCEDMATERIAL)
+ sceneEditor->SetMaterialType(instance, QStringLiteral("Referenced Material"));
+ sceneEditor->setMaterialReferenceByPath(instance, absPath);
+ sceneEditor->SetName(instance, getBridge()->GetName(newMaterial, true));
+ sceneEditor->setMaterialSourcePath(instance, Q3DStudio::CString::fromQString(relPath));
+ doc->GetStudioSystem()->GetFullSystemSignalSender()->SendInstancePropertyValue(
+ instance, getBridge()->GetNameProperty());
+}
+
+void InspectorControlModel::duplicateMaterial()
+{
+ qt3dsdm::Qt3DSDMInstanceHandle instance = m_inspectableBase->getInstance();
+ if (!instance.Valid())
+ return;
+
+ const auto doc = g_StudioApp.GetCore()->GetDoc();
+ const auto type = m_inspectableBase->getObjectType();
+
+ if (type & ~OBJTYPE_IS_MATERIAL)
+ return;
+
+ auto material = instance;
+ if (type == OBJTYPE_REFERENCEDMATERIAL)
+ material = getReferenceMaterialRecursively(m_inspectableBase);
+
+ if (material.Valid()) {
+ const auto sceneEditor = doc->getSceneEditor();
+ auto originalMaterialName = sceneEditor->GetName(material).toQString()
+ + QStringLiteral(" Copy");
+ int slashIndex = originalMaterialName.lastIndexOf(QLatin1Char('/'));
+ if (slashIndex != -1)
+ originalMaterialName = originalMaterialName.mid(slashIndex + 1);
+ auto materialName = originalMaterialName;
+ int i = 1;
+ auto absPath = sceneEditor->getMaterialFilePath(materialName);
+ while (QFileInfo(absPath).exists()) {
+ i++;
+ materialName = originalMaterialName + QString::number(i);
+ absPath = sceneEditor->getMaterialFilePath(materialName);
+ }
+ const auto relPath = doc->GetRelativePathToDoc(absPath);
+
+ qt3dsdm::Qt3DSDMInstanceHandle duplicate;
+ {
+ Q3DStudio::ScopedDocumentEditor scopedEditor(
+ Q3DStudio::SCOPED_DOCUMENT_EDITOR(*doc, {}));
+ duplicate = scopedEditor->getOrCreateMaterial(materialName, false);
+ scopedEditor->copyMaterialProperties(material, duplicate);
+ }
+ // Several aspects of the editor are not updated correctly
+ // if the data core is changed without a transaction
+ // The above scope completes the transaction for creating a new material
+ // Next the added undo has to be popped from the stack
+ // TODO: Find a way to update the editor fully without a transaction
+ g_StudioApp.GetCore()->GetCmdStack()->RemoveLastUndo();
+
+ saveIfMaterial(duplicate);
+
+ Q3DStudio::ScopedDocumentEditor scopedEditor(
+ Q3DStudio::SCOPED_DOCUMENT_EDITOR(*doc, tr("Set Material Type")));
+ doc->SelectDataModelObject(duplicate);
+
+ // Change all material that reference this material to basic materials too
+ Q3DStudio::CString relPathCString = Q3DStudio::CString::fromQString(relPath);
+ Q3DStudio::CString duplicateName = getBridge()->GetName(duplicate, true);
+ auto nameProperty = getBridge()->GetNameProperty();
+ QVector<qt3dsdm::Qt3DSDMInstanceHandle> refMats;
+ doc->getSceneReferencedMaterials(doc->GetSceneInstance(), refMats);
+ for (auto &refMat : qAsConst(refMats)) {
+ if (getBridge()->getMaterialReference(refMat) == instance) {
+ scopedEditor->setMaterialReferenceByPath(refMat, absPath);
+ scopedEditor->setMaterialSourcePath(refMat, relPathCString);
+ scopedEditor->SetName(refMat, duplicateName);
+ doc->GetStudioSystem()->GetFullSystemSignalSender()->SendInstancePropertyValue(
+ refMat, nameProperty);
+ }
+ }
+
+ if (type != OBJTYPE_REFERENCEDMATERIAL)
+ sceneEditor->SetMaterialType(instance, QStringLiteral("Referenced Material"));
+ scopedEditor->setMaterialReferenceByPath(instance, absPath);
+ scopedEditor->setMaterialSourcePath(instance, relPathCString);
+ scopedEditor->SetName(instance, duplicateName);
+ doc->GetStudioSystem()->GetFullSystemSignalSender()->SendInstancePropertyValue(
+ instance, nameProperty);
+ }
+}
+
+void InspectorControlModel::updateMaterialValues(const QStringList &values, int elementIndex,
+ bool updatingShaders)
+{
+ // Find if there are any material items and update the values of those
+ int startIndex = 0;
+ bool isReferenced = !isAnimatableMaterial() && updatingShaders;
+ if (isReferenced && m_groupElements.count() > 0)
+ startIndex = m_groupElements.count() - 1; // Update the last group for referenced materials
+ for (int row = startIndex; row < m_groupElements.count(); ++row) {
+ const CInspectorGroup *inspectorGroup = m_inspectableBase->getGroup(row);
+ const auto group = static_cast<const Qt3DSDMInspectorGroup *>(inspectorGroup);
+ if (group->isMaterial() || isReferenced) {
+ if (m_groupElements[row].controlElements.size()) {
+ auto item = m_groupElements[row].controlElements[elementIndex]
+ .value<InspectorControlBase *>();
+ item->m_values = values;
+ Q_EMIT item->valuesChanged();
+ // Changing values resets the selected index, so pretend the value has also changed
+ Q_EMIT item->valueChanged();
+ updateValidState(item);
+ }
+ }
+ }
+}
+
+void InspectorControlModel::updateShaderValues()
+{
+ int index = 0;
+ if (!isInsideMaterialContainer())
+ index = 1;
+ updateMaterialValues(shaderValues(), index, true);
+}
+
+void InspectorControlModel::updateMatDataValues()
+{
+ int index = 0;
+ if (!isInsideMaterialContainer())
+ index = 1;
+ updateMaterialValues(matDataValues(), index);
+}
+
+void InspectorControlModel::setMaterials(std::vector<Q3DStudio::CFilePath> &materials)
+{
+ m_materials.clear();
+ for (Q3DStudio::CFilePath &path : materials) {
+ const QString absolutePath = g_StudioApp.GetCore()->GetDoc()->GetResolvedPathToDoc(path);
+ const QString name = g_StudioApp.GetCore()->GetDoc()->GetDocumentReader()
+ .GetCustomMaterialName(absolutePath).toQString();
+
+ m_materials.push_back({name, path.toQString()});
+ }
+
+ if (isAnimatableMaterial() && !isDefaultMaterial())
+ updateShaderValues();
+}
+
+void InspectorControlModel::setMatDatas(const std::vector<Q3DStudio::CFilePath> &matDatas)
+{
+ m_matDatas.clear();
+
+ const auto doc = g_StudioApp.GetCore()->GetDoc();
+ bool forceModified = false;
+ bool isDocModified = doc->isModified();
+ const auto sceneEditor = doc->getSceneEditor();
+ if (!sceneEditor)
+ return;
+
+ bool newMaterialSelected = false;
+ for (const Q3DStudio::CFilePath &path : matDatas) {
+ bool isNewFile = true;
+ for (auto &oldPath : m_cachedMatDatas) {
+ if (path.toQString() == oldPath.toQString()) {
+ isNewFile = false;
+ break;
+ }
+ }
+
+ const QString relativePath = path.toQString();
+ const Q3DStudio::CFilePath absolutePath
+ = Q3DStudio::CFilePath::CombineBaseAndRelative(doc->GetDocumentDirectory(), path);
+ const QDir projDir = QDir(g_StudioApp.GetCore()->getProjectFile().getProjectPath());
+ const QString projRelativePath = projDir.relativeFilePath(absolutePath.toQString());
+
+ QString name;
+ QMap<QString, QString> values;
+ QMap<QString, QMap<QString, QString>> textureValues;
+ sceneEditor->getMaterialInfo(
+ absolutePath.toQString(), name, values, textureValues);
+
+ m_matDatas.push_back({name, relativePath, values, textureValues});
+
+ bool needRewrite = false;
+ if (values.contains(QStringLiteral("path"))) {
+ const QString oldPath = values[QStringLiteral("path")];
+ needRewrite = oldPath != projRelativePath;
+ const QString oldAbsolutePath = projDir.absoluteFilePath(oldPath);
+ if (!QFileInfo(oldAbsolutePath).exists()) {
+ const auto instance = sceneEditor->getMaterial(oldPath);
+ if (instance.Valid()) {
+ const QString oldName = sceneEditor->GetName(instance).toQString();
+ const QString newName = sceneEditor
+ ->getMaterialNameFromFilePath(projRelativePath);
+ const QString actualPath = sceneEditor
+ ->getFilePathFromMaterialName(oldName);
+ if (projDir.relativeFilePath(actualPath) == oldPath) {
+ doc->queueMaterialRename(oldName, newName);
+ Q3DStudio::SCOPED_DOCUMENT_EDITOR(*doc, tr("Set Name"))
+ ->setMaterialNameByPath(instance, projRelativePath);
+ forceModified = true;
+ }
+ }
+ }
+ }
+
+ auto material = sceneEditor->getMaterial(relativePath);
+ if (isNewFile && !newMaterialSelected && !material.Valid()) {
+ {
+ material = Q3DStudio::SCOPED_DOCUMENT_EDITOR(*doc, QString())
+ ->getOrCreateMaterial(relativePath, false);
+ }
+ // Several aspects of the editor are not updated correctly
+ // if the data core is changed without a transaction
+ // The above scope completes the transaction for creating a new material
+ // Next the added undo has to be popped from the stack
+ // TODO: Find a way to update the editor fully without a transaction
+ g_StudioApp.GetCore()->GetCmdStack()->RemoveLastUndo();
+ }
+
+ if (material.Valid())
+ sceneEditor->setMaterialValues(relativePath, values, textureValues);
+
+ if (isNewFile && !newMaterialSelected) {
+ doc->SelectDataModelObject(material);
+ newMaterialSelected = true;
+ }
+
+ if (needRewrite && material.Valid())
+ sceneEditor->writeMaterialFile(material, name, false, absolutePath.toQString());
+ }
+
+ if (isBasicMaterial())
+ updateMatDataValues();
+
+ sceneEditor->removeDeletedFromMaterialContainer();
+ // Modified flag has to be restored because of the hidden transaction
+ if (!forceModified)
+ doc->SetModifiedFlag(isDocModified);
+ else
+ doc->SetModifiedFlag(true);
+
+ m_cachedMatDatas = matDatas;
+}
+
+QString InspectorControlModel::getBasicMaterialString() const
+{
+ return QObject::tr("Basic Material");
+}
+
+QString InspectorControlModel::getAnimatableMaterialString() const
+{
+ return QObject::tr("Animatable Material");
+}
+
+QString InspectorControlModel::getReferencedMaterialString() const
+{
+ return QObject::tr("Referenced Material");
+}
+
+QString InspectorControlModel::getStandardMaterialString() const
+{
+ return QObject::tr("Standard");
+}
+
+QString InspectorControlModel::getDefaultMaterialString() const
+{
+ return QObject::tr("Default");
+}
+
+bool InspectorControlModel::isGroupCollapsed(int groupIdx) const
+{
+ if (m_inspectableBase) {
+ auto instance = m_inspectableBase->getInstance();
+ if (instance && groupIdx > -1 && groupIdx < m_groupElements.size()
+ && m_collapseMap.contains(instance)) {
+ return m_collapseMap[instance].contains(groupIdx);
+ }
+ }
+
+ return false;
+}
+
+void InspectorControlModel::updateGroupCollapseState(int groupIdx, bool isCollapsed)
+{
+ if (m_inspectableBase) {
+ auto instance = m_inspectableBase->getInstance();
+ if (instance && groupIdx > -1 && groupIdx < m_groupElements.size()) {
+ if (isCollapsed)
+ m_collapseMap[instance][groupIdx] = true;
+ else
+ m_collapseMap[instance].remove(groupIdx);
+ }
+ }
+}
+
+void InspectorControlModel::updateFontValues(InspectorControlBase *element) const
+{
+ // Find if there are any font items and update the values of those
+ QVector<InspectorControlBase *> fontElements;
+ if (element) {
+ fontElements.append(element);
+ } else {
+ for (int row = 0; row < m_groupElements.count(); ++row) {
+ auto group = m_groupElements[row];
+ for (int p = 0; p < group.controlElements.count(); ++p) {
+ QVariant &element = group.controlElements[p];
+ InspectorControlBase *property = element.value<InspectorControlBase *>();
+ if (property->m_propertyType == qt3dsdm::AdditionalMetaDataType::Font)
+ fontElements.append(property);
+ }
+ }
+ }
+
+ if (fontElements.size()) {
+ std::vector<QString> fontNames;
+ g_StudioApp.GetCore()->GetDoc()->GetProjectFonts(fontNames);
+ QStringList possibleValues;
+ for (const auto &fontName : fontNames)
+ possibleValues.append(fontName);
+ for (auto fontElement : qAsConst(fontElements)) {
+ fontElement->m_values = possibleValues;
+ Q_EMIT fontElement->valuesChanged();
+ // Changing values resets the selected index, so pretend the value has also changed
+ Q_EMIT fontElement->valueChanged();
+ }
+ }
+}
+
+QStringList InspectorControlModel::materialTypeValues() const
+{
+ QStringList values;
+ values.push_back(getBasicMaterialString());
+ values.push_back(getAnimatableMaterialString());
+ values.push_back(getReferencedMaterialString());
+ return values;
+}
+
+QStringList InspectorControlModel::shaderValues() const
+{
+ QStringList values;
+ values.push_back(getStandardMaterialString());
+ for (size_t matIdx = 0, end = m_materials.size(); matIdx < end; ++matIdx)
+ values.push_back(m_materials[matIdx].m_name);
+ return values;
+}
+
+QStringList InspectorControlModel::matDataValues() const
+{
+ QStringList values;
+ QStringList names;
+ const QString defaultMaterialShownName = getDefaultMaterialString();
+ values.push_back(defaultMaterialShownName);
+ names.push_back(defaultMaterialShownName);
+ for (size_t matIdx = 0, end = m_matDatas.size(); matIdx < end; ++matIdx) {
+ QString shownName = m_matDatas[matIdx].m_name;
+ int slashIndex = shownName.lastIndexOf(QLatin1Char('/'));
+ if (slashIndex != -1)
+ shownName = shownName.mid(slashIndex + 1);
+ if (names.contains(shownName))
+ shownName += QLatin1String(" (") + m_matDatas[matIdx].m_relativePath + QLatin1Char(')');
+ else
+ names.push_back(shownName);
+ values.push_back(shownName);
+ }
+ return values;
+}
+
+InspectorControlBase *InspectorControlModel::createMaterialTypeItem(
+ Qt3DSDMInspectable *inspectable, int groupIndex)
+{
+ InspectorControlBase *item = new InspectorControlBase;
+ item->m_instance = inspectable->GetGroupInstance(groupIndex);
+
+ item->m_title = tr("Material Type");
+ item->m_dataType = qt3dsdm::DataModelDataType::StringRef;
+ item->m_propertyType = qt3dsdm::AdditionalMetaDataType::None;
+ item->m_tooltip = tr("Type of material being used");
+ item->m_animatable = false;
+ item->m_values = materialTypeValues();
+
+ switch (inspectable->getObjectType()) {
+ case OBJTYPE_MATERIAL:
+ case OBJTYPE_CUSTOMMATERIAL:
+ item->m_value = getAnimatableMaterialString();
+ break;
+
+ case OBJTYPE_REFERENCEDMATERIAL: {
+ QString sourcePath = getBridge()->GetSourcePath(item->m_instance);
+ item->m_value = sourcePath == getBridge()->getDefaultMaterialName()
+ ? getBasicMaterialString()
+ : getReferencedMaterialString();
+
+ for (auto &mData : m_matDatas) {
+ if (QString::compare(mData.m_relativePath, sourcePath, Qt::CaseInsensitive) == 0) {
+ item->m_value = getBasicMaterialString();
+ break;
+ }
+ }
+ } break;
+
+ default:
+ break;
+ }
+
+ return item;
+}
+
+InspectorControlBase *InspectorControlModel::createShaderItem(
+ Qt3DSDMInspectable *inspectable, int groupIndex)
+{
+ InspectorControlBase *item = new InspectorControlBase;
+ item->m_instance = inspectable->GetGroupInstance(groupIndex);
+
+ item->m_title = tr("Shader");
+ item->m_dataType = qt3dsdm::DataModelDataType::StringRef;
+ item->m_propertyType = qt3dsdm::AdditionalMetaDataType::Renderable;
+ item->m_tooltip = tr("Shader being used");
+ item->m_animatable = false;
+
+ const QStringList values = shaderValues();
+ item->m_values = values;
+
+ QString sourcePath = getBridge()->GetSourcePath(item->m_instance);
+
+ item->m_value = values[0];
+ for (int i = 0, end = int(m_materials.size()); i < end; ++i) {
+ if (m_materials[i].m_relativePath == sourcePath) {
+ item->m_value = values[i + 1]; // + 1 for Standard shader
+ break;
+ }
+ }
+ updateValidState(item);
+ return item;
+}
+
+InspectorControlBase *InspectorControlModel::createMatDataItem(
+ Qt3DSDMInspectable *inspectable, int groupIndex)
+{
+ InspectorControlBase *item = new InspectorControlBase;
+ item->m_instance = inspectable->GetGroupInstance(groupIndex);
+
+ item->m_title = tr("Source Material");
+ item->m_dataType = qt3dsdm::DataModelDataType::StringRef;
+ item->m_propertyType = qt3dsdm::AdditionalMetaDataType::ObjectRef;
+ item->m_tooltip = tr("Source material definitions used");
+ item->m_animatable = false;
+
+ const QStringList values = matDataValues();
+ item->m_values = values;
+
+ QString sourcePath = getBridge()->GetSourcePath(item->m_instance);
+
+ item->m_value = getDefaultMaterialString();
+ for (int i = 0, end = int(m_matDatas.size()); i < end; ++i) {
+ if (QString::compare(m_matDatas[i].m_relativePath, sourcePath, Qt::CaseInsensitive) == 0) {
+ item->m_value = values[i + 1]; // + 1 for Default basic material
+ break;
+ }
+ }
+
+ return item;
+}
+
+InspectorControlBase* InspectorControlModel::createItem(Qt3DSDMInspectable *inspectable,
+ Q3DStudio::Qt3DSDMInspectorRow *row,
+ int groupIndex)
+{
+ return createItem(inspectable, row->GetMetaDataPropertyInfo(), groupIndex);
+}
+
+InspectorControlBase* InspectorControlModel::createItem(Qt3DSDMInspectable *inspectable,
+ const qt3dsdm::SMetaDataPropertyInfo &metaProperty,
+ int groupIndex)
+{
+ const auto studio = g_StudioApp.GetCore()->GetDoc()->GetStudioSystem();
+ if (metaProperty.m_IsHidden)
+ return nullptr;
+
+ Q3DStudio::CString title;
+ title.Assign(metaProperty.m_FormalName.c_str());
+ if (title.IsEmpty())
+ title.Assign(metaProperty.m_Name.c_str());
+
+ // Hide name for basic materials
+ if (title == "Name" && isBasicMaterial())
+ return nullptr;
+
+ // Hide everything else than name for signals
+ if (title != "Name" && getBridge()->IsSignalInstance(inspectable->getInstance()))
+ return nullptr;
+
+ InspectorControlBase *item = new InspectorControlBase;
+ item->m_property = metaProperty.m_Property;
+ item->m_instance = inspectable->GetGroupInstance(groupIndex);
+ item->m_metaProperty = metaProperty;
+
+ item->m_title = title.toQString();
+
+ const auto propertySystem = studio->GetPropertySystem();
+ item->m_dataType = propertySystem->GetDataType(metaProperty.m_Property);
+ item->m_propertyType = static_cast<qt3dsdm::AdditionalMetaDataType::Value>
+ (propertySystem->GetAdditionalMetaDataType(item->m_instance, metaProperty.m_Property));
+ item->m_tooltip = Q3DStudio::CString(metaProperty.m_Description.c_str()).toQString();
+ // \n is parsed as \\n from the material and effect files. Replace them to fix multi-line
+ // tooltips
+ item->m_tooltip.replace(QLatin1String("\\n"), QLatin1String("\n"));
+
+ item->m_animatable = metaProperty.m_Animatable &&
+ studio->GetAnimationSystem()->IsPropertyAnimatable(item->m_instance,
+ metaProperty.m_Property);
+ // If a property is animatable, it should be controllable in addition to
+ // properties explicitly set as controllable in metadata
+ item->m_controllable = item->m_animatable || metaProperty.m_Controllable;
+
+ // disable IBL Override for reference and 'default basic' materials
+ if (item->m_title == QLatin1String("IBL Override")
+ && getBridge()->GetObjectType(item->m_instance) == OBJTYPE_REFERENCEDMATERIAL
+ && (!isBasicMaterial() || getBridge()->isDefaultMaterial(item->m_instance))) {
+ item->m_enabled = false;
+ }
+ auto signalProvider = studio->GetFullSystemSignalProvider();
+ if (item->m_animatable) {
+ item->m_animated = studio->GetAnimationSystem()->IsPropertyAnimated(item->m_instance,
+ metaProperty.m_Property);
+
+ // Update the Animate Toggle on undo/redo
+ item->m_connections.push_back(signalProvider->ConnectAnimationCreated(
+ std::bind(&InspectorControlModel::updateAnimateToggleState,
+ this, item)));
+
+ item->m_connections.push_back(signalProvider->ConnectAnimationDeleted(
+ std::bind(&InspectorControlModel::updateAnimateToggleState,
+ this, item)));
+ }
+
+ if (item->m_controllable) {
+ // Set the name of current controller
+ item->m_controller = currentControllerValue(item->m_instance, item->m_property);
+ // Update UI icon state and tooltip
+ updateControlledToggleState(item);
+ item->m_connections.push_back(signalProvider->ConnectControlledToggled(
+ std::bind(&InspectorControlModel::updateControlledToggleState, this, item)));
+ }
+
+ // synchronize the value itself
+ updatePropertyValue(item);
+ updateValidState(item);
+ return item;
+}
+
+qt3dsdm::SValue InspectorControlModel::currentPropertyValue(long instance, int handle) const
+{
+ auto propertySystem = g_StudioApp.GetCore()->GetDoc()->GetStudioSystem()->GetPropertySystem();
+ qt3dsdm::SValue value;
+ propertySystem->GetInstancePropertyValue(instance, handle, value);
+
+ return value;
+}
+
+QString InspectorControlModel::currentControllerValue(long instance, int handle) const
+{
+ return g_StudioApp.GetCore()->GetDoc()->GetCurrentController(instance, handle);
+}
+
+void InspectorControlModel::updateControlledToggleState(InspectorControlBase* inItem) const
+{
+ if (inItem->m_instance) {
+ const auto studio = g_StudioApp.GetCore()->GetDoc()->GetStudioSystem();
+ // toggle if controlledproperty contains the name of this property
+ qt3dsdm::SValue currPropVal = currentPropertyValue(
+ inItem->m_instance, studio->GetPropertySystem()->GetAggregateInstancePropertyByName(
+ inItem->m_instance, qt3dsdm::TCharStr(L"controlledproperty")));
+ Q3DStudio::CString currPropValStr;
+ if (!currPropVal.empty())
+ currPropValStr = qt3dsdm::get<qt3dsdm::TDataStrPtr>(currPropVal)->GetData();
+ // Restore original tool tip from metadata when turning control off
+ if (!currPropValStr.size()) {
+ inItem->m_controlled = false;
+ inItem->m_controller = "";
+ } else {
+ Q3DStudio::CString propName
+ = studio->GetPropertySystem()->GetName(inItem->m_property).c_str();
+ // Search specifically for whitespace followed with registered property name.
+ // This avoids finding datainput with same name as the property, as datainput
+ // name is always prepended with "$"
+ long propNamePos = currPropValStr.find(" " + propName);
+ if ((propNamePos == currPropValStr.ENDOFSTRING)
+ && (propNamePos != 0)) {
+ inItem->m_controlled = false;
+ inItem->m_controller = "";
+ } else {
+ inItem->m_controlled = true;
+ // controller name is prepended with "$" to differentiate from property
+ // with same name. Reverse find specifically for $.
+ long posCtrlr = currPropValStr.substr(0, propNamePos).ReverseFind("$");
+
+ // this is the first controller - property pair in controlledproperty
+ if (posCtrlr < 0)
+ posCtrlr = 0;
+
+ // remove $ from controller name for showing it in UI
+ posCtrlr++;
+ const QString ctrlName = currPropValStr.substr(
+ posCtrlr, propNamePos - posCtrlr).toQString();
+
+ inItem->m_controller = ctrlName;
+ }
+ }
+
+ Q_EMIT inItem->tooltipChanged();
+ // Emit signal always to trigger updating of controller name in UI
+ // also when user switches from one controller to another
+ Q_EMIT inItem->controlledChanged();
+ }
+}
+
+void InspectorControlModel::updateAnimateToggleState(InspectorControlBase* inItem)
+{
+ const auto studio = g_StudioApp.GetCore()->GetDoc()->GetStudioSystem();
+ bool animated = studio->GetAnimationSystem()->IsPropertyAnimated(inItem->m_instance,
+ inItem->m_property);
+ if (animated != inItem->m_animated) {
+ inItem->m_animated = animated;
+ Q_EMIT inItem->animatedChanged();
+ }
+}
+
+void InspectorControlModel::updateValidState(InspectorControlBase *inItem) const
+{
+ const auto bridge = g_StudioApp.GetCore()->GetDoc()->GetStudioSystem()
+ ->GetClientDataModelBridge();
+ if (bridge->GetSourcePathProperty() == inItem->m_property) {
+ QFileInfo fileinfo(g_StudioApp.GetCore()->GetDoc()->GetResolvedPathToDoc(
+ bridge->GetSourcePath(inItem->m_instance)));
+ if (fileinfo.exists() != inItem->m_valid) {
+ inItem->m_valid = !inItem->m_valid;
+ Q_EMIT inItem->validDataChanged();
+ }
+ }
+
+ // Check the validity of shader.
+ if (inItem->m_title == tr("Shader")) {
+ QString err;
+ g_StudioApp.getRenderer().getObjectError(inItem->m_instance, err);
+ if (!err.isEmpty()) {
+ inItem->m_tooltip = err;
+ inItem->m_valid = false;
+ Q_EMIT inItem->validDataChanged();
+ Q_EMIT inItem->tooltipChanged();
+ } else {
+ inItem->m_tooltip = tr("Shader being used");
+ inItem->m_valid = true;
+ Q_EMIT inItem->validDataChanged();
+ Q_EMIT inItem->tooltipChanged();
+ }
+ }
+}
+
+bool InspectorControlModel::isTreeRebuildRequired(CInspectableBase* inspectBase)
+{
+ if (inspectBase != m_inspectableBase || !inspectBase)
+ return true;
+
+ long theCount = m_inspectableBase->getGroupCount();
+ auto refMaterial = getReferenceMaterial(inspectBase);
+ if (refMaterial != m_refMaterial)
+ return true;
+ long refMaterialGroupCount = 0;
+ if (refMaterial.Valid())
+ refMaterialGroupCount = 1; // Only the last group of the refMaterial is used
+
+ if (m_groupElements.size() != theCount + refMaterialGroupCount)
+ return true;
+
+ for (long theIndex = 0; theIndex < theCount; ++theIndex) {
+ const CInspectorGroup *theInspectorGroup = m_inspectableBase->getGroup(theIndex);
+ if (m_groupElements.at(theIndex).groupTitle != theInspectorGroup->GetName())
+ return true;
+ }
+
+ return false;
+}
+
+bool InspectorControlModel::isGroupRebuildRequired(CInspectableBase *inspectable,
+ int theIndex) const
+{
+ Q_ASSERT(theIndex < m_groupElements.size());
+ const CInspectorGroup *theInspectorGroup = inspectable->getGroup(theIndex);
+ const auto existingGroup = m_groupElements.at(theIndex);
+ if (existingGroup.groupTitle != theInspectorGroup->GetName())
+ return true;
+
+ if (inspectable->getObjectType() != OBJTYPE_GUIDE) {
+ const auto dmInspectable = static_cast<Qt3DSDMInspectable *>(inspectable);
+ const auto group = static_cast<const Qt3DSDMInspectorGroup *>(theInspectorGroup);
+ int existingIndex = 0;
+ if (group && group->isMaterial()) {
+ auto i = existingGroup.controlElements.at(existingIndex++)
+ .value<InspectorControlBase *>();
+ if (i->m_instance != dmInspectable->GetGroupInstance(theIndex))
+ return true;
+
+ if (!isInsideMaterialContainer())
+ existingIndex++; // Add material type dropdown to existing elements
+ }
+
+ if ((existingGroup.controlElements.size() - existingIndex) != group->GetRows().size())
+ return true;
+
+ for (const auto row : group->GetRows()) {
+ auto i = existingGroup.controlElements.at(existingIndex++)
+ .value<InspectorControlBase *>();
+ if (i->m_instance != dmInspectable->GetGroupInstance(theIndex))
+ return true;
+
+ if (i->m_property != row->GetMetaDataPropertyInfo().m_Property)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+CClientDataModelBridge *InspectorControlModel::getBridge() const
+{
+ return g_StudioApp.GetCore()->GetDoc()->GetStudioSystem()->GetClientDataModelBridge();
+}
+
+CInspectableBase *InspectorControlModel::getInspectableFromInstance(
+ qt3dsdm::Qt3DSDMInstanceHandle inInstance)
+{
+ CDoc *doc = g_StudioApp.GetCore()->GetDoc();
+ qt3dsdm::ISlideSystem *slideSystem = doc->GetStudioSystem()->GetSlideSystem();
+
+ if (doc->GetDocumentReader().IsInstance(inInstance)) {
+ qt3dsdm::Qt3DSDMSlideHandle activeSlide = doc->GetActiveSlide();
+ qt3dsdm::Qt3DSDMInstanceHandle activeSlideInstance = 0;
+
+ // Slide, scene or component
+ if (inInstance == getBridge()->GetOwningComponentInstance(activeSlide))
+ activeSlideInstance = slideSystem->GetSlideInstance(activeSlide);
+
+ return new Qt3DSDMInspectable(inInstance, activeSlideInstance);
+ }
+
+ return nullptr;
+}
+
+auto InspectorControlModel::computeTree(CInspectableBase *inspectableBase)
+ -> QVector<GroupInspectorControl>
+{
+ QVector<GroupInspectorControl> result;
+
+ if (inspectableBase) {
+ qt3dsdm::Qt3DSDMInstanceHandle instance = inspectableBase->getInstance();
+
+ if (isDefaultMaterial()) {
+ // for default materials skip the first 2 groups (Basic Properties, Lighting) and only
+ // show the Material group
+ result.append(computeGroup(inspectableBase, 2));
+ result[0].groupInfo = tr("\nDefault material cannot be edited. Create new or import "
+ "material, then apply.");
+ } else {
+ // disable animation for basic materials, this applies only when editing the basic
+ // material from the project palette.
+ bool disableAnim = instance.Valid()
+ && getBridge()->isInsideMaterialContainer(instance);
+ long groupCount = inspectableBase->getGroupCount();
+ for (long idx = 0; idx < groupCount; ++idx)
+ result.append(computeGroup(inspectableBase, idx, disableAnim));
+ }
+
+ // show original material properties for referenced materials
+ auto refMaterial = getReferenceMaterial(inspectableBase);
+ if (refMaterial.Valid()) {
+ auto refMaterialInspectable = getInspectableFromInstance(refMaterial);
+ if (refMaterialInspectable) {
+ QString materialSrcPath;
+ if (instance.Valid())
+ materialSrcPath = getBridge()->GetSourcePath(instance);
+
+ if (materialSrcPath != getBridge()->getDefaultMaterialName()
+ && getBridge()->GetSourcePath(refMaterial)
+ != getBridge()->getDefaultMaterialName()) {
+ result.append(computeGroup(refMaterialInspectable,
+ refMaterialInspectable->getGroupCount() - 1,
+ true, true));
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+auto InspectorControlModel::computeGroup(CInspectableBase *inspectable, int index,
+ bool disableAnimation, bool isReference)
+ -> GroupInspectorControl
+{
+ CInspectorGroup *theInspectorGroup = inspectable->getGroup(index);
+ GroupInspectorControl result;
+ result.groupTitle = theInspectorGroup->GetName();
+ result.groupInfo.clear();
+
+ if (isReference)
+ result.groupTitle += tr(" (Reference)");
+
+ if (inspectable->getObjectType() != OBJTYPE_GUIDE) {
+ const auto dmInspectable = static_cast<Qt3DSDMInspectable *>(inspectable);
+ const auto group = static_cast<Qt3DSDMInspectorGroup *>(theInspectorGroup);
+
+ bool isBasicMat = isBasicMaterial(dmInspectable);
+ if (group->isMaterial()) {
+ InspectorControlBase *item = nullptr;
+
+ if (!isInsideMaterialContainer(dmInspectable) && !isReference) {
+ item = createMaterialTypeItem(dmInspectable, index);
+ if (item)
+ result.controlElements.push_back(QVariant::fromValue(item));
+ }
+
+ if (isAnimatableMaterial(dmInspectable)) {
+ item = createShaderItem(dmInspectable, index);
+ if (item)
+ result.controlElements.push_back(QVariant::fromValue(item));
+ } else if (isBasicMat) {
+ item = createMatDataItem(dmInspectable, index);
+ if (item)
+ result.controlElements.push_back(QVariant::fromValue(item));
+ }
+ }
+
+ for (const auto row : group->GetRows()) {
+ InspectorControlBase *item = createItem(dmInspectable, row, index);
+ if (!item)
+ continue;
+
+ if (disableAnimation)
+ item->m_animatable = false;
+
+ if (!isBasicMat || item->m_title != getReferencedMaterialString())
+ result.controlElements.push_back(QVariant::fromValue(item));
+ }
+ } else { // Guide
+ const auto guideInspectable = static_cast<GuideInspectable *>(inspectable);
+ // Guide properties don't come from metadata as they are not actual objects
+ m_guideInspectable = guideInspectable;
+ const auto &properties = m_guideInspectable->properties();
+ for (int i = 0, count = int(properties.size()); i < count; ++i) {
+ auto &prop = properties[i];
+ InspectorControlBase *item = new InspectorControlBase;
+ item->m_title = prop->GetInspectableFormalName();
+ item->m_dataType = prop->GetInspectableType();
+ item->m_propertyType = prop->GetInspectableAdditionalType();
+ item->m_tooltip = prop->GetInspectableDescription();
+ item->m_animatable = false;
+ item->m_controllable = false;
+ item->m_property = i + 1; // Zero property is considered invalid, so +1
+ result.controlElements.push_back(QVariant::fromValue(item));
+ updatePropertyValue(item);
+ }
+ }
+
+ return result;
+}
+
+void InspectorControlModel::rebuildTree()
+{
+ beginResetModel();
+ m_guideInspectable = nullptr;
+ QVector<QObject *> deleteVector;
+ for (int i = 0; i < m_groupElements.count(); ++i) {
+ auto group = m_groupElements[i];
+ for (int p = 0; p < group.controlElements.count(); ++p)
+ deleteVector.append(group.controlElements[p].value<QObject *>());
+ }
+ m_groupElements = computeTree(m_inspectableBase);
+ endResetModel();
+
+ // Clean the old objects after reset is done so that qml will not freak out about null pointers
+ for (int i = 0; i < deleteVector.count(); ++i)
+ deleteVector[i]->deleteLater();
+
+ m_refMaterial = getReferenceMaterial(m_inspectableBase);
+}
+
+int InspectorControlModel::rowCount(const QModelIndex &parent) const
+{
+ if (parent.isValid())
+ return 0;
+
+ return m_groupElements.count();
+}
+
+void InspectorControlModel::updatePropertyValue(InspectorControlBase *element) const
+{
+ const auto doc = g_StudioApp.GetCore()->GetDoc();
+ auto studioSystem = doc->GetStudioSystem();
+ const auto propertySystem = studioSystem->GetPropertySystem();
+ qt3dsdm::SValue value;
+ auto instance = element->m_instance;
+ qt3dsdm::Option<qt3dsdm::SMetaDataPropertyInfo> info;
+
+ // For ref materials update IBL Override from the referenced material. This applies only for
+ // basic materials as IBL Override is disabled for referenced (and default basic) materials.
+ if (element->m_property
+ == getBridge()->GetObjectDefinitions().m_MaterialBase.m_IblProbe.m_Property) {
+ int refInstance = getBridge()->getMaterialReference(instance);
+ if (refInstance)
+ instance = refInstance;
+ }
+
+ if (m_guideInspectable) {
+ value = m_guideInspectable->properties()
+ [handleToGuidePropIndex(element->m_property)]->GetInspectableData();
+ } else {
+ if (!propertySystem->HandleValid(instance))
+ return;
+ propertySystem->GetInstancePropertyValue(instance, element->m_property, value);
+
+ if (value.getType() == qt3dsdm::DataModelDataType::None) {
+ updateValidState(element); // Shader property has type none, still need to update valid
+ return;
+ }
+
+ const auto metaDataProvider = doc->GetStudioSystem()->GetActionMetaData();
+ info = metaDataProvider->GetMetaDataPropertyInfo(
+ metaDataProvider->GetMetaDataProperty(instance, element->m_property));
+ }
+ bool isTexture = info.hasValue()
+ && info->GetAdditionalType() == qt3dsdm::AdditionalMetaDataType::Texture;
+ bool skipEmits = false;
+ switch (element->m_dataType) {
+ case qt3dsdm::DataModelDataType::String: {
+ if (value.getType() == qt3dsdm::DataModelDataType::Long4) {
+ qt3dsdm::Option<qt3dsdm::SLong4> guid = qt3dsdm::get<qt3dsdm::SLong4>(value);
+ qt3dsdm::Qt3DSDMInstanceHandle imageInstance = doc->GetDocumentReader()
+ .GetInstanceForGuid(guid);
+ if (imageInstance.Valid()) {
+ Q3DStudio::CString path = doc->GetDocumentReader().GetSourcePath(imageInstance);
+ Q3DStudio::CFilePath relPath(path);
+ element->m_value = QVariant(relPath.GetFileName().toQString());
+ } else {
+ element->m_value = QVariant(QString());
+ }
+ } else {
+ QString stringValue = qt3dsdm::get<QString>(value);
+ if (getBridge()->isInsideMaterialContainer(element->m_instance)) {
+ int index = stringValue.lastIndexOf(QLatin1Char('/'));
+ if (index != -1)
+ stringValue = stringValue.mid(index + 1);
+ }
+
+ if (!isTexture)
+ element->m_value = stringValue;
+ else
+ element->m_value = {};
+ }
+ }
+ Q_FALLTHROUGH(); // fall-through for other String-derived datatypes
+
+ case qt3dsdm::DataModelDataType::StringOrInt:
+ if (element->m_propertyType == qt3dsdm::AdditionalMetaDataType::StringList) {
+ QStringList stringlist;
+ if (m_guideInspectable) {
+ const auto strings = m_guideInspectable->properties()
+ [handleToGuidePropIndex(element->m_property)]->GetInspectableList();
+ for (auto &str : strings)
+ stringlist.append(QString::fromWCharArray(str.wide_str()));
+ } else {
+ stringlist = qt3dsdm::get<QStringList>(info->m_MetaDataData);
+ }
+ auto slideSystem = studioSystem->GetSlideSystem();
+
+ if (element->m_title == QLatin1String("Play Mode")) {
+ std::pair<bool, bool> slideData(
+ getSlideCharacteristics(element->m_instance, *studioSystem->GetSlideCore(),
+ *slideSystem));
+ bool hasNextSlide(slideData.first);
+ bool hasPreviousSlide(slideData.second);
+ if (!hasNextSlide && !hasPreviousSlide)
+ stringlist.removeAll("Play Through To...");
+ } else if (element->m_title == QLatin1String("Play Through To")) {
+ // the code duplication is intentional as we may ask for slide characteristics
+ // only if the property refers to slides
+ std::pair<bool, bool> slideData(
+ getSlideCharacteristics(element->m_instance, *studioSystem->GetSlideCore(),
+ *slideSystem));
+ bool hasNextSlide(slideData.first);
+ bool hasPreviousSlide(slideData.second);
+ if (!hasNextSlide)
+ stringlist.removeAll("Next");
+ if (!hasPreviousSlide)
+ stringlist.removeAll("Previous");
+
+ auto itemCount = stringlist.count();
+ QString listOpt;
+ int selectedSlideHandle = 0;
+ int selectedIndex = -1;
+ qt3dsdm::SStringOrInt stringOrInt = qt3dsdm::get<qt3dsdm::SStringOrInt>(value);
+ if (stringOrInt.GetType() == qt3dsdm::SStringOrIntTypes::String)
+ listOpt = QString::fromWCharArray(qt3dsdm::get<qt3dsdm::TDataStrPtr>
+ (stringOrInt.m_Value)->GetData());
+ else
+ selectedSlideHandle = qt3dsdm::get<long>(stringOrInt.m_Value);
+
+ selectedIndex = stringlist.indexOf(listOpt);
+ // Add the slide names (exclude the master slide)
+ auto slideHandle = slideSystem->GetSlideByInstance(instance);
+ auto masterSlide = slideSystem->GetMasterSlide(slideHandle);
+ long slideCount = long(slideSystem->GetSlideCount(masterSlide));
+ for (long slideIndex = 1; slideIndex < slideCount; ++slideIndex) {
+ auto currentSlide = slideSystem->GetSlideByIndex(masterSlide, slideIndex);
+ auto currentInstance = slideSystem->GetSlideInstance(currentSlide);
+
+ QString slideName = getBridge()->GetName(currentInstance).toQString();
+ //hack to add a separator before the item
+ if (slideIndex == 1 && itemCount > 0)
+ slideName += "|separator";
+ stringlist.append(slideName);
+
+ if (currentSlide.GetHandleValue() == selectedSlideHandle)
+ selectedIndex = slideIndex + itemCount - 1;
+ }
+
+ element->m_value = QString(selectedIndex > 0 ? stringlist[selectedIndex]
+ : stringlist.first()).replace(QLatin1String("|separator"),
+ QString());
+ }
+ element->m_values = stringlist;
+ } else if (element->m_propertyType == qt3dsdm::AdditionalMetaDataType::Import) {
+ QStringList stringlist = qt3dsdm::get<QStringList>(info->m_MetaDataData);
+ element->m_values = stringlist;
+ } else if (element->m_propertyType == qt3dsdm::AdditionalMetaDataType::Renderable) {
+ element->m_values = renderableItems();
+ if (element->m_value.toString().isEmpty())
+ element->m_value = element->m_values.toStringList().at(0);
+ } else if (element->m_propertyType == qt3dsdm::AdditionalMetaDataType::MultiLine) {
+ element->m_value = qt3dsdm::get<QString>(value);
+ } else if (element->m_propertyType == qt3dsdm::AdditionalMetaDataType::Font) {
+ updateFontValues(element);
+ skipEmits = true; // updateFontValues handles emits in correct order
+ } else if (element->m_propertyType == qt3dsdm::AdditionalMetaDataType::Mesh) {
+ QString meshValue = QFileInfo(qt3dsdm::get<QString>(value)).fileName();
+ element->m_value = meshValue.startsWith('#'_L1) ? meshValue.mid(1) : meshValue;
+ } else if (element->m_propertyType == qt3dsdm::AdditionalMetaDataType::Texture) {
+ if (value.getType() == qt3dsdm::DataModelDataType::Long4) {
+ qt3dsdm::Option<qt3dsdm::SLong4> guid = qt3dsdm::get<qt3dsdm::SLong4>(value);
+ qt3dsdm::Qt3DSDMInstanceHandle imageInstance = doc->GetDocumentReader()
+ .GetInstanceForGuid(guid);
+ if (imageInstance.Valid()) {
+ Q3DStudio::CString path = doc->GetDocumentReader().GetSourcePath(imageInstance);
+ Q3DStudio::CFilePath relPath(path);
+ element->m_value = QVariant(relPath.GetFileName().toQString());
+ } else {
+ element->m_value = QVariant(QString());
+ }
+ } else {
+ element->m_value = {};
+ }
+ } else if (element->m_propertyType == qt3dsdm::AdditionalMetaDataType::PathBuffer) {
+ element->m_value = qt3dsdm::get<QString>(value);
+ } else if (element->m_propertyType == qt3dsdm::AdditionalMetaDataType::String) {
+ // Basic string already handled, do not warn about that.
+ // If we hit any other datatypes then give a warning
+ } else {
+ qWarning() << "KDAB_TODO: InspectorControlModel::updatePropertyValue: need to implement:"
+ << element->m_dataType << " element->m_propertyType : "
+ << element->m_propertyType;
+ }
+ break;
+
+ case qt3dsdm::DataModelDataType::StringRef:
+ if (element->m_propertyType == qt3dsdm::AdditionalMetaDataType::None) {
+ element->m_value = qt3dsdm::get<QString>(value);
+ }
+ break;
+
+ case qt3dsdm::DataModelDataType::Bool:
+ element->m_value = qt3dsdm::get<bool>(value);
+ break;
+
+ case qt3dsdm::DataModelDataType::Long4:
+ if (element->m_propertyType == qt3dsdm::AdditionalMetaDataType::Image) {
+ qt3dsdm::Option<qt3dsdm::SLong4> guid = qt3dsdm::get<qt3dsdm::SLong4>(value);
+ qt3dsdm::Qt3DSDMInstanceHandle imageInstance = doc->GetDocumentReader()
+ .GetInstanceForGuid(guid);
+ if (imageInstance.Valid()) {
+ Q3DStudio::CString path = doc->GetDocumentReader().GetSourcePath(imageInstance);
+ Q3DStudio::CFilePath relPath(path);
+ element->m_value = QVariant(relPath.GetFileName().toQString());
+ } else {
+ element->m_value = QVariant(QString());
+ }
+ } else {
+ qWarning() << "KDAB_TODO: InspectorControlModel::updatePropertyValue: need to implement:"
+ << element->m_dataType << " " << element->m_title;
+ }
+ break;
+
+ case qt3dsdm::DataModelDataType::Long:
+ if (element->m_propertyType == qt3dsdm::AdditionalMetaDataType::Range) {
+ element->m_value = qt3dsdm::get<int>(value);
+ qt3dsdm::SMetaDataRange ranges;
+ if (m_guideInspectable) {
+ const auto prop = m_guideInspectable->properties()
+ [handleToGuidePropIndex(element->m_property)];
+ ranges.m_min = prop->GetInspectableMin();
+ ranges.m_max = prop->GetInspectableMax();
+ } else {
+ ranges = qt3dsdm::get<qt3dsdm::SMetaDataRange>(info->m_MetaDataData);
+ }
+ const QList<double> rangesValues{ranges.m_min, ranges.m_max, double(ranges.m_decimals)};
+ element->m_values = QVariant::fromValue<QList<double> >(rangesValues);
+ }
+ else if (element->m_propertyType == qt3dsdm::AdditionalMetaDataType::ShadowMapResolution) {
+ element->m_value = qt3dsdm::get<int>(value);
+ } else {
+ qWarning() << "KDAB_TODO: InspectorControlModel::updatePropertyValue: need to implement:"
+ << element->m_dataType;
+ }
+ break;
+
+ case qt3dsdm::DataModelDataType::Float3:
+ if (element->m_propertyType == qt3dsdm::AdditionalMetaDataType::Color) {
+ element->m_value = qt3dsdm::get<QColor>(value);
+ } else if (element->m_propertyType == qt3dsdm::AdditionalMetaDataType::Rotation) {
+ const QVector3D theFloat3 = qt3dsdm::get<QVector3D>(value);
+ const QList<double> float3Values{theFloat3.x(), theFloat3.y(), theFloat3.z()};
+ element->m_values = QVariant::fromValue<QList<double> >(float3Values);
+ } else if (element->m_propertyType == qt3dsdm::AdditionalMetaDataType::None) {
+ const QVector3D theFloat3 = qt3dsdm::get<QVector3D>(value);
+ const QList<double> float3Values{theFloat3.x(), theFloat3.y(), theFloat3.z()};
+ element->m_values = QVariant::fromValue<QList<double> >(float3Values);
+ }
+ break;
+
+ case qt3dsdm::DataModelDataType::Float4:
+ if (element->m_propertyType == qt3dsdm::AdditionalMetaDataType::Color) {
+ element->m_value = qt3dsdm::get<QColor>(value);
+ } else if (element->m_propertyType == qt3dsdm::AdditionalMetaDataType::None) {
+ const QVector4D theFloat4 = qt3dsdm::get<QVector4D>(value);
+ const QList<double> float4Values{theFloat4.x(), theFloat4.y(), theFloat4.z(),
+ theFloat4.w()};
+ element->m_values = QVariant::fromValue<QList<double> >(float4Values);
+ }
+ break;
+
+ case qt3dsdm::DataModelDataType::Float2:
+ if (element->m_propertyType == qt3dsdm::AdditionalMetaDataType::None) {
+ const QVector2D theFloat2 = qt3dsdm::get<QVector2D>(value);
+ const QList<double> float2Values{theFloat2.x(), theFloat2.y()};
+ element->m_values = QVariant::fromValue<QList<double> >(float2Values);
+ } else {
+ qWarning() << "TODO: InspectorControlModel::updatePropertyValue: need to implement:"
+ << element->m_dataType << element->m_propertyType;
+ }
+ break;
+
+ case qt3dsdm::DataModelDataType::Float:
+ if (element->m_propertyType == qt3dsdm::AdditionalMetaDataType::None) {
+ element->m_value = qt3dsdm::get<float>(value);
+ } else if (element->m_propertyType == qt3dsdm::AdditionalMetaDataType::Range) {
+ element->m_value = qt3dsdm::get<float>(value);
+ const qt3dsdm::SMetaDataRange ranges = qt3dsdm::get<qt3dsdm::SMetaDataRange>(info->m_MetaDataData);
+ const QList<double> rangesValues{ranges.m_min, ranges.m_max, double(ranges.m_decimals)};
+ element->m_values = QVariant::fromValue<QList<double> >(rangesValues);
+ } else if (element->m_propertyType == qt3dsdm::AdditionalMetaDataType::FontSize) {
+ element->m_value = qt3dsdm::get<float>(value);
+ }
+ break;
+
+ case qt3dsdm::DataModelDataType::ObjectRef:
+ if (element->m_propertyType == qt3dsdm::AdditionalMetaDataType::ObjectRef) {
+ IObjectReferenceHelper *objRefHelper = doc->GetDataModelObjectReferenceHelper();
+ if (objRefHelper) {
+ qt3dsdm::Qt3DSDMInstanceHandle refInstance = objRefHelper->Resolve(value, instance);
+ QString refName = objRefHelper->LookupObjectFormalName(refInstance).toQString();
+ if (getBridge()->IsReferencedMaterialInstance(instance) && !refName.isEmpty()) {
+ // get the material's object name (parent)
+ auto parentInstance = getBridge()->GetParentInstance(refInstance);
+ qt3dsdm::SValue vParent;
+ propertySystem->GetInstancePropertyValue(parentInstance,
+ getBridge()->GetObjectDefinitions().m_Named.m_NameProp, vParent);
+ QString parentName = qt3dsdm::get<QString>(vParent);
+ refName.append(QLatin1String(" (") + parentName + QLatin1String(")"));
+ }
+ element->m_value = refName;
+ }
+ }
+ break;
+
+ default:
+ qWarning() << "TODO: InspectorControlModel::updatePropertyValue: I've no idea how to handle this datatype"
+ << element->m_dataType;
+ break;
+ }
+
+ if (!skipEmits) {
+ Q_EMIT element->valueChanged();
+ Q_EMIT element->valuesChanged();
+ }
+
+ // Controlled state must be manually set after undo operations,
+ // as only the "controlledproperty" is restored in undo,
+ // not the controlled flag nor the tooltip
+ if (element->m_controllable)
+ updateControlledToggleState(element);
+
+ updateValidState(element);
+}
+
+void InspectorControlModel::refreshRenderables()
+{
+ for (int row = 0; row < m_groupElements.count(); ++row) {
+ auto group = m_groupElements[row];
+ for (int p = 0; p < group.controlElements.count(); ++p) {
+ QVariant& element = group.controlElements[p];
+ InspectorControlBase *property = element.value<InspectorControlBase *>();
+ if (property->m_property.Valid()
+ && property->m_propertyType == qt3dsdm::AdditionalMetaDataType::Renderable) {
+ updatePropertyValue(property);
+ }
+ }
+ }
+}
+
+void InspectorControlModel::refreshTree()
+{
+ //check if the structure has changed
+ if (isTreeRebuildRequired(m_inspectableBase)) {
+ rebuildTree();
+ } else {
+ // group structure is intact, let's walk to see which rows changed
+ QVector<QObject *> deleteVector;
+ long theCount = m_inspectableBase->getGroupCount();
+ for (long theIndex = 0; theIndex < theCount; ++theIndex) {
+ if (isGroupRebuildRequired(m_inspectableBase, theIndex)) {
+ auto group = m_groupElements[theIndex];
+ for (int p = 0; p < group.controlElements.count(); ++p)
+ deleteVector.append(group.controlElements[p].value<QObject *>());
+ m_groupElements[theIndex] = computeGroup(m_inspectableBase, theIndex);
+ Q_EMIT dataChanged(index(theIndex), index(theIndex));
+ }
+ }
+ }
+}
+
+void InspectorControlModel::refresh()
+{
+ refreshTree();
+ // update values
+ for (int row = 0; row < m_groupElements.count(); ++row) {
+ auto group = m_groupElements[row];
+ for (int p = 0; p < group.controlElements.count(); ++p) {
+ QVariant& element = group.controlElements[p];
+ InspectorControlBase *property = element.value<InspectorControlBase *>();
+ if (property->m_property.Valid() || property->m_title == tr("Shader")) {
+ updatePropertyValue(property);
+ updateControlledToggleState(property);
+ }
+ }
+ }
+ Q_EMIT dataChanged(index(0), index(rowCount() - 1));
+}
+
+void InspectorControlModel::saveIfMaterial(qt3dsdm::Qt3DSDMInstanceHandle instance)
+{
+ if (!instance.Valid())
+ return;
+
+ const auto doc = g_StudioApp.GetCore()->GetDoc();
+ const auto sceneEditor = doc->getSceneEditor();
+
+ const auto studio = doc->GetStudioSystem();
+ EStudioObjectType type = getBridge()->GetObjectType(instance);
+
+ auto material = instance;
+ if (type == OBJTYPE_IMAGE)
+ material = sceneEditor->GetParent(instance);
+
+ if (!material.Valid())
+ return;
+
+ const auto refMaterial = getBridge()->getMaterialReference(material);
+ if (refMaterial.Valid())
+ material = refMaterial;
+
+ if (!getBridge()->isInsideMaterialContainer(material))
+ return;
+
+ type = getBridge()->GetObjectType(material);
+
+ if (type & (OBJTYPE_MATERIAL | OBJTYPE_CUSTOMMATERIAL)) {
+ qt3dsdm::SValue value;
+ studio->GetPropertySystem()->GetInstancePropertyValue(
+ material, getBridge()->GetObjectDefinitions().m_Named.m_NameProp, value);
+ qt3dsdm::TDataStrPtr namePtr(qt3dsdm::get<qt3dsdm::TDataStrPtr>(value));
+ QString materialName = QString::fromWCharArray(namePtr->GetData(),
+ int(namePtr->GetLength()));
+ QString sourcePath;
+ for (int i = 0; i < m_matDatas.size(); ++i) {
+ if (QString::compare(m_matDatas[i].m_name, materialName, Qt::CaseInsensitive) == 0) {
+ sourcePath = doc->GetDocumentDirectory() + QLatin1Char('/')
+ + m_matDatas[i].m_relativePath;
+ }
+ }
+
+ sceneEditor->writeMaterialFile(material, materialName, sourcePath.isEmpty(), sourcePath);
+ }
+}
+
+void InspectorControlModel::setMaterialTypeValue(long instance, int handle, const QVariant &value)
+{
+ Q_UNUSED(handle)
+
+ const QString typeValue = value.toString();
+ QString v;
+
+ const auto doc = g_StudioApp.GetCore()->GetDoc();
+ const auto sceneEditor = doc->getSceneEditor();
+ const Q3DStudio::CString oldType = sceneEditor->GetObjectTypeName(instance);
+ qt3dsdm::Qt3DSDMInstanceHandle refMaterial;
+ if (oldType == "ReferencedMaterial")
+ refMaterial = getBridge()->getMaterialReference(instance);
+
+ bool changeMaterialFile = false;
+ bool canCopyProperties = false;
+ if (typeValue == getAnimatableMaterialString()) {
+ v = QStringLiteral("Standard Material");
+ if (refMaterial.Valid()) {
+ const auto refSourcePath = getBridge()->GetSourcePath(refMaterial);
+ for (auto &material : m_materials) {
+ if (refSourcePath == material.m_relativePath) {
+ v = material.m_relativePath;
+ break;
+ }
+ }
+ }
+ canCopyProperties = true;
+ } else if (typeValue == getBasicMaterialString()) {
+ v = QStringLiteral("Referenced Material");
+ changeMaterialFile = true;
+ } else if (typeValue == getReferencedMaterialString()) {
+ v = QStringLiteral("Referenced Material");
+ }
+
+ Q3DStudio::ScopedDocumentEditor scopedEditor(
+ Q3DStudio::SCOPED_DOCUMENT_EDITOR(*doc, tr("Set Material Type")));
+
+ scopedEditor->SetMaterialType(instance, v);
+
+ if (refMaterial.Valid() && canCopyProperties) {
+ const Q3DStudio::CString newType = sceneEditor->GetObjectTypeName(instance);
+ const Q3DStudio::CString refType = sceneEditor->GetObjectTypeName(refMaterial);
+ if (refType == newType)
+ scopedEditor->copyMaterialProperties(refMaterial, instance);
+
+ if (getBridge()->isInsideMaterialContainer(refMaterial)) {
+ const auto name = scopedEditor->GetName(instance);
+ if (!name.toQString().endsWith(QLatin1String("_animatable")))
+ scopedEditor->SetName(instance, name + "_animatable");
+ }
+ }
+
+ if (changeMaterialFile) {
+ scopedEditor->setMaterialProperties(instance, Q3DStudio::CString::fromQString(
+ getBridge()->getDefaultMaterialName()), {}, {});
+
+ // Select the original instance again since potentially creating a material selects the
+ // created one
+ doc->SelectDataModelObject(instance);
+
+ rebuildTree(); // Hack to mimic value changing behavior of the type selector
+ }
+
+ saveIfMaterial(instance);
+}
+
+void InspectorControlModel::setShaderValue(long instance, int handle, const QVariant &value)
+{
+ Q_UNUSED(handle)
+
+ const QString typeValue = value.toString();
+ QString v = QStringLiteral("Standard Material");
+ for (size_t matIdx = 0, end = m_materials.size(); matIdx < end; ++matIdx) {
+ if (m_materials[matIdx].m_name == typeValue)
+ v = m_materials[matIdx].m_relativePath;
+ }
+
+ const auto doc = g_StudioApp.GetCore()->GetDoc();
+
+ Q3DStudio::SCOPED_DOCUMENT_EDITOR(*doc, QObject::tr("Set Material Type"))
+ ->SetMaterialType(instance, v);
+
+ const auto dispatch = g_StudioApp.GetCore()->GetDispatch();
+ QVector<qt3dsdm::Qt3DSDMInstanceHandle> refMats;
+ doc->getSceneReferencedMaterials(doc->GetSceneInstance(), refMats);
+ for (auto &refMat : qAsConst(refMats)) {
+ const auto origMat = getBridge()->getMaterialReference(refMat);
+ if (origMat.Valid() && long(origMat) == instance)
+ dispatch->FireImmediateRefreshInstance(refMat);
+ }
+
+ saveIfMaterial(instance);
+
+ // Make sure that inspector panel is up-to-date with regards to result of shader compilation
+ // errors.
+ rebuildTree();
+}
+
+void InspectorControlModel::setMatDataValue(long instance, int handle, const QVariant &value)
+{
+ Q_UNUSED(handle)
+
+ const QString typeValue = value.toString();
+ QString v;
+ QString name;
+ QString srcPath;
+ QMap<QString, QString> values;
+ QMap<QString, QMap<QString, QString>> textureValues;
+
+ const auto doc = g_StudioApp.GetCore()->GetDoc();
+
+ bool changeMaterialFile = false;
+ if (typeValue == getDefaultMaterialString()) {
+ v = QStringLiteral("Referenced Material");
+ name = getBridge()->getDefaultMaterialName();
+ srcPath = name;
+ changeMaterialFile = true;
+ } else {
+ const auto sceneEditor = doc->getSceneEditor();
+ for (size_t matIdx = 0, end = m_matDatas.size(); matIdx < end; ++matIdx) {
+ QString shownName = m_matDatas[matIdx].m_name;
+ int slashIndex = shownName.lastIndexOf(QLatin1Char('/'));
+ if (slashIndex != -1)
+ shownName = shownName.mid(slashIndex + 1);
+ if (QString::compare(shownName + QLatin1String(" (")
+ + m_matDatas[matIdx].m_relativePath + QLatin1Char(')'),
+ typeValue, Qt::CaseInsensitive) == 0
+ || QString::compare(shownName, typeValue, Qt::CaseInsensitive) == 0) {
+ v = QStringLiteral("Referenced Material");
+ changeMaterialFile = true;
+ name = m_matDatas[matIdx].m_name;
+ srcPath = m_matDatas[matIdx].m_relativePath;
+ const auto material = sceneEditor->getMaterial(srcPath);
+ if (material.Valid()) {
+ // Get the correct case source path
+ const auto absPath = sceneEditor->getFilePathFromMaterialName(
+ sceneEditor->GetName(material).toQString());
+ const auto relPath = QDir(doc->GetDocumentDirectory())
+ .relativeFilePath(absPath);
+ srcPath = relPath;
+ }
+ values = m_matDatas[matIdx].m_values;
+ textureValues = m_matDatas[matIdx].m_textureValues;
+ break;
+ }
+ }
+ }
+
+ if (changeMaterialFile) {
+ {
+ Q3DStudio::SCOPED_DOCUMENT_EDITOR(*doc, QString())
+ ->setMaterialValues(srcPath, values, textureValues);
+ }
+ // Several aspects of the editor are not updated correctly
+ // if the data core is changed without a transaction
+ // The above scope completes the transaction for creating a new material
+ // Next the added undo has to be popped from the stack
+ // TODO: Find a way to update the editor fully without a transaction
+ g_StudioApp.GetCore()->GetCmdStack()->RemoveLastUndo();
+ }
+
+ Q3DStudio::ScopedDocumentEditor scopedEditor(
+ Q3DStudio::SCOPED_DOCUMENT_EDITOR(*doc, tr("Set Material Type")));
+ scopedEditor->SetMaterialType(instance, v);
+
+ if (changeMaterialFile) {
+ scopedEditor->setMaterialSourcePath(instance, Q3DStudio::CString::fromQString(srcPath));
+ scopedEditor->setMaterialReferenceByPath(instance, srcPath);
+
+ // Select original instance again since potentially
+ // creating a material selects the created one
+ doc->SelectDataModelObject(instance);
+
+ rebuildTree(); // Hack to mimic value changing behavior of the type selector
+ }
+
+ saveIfMaterial(instance);
+}
+
+void InspectorControlModel::setRenderableValue(long instance, int handle, const QVariant &value)
+{
+ qt3dsdm::SValue oldValue = currentPropertyValue(instance, handle);
+
+ QString v = value.toString();
+ if (v == QObject::tr("No renderable item"))
+ v = QString();
+
+ if (v == qt3dsdm::get<QString>(oldValue))
+ return;
+
+ Q3DStudio::SCOPED_DOCUMENT_EDITOR(*g_StudioApp.GetCore()->GetDoc(), QObject::tr("Set Property"))
+ ->SetInstancePropertyValueAsRenderable(instance, handle,
+ Q3DStudio::CString::fromQString(v));
+}
+
+void InspectorControlModel::setPropertyValue(long instance, int handle, const QVariant &value,
+ bool commit)
+{
+ const auto doc = g_StudioApp.GetCore()->GetDoc();
+ const auto studio = doc->GetStudioSystem();
+ // Name property needs special handling
+ if (instance && handle == getBridge()->GetNameProperty()) {
+ // Ignore preview of name property change
+ if (!commit)
+ return;
+
+ m_modifiedProperty.first = 0;
+ m_modifiedProperty.second = 0;
+ m_previouslyCommittedValue = {};
+
+ Q3DStudio::CString currentName = getBridge()->GetName(instance, true);
+ Q3DStudio::CString newName = Q3DStudio::CString::fromQString(value.toString());
+ if (!newName.IsEmpty()) {
+ if (getBridge()->isInsideMaterialContainer(instance)
+ && ((newName.Find('/') != Q3DStudio::CString::ENDOFSTRING
+ || newName.Find('#') != Q3DStudio::CString::ENDOFSTRING
+ || newName.Find(':') != Q3DStudio::CString::ENDOFSTRING)
+ || m_suspendMaterialRename)
+ || newName.toQString().contains(QChar('.'))) {
+ return;
+ }
+ qt3dsdm::Qt3DSDMInstanceHandle parentInstance = getBridge()
+ ->GetParentInstance(instance);
+
+ if (parentInstance == doc->GetSceneInstance()
+ && newName.toQString() == getBridge()->getMaterialContainerName()) {
+ QString theTitle = QObject::tr("Rename Object Error");
+ QString theString = getBridge()->getMaterialContainerName()
+ + QObject::tr(" is a reserved name.");
+ // Display error message box asynchronously so focus loss won't trigger setting
+ // the name again
+ g_StudioApp.GetDialogs()->asyncDisplayMessageBox(theTitle, theString,
+ Qt3DSMessageBox::ICON_ERROR);
+ return;
+ }
+
+ Q3DStudio::CString realNewName = newName;
+ if (getBridge()->isInsideMaterialContainer(instance)) {
+ Q3DStudio::CString realName = getBridge()->GetName(instance);
+ int slashIndex = realName.rfind('/');
+ if (slashIndex != Q3DStudio::CString::ENDOFSTRING)
+ realNewName = realName.Left(slashIndex + 1) + newName;
+ }
+
+ if (!getBridge()->CheckNameUnique(parentInstance, instance, realNewName)) {
+ QString origNewName = newName.toQString();
+ realNewName = getBridge()->GetUniqueChildName(parentInstance, instance,
+ realNewName);
+ newName = realNewName;
+ if (getBridge()->isInsideMaterialContainer(instance)) {
+ int slashIndex = newName.rfind('/');
+ if (slashIndex != Q3DStudio::CString::ENDOFSTRING)
+ newName = newName.substr(slashIndex + 1);
+ }
+ // Display rename message box asynchronously so focus loss won't trigger setting
+ // the name again
+ g_StudioApp.GetDialogs()->DisplayObjectRenamed(origNewName, newName.toQString(),
+ true);
+ }
+
+ const auto sceneEditor = doc->getSceneEditor();
+
+ // A materialdef with the same name might exists as a file but not in the container,
+ // so an additional check is needed for that case
+ if (getBridge()->isInsideMaterialContainer(instance)) {
+ int i = 1;
+ while (QFileInfo(sceneEditor->getFilePathFromMaterialName(
+ realNewName.toQString())).exists()) {
+ ++i;
+ realNewName = Q3DStudio::CString::fromQString(
+ realNewName.toQString() + QString::number(i));
+ if (!getBridge()->CheckNameUnique(parentInstance, instance, realNewName)) {
+ realNewName = getBridge()->GetUniqueChildName(parentInstance, instance,
+ realNewName);
+ }
+ }
+ newName = realNewName;
+ int slashIndex = newName.rfind('/');
+ if (slashIndex != Q3DStudio::CString::ENDOFSTRING)
+ newName = newName.substr(slashIndex + 1);
+ }
+
+ if (newName != currentName) {
+ if (getBridge()->isInsideMaterialContainer(instance)) {
+ const auto properOldName = sceneEditor->GetName(instance).toQString();
+ const QString dirPath = doc->GetDocumentDirectory();
+ for (size_t matIdx = 0, end = m_matDatas.size(); matIdx < end; ++matIdx) {
+ if (m_matDatas[matIdx].m_name == properOldName) {
+ QFileInfo fileInfo(dirPath + QLatin1Char('/')
+ + m_matDatas[matIdx].m_relativePath);
+ const QString newFile = fileInfo.absolutePath()
+ + QLatin1Char('/')
+ + newName.toQString()
+ + QStringLiteral(".materialdef");
+ const auto properNewName
+ = sceneEditor->getMaterialNameFromFilePath(newFile);
+ newName = Q3DStudio::CString::fromQString(properNewName);
+ doc->queueMaterialRename(properOldName, properNewName);
+ }
+ }
+ }
+ Q3DStudio::SCOPED_DOCUMENT_EDITOR(
+ *g_StudioApp.GetCore()->GetDoc(),
+ QObject::tr("Set Name"))->SetName(instance, newName, false);
+ }
+ }
+ return;
+ }
+
+ qt3dsdm::SValue oldValue = m_guideInspectable
+ ? m_guideInspectable->properties()[handleToGuidePropIndex(handle)]->GetInspectableData()
+ : currentPropertyValue(instance, handle);
+ qt3dsdm::SValue v = value;
+
+ const bool hasPreview = (m_modifiedProperty.first == instance
+ && m_modifiedProperty.second == handle);
+
+ // If this set is a commit for property that was previously changed without
+ // committing, we must let the set go through even if the value hasn't changed
+ // to finish the transaction.
+ if (v == oldValue && !(commit && hasPreview))
+ return;
+
+ if (!commit && !hasPreview) {
+ m_previouslyCommittedValue = oldValue;
+ m_modifiedProperty.first = instance;
+ m_modifiedProperty.second = handle;
+ }
+
+ if (instance) {
+ // If the user enters 0.0 to any (x, y, z) values of camera scale,
+ // we reset the value back to original, because zero scale factor will crash
+ // camera-specific inverse matrix math. (Additionally, scale of zero for a camera
+ // is generally not useful anyway.) We could silently discard zero values also deeper in the
+ // value setter code, but then the inspector panel value would not be updated as opposed
+ // to both rejecting invalid and resetting the original value here.
+ EStudioObjectType theType = getBridge()->GetObjectType(instance);
+
+ if (theType == EStudioObjectType::OBJTYPE_CAMERA &&
+ studio->GetPropertySystem()->GetName(handle) == Q3DStudio::CString("scale")) {
+ const QVector3D theFloat3 = qt3dsdm::get<QVector3D>(v);
+ if (theFloat3.x() == 0.0f || theFloat3.y() == 0.0f || theFloat3.z() == 0.0f )
+ v = oldValue;
+ }
+
+ // some properties may initialize OpenGL resources (e.g. loading meshes will
+ // initialize vertex buffers), so the renderer's OpenGL context must be current
+ Q3DStudio::IStudioRenderer &theRenderer(g_StudioApp.getRenderer());
+ theRenderer.MakeContextCurrent();
+ m_UpdatableEditor.EnsureEditor(QObject::tr("Set Property"), __FILE__, __LINE__)
+ .SetInstancePropertyValue(instance, handle, v);
+
+ theRenderer.ReleaseContext();
+
+ m_UpdatableEditor.FireImmediateRefresh(instance);
+ } else if (m_guideInspectable) {
+ m_guideInspectable->properties()[handleToGuidePropIndex(handle)]->ChangeInspectableData(v);
+ }
+
+ if (commit) {
+ m_modifiedProperty.first = 0;
+ m_modifiedProperty.second = 0;
+ if (m_previouslyCommittedValue == v) {
+ if (m_guideInspectable)
+ m_guideInspectable->Rollback();
+ else
+ m_UpdatableEditor.RollbackEditor();
+ } else {
+ if (m_guideInspectable) {
+ // If the guide ends up over the matte, destroy it
+ QSize presSize = g_StudioApp.GetCore()->GetStudioProjectSettings()
+ ->getPresentationSize();
+ bool isInPres = true;
+ qt3dsdm::SValue posValue = m_guideInspectable->GetPosition();
+ float position = qt3dsdm::get<float>(posValue);
+ if (m_guideInspectable->isHorizontal())
+ isInPres = 0.f <= position && float(presSize.height()) >= position;
+ else
+ isInPres = 0.f <= position && float(presSize.width()) >= position;
+ if (isInPres)
+ m_guideInspectable->Commit();
+ else
+ m_guideInspectable->Destroy();
+ } else {
+ m_UpdatableEditor.CommitEditor();
+ }
+ }
+
+ m_previouslyCommittedValue = {};
+ refreshTree();
+
+ saveIfMaterial(instance);
+ }
+}
+
+void InspectorControlModel::setSlideSelection(long instance, int handle, int index,
+ const QStringList &list)
+{
+ const auto doc = g_StudioApp.GetCore()->GetDoc();
+ auto studioSystem = doc->GetStudioSystem();
+ const auto metaDataProvider = doc->GetStudioSystem()->GetActionMetaData();
+ const auto info = metaDataProvider->GetMetaDataPropertyInfo(
+ metaDataProvider->GetMetaDataProperty(instance, handle));
+ QStringList stringlist = qt3dsdm::get<QStringList>(info->m_MetaDataData);
+
+ auto slideSystem = studioSystem->GetSlideSystem();
+ std::pair<bool, bool> slideData(
+ getSlideCharacteristics(instance, *studioSystem->GetSlideCore(),
+ *slideSystem));
+ bool hasNextSlide(slideData.first);
+ bool hasPreviousSlide(slideData.second);
+ qt3dsdm::SStringOrInt newSelectedData;
+ if (!hasNextSlide)
+ stringlist.removeAll("Next");
+ if (!hasPreviousSlide)
+ stringlist.removeAll("Previous");
+
+ auto itemCount = stringlist.count();
+ if (index < itemCount) {
+ newSelectedData = qt3dsdm::SStringOrInt(std::make_shared<qt3dsdm::CDataStr>
+ (Q3DStudio::CString::fromQString(list[index]).c_str()));
+ } else {
+ auto slideHandle = slideSystem->GetSlideByInstance(instance);
+ auto masterSlide = slideSystem->GetMasterSlide(slideHandle);
+ long slideIndex = index - itemCount + 1;
+ auto newSelectedSlide = slideSystem->GetSlideByIndex(masterSlide, slideIndex);
+ newSelectedData = qt3dsdm::SStringOrInt((long)newSelectedSlide);
+ }
+
+ Q3DStudio::SCOPED_DOCUMENT_EDITOR(*g_StudioApp.GetCore()->GetDoc(), QObject::tr("Set Property"))
+ ->SetInstancePropertyValue(instance, handle, newSelectedData);
+}
+
+// temporarily prevent material renaming when opening the colors dialog (fix for QT3DS-3407)
+void InspectorControlModel::suspendMaterialRename(bool flag)
+{
+ m_suspendMaterialRename = flag;
+}
+
+void InspectorControlModel::setPropertyControllerInstance(
+ long instance,int property, Q3DStudio::CString controllerInstance, bool controlled)
+{
+ CDoc *doc = g_StudioApp.GetCore()->GetDoc();
+ IObjectReferenceHelper *objRefHelper = doc->GetDataModelObjectReferenceHelper();
+
+ Q3DStudio::CString instancepath = Q3DStudio::CString(
+ objRefHelper->GetObjectReferenceString(doc->GetSceneInstance(),
+ CRelativePathTools::EPATHTYPE_GUID, instance));
+ Q_ASSERT(instancepath.size());
+
+ doc->SetInstancePropertyControlled(instance, instancepath, property,
+ controllerInstance, controlled);
+}
+
+void InspectorControlModel::setPropertyControlled(long instance, int property)
+{
+ const auto signalSender
+ = g_StudioApp.GetCore()->GetDoc()->GetStudioSystem()->GetFullSystemSignalSender();
+
+ signalSender->SendControlledToggled(instance, property);
+}
+
+bool InspectorControlModel::isLayer(long instance) const
+{
+ return getBridge()->GetObjectType(instance) == EStudioObjectType::OBJTYPE_LAYER;
+}
+
+QString InspectorControlModel::renderableId(const QString &filePath) const
+{
+ return g_StudioApp.getRenderableId(filePath);
+}
+
+void InspectorControlModel::setPropertyAnimated(long instance, int handle, bool animated)
+{
+ CCmd* cmd = nullptr;
+ auto doc = g_StudioApp.GetCore()->GetDoc();
+ if (animated)
+ cmd = new CCmdDataModelAnimate(doc, instance, handle);
+ else
+ cmd = new CCmdDataModelDeanimate(doc, instance, handle);
+
+ g_StudioApp.GetCore()->ExecuteCommand(cmd);
+}
+
+QVariant InspectorControlModel::data(const QModelIndex &index, int role) const
+{
+ if (!hasIndex(index.row(), index.column(),index.parent()))
+ return {};
+
+ const auto row = index.row();
+
+ switch (role) {
+ case GroupValuesRole:
+ return m_groupElements.at(row).controlElements;
+ case GroupTitleRole:
+ return m_groupElements.at(row).groupTitle;
+ case GroupInfoRole:
+ return m_groupElements.at(row).groupInfo;
+ }
+ return {};
+}
+
+QHash<int, QByteArray> InspectorControlModel::roleNames() const
+{
+ auto names = QAbstractListModel::roleNames();
+ names.insert(GroupValuesRole, "values");
+ names.insert(GroupTitleRole, "title");
+ names.insert(GroupInfoRole, "info");
+ return names;
+}
+
+void InspectorControlModel::undo()
+{
+ g_StudioApp.m_pMainWnd->OnEditUndo();
+}
+
+void InspectorControlModel::redo()
+{
+ g_StudioApp.m_pMainWnd->OnEditRedo();
+}
+
+InspectorControlBase::~InspectorControlBase()
+{
+}