Material: Compatibility with older FCMat files

Provides compatibility loading older files outside the context of
a library.

Older material files were loaded by specifying a path. The new
material system used the path to associated the material with a
library, which may not be appropriate for legacy files. This change
allows the use of materials outside of a library.

Additionally, legacy files often have name/value pairs not part of the
standard list of properties. Since these were unable to be mapped to
a model property they were ignored. Materials now maintain a legacy
map to hold properties not associated with a property model. These
properties are considered transient and will not be saved. It is not
intended for this feature to be used as a generic container for
properties not mapped to an appropriate model.

Fixes #13302
This commit is contained in:
David Carter
2024-04-06 15:41:07 -04:00
committed by Yorik van Havre
parent 0056038ff4
commit f950a0c086
8 changed files with 135 additions and 0 deletions

View File

@@ -1017,6 +1017,22 @@ void MaterialConfigLoader::addMechanical(const QMap<QString, QString>& fcmat,
setPhysicalValue(finalModel, "Stiffness", stiffness);
}
void MaterialConfigLoader::addLegacy(const QMap<QString, QString>& fcmat,
const std::shared_ptr<Material>& finalModel)
{
for (auto const& legacy : fcmat.keys()) {
auto name = legacy;
int last = name.lastIndexOf(QLatin1String("/"));
if (last > 0) {
name = name.mid(last + 1);
}
if (!finalModel->hasNonLegacyProperty(name)) {
setLegacyValue(finalModel, name.toStdString(), fcmat[legacy]);
}
}
}
std::shared_ptr<Material>
MaterialConfigLoader::getMaterialFromPath(const std::shared_ptr<MaterialLibrary>& library,
const QString& path)
@@ -1081,6 +1097,7 @@ MaterialConfigLoader::getMaterialFromPath(const std::shared_ptr<MaterialLibrary>
addRendering(fcmat, finalModel);
addVectorRendering(fcmat, finalModel);
addRenderWB(fcmat, finalModel);
addLegacy(fcmat, finalModel);
return finalModel;
}

View File

@@ -85,6 +85,14 @@ private:
finalModel->setAppearanceValue(QString::fromStdString(name), value);
}
}
static void setLegacyValue(const std::shared_ptr<Material>& finalModel,
const std::string& name,
const QString& value)
{
if (!value.isEmpty()) {
finalModel->setLegacyValue(QString::fromStdString(name), value);
}
}
static bool isTexture(const QString& value)
{
@@ -147,6 +155,8 @@ private:
const std::shared_ptr<Material>& finalModel);
static void addRenderWB(QMap<QString, QString>& fcmat,
const std::shared_ptr<Material>& finalModel);
static void addLegacy(const QMap<QString, QString>& fcmat,
const std::shared_ptr<Material>& finalModel);
};
} // namespace Materials

View File

@@ -196,6 +196,17 @@ std::shared_ptr<Material> MaterialManager::getMaterialByPath(const QString& path
}
}
// Older workbenches may try files outside the context of a library
{
QMutexLocker locker(&_mutex);
if (MaterialConfigLoader::isConfigStyle(path)) {
auto material = MaterialConfigLoader::getMaterialFromPath(nullptr, path);
return material;
}
}
throw MaterialNotFound();
}

View File

@@ -141,6 +141,11 @@
<UserDocu>Check if the material implements the appearance property with the given name</UserDocu>
</Documentation>
</Methode>
<Methode Name="hasLegacyProperties" ReadOnly="true">
<Documentation>
<UserDocu>Returns true of there are legacy properties</UserDocu>
</Documentation>
</Methode>
<Attribute Name="Properties" ReadOnly="true">
<Documentation>
<UserDocu>deprecated -- Dictionary of all material properties.</UserDocu>
@@ -159,6 +164,12 @@
</Documentation>
<Parameter Name="AppearanceProperties" Type="Dict"/>
</Attribute>
<Attribute Name="LegacyProperties" ReadOnly="true">
<Documentation>
<UserDocu>deprecated -- Dictionary of material legacy properties.</UserDocu>
</Documentation>
<Parameter Name="LegacyProperties" Type="Dict"/>
</Attribute>
<Methode Name="getPhysicalValue" ReadOnly="true">
<Documentation>
<UserDocu>Get the value associated with the property</UserDocu>

View File

