Download src/Mod/Material/App/Materials.cpp from AbdulElahGwaith/FreeCAD: direct link, hf CLI and curl.
- Browser
- Download file 51.5 kB
-
https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/Material/App/Materials.cpp
- Command line
-
hf download hf://AbdulElahGwaith/FreeCAD/src/Mod/Material/App/Materials.cpp
-
curl -L -o Materials.cpp https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/Material/App/Materials.cpp
51.5 kB
| // SPDX-License-Identifier: LGPL-2.1-or-later | |
| /*************************************************************************** | |
| * Copyright (c) 2023 David Carter <dcarter@david.carter.ca> * | |
| * * | |
| * This file is part of FreeCAD. * | |
| * * | |
| * FreeCAD is free software: you can redistribute it and/or modify it * | |
| * under the terms of the GNU Lesser General Public License as * | |
| * published by the Free Software Foundation, either version 2.1 of the * | |
| * License, or (at your option) any later version. * | |
| * * | |
| * FreeCAD is distributed in the hope that it will be useful, but * | |
| * WITHOUT ANY WARRANTY; without even the implied warranty of * | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * | |
| * Lesser General Public License for more details. * | |
| * * | |
| * You should have received a copy of the GNU Lesser General Public * | |
| * License along with FreeCAD. If not, see * | |
| * <https://www.gnu.org/licenses/>. * | |
| * * | |
| **************************************************************************/ | |
| using namespace Materials; | |
| /* TRANSLATOR Material::Materials */ | |
| TYPESYSTEM_SOURCE(Materials::MaterialProperty, Materials::ModelProperty) | |
| MaterialProperty::MaterialProperty() | |
| { | |
| _valuePtr = std::make_shared<MaterialValue>(MaterialValue::None); | |
| } | |
| MaterialProperty::MaterialProperty(const ModelProperty& other, QString modelUUID) | |
| : ModelProperty(other) | |
| , _modelUUID(modelUUID) | |
| , _valuePtr(nullptr) | |
| { | |
| setType(getPropertyType()); | |
| auto columns = other.getColumns(); | |
| for (auto& it : columns) { | |
| MaterialProperty prop(it, modelUUID); | |
| addColumn(prop); | |
| } | |
| } | |
| void MaterialProperty::copyValuePtr(const std::shared_ptr<MaterialValue>& value) | |
| { | |
| if (value->getType() == MaterialValue::Array2D) { | |
| _valuePtr = | |
| std::make_shared<Array2D>(*(std::static_pointer_cast<Array2D>(value))); | |
| } | |
| else if (value->getType() == MaterialValue::Array3D) { | |
| _valuePtr = | |
| std::make_shared<Array3D>(*(std::static_pointer_cast<Array3D>(value))); | |
| } | |
| else { | |
| _valuePtr = std::make_shared<MaterialValue>(*value); | |
| } | |
| } | |
| MaterialProperty::MaterialProperty(const MaterialProperty& other) | |
| : ModelProperty(other) | |
| , _modelUUID(other._modelUUID) | |
| { | |
| copyValuePtr(other._valuePtr); | |
| for (auto& it : other._columns) { | |
| _columns.push_back(it); | |
| } | |
| } | |
| MaterialProperty::MaterialProperty(const std::shared_ptr<MaterialProperty>& other) | |
| : MaterialProperty(*other) | |
| {} | |
| void MaterialProperty::setModelUUID(const QString& uuid) | |
| { | |
| _modelUUID = uuid; | |
| } | |
| QVariant MaterialProperty::getValue() | |
| { | |
| return _valuePtr->getValue(); | |
| } | |
| QVariant MaterialProperty::getValue() const | |
| { | |
| return _valuePtr->getValue(); | |
| } | |
| std::shared_ptr<MaterialValue> MaterialProperty::getMaterialValue() | |
| { | |
| return _valuePtr; | |
| } | |
| std::shared_ptr<MaterialValue> MaterialProperty::getMaterialValue() const | |
| { | |
| return _valuePtr; | |
| } | |
| QString MaterialProperty::getString() const | |
| { | |
| // This method produces a localized string. For a non-localized string use | |
| // getDictionaryString() | |
| if (isNull()) { | |
| return {}; | |
| } | |
| if (getType() == MaterialValue::Quantity) { | |
| auto quantity = getValue().value<Base::Quantity>(); | |
| return QString::fromStdString(quantity.getUserString()); | |
| } | |
| if (getType() == MaterialValue::Float) { | |
| auto value = getValue(); | |
| if (value.isNull()) { | |
| return {}; | |
| } | |
| return QString(QStringLiteral("%L1")).arg(value.toFloat(), 0, 'g', MaterialValue::PRECISION); | |
| } | |
| return getValue().toString(); | |
| } | |
| QString MaterialProperty::getYAMLString() const | |
| { | |
| return _valuePtr->getYAMLString(); | |
| } | |
| Base::Color MaterialProperty::getColor() const | |
| { | |
| auto colorString = getValue().toString(); | |
| std::stringstream stream(colorString.toStdString()); | |
| char c; | |
| stream >> c; // read "(" | |
| float red; | |
| stream >> red; | |
| stream >> c; // "," | |
| float green; | |
| stream >> green; | |
| stream >> c; // "," | |
| float blue; | |
| stream >> blue; | |
| stream >> c; // "," | |
| float alpha = 1.0; | |
| if (c == ',') { | |
| stream >> alpha; | |
| } | |
| Base::Color color(red, green, blue, alpha); | |
| return color; | |
| } | |
| QString MaterialProperty::getDictionaryString() const | |
| { | |
| // This method produces a non-localized string. For a localized string use | |
| // getString() | |
| if (isNull()) { | |
| return {}; | |
| } | |
| if (getType() == MaterialValue::Quantity) { | |
| auto quantity = getValue().value<Base::Quantity>(); | |
| auto string = QString(QStringLiteral("%1 %2")) | |
| .arg(quantity.getValue(), 0, 'g', MaterialValue::PRECISION) | |
| .arg(QString::fromStdString(quantity.getUnit().getString())); | |
| return string; | |
| } | |
| if (getType() == MaterialValue::Float) { | |
| auto value = getValue(); | |
| if (value.isNull()) { | |
| return {}; | |
| } | |
| return QString(QStringLiteral("%1")).arg(value.toFloat(), 0, 'g', MaterialValue::PRECISION); | |
| } | |
| return getValue().toString(); | |
| } | |
| void MaterialProperty::setPropertyType(const QString& type) | |
| { | |
| ModelProperty::setPropertyType(type); | |
| setType(type); | |
| } | |
| void MaterialProperty::setType(const QString& type) | |
| { | |
| auto mappedType = MaterialValue::mapType(type); | |
| if (mappedType == MaterialValue::None) { | |
| throw UnknownValueType(); | |
| } | |
| if (mappedType == MaterialValue::Array2D) { | |
| auto arrayPtr = std::make_shared<Array2D>(); | |
| arrayPtr->setColumns(columns()); | |
| _valuePtr = arrayPtr; | |
| } | |
| else if (mappedType == MaterialValue::Array3D) { | |
| auto arrayPtr = std::make_shared<Array3D>(); | |
| // First column is third dimension | |
| arrayPtr->setColumns(columns() - 1); | |
| _valuePtr = arrayPtr; | |
| } | |
| else { | |
| _valuePtr = std::make_shared<MaterialValue>(mappedType); | |
| } | |
| } | |
| MaterialProperty& MaterialProperty::getColumn(int column) | |
| { | |
| try { | |
| return _columns.at(column); | |
| } | |
| catch (std::out_of_range const&) { | |
| throw InvalidIndex(); | |
| } | |
| } | |
| const MaterialProperty& MaterialProperty::getColumn(int column) const | |
| { | |
| try { | |
| return _columns.at(column); | |
| } | |
| catch (std::out_of_range const&) { | |
| throw InvalidIndex(); | |
| } | |
| } | |
| MaterialValue::ValueType MaterialProperty::getColumnType(int column) const | |
| { | |
| try { | |
| return _columns.at(column).getType(); | |
| } | |
| catch (std::out_of_range const&) { | |
| throw InvalidIndex(); | |
| } | |
| } | |
| QString MaterialProperty::getColumnUnits(int column) const | |
| { | |
| try { | |
| return _columns.at(column).getUnits(); | |
| } | |
| catch (std::out_of_range const&) { | |
| throw InvalidIndex(); | |
| } | |
| } | |
| QVariant MaterialProperty::getColumnNull(int column) const | |
| { | |
| MaterialValue::ValueType valueType = getColumnType(column); | |
| switch (valueType) { | |
| case MaterialValue::Quantity: { | |
| Base::Quantity quant = Base::Quantity(0, getColumnUnits(column).toStdString()); | |
| return QVariant::fromValue(quant); | |
| } | |
| case MaterialValue::Float: | |
| case MaterialValue::Integer: | |
| return 0; | |
| default: | |
| break; | |
| } | |
| return QString(); | |
| } | |
| void MaterialProperty::setValue(const QVariant& value) | |
| { | |
| if (_valuePtr->getType() == MaterialValue::Quantity && value.canConvert<Base::Quantity>()) { | |
| // Ensure the units are set correctly | |
| auto quantity = value.value<Base::Quantity>(); | |
| if (quantity.isValid()) { | |
| setQuantity(quantity); | |
| } | |
| else { | |
| // Set a default value with default units | |
| setValue(QStringLiteral("0")); | |
| } | |
| } | |
| else { | |
| _valuePtr->setValue(value); | |
| } | |
| } | |
| void MaterialProperty::setValue(const QString& value) | |
| { | |
| if (_valuePtr->getType() == MaterialValue::Boolean) { | |
| setBoolean(value); | |
| } | |
| else if (_valuePtr->getType() == MaterialValue::Integer) { | |
| setInt(value); | |
| } | |
| else if (_valuePtr->getType() == MaterialValue::Float) { | |
| setFloat(value); | |
| } | |
| else if (_valuePtr->getType() == MaterialValue::URL) { | |
| setURL(value); | |
| } | |
| else if (_valuePtr->getType() == MaterialValue::Array2D | |
| || _valuePtr->getType() == MaterialValue::Array3D) { | |
| // This value can't be directly assigned | |
| } | |
| else if (_valuePtr->getType() == MaterialValue::Quantity) { | |
| try { | |
| setQuantity(Base::Quantity::parse(value.toStdString())); | |
| } | |
| catch (const Base::ParserError& e) { | |
| Base::Console().log("MaterialProperty::setValue Error '%s' - '%s'\n", | |
| e.what(), | |
| value.toStdString().c_str()); | |
| // Save as a string | |
| setString(value); | |
| } | |
| } | |
| else { | |
| setString(value); | |
| } | |
| } | |
| void MaterialProperty::setValue(const std::shared_ptr<MaterialValue>& value) | |
| { | |
| _valuePtr = value; | |
| } | |
| void MaterialProperty::setString(const QString& value) | |
| { | |
| _valuePtr->setValue(QVariant(value)); | |
| } | |
| void MaterialProperty::setString(const std::string& value) | |
| { | |
| _valuePtr->setValue(QVariant(QString::fromStdString(value))); | |
| } | |
| void MaterialProperty::setBoolean(bool value) | |
| { | |
| _valuePtr->setValue(QVariant(value)); | |
| } | |
| void MaterialProperty::setBoolean(int value) | |
| { | |
| _valuePtr->setValue(QVariant(value != 0)); | |
| } | |
| void MaterialProperty::setBoolean(const QString& value) | |
| { | |
| bool boolean = false; | |
| std::string val = value.toStdString(); | |
| if ((val == "true") || (val == "True")) { | |
| boolean = true; | |
| } | |
| else if ((val == "false") || (val == "False")) { | |
| boolean = false; | |
| } | |
| else { | |
| boolean = (std::stoi(val) != 0); | |
| } | |
| setBoolean(boolean); | |
| } | |
| void MaterialProperty::setInt(int value) | |
| { | |
| _valuePtr->setValue(QVariant(value)); | |
| } | |
| void MaterialProperty::setInt(const QString& value) | |
| { | |
| _valuePtr->setValue(value.toInt()); | |
| } | |
| void MaterialProperty::setFloat(double value) | |
| { | |
| _valuePtr->setValue(QVariant(value)); | |
| } | |
| void MaterialProperty::setFloat(const QString& value) | |
| { | |
| _valuePtr->setValue(QVariant(value.toFloat())); | |
| } | |
| void MaterialProperty::setQuantity(const Base::Quantity& value) | |
| { | |
| auto quantity = value; | |
| if (quantity.isDimensionless()) { | |
| // Assign the default units when none are provided. | |
| // | |
| // This needs to be parsed rather than just setting units. Otherwise we get mm->m conversion | |
| // errors, etc | |
| quantity = Base::Quantity::parse(quantity.getUserString() + getUnits().toStdString()); | |
| } | |
| else { | |
| auto propertyUnit = Base::Quantity::parse(getUnits().toStdString()).getUnit(); | |
| auto units = quantity.getUnit(); | |
| if (propertyUnit != units) { | |
| throw Base::ValueError("Incompatible material units"); | |
| } | |
| } | |
| quantity.setFormat(MaterialValue::getQuantityFormat()); | |
| _valuePtr->setValue(QVariant(QVariant::fromValue(quantity))); | |
| } | |
| void MaterialProperty::setQuantity(double value, const QString& units) | |
| { | |
| setQuantity(Base::Quantity(value, units.toStdString())); | |
| } | |
| void MaterialProperty::setQuantity(const QString& value) | |
| { | |
| setQuantity(Base::Quantity::parse(value.toStdString())); | |
| } | |
| void MaterialProperty::setList(const QList<QVariant>& value) | |
| { | |
| _valuePtr->setList(value); | |
| } | |
| void MaterialProperty::setURL(const QString& value) | |
| { | |
| _valuePtr->setValue(QVariant(value)); | |
| } | |
| void MaterialProperty::setColor(const Base::Color& value) | |
| { | |
| std::stringstream ss; | |
| ss << "(" << value.r << ", " << value.g << ", " << value.b << ", " << value.a << ")"; | |
| _valuePtr->setValue(QVariant(QString::fromStdString(ss.str()))); | |
| } | |
| MaterialProperty& MaterialProperty::operator=(const MaterialProperty& other) | |
| { | |
| if (this == &other) { | |
| return *this; | |
| } | |
| ModelProperty::operator=(other); | |
| _modelUUID = other._modelUUID; | |
| copyValuePtr(other._valuePtr); | |
| _columns.clear(); | |
| for (auto& it : other._columns) { | |
| _columns.push_back(it); | |
| } | |
| return *this; | |
| } | |
| bool MaterialProperty::operator==(const MaterialProperty& other) const | |
| { | |
| if (this == &other) { | |
| return true; | |
| } | |
| if (ModelProperty::operator==(other)) { | |
| return (*_valuePtr == *other._valuePtr); | |
| } | |
| return false; | |
| } | |
| void MaterialProperty::validate(const MaterialProperty& other) const { | |
| _valuePtr->validate(*other._valuePtr); | |
| if (_columns.size() != other._columns.size()) { | |
| throw InvalidProperty("Model property column counts don't match"); | |
| } | |
| for (size_t i = 0; i < _columns.size(); i++) { | |
| _columns[i].validate(other._columns[i]); | |
| } | |
| } | |
| TYPESYSTEM_SOURCE(Materials::Material, Base::BaseClass) | |
| Material::Material() | |
| : _dereferenced(false) | |
| , _oldFormat(false) | |
| , _editState(ModelEdit_None) | |
| { | |
| // Create an initial UUID | |
| newUuid(); | |
| } | |
| Material::Material(const std::shared_ptr<MaterialLibrary>& library, | |
| const QString& directory, | |
| const QString& uuid, | |
| const QString& name) | |
| : _library(library) | |
| , _uuid(uuid) | |
| , _name(name) | |
| , _dereferenced(false) | |
| , _oldFormat(false) | |
| , _editState(ModelEdit_None) | |
| { | |
| setDirectory(directory); | |
| } | |
| Material::Material(const Material& other) | |
| : _library(other._library) | |
| , _directory(other._directory) | |
| , _filename(other._filename) | |
| , _uuid(other._uuid) | |
| , _name(other._name) | |
| , _author(other._author) | |
| , _license(other._license) | |
| , _parentUuid(other._parentUuid) | |
| , _description(other._description) | |
| , _url(other._url) | |
| , _reference(other._reference) | |
| , _dereferenced(other._dereferenced) | |
| , _oldFormat(other._oldFormat) | |
| , _editState(other._editState) | |
| { | |
| for (auto& it : other._tags) { | |
| _tags.insert(it); | |
| } | |
| for (auto& it : other._physicalUuids) { | |
| _physicalUuids.insert(it); | |
| } | |
| for (auto& it : other._appearanceUuids) { | |
| _appearanceUuids.insert(it); | |
| } | |
| for (auto& it : other._allUuids) { | |
| _allUuids.insert(it); | |
| } | |
| for (auto& it : other._physical) { | |
| MaterialProperty prop(it.second); | |
| _physical[it.first] = std::make_shared<MaterialProperty>(prop); | |
| } | |
| for (auto& it : other._appearance) { | |
| MaterialProperty prop(it.second); | |
| _appearance[it.first] = std::make_shared<MaterialProperty>(prop); | |
| } | |
| for (auto& it : other._legacy) { | |
| _legacy[it.first] = it.second; | |
| } | |
| } | |
| QString Material::getDirectory() const | |
| { | |
| return _directory; | |
| } | |
| void Material::setDirectory(const QString& directory) | |
| { | |
| _directory = directory; | |
| } | |
| QString Material::getFilename() const | |
| { | |
| return _filename; | |
| } | |
| void Material::setFilename(const QString& filename) | |
| { | |
| _filename = filename; | |
| } | |
| QString Material::getFilePath() const | |
| { | |
| return QDir(_directory + QStringLiteral("/") + _filename).absolutePath(); | |
| } | |
| QString Material::getAuthorAndLicense() const | |
| { | |
| QString authorAndLicense; | |
| // Combine the author and license field for backwards compatibility | |
| if (!_author.isNull()) { | |
| authorAndLicense = _author; | |
| if (!_license.isNull()) { | |
| authorAndLicense += QStringLiteral(" ") + _license; | |
| } | |
| } | |
| else if (!_license.isNull()) { | |
| authorAndLicense = _license; | |
| } | |
| return _license; | |
| } | |
| void Material::addModel(const QString& uuid) | |
| { | |
| for (const auto& modelUUID : std::as_const(_allUuids)) { | |
| if (modelUUID == uuid) { | |
| return; | |
| } | |
| } | |
| _allUuids << uuid; | |
| auto& manager = ModelManager::getManager(); | |
| try { | |
| auto model = manager.getModel(uuid); | |
| auto inheritance = model->getInheritance(); | |
| for (auto& inherits : inheritance) { | |
| addModel(inherits); | |
| } | |
| } | |
| catch (ModelNotFound const&) { | |
| } | |
| } | |
| void Material::clearModels() | |
| { | |
| _physicalUuids.clear(); | |
| _appearanceUuids.clear(); | |
| _allUuids.clear(); | |
| _physical.clear(); | |
| _appearance.clear(); | |
| } | |
| void Material::clearInherited() | |
| { | |
| _allUuids.clear(); | |
| // Rebuild the UUID lists without the inherited UUIDs | |
| for (auto& uuid : _physicalUuids) { | |
| _allUuids << uuid; | |
| } | |
| for (auto& uuid : _appearanceUuids) { | |
| _allUuids << uuid; | |
| } | |
| } | |
| void Material::setName(const QString& name) | |
| { | |
| _name = name; | |
| setEditStateExtend(); | |
| } | |
| void Material::setAuthor(const QString& author) | |
| { | |
| _author = author; | |
| setEditStateExtend(); | |
| } | |
| void Material::setLicense(const QString& license) | |
| { | |
| _license = license; | |
| setEditStateExtend(); | |
| } | |
| void Material::setParentUUID(const QString& uuid) | |
| { | |
| _parentUuid = uuid; | |
| setEditStateExtend(); | |
| } | |
| void Material::setDescription(const QString& description) | |
| { | |
| _description = description; | |
| setEditStateExtend(); | |
| } | |
| void Material::setURL(const QString& url) | |
| { | |
| _url = url; | |
| setEditStateExtend(); | |
| } | |
| void Material::setReference(const QString& reference) | |
| { | |
| _reference = reference; | |
| setEditStateExtend(); | |
| } | |
| void Material::setEditState(ModelEdit newState) | |
| { | |
| if (newState == ModelEdit_Extend) { | |
| if (_editState != ModelEdit_Alter) { | |
| _editState = newState; | |
| } | |
| } | |
| else if (newState == ModelEdit_Alter) { | |
| _editState = newState; | |
| } | |
| } | |
| void Material::removeUUID(QSet<QString>& uuidList, const QString& uuid) | |
| { | |
| uuidList.remove(uuid); | |
| } | |
| void Material::addTag(const QString& tag) | |
| { | |
| auto trimmed = tag.trimmed(); | |
| if (!trimmed.isEmpty()) { | |
| _tags.insert(trimmed); | |
| } | |
| } | |
| void Material::removeTag(const QString& tag) | |
| { | |
| _tags.remove(tag); | |
| } | |
| void Material::addPhysical(const QString& uuid) | |
| { | |
| if (hasPhysicalModel(uuid)) { | |
| return; | |
| } | |
| auto& manager = ModelManager::getManager(); | |
| try { | |
| auto model = manager.getModel(uuid); | |
| auto& inheritance = model->getInheritance(); | |
| for (auto& it : inheritance) { | |
| // Inherited models may already have the properties, so just | |
| // remove the uuid | |
| removeUUID(_physicalUuids, it); | |
| } | |
| _physicalUuids.insert(uuid); | |
| addModel(uuid); | |
| setEditStateExtend(); | |
| for (auto& it : *model) { | |
| QString propertyName = it.first; | |
| if (!hasPhysicalProperty(propertyName)) { | |
| ModelProperty property = static_cast<ModelProperty>(it.second); | |
| try { | |
| _physical[propertyName] = std::make_shared<MaterialProperty>(property, uuid); | |
| } | |
| catch (const UnknownValueType&) { | |
| Base::Console().error("Property '%s' has unknown type '%s'. Ignoring\n", | |
| property.getName().toStdString().c_str(), | |
| property.getPropertyType().toStdString().c_str()); | |
| } | |
| } | |
| } | |
| } | |
| catch (ModelNotFound const&) { | |
| } | |
| } | |
| void Material::removePhysical(const QString& uuid) | |
| { | |
| if (!hasPhysicalModel(uuid)) { | |
| return; | |
| } | |
| // If it's an inherited model, do nothing | |
| if (isInherited(uuid)) { | |
| return; | |
| } | |
| auto& manager = ModelManager::getManager(); | |
| try { | |
| auto model = manager.getModel(uuid); | |
| auto& inheritance = model->getInheritance(); | |
| for (auto& it : inheritance) { | |
| removeUUID(_physicalUuids, it); | |
| removeUUID(_allUuids, it); | |
| } | |
| removeUUID(_physicalUuids, uuid); | |
| removeUUID(_allUuids, uuid); | |
| for (auto& it : *model) { | |
| _physical.erase(it.first); | |
| } | |
| setEditStateAlter(); | |
| } | |
| catch (ModelNotFound const&) { | |
| Base::Console().log("Physical model not found '%s'\n", uuid.toStdString().c_str()); | |
| } | |
| } | |
| void Material::addAppearance(const QString& uuid) | |
| { | |
| if (hasAppearanceModel(uuid)) { | |
| return; | |
| } | |
| auto& manager = ModelManager::getManager(); | |
| try { | |
| auto model = manager.getModel(uuid); | |
| auto& inheritance = model->getInheritance(); | |
| for (auto& it : inheritance) { | |
| // Inherited models may already have the properties, so just | |
| // remove the uuid | |
| removeUUID(_appearanceUuids, it); | |
| } | |
| _appearanceUuids.insert(uuid); | |
| addModel(uuid); | |
| setEditStateExtend(); | |
| for (auto& it : *model) { | |
| QString propertyName = it.first; | |
| if (!hasAppearanceProperty(propertyName)) { | |
| ModelProperty property = static_cast<ModelProperty>(it.second); | |
| _appearance[propertyName] = std::make_shared<MaterialProperty>(property, uuid); | |
| } | |
| } | |
| } | |
| catch (ModelNotFound const&) { | |
| Base::Console().log("Appearance model not found '%s'\n", uuid.toStdString().c_str()); | |
| } | |
| } | |
| void Material::removeAppearance(const QString& uuid) | |
| { | |
| if (!hasAppearanceModel(uuid)) { | |
| return; | |
| } | |
| // If it's an inherited model, do nothing | |
| if (isInherited(uuid)) { | |
| return; | |
| } | |
| auto& manager = ModelManager::getManager(); | |
| try { | |
| auto model = manager.getModel(uuid); | |
| auto& inheritance = model->getInheritance(); | |
| for (auto& it : inheritance) { | |
| removeUUID(_appearanceUuids, it); | |
| removeUUID(_allUuids, it); | |
| } | |
| removeUUID(_appearanceUuids, uuid); | |
| removeUUID(_allUuids, uuid); | |
| for (auto& it : *model) { | |
| _appearance.erase(it.first); | |
| } | |
| setEditStateAlter(); | |
| } | |
| catch (ModelNotFound const&) { | |
| } | |
| } | |
| void Material::setPropertyEditState(const QString& name) | |
| { | |
| try { | |
| if (hasPhysicalProperty(name)) { | |
| setPhysicalEditState(name); | |
| } | |
| else if (hasAppearanceProperty(name)) { | |
| setAppearanceEditState(name); | |
| } | |
| } | |
| catch (const PropertyNotFound&) { | |
| } | |
| } | |
| void Material::setPhysicalEditState(const QString& name) | |
| { | |
| if (getPhysicalProperty(name)->isNull()) { | |
| setEditStateExtend(); | |
| } | |
| else { | |
| setEditStateAlter(); | |
| } | |
| } | |
| void Material::setAppearanceEditState(const QString& name) | |
| { | |
| try { | |
| if (getAppearanceProperty(name)->isNull()) { | |
| setEditStateExtend(); | |
| } | |
| else { | |
| setEditStateAlter(); | |
| } | |
| } | |
| catch (const PropertyNotFound&) { | |
| } | |
| } | |
| void Material::setPhysicalValue(const QString& name, const QString& value) | |
| { | |
| setPhysicalEditState(name); | |
| if (hasPhysicalProperty(name)) { | |
| _physical[name]->setValue(value); // may not be a string type, conversion may be required | |
| } | |
| } | |
| void Material::setPhysicalValue(const QString& name, int value) | |
| { | |
| setPhysicalEditState(name); | |
| if (hasPhysicalProperty(name)) { | |
| _physical[name]->setInt(value); | |
| } | |
| } | |
| void Material::setPhysicalValue(const QString& name, double value) | |
| { | |
| setPhysicalEditState(name); | |
| if (hasPhysicalProperty(name)) { | |
| _physical[name]->setFloat(value); | |
| } | |
| } | |
| void Material::setPhysicalValue(const QString& name, const Base::Quantity& value) | |
| { | |
| setPhysicalEditState(name); | |
| if (hasPhysicalProperty(name)) { | |
| _physical[name]->setQuantity(value); | |
| } | |
| } | |
| void Material::setPhysicalValue(const QString& name, const std::shared_ptr<MaterialValue>& value) | |
| { | |
| setPhysicalEditState(name); | |
| if (hasPhysicalProperty(name)) { | |
| _physical[name]->setValue(value); | |
| } | |
| } | |
| void Material::setPhysicalValue(const QString& name, const std::shared_ptr<QList<QVariant>>& value) | |
| { | |
| setPhysicalEditState(name); | |
| if (hasPhysicalProperty(name)) { | |
| _physical[name]->setList(*value); | |
| } | |
| } | |
| void Material::setPhysicalValue(const QString& name, const QVariant& value) | |
| { | |
| setPhysicalEditState(name); | |
| if (hasPhysicalProperty(name)) { | |
| _physical[name]->setValue(value); | |
| } | |
| } | |
| void Material::setAppearanceValue(const QString& name, const QString& value) | |
| { | |
| setAppearanceEditState(name); | |
| if (hasAppearanceProperty(name)) { | |
| _appearance[name]->setValue(value); // may not be a string type, conversion may be required | |
| } | |
| } | |
| void Material::setAppearanceValue(const QString& name, const std::shared_ptr<MaterialValue>& value) | |
| { | |
| setAppearanceEditState(name); | |
| if (hasAppearanceProperty(name)) { | |
| _appearance[name]->setValue(value); | |
| } | |
| } | |
| void Material::setAppearanceValue(const QString& name, | |
| const std::shared_ptr<QList<QVariant>>& value) | |
| { | |
| setAppearanceEditState(name); | |
| if (hasAppearanceProperty(name)) { | |
| _appearance[name]->setList(*value); | |
| } | |
| } | |
| void Material::setAppearanceValue(const QString& name, const QVariant& value) | |
| { | |
| setAppearanceEditState(name); | |
| if (hasAppearanceProperty(name)) { | |
| _appearance[name]->setValue(value); | |
| } | |
| } | |
| void Material::setValue(const QString& name, const QString& value) | |
| { | |
| if (hasPhysicalProperty(name)) { | |
| setPhysicalValue(name, value); | |
| } | |
| else if (hasAppearanceProperty(name)) { | |
| setAppearanceValue(name, value); | |
| } | |
| else { | |
| throw PropertyNotFound(); | |
| } | |
| } | |
| void Material::setValue(const QString& name, const QVariant& value) | |
| { | |
| if (hasPhysicalProperty(name)) { | |
| setPhysicalValue(name, value); | |
| } | |
| else if (hasAppearanceProperty(name)) { | |
| setAppearanceValue(name, value); | |
| } | |
| else { | |
| throw PropertyNotFound(); | |
| } | |
| } | |
| void Material::setValue(const QString& name, const std::shared_ptr<MaterialValue>& value) | |
| { | |
| if (hasPhysicalProperty(name)) { | |
| setPhysicalValue(name, value); | |
| } | |
| else if (hasAppearanceProperty(name)) { | |
| setAppearanceValue(name, value); | |
| } | |
| else { | |
| throw PropertyNotFound(); | |
| } | |
| } | |
| void Material::setLegacyValue(const QString& name, const QString& value) | |
| { | |
| setEditStateAlter(); | |
| _legacy[name] = value; | |
| } | |
| std::shared_ptr<MaterialProperty> Material::getPhysicalProperty(const QString& name) | |
| { | |
| try { | |
| return _physical.at(name); | |
| } | |
| catch (std::out_of_range const&) { | |
| throw PropertyNotFound(); | |
| } | |
| } | |
| std::shared_ptr<MaterialProperty> Material::getPhysicalProperty(const QString& name) const | |
| { | |
| try { | |
| return _physical.at(name); | |
| } | |
| catch (std::out_of_range const&) { | |
| throw PropertyNotFound(); | |
| } | |
| } | |
| std::shared_ptr<MaterialProperty> Material::getAppearanceProperty(const QString& name) | |
| { | |
| try { | |
| return _appearance.at(name); | |
| } | |
| catch (std::out_of_range const&) { | |
| throw PropertyNotFound(); | |
| } | |
| } | |
| std::shared_ptr<MaterialProperty> Material::getAppearanceProperty(const QString& name) const | |
| { | |
| try { | |
| return _appearance.at(name); | |
| } | |
| catch (std::out_of_range const&) { | |
| throw PropertyNotFound(); | |
| } | |
| } | |
| std::shared_ptr<MaterialProperty> Material::getProperty(const QString& name) | |
| { | |
| if (hasPhysicalProperty(name)) { | |
| return getPhysicalProperty(name); | |
| } | |
| if (hasAppearanceProperty(name)) { | |
| return getAppearanceProperty(name); | |
| } | |
| throw PropertyNotFound(); | |
| } | |
| std::shared_ptr<MaterialProperty> Material::getProperty(const QString& name) const | |
| { | |
| if (hasPhysicalProperty(name)) { | |
| return getPhysicalProperty(name); | |
| } | |
| if (hasAppearanceProperty(name)) { | |
| return getAppearanceProperty(name); | |
| } | |
| throw PropertyNotFound(); | |
| } | |
| QVariant | |
| Material::getValue(const std::map<QString, std::shared_ptr<MaterialProperty>>& propertyList, | |
| const QString& name) | |
| { | |
| try { | |
| return propertyList.at(name)->getValue(); | |
| } | |
| catch (std::out_of_range const&) { | |
| throw PropertyNotFound(); | |
| } | |
| } | |
| QString | |
| Material::getValueString(const std::map<QString, std::shared_ptr<MaterialProperty>>& propertyList, | |
| const QString& name) | |
| { | |
| try { | |
| const auto& property = propertyList.at(name); | |
| if (property->isNull()) { | |
| return {}; | |
| } | |
| if (property->getType() == MaterialValue::Quantity) { | |
| auto value = property->getValue(); | |
| if (value.isNull()) { | |
| return {}; | |
| } | |
| return QString::fromStdString(value.value<Base::Quantity>().getUserString()); | |
| } | |
| if (property->getType() == MaterialValue::Float) { | |
| auto value = property->getValue(); | |
| if (value.isNull()) { | |
| return {}; | |
| } | |
| return QString(QStringLiteral("%L1")) | |
| .arg(value.toFloat(), 0, 'g', MaterialValue::PRECISION); | |
| } | |
| return property->getValue().toString(); | |
| } | |
| catch (std::out_of_range const&) { | |
| throw PropertyNotFound(); | |
| } | |
| } | |
| QVariant Material::getPhysicalValue(const QString& name) const | |
| { | |
| return getValue(_physical, name); | |
| } | |
| Base::Quantity Material::getPhysicalQuantity(const QString& name) const | |
| { | |
| return getValue(_physical, name).value<Base::Quantity>(); | |
| } | |
| QString Material::getPhysicalValueString(const QString& name) const | |
| { | |
| return getValueString(_physical, name); | |
| } | |
| QVariant Material::getAppearanceValue(const QString& name) const | |
| { | |
| return getValue(_appearance, name); | |
| } | |
| Base::Quantity Material::getAppearanceQuantity(const QString& name) const | |
| { | |
| return getValue(_appearance, name).value<Base::Quantity>(); | |
| } | |
| QString Material::getAppearanceValueString(const QString& name) const | |
| { | |
| return getValueString(_appearance, name); | |
| } | |
| bool Material::hasPhysicalProperty(const QString& name) const | |
| { | |
| return _physical.find(name) != _physical.end(); | |
| } | |
| bool Material::hasAppearanceProperty(const QString& name) const | |
| { | |
| return _appearance.find(name) != _appearance.end(); | |
| } | |
| 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::hasPhysicalProperties() const | |
| { | |
| return !_physicalUuids.isEmpty(); | |
| } | |
| bool Material::hasAppearanceProperties() const | |
| { | |
| return !_appearanceUuids.isEmpty(); | |
| } | |
| bool Material::isInherited(const QString& uuid) const | |
| { | |
| if (_physicalUuids.contains(uuid)) { | |
| return false; | |
| } | |
| if (_appearanceUuids.contains(uuid)) { | |
| return false; | |
| } | |
| return _allUuids.contains(uuid); | |
| } | |
| bool Material::hasModel(const QString& uuid) const | |
| { | |
| return _allUuids.contains(uuid); | |
| } | |
| bool Material::hasPhysicalModel(const QString& uuid) const | |
| { | |
| if (!hasModel(uuid)) { | |
| return false; | |
| } | |
| auto& manager = ModelManager::getManager(); | |
| try { | |
| auto model = manager.getModel(uuid); | |
| if (model->getType() == Model::ModelType_Physical) { | |
| return true; | |
| } | |
| } | |
| catch (ModelNotFound const&) { | |
| } | |
| return false; | |
| } | |
| bool Material::hasAppearanceModel(const QString& uuid) const | |
| { | |
| if (!hasModel(uuid)) { | |
| return false; | |
| } | |
| auto& manager = ModelManager::getManager(); | |
| try { | |
| auto model = manager.getModel(uuid); | |
| if (model->getType() == Model::ModelType_Appearance) { | |
| return true; | |
| } | |
| } | |
| catch (ModelNotFound const&) { | |
| } | |
| return false; | |
| } | |
| bool Material::isPhysicalModelComplete(const QString& uuid) const | |
| { | |
| if (!hasPhysicalModel(uuid)) { | |
| return false; | |
| } | |
| auto& manager = ModelManager::getManager(); | |
| try { | |
| auto model = manager.getModel(uuid); | |
| for (auto& it : *model) { | |
| QString propertyName = it.first; | |
| auto property = getPhysicalProperty(propertyName); | |
| if (property->isNull()) { | |
| return false; | |
| } | |
| } | |
| } | |
| catch (ModelNotFound const&) { | |
| return false; | |
| } | |
| return true; | |
| } | |
| bool Material::isAppearanceModelComplete(const QString& uuid) const | |
| { | |
| if (!hasAppearanceModel(uuid)) { | |
| return false; | |
| } | |
| auto& manager = ModelManager::getManager(); | |
| try { | |
| auto model = manager.getModel(uuid); | |
| for (auto& it : *model) { | |
| QString propertyName = it.first; | |
| auto property = getAppearanceProperty(propertyName); | |
| if (property->isNull()) { | |
| return false; | |
| } | |
| } | |
| } | |
| catch (ModelNotFound const&) { | |
| return false; | |
| } | |
| return true; | |
| } | |
| void Material::saveGeneral(QTextStream& stream) const | |
| { | |
| stream << "General:\n"; | |
| stream << " UUID: \"" << _uuid << "\"\n"; | |
| stream << " Name: \"" << MaterialValue::escapeString(_name) << "\"\n"; | |
| if (!_author.isEmpty()) { | |
| stream << " Author: \"" << MaterialValue::escapeString(_author) << "\"\n"; | |
| } | |
| if (!_license.isEmpty()) { | |
| stream << " License: \"" << MaterialValue::escapeString(_license) << "\"\n"; | |
| } | |
| if (!_description.isEmpty()) { | |
| stream << " Description: \"" << MaterialValue::escapeString(_description) << "\"\n"; | |
| } | |
| if (!_url.isEmpty()) { | |
| stream << " SourceURL: \"" << MaterialValue::escapeString(_url) << "\"\n"; | |
| } | |
| if (!_reference.isEmpty()) { | |
| stream << " ReferenceSource: \"" << MaterialValue::escapeString(_reference) << "\"\n"; | |
| } | |
| if (!_tags.isEmpty()) { | |
| stream << " Tags:\n"; | |
| for (auto tag : _tags) { | |
| stream << " - \"" << tag << "\"\n"; | |
| } | |
| } | |
| } | |
| void Material::saveInherits(QTextStream& stream) const | |
| { | |
| if (!_parentUuid.isEmpty()) { | |
| try { | |
| auto material = MaterialManager::getManager().getMaterial(_parentUuid); | |
| stream << "Inherits:\n"; | |
| stream << " " << material->getName() << ":\n"; | |
| stream << " UUID: \"" << _parentUuid << "\"\n"; | |
| } | |
| catch (const MaterialNotFound&) { | |
| } | |
| } | |
| } | |
| bool Material::modelChanged(const Material& parent, | |
| const Model& model) const | |
| { | |
| for (auto& it : model) { | |
| QString propertyName = it.first; | |
| auto property = getPhysicalProperty(propertyName); | |
| try { | |
| auto parentProperty = parent.getPhysicalProperty(propertyName); | |
| if (*property != *parentProperty) { | |
| return true; | |
| } | |
| } | |
| catch (const PropertyNotFound&) { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| bool Material::modelAppearanceChanged(const Material& parent, | |
| const Model& model) const | |
| { | |
| for (auto& it : model) { | |
| QString propertyName = it.first; | |
| auto property = getAppearanceProperty(propertyName); | |
| try { | |
| auto parentProperty = parent.getAppearanceProperty(propertyName); | |
| if (*property != *parentProperty) { | |
| return true; | |
| } | |
| } | |
| catch (const PropertyNotFound&) { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| void Material::saveModels(QTextStream& stream, bool saveInherited) const | |
| { | |
| if (_physical.empty()) { | |
| return; | |
| } | |
| auto& modelManager = ModelManager::getManager(); | |
| auto& materialManager = MaterialManager::getManager(); | |
| bool inherited = saveInherited && (_parentUuid.size() > 0); | |
| std::shared_ptr<Material> parent; | |
| if (inherited) { | |
| try { | |
| parent = materialManager.getMaterial(_parentUuid); | |
| } | |
| catch (const MaterialNotFound&) { | |
| inherited = false; | |
| } | |
| } | |
| bool headerPrinted = false; | |
| for (auto& itm : _physicalUuids) { | |
| auto model = modelManager.getModel(itm); | |
| if (!inherited || modelChanged(*parent, *model)) { | |
| if (!headerPrinted) { | |
| stream << "Models:\n"; | |
| headerPrinted = true; | |
| } | |
| stream << " " << MaterialValue::escapeString(model->getName()) << ":\n"; | |
| stream << " UUID: \"" << model->getUUID() << "\"\n"; | |
| for (const auto& it : *model) { | |
| QString propertyName = it.first; | |
| std::shared_ptr<MaterialProperty> property = getPhysicalProperty(propertyName); | |
| std::shared_ptr<MaterialProperty> parentProperty; | |
| try { | |
| if (inherited) { | |
| parentProperty = parent->getPhysicalProperty(propertyName); | |
| } | |
| } | |
| catch (const PropertyNotFound&) { | |
| Base::Console().log("Material::saveModels Property not found '%s'\n", | |
| propertyName.toStdString().c_str()); | |
| } | |
| if (!inherited || !parentProperty || (*property != *parentProperty)) { | |
| if (!property->isNull()) { | |
| stream << " " << *property << "\n"; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| void Material::saveAppearanceModels(QTextStream& stream, bool saveInherited) const | |
| { | |
| if (_appearance.empty()) { | |
| return; | |
| } | |
| auto& modelManager = ModelManager::getManager(); | |
| auto& materialManager = MaterialManager::getManager(); | |
| bool inherited = saveInherited && (_parentUuid.size() > 0); | |
| std::shared_ptr<Material> parent; | |
| if (inherited) { | |
| try { | |
| parent = materialManager.getMaterial(_parentUuid); | |
| } | |
| catch (const MaterialNotFound&) { | |
| inherited = false; | |
| } | |
| } | |
| bool headerPrinted = false; | |
| for (auto& itm : _appearanceUuids) { | |
| auto model = modelManager.getModel(itm); | |
| if (!inherited || modelAppearanceChanged(*parent, *model)) { | |
| if (!headerPrinted) { | |
| stream << "AppearanceModels:\n"; | |
| headerPrinted = true; | |
| } | |
| stream << " " << MaterialValue::escapeString(model->getName()) << ":\n"; | |
| stream << " UUID: \"" << model->getUUID() << "\"\n"; | |
| for (const auto& it : *model) { | |
| QString propertyName = it.first; | |
| std::shared_ptr<MaterialProperty> property = getAppearanceProperty(propertyName); | |
| std::shared_ptr<MaterialProperty> parentProperty; | |
| try { | |
| if (inherited) { | |
| parentProperty = parent->getAppearanceProperty(propertyName); | |
| } | |
| } | |
| catch (const PropertyNotFound&) { | |
| } | |
| if (!inherited || !parentProperty || (*property != *parentProperty)) { | |
| if (!property->isNull()) { | |
| stream << " " << *property << "\n"; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| void Material::newUuid() | |
| { | |
| _uuid = QUuid::createUuid().toString(QUuid::WithoutBraces); | |
| } | |
| QString Material::getModelByName(const QString& name) const | |
| { | |
| auto& manager = ModelManager::getManager(); | |
| for (auto& it : _allUuids) { | |
| try { | |
| auto model = manager.getModel(it); | |
| if (model->getName() == name) { | |
| return it; | |
| } | |
| } | |
| catch (ModelNotFound const&) { | |
| } | |
| } | |
| return {}; | |
| } | |
| void Material::save(QTextStream& stream, bool overwrite, bool saveAsCopy, bool saveInherited) | |
| { | |
| if (saveInherited && !saveAsCopy) { | |
| // Check to see if we're an original or if we're already in the list of | |
| // models | |
| if (MaterialManager::getManager().exists(_uuid) && !overwrite) { | |
| // Make a new version based on the current | |
| setParentUUID(_uuid); | |
| } | |
| } | |
| // Prevent self inheritance | |
| if (_parentUuid == _uuid) { | |
| _parentUuid = QString(); | |
| } | |
| if (saveAsCopy) { | |
| // Save it in the same format as the parent | |
| if (_parentUuid.isEmpty()) { | |
| saveInherited = false; | |
| } | |
| else { | |
| saveInherited = true; | |
| } | |
| } | |
| else { | |
| if (!overwrite) { | |
| // Creating a new derived model when overwriting sets itself as a | |
| // parent, that will no longer exist because it's been overwritten | |
| newUuid(); | |
| } | |
| } | |
| stream << "---\n"; | |
| stream << "# File created by " << QString::fromStdString(App::Application::Config()["ExeName"]) | |
| << " " << QString::fromStdString(App::Application::Config()["ExeVersion"]) | |
| << " Revision: " << QString::fromStdString(App::Application::Config()["BuildRevision"]) | |
| << "\n"; | |
| saveGeneral(stream); | |
| if (saveInherited) { | |
| saveInherits(stream); | |
| } | |
| saveModels(stream, saveInherited); | |
| saveAppearanceModels(stream, saveInherited); | |
| setOldFormat(false); | |
| } | |
| Material& Material::operator=(const Material& other) | |
| { | |
| if (this == &other) { | |
| return *this; | |
| } | |
| _library = other._library; | |
| _directory = other._directory; | |
| _filename = other._filename; | |
| _uuid = other._uuid; | |
| _name = other._name; | |
| _author = other._author; | |
| _license = other._license; | |
| _parentUuid = other._parentUuid; | |
| _description = other._description; | |
| _url = other._url; | |
| _reference = other._reference; | |
| _dereferenced = other._dereferenced; | |
| _oldFormat = other._oldFormat; | |
| _editState = other._editState; | |
| _tags.clear(); | |
| for (auto& it : other._tags) { | |
| _tags.insert(it); | |
| } | |
| _physicalUuids.clear(); | |
| for (auto& it : other._physicalUuids) { | |
| _physicalUuids.insert(it); | |
| } | |
| _appearanceUuids.clear(); | |
| for (auto& it : other._appearanceUuids) { | |
| _appearanceUuids.insert(it); | |
| } | |
| _allUuids.clear(); | |
| for (auto& it : other._allUuids) { | |
| _allUuids.insert(it); | |
| } | |
| // Create copies of the properties rather than modify the originals | |
| _physical.clear(); | |
| for (auto& it : other._physical) { | |
| MaterialProperty prop(it.second); | |
| _physical[it.first] = std::make_shared<MaterialProperty>(prop); | |
| } | |
| _appearance.clear(); | |
| for (auto& it : other._appearance) { | |
| 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; | |
| } | |
| Material& Material::operator=(const App::Material& other) | |
| { | |
| if (!hasAppearanceModel(ModelUUIDs::ModelUUID_Rendering_Basic)) { | |
| addAppearance(ModelUUIDs::ModelUUID_Rendering_Basic); | |
| } | |
| getAppearanceProperty(QStringLiteral("AmbientColor"))->setColor(other.ambientColor); | |
| getAppearanceProperty(QStringLiteral("DiffuseColor"))->setColor(other.diffuseColor); | |
| getAppearanceProperty(QStringLiteral("SpecularColor"))->setColor(other.specularColor); | |
| getAppearanceProperty(QStringLiteral("EmissiveColor"))->setColor(other.emissiveColor); | |
| getAppearanceProperty(QStringLiteral("Shininess"))->setFloat(other.shininess); | |
| getAppearanceProperty(QStringLiteral("Transparency"))->setFloat(other.transparency); | |
| if (!other.image.empty() || !other.imagePath.empty()) { | |
| if (!hasAppearanceModel(ModelUUIDs::ModelUUID_Rendering_Texture)) { | |
| addAppearance(ModelUUIDs::ModelUUID_Rendering_Texture); | |
| } | |
| getAppearanceProperty(QStringLiteral("TextureImage"))->setString(other.image); | |
| getAppearanceProperty(QStringLiteral("TexturePath"))->setString(other.imagePath); | |
| } | |
| return *this; | |
| } | |
| /* | |
| * Normalize models by removing any inherited models | |
| */ | |
| QStringList Material::normalizeModels(const QStringList& models) | |
| { | |
| QStringList normalized; | |
| auto& manager = ModelManager::getManager(); | |
| for (auto& uuid : models) { | |
| auto model = manager.getModel(uuid); | |
| bool found = false; | |
| for (auto& childUuid : models) { | |
| if (uuid != childUuid) { | |
| auto childModel = manager.getModel(childUuid); | |
| if (childModel->inherits(childUuid)) { | |
| // We're an inherited model | |
| found = true; | |
| break; | |
| } | |
| } | |
| } | |
| if (!found) { | |
| normalized << uuid; | |
| } | |
| } | |
| return normalized; | |
| } | |
| /* | |
| * Set or change the base material for the current material, updating the | |
| * properties as required. | |
| */ | |
| void Material::updateInheritance([[maybe_unused]] const QString& parent) | |
| {} | |
| /* | |
| * Return a list of models that are defined in the parent material but not in | |
| * this one | |
| */ | |
| QStringList Material::inheritedMissingModels(const Material& parent) const | |
| { | |
| QStringList missing; | |
| for (auto& uuid : parent._allUuids) { | |
| if (!hasModel(uuid)) { | |
| missing << uuid; | |
| } | |
| } | |
| return normalizeModels(missing); | |
| } | |
| /* | |
| * Return a list of models that are defined in this model but not the parent | |
| */ | |
| QStringList Material::inheritedAddedModels(const Material& parent) const | |
| { | |
| QStringList added; | |
| for (auto& uuid : _allUuids) { | |
| if (!parent.hasModel(uuid)) { | |
| added << uuid; | |
| } | |
| } | |
| return normalizeModels(added); | |
| } | |
| /* | |
| * Return a list of properties that have different values from the parent | |
| * material | |
| */ | |
| void Material::inheritedPropertyDiff([[maybe_unused]] const QString& parent) | |
| {} | |
| /* | |
| * Return an App::Material object describing the materials appearance, or DEFAULT if | |
| * undefined. | |
| */ | |
| App::Material Material::getMaterialAppearance() const | |
| { | |
| App::Material material(App::Material::DEFAULT); | |
| bool custom = false; | |
| if (hasAppearanceProperty(QStringLiteral("AmbientColor"))) { | |
| material.ambientColor = getAppearanceProperty(QStringLiteral("AmbientColor"))->getColor(); | |
| custom = true; | |
| } | |
| if (hasAppearanceProperty(QStringLiteral("DiffuseColor"))) { | |
| material.diffuseColor = getAppearanceProperty(QStringLiteral("DiffuseColor"))->getColor(); | |
| custom = true; | |
| } | |
| if (hasAppearanceProperty(QStringLiteral("SpecularColor"))) { | |
| material.specularColor = getAppearanceProperty(QStringLiteral("SpecularColor"))->getColor(); | |
| custom = true; | |
| } | |
| if (hasAppearanceProperty(QStringLiteral("EmissiveColor"))) { | |
| material.emissiveColor = getAppearanceProperty(QStringLiteral("EmissiveColor"))->getColor(); | |
| custom = true; | |
| } | |
| if (hasAppearanceProperty(QStringLiteral("Shininess"))) { | |
| material.shininess = getAppearanceProperty(QStringLiteral("Shininess"))->getFloat(); | |
| custom = true; | |
| } | |
| if (hasAppearanceProperty(QStringLiteral("Transparency"))) { | |
| material.transparency = getAppearanceProperty(QStringLiteral("Transparency"))->getFloat(); | |
| custom = true; | |
| } | |
| if (hasAppearanceProperty(QStringLiteral("TextureImage"))) { | |
| auto property = getAppearanceProperty(QStringLiteral("TextureImage")); | |
| if (!property->isNull()) { | |
| Base::Console().log("Has 'TextureImage'\n"); | |
| material.image = property->getString().toStdString(); | |
| } | |
| custom = true; | |
| } | |
| else if (hasAppearanceProperty(QStringLiteral("TexturePath"))) { | |
| auto property = getAppearanceProperty(QStringLiteral("TexturePath")); | |
| if (!property->isNull()) { | |
| Base::Console().log("Has 'TexturePath'\n"); | |
| material.imagePath = property->getString().toStdString(); | |
| } | |
| custom = true; | |
| } | |
| if (custom) { | |
| material.setType(App::Material::USER_DEFINED); | |
| material.uuid = getUUID().toStdString(); | |
| } | |
| return material; | |
| } | |
| void Material::validate(Material& other) const | |
| { | |
| try { | |
| _library->validate(*other._library); | |
| } | |
| catch (const InvalidLibrary& e) { | |
| throw InvalidMaterial(e.what()); | |
| } | |
| if (_directory != other._directory) { | |
| throw InvalidMaterial("Model directories don't match"); | |
| } | |
| if (!other._filename.isEmpty()) { | |
| throw InvalidMaterial("Remote filename is not empty"); | |
| } | |
| if (_uuid != other._uuid) { | |
| throw InvalidMaterial("Model UUIDs don't match"); | |
| } | |
| if (_name != other._name) { | |
| throw InvalidMaterial("Model names don't match"); | |
| } | |
| if (_author != other._author) { | |
| throw InvalidMaterial("Model authors don't match"); | |
| } | |
| if (_license != other._license) { | |
| throw InvalidMaterial("Model licenses don't match"); | |
| } | |
| if (_parentUuid != other._parentUuid) { | |
| throw InvalidMaterial("Model parents don't match"); | |
| } | |
| if (_description != other._description) { | |
| throw InvalidMaterial("Model descriptions don't match"); | |
| } | |
| if (_url != other._url) { | |
| throw InvalidMaterial("Model URLs don't match"); | |
| } | |
| if (_reference != other._reference) { | |
| throw InvalidMaterial("Model references don't match"); | |
| } | |
| if (_tags.size() != other._tags.size()) { | |
| Base::Console().log("Local tags count %d\n", _tags.size()); | |
| Base::Console().log("Remote tags count %d\n", other._tags.size()); | |
| throw InvalidMaterial("Material tags counts don't match"); | |
| } | |
| if (!other._tags.contains(_tags)) { | |
| throw InvalidMaterial("Material tags don't match"); | |
| } | |
| if (_physicalUuids.size() != other._physicalUuids.size()) { | |
| Base::Console().log("Local physical model count %d\n", _physicalUuids.size()); | |
| Base::Console().log("Remote physical model count %d\n", other._physicalUuids.size()); | |
| throw InvalidMaterial("Material physical model counts don't match"); | |
| } | |
| if (!other._physicalUuids.contains(_physicalUuids)) { | |
| throw InvalidMaterial("Material physical models don't match"); | |
| } | |
| if (_physicalUuids.size() != other._physicalUuids.size()) { | |
| Base::Console().log("Local appearance model count %d\n", _physicalUuids.size()); | |
| Base::Console().log("Remote appearance model count %d\n", other._physicalUuids.size()); | |
| throw InvalidMaterial("Material appearance model counts don't match"); | |
| } | |
| if (!other._physicalUuids.contains(_physicalUuids)) { | |
| throw InvalidMaterial("Material appearance models don't match"); | |
| } | |
| if (_allUuids.size() != other._allUuids.size()) { | |
| Base::Console().log("Local model count %d\n", _allUuids.size()); | |
| Base::Console().log("Remote model count %d\n", other._allUuids.size()); | |
| throw InvalidMaterial("Material model counts don't match"); | |
| } | |
| if (!other._allUuids.contains(_allUuids)) { | |
| throw InvalidMaterial("Material models don't match"); | |
| } | |
| // Need to compare properties | |
| if (_physical.size() != other._physical.size()) { | |
| throw InvalidMaterial("Material physical property counts don't match"); | |
| } | |
| for (auto& property : _physical) { | |
| auto& remote = other._physical[property.first]; | |
| property.second->validate(*remote); | |
| } | |
| if (_appearance.size() != other._appearance.size()) { | |
| throw InvalidMaterial("Material appearance property counts don't match"); | |
| } | |
| for (auto& property : _appearance) { | |
| auto& remote = other._appearance[property.first]; | |
| property.second->validate(*remote); | |
| } | |
| } | |