1
/***************************************************************************
2
* Copyright (c) 2023 David Carter <dcarter@david.carter.ca> *
4
* This file is part of FreeCAD. *
6
* FreeCAD is free software: you can redistribute it and/or modify it *
7
* under the terms of the GNU Lesser General Public License as *
8
* published by the Free Software Foundation, either version 2.1 of the *
9
* License, or (at your option) any later version. *
11
* FreeCAD is distributed in the hope that it will be useful, but *
12
* WITHOUT ANY WARRANTY; without even the implied warranty of *
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
14
* Lesser General Public License for more details. *
16
* You should have received a copy of the GNU Lesser General Public *
17
* License along with FreeCAD. If not, see *
18
* <https://www.gnu.org/licenses/>. *
20
**************************************************************************/
22
#include "PreCompiled.h"
29
#include <App/Application.h>
30
#include <Gui/MetaTypes.h>
34
#include "MaterialLibrary.h"
35
#include "MaterialManager.h"
36
#include "ModelManager.h"
37
#include "ModelUuids.h"
40
using namespace Materials;
42
/* TRANSLATOR Material::Materials */
44
TYPESYSTEM_SOURCE(Materials::MaterialProperty, Materials::ModelProperty)
46
int const MaterialProperty::PRECISION = 6;
48
MaterialProperty::MaterialProperty()
50
_valuePtr = std::make_shared<MaterialValue>(MaterialValue::None);
53
MaterialProperty::MaterialProperty(const ModelProperty& other, QString modelUUID)
54
: ModelProperty(other)
55
, _modelUUID(modelUUID)
58
setType(getPropertyType());
59
auto columns = other.getColumns();
60
for (auto& it : columns) {
61
MaterialProperty prop(it, modelUUID);
66
void MaterialProperty::copyValuePtr(const std::shared_ptr<MaterialValue>& value)
68
if (value->getType() == MaterialValue::Array2D) {
70
std::make_shared<Material2DArray>(*(std::static_pointer_cast<Material2DArray>(value)));
72
else if (value->getType() == MaterialValue::Array3D) {
74
std::make_shared<Material3DArray>(*(std::static_pointer_cast<Material3DArray>(value)));
77
_valuePtr = std::make_shared<MaterialValue>(*value);
81
MaterialProperty::MaterialProperty(const MaterialProperty& other)
82
: ModelProperty(other)
83
, _modelUUID(other._modelUUID)
85
copyValuePtr(other._valuePtr);
87
for (auto& it : other._columns) {
88
_columns.push_back(it);
92
MaterialProperty::MaterialProperty(const std::shared_ptr<MaterialProperty>& other)
93
: MaterialProperty(*other)
96
void MaterialProperty::setModelUUID(const QString& uuid)
101
QVariant MaterialProperty::getValue()
103
return _valuePtr->getValue();
106
QVariant MaterialProperty::getValue() const
108
return _valuePtr->getValue();
111
std::shared_ptr<MaterialValue> MaterialProperty::getMaterialValue()
116
std::shared_ptr<MaterialValue> MaterialProperty::getMaterialValue() const
121
QString MaterialProperty::getString() const
123
// This method produces a localized string. For a non-localized string use
124
// getDictionaryString()
128
if (getType() == MaterialValue::Quantity) {
129
auto quantity = getValue().value<Base::Quantity>();
130
return quantity.getUserString();
132
if (getType() == MaterialValue::Float) {
133
auto value = getValue();
134
if (value.isNull()) {
137
return QString(QLatin1String("%L1")).arg(value.toFloat(), 0, 'g', PRECISION);
139
return getValue().toString();
142
QString MaterialProperty::getYAMLString() const
144
return _valuePtr->getYAMLString();
147
App::Color MaterialProperty::getColor() const
149
auto colorString = getValue().toString();
150
std::stringstream stream(colorString.toStdString());
153
stream >> c; // read "("
168
App::Color color(red, green, blue, alpha);
173
QString MaterialProperty::getDictionaryString() const
175
// This method produces a non-localized string. For a localized string use
180
if (getType() == MaterialValue::Quantity) {
181
auto quantity = getValue().value<Base::Quantity>();
182
auto string = QString(QLatin1String("%1 %2"))
183
.arg(quantity.getValue(), 0, 'g', PRECISION)
184
.arg(quantity.getUnit().getString());
187
if (getType() == MaterialValue::Float) {
188
auto value = getValue();
189
if (value.isNull()) {
192
return QString(QLatin1String("%1")).arg(value.toFloat(), 0, 'g', PRECISION);
194
return getValue().toString();
197
void MaterialProperty::setPropertyType(const QString& type)
199
ModelProperty::setPropertyType(type);
203
void MaterialProperty::setType(const QString& type)
205
auto mappedType = MaterialValue::mapType(type);
206
if (mappedType == MaterialValue::None) {
207
throw UnknownValueType();
209
if (mappedType == MaterialValue::Array2D) {
210
auto arrayPtr = std::make_shared<Material2DArray>();
211
arrayPtr->setColumns(columns());
212
_valuePtr = arrayPtr;
214
else if (mappedType == MaterialValue::Array3D) {
215
auto arrayPtr = std::make_shared<Material3DArray>();
216
// First column is third dimension
217
arrayPtr->setColumns(columns() - 1);
218
_valuePtr = arrayPtr;
221
_valuePtr = std::make_shared<MaterialValue>(mappedType);
225
MaterialProperty& MaterialProperty::getColumn(int column)
228
return _columns.at(column);
230
catch (std::out_of_range const&) {
231
throw InvalidIndex();
235
const MaterialProperty& MaterialProperty::getColumn(int column) const
238
return _columns.at(column);
240
catch (std::out_of_range const&) {
241
throw InvalidIndex();
245
MaterialValue::ValueType MaterialProperty::getColumnType(int column) const
248
return _columns.at(column).getType();
250
catch (std::out_of_range const&) {
251
throw InvalidIndex();
255
QString MaterialProperty::getColumnUnits(int column) const
258
return _columns.at(column).getUnits();
260
catch (std::out_of_range const&) {
261
throw InvalidIndex();
265
QVariant MaterialProperty::getColumnNull(int column) const
267
MaterialValue::ValueType valueType = getColumnType(column);
270
case MaterialValue::Quantity: {
271
Base::Quantity quant = Base::Quantity(0, getColumnUnits(column));
272
return QVariant::fromValue(quant);
275
case MaterialValue::Float:
276
case MaterialValue::Integer:
286
void MaterialProperty::setValue(const QVariant& value)
288
_valuePtr->setValue(value);
291
void MaterialProperty::setValue(const QString& value)
293
if (_valuePtr->getType() == MaterialValue::Boolean) {
296
else if (_valuePtr->getType() == MaterialValue::Integer) {
299
else if (_valuePtr->getType() == MaterialValue::Float) {
302
else if (_valuePtr->getType() == MaterialValue::URL) {
305
else if (_valuePtr->getType() == MaterialValue::Array2D
306
|| _valuePtr->getType() == MaterialValue::Array3D) {
307
// This value can't be directly assigned
309
else if (_valuePtr->getType() == MaterialValue::Quantity) {
311
setQuantity(Base::Quantity::parse(value));
313
catch (const Base::ParserError& e) {
314
Base::Console().Log("MaterialProperty::setValue Error '%s' - '%s'\n",
316
value.toStdString().c_str());
326
void MaterialProperty::setValue(const std::shared_ptr<MaterialValue>& value)
331
void MaterialProperty::setString(const QString& value)
333
_valuePtr->setValue(QVariant(value));
336
void MaterialProperty::setString(const std::string& value)
338
_valuePtr->setValue(QVariant(QString::fromStdString(value)));
341
void MaterialProperty::setBoolean(bool value)
343
_valuePtr->setValue(QVariant(value));
346
void MaterialProperty::setBoolean(int value)
348
_valuePtr->setValue(QVariant(value != 0));
351
void MaterialProperty::setBoolean(const QString& value)
353
bool boolean = false;
354
std::string val = value.toStdString();
355
if ((val == "true") || (val == "True")) {
358
else if ((val == "false") || (val == "False")) {
362
boolean = (std::stoi(val) != 0);
368
void MaterialProperty::setInt(int value)
370
_valuePtr->setValue(QVariant(value));
373
void MaterialProperty::setInt(const QString& value)
375
_valuePtr->setValue(value.toInt());
378
void MaterialProperty::setFloat(double value)
380
_valuePtr->setValue(QVariant(value));
383
void MaterialProperty::setFloat(const QString& value)
385
_valuePtr->setValue(QVariant(value.toFloat()));
388
void MaterialProperty::setQuantity(const Base::Quantity& value)
390
_valuePtr->setValue(QVariant(QVariant::fromValue(value)));
393
void MaterialProperty::setQuantity(double value, const QString& units)
395
setQuantity(Base::Quantity(value, units));
398
void MaterialProperty::setQuantity(const QString& value)
400
setQuantity(Base::Quantity::parse(value));
403
void MaterialProperty::setList(const QList<QVariant>& value)
405
_valuePtr->setList(value);
408
void MaterialProperty::setURL(const QString& value)
410
_valuePtr->setValue(QVariant(value));
413
void MaterialProperty::setColor(const App::Color& value)
415
std::stringstream ss;
416
ss << "(" << value.r << ", " << value.g << ", " << value.b << ", " << value.a << ")";
417
_valuePtr->setValue(QVariant(QString::fromStdString(ss.str())));
420
MaterialProperty& MaterialProperty::operator=(const MaterialProperty& other)
422
if (this == &other) {
426
ModelProperty::operator=(other);
428
_modelUUID = other._modelUUID;
429
copyValuePtr(other._valuePtr);
432
for (auto& it : other._columns) {
433
_columns.push_back(it);
439
bool MaterialProperty::operator==(const MaterialProperty& other) const
441
if (this == &other) {
445
if (ModelProperty::operator==(other)) {
446
return (*_valuePtr == *other._valuePtr);
451
TYPESYSTEM_SOURCE(Materials::Material, Base::BaseClass)
454
: _dereferenced(false)
456
, _editState(ModelEdit_None)
458
// Create an initial UUID
462
Material::Material(const std::shared_ptr<MaterialLibrary>& library,
463
const QString& directory,
469
, _dereferenced(false)
471
, _editState(ModelEdit_None)
473
setDirectory(directory);
476
Material::Material(const Material& other)
477
: _library(other._library)
478
, _directory(other._directory)
481
, _author(other._author)
482
, _license(other._license)
483
, _parentUuid(other._parentUuid)
484
, _description(other._description)
486
, _reference(other._reference)
487
, _dereferenced(other._dereferenced)
488
, _oldFormat(other._oldFormat)
489
, _editState(other._editState)
491
for (auto& it : other._tags) {
494
for (auto& it : other._physicalUuids) {
495
_physicalUuids.insert(it);
497
for (auto& it : other._appearanceUuids) {
498
_appearanceUuids.insert(it);
500
for (auto& it : other._allUuids) {
501
_allUuids.insert(it);
503
for (auto& it : other._physical) {
504
MaterialProperty prop(it.second);
505
_physical[it.first] = std::make_shared<MaterialProperty>(prop);
507
for (auto& it : other._appearance) {
508
MaterialProperty prop(it.second);
509
_appearance[it.first] = std::make_shared<MaterialProperty>(prop);
511
for (auto& it : other._legacy) {
512
_legacy[it.first] = it.second;
516
QString Material::getAuthorAndLicense() const
518
QString authorAndLicense;
520
// Combine the author and license field for backwards compatibility
521
if (!_author.isNull()) {
522
authorAndLicense = _author;
523
if (!_license.isNull()) {
524
authorAndLicense += QLatin1String(" ") + _license;
527
else if (!_license.isNull()) {
528
authorAndLicense = _license;
534
void Material::addModel(const QString& uuid)
536
for (const auto& modelUUID : std::as_const(_allUuids)) {
537
if (modelUUID == uuid) {
544
ModelManager manager;
547
auto model = manager.getModel(uuid);
548
auto inheritance = model->getInheritance();
549
for (auto& inherits : inheritance) {
553
catch (ModelNotFound const&) {
557
void Material::clearModels()
559
_physicalUuids.clear();
560
_appearanceUuids.clear();
566
void Material::clearInherited()
570
// Rebuild the UUID lists without the inherited UUIDs
571
for (auto& uuid : _physicalUuids) {
574
for (auto& uuid : _appearanceUuids) {
579
void Material::setName(const QString& name)
582
setEditStateExtend();
585
void Material::setAuthor(const QString& author)
588
setEditStateExtend();
591
void Material::setLicense(const QString& license)
594
setEditStateExtend();
597
void Material::setParentUUID(const QString& uuid)
600
setEditStateExtend();
603
void Material::setDescription(const QString& description)
605
_description = description;
606
setEditStateExtend();
609
void Material::setURL(const QString& url)
612
setEditStateExtend();
615
void Material::setReference(const QString& reference)
617
_reference = reference;
618
setEditStateExtend();
621
void Material::setEditState(ModelEdit newState)
623
if (newState == ModelEdit_Extend) {
624
if (_editState != ModelEdit_Alter) {
625
_editState = newState;
628
else if (newState == ModelEdit_Alter) {
629
_editState = newState;
633
void Material::removeUUID(QSet<QString>& uuidList, const QString& uuid)
635
uuidList.remove(uuid);
638
void Material::addPhysical(const QString& uuid)
640
if (hasPhysicalModel(uuid)) {
644
ModelManager manager;
647
auto model = manager.getModel(uuid);
649
auto& inheritance = model->getInheritance();
650
for (auto& it : inheritance) {
651
// Inherited models may already have the properties, so just
653
removeUUID(_physicalUuids, it);
656
_physicalUuids.insert(uuid);
658
setEditStateExtend();
660
for (auto& it : *model) {
661
QString propertyName = it.first;
662
if (!hasPhysicalProperty(propertyName)) {
663
ModelProperty property = static_cast<ModelProperty>(it.second);
666
_physical[propertyName] = std::make_shared<MaterialProperty>(property, uuid);
668
catch (const UnknownValueType&) {
669
Base::Console().Error("Property '%s' has unknown type '%s'. Ignoring\n",
670
property.getName().toStdString().c_str(),
671
property.getPropertyType().toStdString().c_str());
676
catch (ModelNotFound const&) {
680
void Material::removePhysical(const QString& uuid)
682
if (!hasPhysicalModel(uuid)) {
686
// If it's an inherited model, do nothing
687
if (isInherited(uuid)) {
691
ModelManager manager;
694
auto model = manager.getModel(uuid);
696
auto& inheritance = model->getInheritance();
697
for (auto& it : inheritance) {
698
removeUUID(_physicalUuids, it);
699
removeUUID(_allUuids, it);
701
removeUUID(_physicalUuids, uuid);
702
removeUUID(_allUuids, uuid);
704
for (auto& it : *model) {
705
_physical.erase(it.first);
710
catch (ModelNotFound const&) {
711
Base::Console().Log("Physical model not found '%s'\n", uuid.toStdString().c_str());
715
void Material::addAppearance(const QString& uuid)
717
if (hasAppearanceModel(uuid)) {
721
ModelManager manager;
724
auto model = manager.getModel(uuid);
726
auto& inheritance = model->getInheritance();
727
for (auto& it : inheritance) {
728
// Inherited models may already have the properties, so just
730
removeUUID(_appearanceUuids, it);
733
_appearanceUuids.insert(uuid);
735
setEditStateExtend();
737
for (auto& it : *model) {
738
QString propertyName = it.first;
739
if (!hasAppearanceProperty(propertyName)) {
740
ModelProperty property = static_cast<ModelProperty>(it.second);
742
_appearance[propertyName] = std::make_shared<MaterialProperty>(property, uuid);
746
catch (ModelNotFound const&) {
747
Base::Console().Log("Appearance model not found '%s'\n", uuid.toStdString().c_str());
751
void Material::removeAppearance(const QString& uuid)
753
if (!hasAppearanceModel(uuid)) {
757
// If it's an inherited model, do nothing
758
if (isInherited(uuid)) {
762
ModelManager manager;
765
auto model = manager.getModel(uuid);
767
auto& inheritance = model->getInheritance();
768
for (auto& it : inheritance) {
769
removeUUID(_appearanceUuids, it);
770
removeUUID(_allUuids, it);
772
removeUUID(_appearanceUuids, uuid);
773
removeUUID(_allUuids, uuid);
775
for (auto& it : *model) {
776
_appearance.erase(it.first);
781
catch (ModelNotFound const&) {
785
void Material::setPropertyEditState(const QString& name)
788
if (hasPhysicalProperty(name)) {
789
setPhysicalEditState(name);
791
else if (hasAppearanceProperty(name)) {
792
setAppearanceEditState(name);
795
catch (const PropertyNotFound&) {
799
void Material::setPhysicalEditState(const QString& name)
801
if (getPhysicalProperty(name)->isNull()) {
802
setEditStateExtend();
809
void Material::setAppearanceEditState(const QString& name)
812
if (getAppearanceProperty(name)->isNull()) {
813
setEditStateExtend();
819
catch (const PropertyNotFound&) {
823
void Material::setPhysicalValue(const QString& name, const QString& value)
825
setPhysicalEditState(name);
827
if (hasPhysicalProperty(name)) {
828
_physical[name]->setValue(value); // may not be a string type, conversion may be required
832
void Material::setPhysicalValue(const QString& name, int value)
834
setPhysicalEditState(name);
836
if (hasPhysicalProperty(name)) {
837
_physical[name]->setInt(value);
841
void Material::setPhysicalValue(const QString& name, double value)
843
setPhysicalEditState(name);
845
if (hasPhysicalProperty(name)) {
846
_physical[name]->setFloat(value);
850
void Material::setPhysicalValue(const QString& name, const Base::Quantity& value)
852
setPhysicalEditState(name);
854
if (hasPhysicalProperty(name)) {
855
_physical[name]->setQuantity(value);
859
void Material::setPhysicalValue(const QString& name, const std::shared_ptr<MaterialValue>& value)
861
setPhysicalEditState(name);
863
if (hasPhysicalProperty(name)) {
864
_physical[name]->setValue(value);
868
void Material::setPhysicalValue(const QString& name, const std::shared_ptr<QList<QVariant>>& value)
870
setPhysicalEditState(name);
872
if (hasPhysicalProperty(name)) {
873
_physical[name]->setList(*value);
877
void Material::setPhysicalValue(const QString& name, const QVariant& value)
879
setPhysicalEditState(name);
881
if (hasPhysicalProperty(name)) {
882
_physical[name]->setValue(value);
886
void Material::setAppearanceValue(const QString& name, const QString& value)
888
setAppearanceEditState(name);
890
if (hasAppearanceProperty(name)) {
891
_appearance[name]->setValue(value); // may not be a string type, conversion may be required
895
void Material::setAppearanceValue(const QString& name, const std::shared_ptr<MaterialValue>& value)
897
setAppearanceEditState(name);
899
if (hasAppearanceProperty(name)) {
900
_appearance[name]->setValue(value);
904
void Material::setAppearanceValue(const QString& name,
905
const std::shared_ptr<QList<QVariant>>& value)
907
setAppearanceEditState(name);
909
if (hasAppearanceProperty(name)) {
910
_appearance[name]->setList(*value);
914
void Material::setAppearanceValue(const QString& name, const QVariant& value)
916
setAppearanceEditState(name);
918
if (hasAppearanceProperty(name)) {
919
_appearance[name]->setValue(value);
923
void Material::setValue(const QString& name, const QString& value)
925
if (hasPhysicalProperty(name)) {
926
setPhysicalValue(name, value);
928
else if (hasAppearanceProperty(name)) {
929
setAppearanceValue(name, value);
932
throw PropertyNotFound();
936
void Material::setValue(const QString& name, const QVariant& value)
938
if (hasPhysicalProperty(name)) {
939
setPhysicalValue(name, value);
942
throw PropertyNotFound();
946
void Material::setLegacyValue(const QString& name, const QString& value)
950
_legacy[name] = value;
953
std::shared_ptr<MaterialProperty> Material::getPhysicalProperty(const QString& name)
956
return _physical.at(name);
958
catch (std::out_of_range const&) {
959
throw PropertyNotFound();
963
std::shared_ptr<MaterialProperty> Material::getPhysicalProperty(const QString& name) const
966
return _physical.at(name);
968
catch (std::out_of_range const&) {
969
throw PropertyNotFound();
973
std::shared_ptr<MaterialProperty> Material::getAppearanceProperty(const QString& name)
976
return _appearance.at(name);
978
catch (std::out_of_range const&) {
979
throw PropertyNotFound();
983
std::shared_ptr<MaterialProperty> Material::getAppearanceProperty(const QString& name) const
986
return _appearance.at(name);
988
catch (std::out_of_range const&) {
989
throw PropertyNotFound();
993
std::shared_ptr<MaterialProperty> Material::getProperty(const QString& name)
995
if (hasPhysicalProperty(name)) {
996
return getPhysicalProperty(name);
998
if (hasAppearanceProperty(name)) {
999
return getAppearanceProperty(name);
1001
throw PropertyNotFound();
1004
std::shared_ptr<MaterialProperty> Material::getProperty(const QString& name) const
1006
if (hasPhysicalProperty(name)) {
1007
return getPhysicalProperty(name);
1009
if (hasAppearanceProperty(name)) {
1010
return getAppearanceProperty(name);
1012
throw PropertyNotFound();
1016
Material::getValue(const std::map<QString, std::shared_ptr<MaterialProperty>>& propertyList,
1017
const QString& name)
1020
return propertyList.at(name)->getValue();
1022
catch (std::out_of_range const&) {
1023
throw PropertyNotFound();
1028
Material::getValueString(const std::map<QString, std::shared_ptr<MaterialProperty>>& propertyList,
1029
const QString& name)
1032
const auto& property = propertyList.at(name);
1033
if (property->isNull()) {
1036
if (property->getType() == MaterialValue::Quantity) {
1037
auto value = property->getValue();
1038
if (value.isNull()) {
1041
return value.value<Base::Quantity>().getUserString();
1043
if (property->getType() == MaterialValue::Float) {
1044
auto value = property->getValue();
1045
if (value.isNull()) {
1048
return QString(QLatin1String("%L1"))
1049
.arg(value.toFloat(), 0, 'g', MaterialProperty::PRECISION);
1051
return property->getValue().toString();
1053
catch (std::out_of_range const&) {
1054
throw PropertyNotFound();
1058
QVariant Material::getPhysicalValue(const QString& name) const
1060
return getValue(_physical, name);
1063
Base::Quantity Material::getPhysicalQuantity(const QString& name) const
1065
return getValue(_physical, name).value<Base::Quantity>();
1068
QString Material::getPhysicalValueString(const QString& name) const
1070
return getValueString(_physical, name);
1073
QVariant Material::getAppearanceValue(const QString& name) const
1075
return getValue(_appearance, name);
1078
Base::Quantity Material::getAppearanceQuantity(const QString& name) const
1080
return getValue(_appearance, name).value<Base::Quantity>();
1083
QString Material::getAppearanceValueString(const QString& name) const
1085
return getValueString(_appearance, name);
1088
bool Material::hasPhysicalProperty(const QString& name) const
1091
static_cast<void>(_physical.at(name));
1093
catch (std::out_of_range const&) {
1099
bool Material::hasAppearanceProperty(const QString& name) const
1102
static_cast<void>(_appearance.at(name));
1104
catch (std::out_of_range const&) {
1110
bool Material::hasNonLegacyProperty(const QString& name) const
1112
if (hasPhysicalProperty(name) || hasAppearanceProperty(name)) {
1118
bool Material::hasLegacyProperties() const
1120
return !_legacy.empty();
1123
bool Material::isInherited(const QString& uuid) const
1125
if (_physicalUuids.contains(uuid)) {
1128
if (_appearanceUuids.contains(uuid)) {
1132
return _allUuids.contains(uuid);
1135
bool Material::hasModel(const QString& uuid) const
1137
return _allUuids.contains(uuid);
1140
bool Material::hasPhysicalModel(const QString& uuid) const
1142
if (!hasModel(uuid)) {
1146
ModelManager manager;
1149
auto model = manager.getModel(uuid);
1150
if (model->getType() == Model::ModelType_Physical) {
1154
catch (ModelNotFound const&) {
1160
bool Material::hasAppearanceModel(const QString& uuid) const
1162
if (!hasModel(uuid)) {
1166
ModelManager manager;
1169
auto model = manager.getModel(uuid);
1170
if (model->getType() == Model::ModelType_Appearance) {
1174
catch (ModelNotFound const&) {
1180
bool Material::isPhysicalModelComplete(const QString& uuid) const
1182
if (!hasPhysicalModel(uuid)) {
1186
ModelManager manager;
1189
auto model = manager.getModel(uuid);
1190
for (auto& it : *model) {
1191
QString propertyName = it.first;
1192
auto property = getPhysicalProperty(propertyName);
1194
if (property->isNull()) {
1199
catch (ModelNotFound const&) {
1206
bool Material::isAppearanceModelComplete(const QString& uuid) const
1208
if (!hasAppearanceModel(uuid)) {
1212
ModelManager manager;
1215
auto model = manager.getModel(uuid);
1216
for (auto& it : *model) {
1217
QString propertyName = it.first;
1218
auto property = getAppearanceProperty(propertyName);
1220
if (property->isNull()) {
1225
catch (ModelNotFound const&) {
1232
void Material::saveGeneral(QTextStream& stream) const
1234
stream << "General:\n";
1235
stream << " UUID: \"" << _uuid << "\"\n";
1236
stream << " Name: \"" << MaterialValue::escapeString(_name) << "\"\n";
1237
if (!_author.isEmpty()) {
1238
stream << " Author: \"" << MaterialValue::escapeString(_author) << "\"\n";
1240
if (!_license.isEmpty()) {
1241
stream << " License: \"" << MaterialValue::escapeString(_license) << "\"\n";
1243
if (!_description.isEmpty()) {
1244
stream << " Description: \"" << MaterialValue::escapeString(_description) << "\"\n";
1246
if (!_url.isEmpty()) {
1247
stream << " SourceURL: \"" << MaterialValue::escapeString(_url) << "\"\n";
1249
if (!_reference.isEmpty()) {
1250
stream << " ReferenceSource: \"" << MaterialValue::escapeString(_reference) << "\"\n";
1254
void Material::saveInherits(QTextStream& stream) const
1256
if (!_parentUuid.isEmpty()) {
1257
MaterialManager manager;
1260
auto material = manager.getMaterial(_parentUuid);
1262
stream << "Inherits:\n";
1263
stream << " " << material->getName() << ":\n";
1264
stream << " UUID: \"" << _parentUuid << "\"\n";
1266
catch (const MaterialNotFound&) {
1271
bool Material::modelChanged(const std::shared_ptr<Material>& parent,
1272
const std::shared_ptr<Model>& model) const
1274
for (auto& it : *model) {
1275
QString propertyName = it.first;
1276
auto property = getPhysicalProperty(propertyName);
1278
auto parentProperty = parent->getPhysicalProperty(propertyName);
1280
if (*property != *parentProperty) {
1284
catch (const PropertyNotFound&) {
1292
bool Material::modelAppearanceChanged(const std::shared_ptr<Material>& parent,
1293
const std::shared_ptr<Model>& model) const
1295
for (auto& it : *model) {
1296
QString propertyName = it.first;
1297
auto property = getAppearanceProperty(propertyName);
1299
auto parentProperty = parent->getAppearanceProperty(propertyName);
1301
if (*property != *parentProperty) {
1305
catch (const PropertyNotFound&) {
1313
void Material::saveModels(QTextStream& stream, bool saveInherited) const
1315
if (_physical.empty()) {
1319
ModelManager modelManager;
1320
MaterialManager materialManager;
1322
bool inherited = saveInherited && (_parentUuid.size() > 0);
1323
std::shared_ptr<Material> parent;
1326
parent = materialManager.getMaterial(_parentUuid);
1328
catch (const MaterialNotFound&) {
1333
bool headerPrinted = false;
1334
for (auto& itm : _physicalUuids) {
1335
auto model = modelManager.getModel(itm);
1336
if (!inherited || modelChanged(parent, model)) {
1337
if (!headerPrinted) {
1338
stream << "Models:\n";
1339
headerPrinted = true;
1341
stream << " " << MaterialValue::escapeString(model->getName()) << ":\n";
1342
stream << " UUID: \"" << model->getUUID() << "\"\n";
1343
for (const auto& it : *model) {
1344
QString propertyName = it.first;
1345
std::shared_ptr<MaterialProperty> property = getPhysicalProperty(propertyName);
1346
std::shared_ptr<MaterialProperty> parentProperty;
1349
parentProperty = parent->getPhysicalProperty(propertyName);
1352
catch (const PropertyNotFound&) {
1353
Base::Console().Log("Material::saveModels Property not found '%s'\n",
1354
propertyName.toStdString().c_str());
1357
if (!inherited || !parentProperty || (*property != *parentProperty)) {
1358
if (!property->isNull()) {
1359
stream << " " << *property << "\n";
1367
void Material::saveAppearanceModels(QTextStream& stream, bool saveInherited) const
1369
if (_appearance.empty()) {
1373
ModelManager modelManager;
1374
MaterialManager materialManager;
1376
bool inherited = saveInherited && (_parentUuid.size() > 0);
1377
std::shared_ptr<Material> parent;
1380
parent = materialManager.getMaterial(_parentUuid);
1382
catch (const MaterialNotFound&) {
1387
bool headerPrinted = false;
1388
for (auto& itm : _appearanceUuids) {
1389
auto model = modelManager.getModel(itm);
1390
if (!inherited || modelAppearanceChanged(parent, model)) {
1391
if (!headerPrinted) {
1392
stream << "AppearanceModels:\n";
1393
headerPrinted = true;
1395
stream << " " << MaterialValue::escapeString(model->getName()) << ":\n";
1396
stream << " UUID: \"" << model->getUUID() << "\"\n";
1397
for (const auto& it : *model) {
1398
QString propertyName = it.first;
1399
std::shared_ptr<MaterialProperty> property = getAppearanceProperty(propertyName);
1400
std::shared_ptr<MaterialProperty> parentProperty;
1403
parentProperty = parent->getAppearanceProperty(propertyName);
1406
catch (const PropertyNotFound&) {
1409
if (!inherited || !parentProperty || (*property != *parentProperty)) {
1410
if (!property->isNull()) {
1411
stream << " " << *property << "\n";
1419
void Material::newUuid()
1421
_uuid = QUuid::createUuid().toString(QUuid::WithoutBraces);
1424
QString Material::getModelByName(const QString& name) const
1426
ModelManager manager;
1428
for (auto& it : _allUuids) {
1430
auto model = manager.getModel(it);
1431
if (model->getName() == name) {
1435
catch (ModelNotFound const&) {
1442
void Material::save(QTextStream& stream, bool overwrite, bool saveAsCopy, bool saveInherited)
1444
if (saveInherited && !saveAsCopy) {
1445
// Check to see if we're an original or if we're already in the list of
1447
MaterialManager materialManager;
1448
if (materialManager.exists(_uuid) && !overwrite) {
1449
// Make a new version based on the current
1450
setParentUUID(_uuid);
1454
// Prevent self inheritance
1455
if (_parentUuid == _uuid) {
1456
_parentUuid = QString();
1460
// Save it in the same format as the parent
1461
if (_parentUuid.isEmpty()) {
1462
saveInherited = false;
1465
saveInherited = true;
1470
// Creating a new derived model when overwriting sets itself as a
1471
// parent, that will no longer exist because it's been overwritten
1477
stream << "# File created by " << QString::fromStdString(App::Application::Config()["ExeName"])
1478
<< " " << QString::fromStdString(App::Application::Config()["ExeVersion"])
1479
<< " Revision: " << QString::fromStdString(App::Application::Config()["BuildRevision"])
1481
saveGeneral(stream);
1482
if (saveInherited) {
1483
saveInherits(stream);
1485
saveModels(stream, saveInherited);
1486
saveAppearanceModels(stream, saveInherited);
1488
setOldFormat(false);
1491
Material& Material::operator=(const Material& other)
1493
if (this == &other) {
1497
_library = other._library;
1498
_directory = other._directory;
1499
_uuid = other._uuid;
1500
_name = other._name;
1501
_author = other._author;
1502
_license = other._license;
1503
_parentUuid = other._parentUuid;
1504
_description = other._description;
1506
_reference = other._reference;
1507
_dereferenced = other._dereferenced;
1508
_oldFormat = other._oldFormat;
1509
_editState = other._editState;
1512
for (auto& it : other._tags) {
1515
_physicalUuids.clear();
1516
for (auto& it : other._physicalUuids) {
1517
_physicalUuids.insert(it);
1519
_appearanceUuids.clear();
1520
for (auto& it : other._appearanceUuids) {
1521
_appearanceUuids.insert(it);
1524
for (auto& it : other._allUuids) {
1525
_allUuids.insert(it);
1528
// Create copies of the properties rather than modify the originals
1530
for (auto& it : other._physical) {
1531
MaterialProperty prop(it.second);
1532
_physical[it.first] = std::make_shared<MaterialProperty>(prop);
1534
_appearance.clear();
1535
for (auto& it : other._appearance) {
1536
MaterialProperty prop(it.second);
1537
_appearance[it.first] = std::make_shared<MaterialProperty>(prop);
1540
for (auto& it : other._legacy) {
1541
_legacy[it.first] = it.second;
1547
Material& Material::operator=(const App::Material& other)
1549
if (!hasAppearanceModel(ModelUUIDs::ModelUUID_Rendering_Basic)) {
1550
addAppearance(ModelUUIDs::ModelUUID_Rendering_Basic);
1553
getAppearanceProperty(QLatin1String("AmbientColor"))->setColor(other.ambientColor);
1554
getAppearanceProperty(QLatin1String("DiffuseColor"))->setColor(other.diffuseColor);
1555
getAppearanceProperty(QLatin1String("SpecularColor"))->setColor(other.specularColor);
1556
getAppearanceProperty(QLatin1String("EmissiveColor"))->setColor(other.emissiveColor);
1557
getAppearanceProperty(QLatin1String("Shininess"))->setFloat(other.shininess);
1558
getAppearanceProperty(QLatin1String("Transparency"))->setFloat(other.transparency);
1560
if (!other.image.empty() || !other.imagePath.empty()) {
1561
if (!hasAppearanceModel(ModelUUIDs::ModelUUID_Rendering_Texture)) {
1562
addAppearance(ModelUUIDs::ModelUUID_Rendering_Texture);
1565
getAppearanceProperty(QLatin1String("TextureImage"))->setString(other.image);
1566
getAppearanceProperty(QLatin1String("TexturePath"))->setString(other.imagePath);
1573
* Normalize models by removing any inherited models
1575
QStringList Material::normalizeModels(const QStringList& models)
1577
QStringList normalized;
1579
ModelManager manager;
1581
for (auto& uuid : models) {
1582
auto model = manager.getModel(uuid);
1585
for (auto& childUuid : models) {
1586
if (uuid != childUuid) {
1587
auto childModel = manager.getModel(childUuid);
1588
if (childModel->inherits(childUuid)) {
1589
// We're an inherited model
1604
* Set or change the base material for the current material, updating the
1605
* properties as required.
1607
void Material::updateInheritance([[maybe_unused]] const QString& parent)
1611
* Return a list of models that are defined in the parent material but not in
1614
QStringList Material::inheritedMissingModels(const Material& parent) const
1616
QStringList missing;
1617
for (auto& uuid : parent._allUuids) {
1618
if (!hasModel(uuid)) {
1623
return normalizeModels(missing);
1627
* Return a list of models that are defined in this model but not the parent
1629
QStringList Material::inheritedAddedModels(const Material& parent) const
1632
for (auto& uuid : _allUuids) {
1633
if (!parent.hasModel(uuid)) {
1638
return normalizeModels(added);
1642
* Return a list of properties that have different values from the parent
1645
void Material::inheritedPropertyDiff([[maybe_unused]] const QString& parent)
1649
* Return an App::Material object describing the materials appearance, or DEFAULT if
1652
App::Material Material::getMaterialAppearance() const
1654
App::Material material(App::Material::DEFAULT);
1656
bool custom = false;
1657
if (hasAppearanceProperty(QLatin1String("AmbientColor"))) {
1658
material.ambientColor = getAppearanceProperty(QLatin1String("AmbientColor"))->getColor();
1661
if (hasAppearanceProperty(QLatin1String("DiffuseColor"))) {
1662
material.diffuseColor = getAppearanceProperty(QLatin1String("DiffuseColor"))->getColor();
1665
if (hasAppearanceProperty(QLatin1String("SpecularColor"))) {
1666
material.specularColor = getAppearanceProperty(QLatin1String("SpecularColor"))->getColor();
1669
if (hasAppearanceProperty(QLatin1String("EmissiveColor"))) {
1670
material.emissiveColor = getAppearanceProperty(QLatin1String("EmissiveColor"))->getColor();
1673
if (hasAppearanceProperty(QLatin1String("Shininess"))) {
1674
material.shininess = getAppearanceProperty(QLatin1String("Shininess"))->getFloat();
1677
if (hasAppearanceProperty(QLatin1String("Transparency"))) {
1678
material.transparency = getAppearanceProperty(QLatin1String("Transparency"))->getFloat();
1681
if (hasAppearanceProperty(QLatin1String("TextureImage"))) {
1682
auto property = getAppearanceProperty(QLatin1String("TextureImage"));
1683
if (!property->isNull()) {
1684
Base::Console().Log("Has 'TextureImage'\n");
1685
material.image = property->getString().toStdString();
1690
else if (hasAppearanceProperty(QLatin1String("TexturePath"))) {
1691
auto property = getAppearanceProperty(QLatin1String("TexturePath"));
1692
if (!property->isNull()) {
1693
Base::Console().Log("Has 'TexturePath'\n");
1694
material.imagePath = property->getString().toStdString();
1701
material.setType(App::Material::USER_DEFINED);
1702
material.uuid = getUUID().toStdString();