@@ -280,6 +280,16 @@ PyObject* MaterialPy::hasAppearanceProperty(PyObject* args)
return PyBool_FromLong(hasProperty ? 1 : 0);
}
PyObject* MaterialPy::hasLegacyProperties(PyObject* args)
{
if (!PyArg_ParseTuple(args, "")) {
return nullptr;
}
bool hasProperty = getMaterialPtr()->hasLegacyProperties();
return PyBool_FromLong(hasProperty ? 1 : 0);
}
Py::Dict MaterialPy::getProperties() const
{
Py::Dict dict;
@@ -319,6 +329,16 @@ Py::Dict MaterialPy::getProperties() const
}
}
auto legacy = getMaterialPtr()->getLegacyProperties();
for (auto& it : legacy) {
auto key = it.first;
auto value = it.second;
if (!value.isEmpty()) {
dict.setItem(Py::String(key.toStdString()), Py::String(value.toStdString()));
}
}
return dict;
}
@@ -358,6 +378,23 @@ Py::Dict MaterialPy::getAppearanceProperties() const
return dict;
}
Py::Dict MaterialPy::getLegacyProperties() const
{
Py::Dict dict;
auto legacy = getMaterialPtr()->getLegacyProperties();
for (auto& it : legacy) {
auto key = it.first;
auto value = it.second;
if (!value.isEmpty()) {
dict.setItem(Py::String(key.toStdString()), Py::String(value.toStdString()));
}
}
return dict;
}
static Py::List getList(const QVariant& value)
{
auto listValue = value.value<QList<QVariant>>();

View File

@@ -499,6 +499,9 @@ Material::Material(const Material& other)
MaterialProperty prop(it.second);
_appearance[it.first] = std::make_shared<MaterialProperty>(prop);
}
for (auto& it : other._legacy) {
_legacy[it.first] = it.second;
}
}
QString Material::getAuthorAndLicense() const
@@ -890,6 +893,13 @@ void Material::setAppearanceValue(const QString& name,
}
}
void Material::setLegacyValue(const QString& name, const QString& value)
{
setEditStateAlter();
_legacy[name] = value;
}
std::shared_ptr<MaterialProperty> Material::getPhysicalProperty(const QString& name)
{
try {
@@ -1047,6 +1057,19 @@ bool Material::hasAppearanceProperty(const QString& name) const
return true;
}
bool Material::hasNonLegacyProperty(const QString& name) const
{
if (hasPhysicalProperty(name) || hasAppearanceProperty(name)) {
return true;
}
return false;
}
bool Material::hasLegacyProperties() const
{
return !_legacy.empty();
}
bool Material::isInherited(const QString& uuid) const
{
if (_physicalUuids.contains(uuid)) {
@@ -1464,6 +1487,10 @@ Material& Material::operator=(const Material& other)
MaterialProperty prop(it.second);
_appearance[it.first] = std::make_shared<MaterialProperty>(prop);
}
_legacy.clear();
for (auto& it : other._legacy) {
_legacy[it.first] = it.second;
}
return *this;
}

View File

@@ -299,6 +299,15 @@ public:
void setAppearanceValue(const QString& name, const std::shared_ptr<MaterialValue>& value);
void setAppearanceValue(const QString& name, const std::shared_ptr<QList<QVariant>>& value);
/*
* Legacy values are thosed contained in old format files that don't fit in the new
* property format. It should not be used as a catch all for defining a property with
* no model.
*
* These values are transient and will not be saved.
*/
void setLegacyValue(const QString& name, const QString& value);
std::shared_ptr<MaterialProperty> getPhysicalProperty(const QString& name);
std::shared_ptr<MaterialProperty> getPhysicalProperty(const QString& name) const;
std::shared_ptr<MaterialProperty> getAppearanceProperty(const QString& name);
@@ -313,6 +322,9 @@ public:
QString getAppearanceValueString(const QString& name) const;
bool hasPhysicalProperty(const QString& name) const;
bool hasAppearanceProperty(const QString& name) const;
bool hasNonLegacyProperty(const QString& name) const;
bool hasLegacyProperty(const QString& name) const;
bool hasLegacyProperties() const;
// Test if the model is defined, and if values are provided for all properties
bool hasModel(const QString& uuid) const;
@@ -334,6 +346,10 @@ public:
{
return _appearance;
}
std::map<QString, QString>& getLegacyProperties()
{
return _legacy;
}
QString getModelByName(const QString& name) const;
@@ -438,6 +454,7 @@ private:
QSet<QString> _allUuids; // Includes inherited models
std::map<QString, std::shared_ptr<MaterialProperty>> _physical;
std::map<QString, std::shared_ptr<MaterialProperty>> _appearance;
std::map<QString, QString> _legacy;
bool _dereferenced;
bool _oldFormat;
ModelEdit _editState;

View File

@@ -71,6 +71,8 @@ class MaterialTestCases(unittest.TestCase):
self.assertFalse(steel.isPhysicalModelComplete(self.uuids.LinearElastic))
self.assertTrue(steel.isAppearanceModelComplete(self.uuids.BasicRendering))
self.assertFalse(steel.hasLegacyProperties())
self.assertTrue(steel.hasPhysicalProperty("Density"))
self.assertTrue(steel.hasPhysicalProperty("BulkModulus"))
self.assertTrue(steel.hasPhysicalProperty("PoissonRatio"))
@@ -118,6 +120,9 @@ class MaterialTestCases(unittest.TestCase):
self.assertIn("SpecularColor", properties)
self.assertIn("Transparency", properties)
properties = steel.LegacyProperties
self.assertEqual(len(properties), 0)
properties = steel.Properties
self.assertIn("Density", properties)
self.assertNotIn("BulkModulus", properties)