Materials: External Modules Part 1

Refactored code to support local and external material sources

This is the first PR in a series to support external modules. External
modules allow materials to be stored in external data sources such as
databases or web services. No new functionality is introduced in this
PR, rather it is a refactoring of code that will allow for changes to
be introduced in future PRs. Minor performance improvements have also
been made in the model and material managers.

The Python API has been enhanced for many data types to allow for
modification within Python.
This commit is contained in:
David Carter
2025-03-07 10:13:56 -05:00
committed by Chris Hennes
parent 3c4977a2d4
commit 00c57a9d08
80 changed files with 4372 additions and 1396 deletions

View File

@@ -65,11 +65,11 @@ void MaterialProperty::copyValuePtr(const std::shared_ptr<MaterialValue>& value)
{
if (value->getType() == MaterialValue::Array2D) {
_valuePtr =
std::make_shared<Material2DArray>(*(std::static_pointer_cast<Material2DArray>(value)));
std::make_shared<Array2D>(*(std::static_pointer_cast<Array2D>(value)));
}
else if (value->getType() == MaterialValue::Array3D) {
_valuePtr =
std::make_shared<Material3DArray>(*(std::static_pointer_cast<Material3DArray>(value)));
std::make_shared<Array3D>(*(std::static_pointer_cast<Array3D>(value)));
}
else {
_valuePtr = std::make_shared<MaterialValue>(*value);
@@ -132,7 +132,7 @@ QString MaterialProperty::getString() const
if (value.isNull()) {
return {};
}
return QString(QLatin1String("%L1")).arg(value.toFloat(), 0, 'g', MaterialValue::PRECISION);
return QString(QStringLiteral("%L1")).arg(value.toFloat(), 0, 'g', MaterialValue::PRECISION);
}
return getValue().toString();
}
@@ -177,7 +177,7 @@ QString MaterialProperty::getDictionaryString() const
}
if (getType() == MaterialValue::Quantity) {
auto quantity = getValue().value<Base::Quantity>();
auto string = QString(QLatin1String("%1 %2"))
auto string = QString(QStringLiteral("%1 %2"))
.arg(quantity.getValue(), 0, 'g', MaterialValue::PRECISION)
.arg(QString::fromStdString(quantity.getUnit().getString()));
return string;
@@ -187,7 +187,7 @@ QString MaterialProperty::getDictionaryString() const
if (value.isNull()) {
return {};
}
return QString(QLatin1String("%1")).arg(value.toFloat(), 0, 'g', MaterialValue::PRECISION);
return QString(QStringLiteral("%1")).arg(value.toFloat(), 0, 'g', MaterialValue::PRECISION);
}
return getValue().toString();
}
@@ -205,12 +205,12 @@ void MaterialProperty::setType(const QString& type)
throw UnknownValueType();
}
if (mappedType == MaterialValue::Array2D) {
auto arrayPtr = std::make_shared<Material2DArray>();
auto arrayPtr = std::make_shared<Array2D>();
arrayPtr->setColumns(columns());
_valuePtr = arrayPtr;
}
else if (mappedType == MaterialValue::Array3D) {
auto arrayPtr = std::make_shared<Material3DArray>();
auto arrayPtr = std::make_shared<Array3D>();
// First column is third dimension
arrayPtr->setColumns(columns() - 1);
_valuePtr = arrayPtr;
@@ -448,6 +448,17 @@ bool MaterialProperty::operator==(const MaterialProperty& other) const
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 (int i = 0; i < _columns.size(); i++) {
_columns[i].validate(other._columns[i]);
}
}
TYPESYSTEM_SOURCE(Materials::Material, Base::BaseClass)
Material::Material()
@@ -476,6 +487,7 @@ Material::Material(const std::shared_ptr<MaterialLibrary>& library,
Material::Material(const Material& other)
: _library(other._library)
, _directory(other._directory)
, _filename(other._filename)
, _uuid(other._uuid)
, _name(other._name)
, _author(other._author)
@@ -513,6 +525,31 @@ Material::Material(const Material& other)
}
}
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;
@@ -521,7 +558,7 @@ QString Material::getAuthorAndLicense() const
if (!_author.isNull()) {
authorAndLicense = _author;
if (!_license.isNull()) {
authorAndLicense += QLatin1String(" ") + _license;
authorAndLicense += QStringLiteral(" ") + _license;
}
}
else if (!_license.isNull()) {
@@ -541,7 +578,7 @@ void Material::addModel(const QString& uuid)
_allUuids << uuid;
ModelManager manager;
auto manager = ModelManager::getManager();
try {
auto model = manager.getModel(uuid);
@@ -641,7 +678,7 @@ void Material::addPhysical(const QString& uuid)
return;
}
ModelManager manager;
auto manager = ModelManager::getManager();
try {
auto model = manager.getModel(uuid);
@@ -688,7 +725,7 @@ void Material::removePhysical(const QString& uuid)
return;
}
ModelManager manager;
auto manager = ModelManager::getManager();
try {
auto model = manager.getModel(uuid);
@@ -718,7 +755,7 @@ void Material::addAppearance(const QString& uuid)
return;
}
ModelManager manager;
auto manager = ModelManager::getManager();
try {
auto model = manager.getModel(uuid);
@@ -759,7 +796,7 @@ void Material::removeAppearance(const QString& uuid)
return;
}
ModelManager manager;
auto manager = ModelManager::getManager();
try {
auto model = manager.getModel(uuid);
@@ -938,6 +975,22 @@ 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();
}
@@ -1045,7 +1098,7 @@ Material::getValueString(const std::map<QString, std::shared_ptr<MaterialPropert
if (value.isNull()) {
return {};
}
return QString(QLatin1String("%L1"))
return QString(QStringLiteral("%L1"))
.arg(value.toFloat(), 0, 'g', MaterialValue::PRECISION);
}
return property->getValue().toString();
@@ -1141,7 +1194,7 @@ bool Material::hasPhysicalModel(const QString& uuid) const
return false;
}
ModelManager manager;
auto manager = ModelManager::getManager();
try {
auto model = manager.getModel(uuid);
@@ -1161,7 +1214,7 @@ bool Material::hasAppearanceModel(const QString& uuid) const
return false;
}
ModelManager manager;
auto manager = ModelManager::getManager();
try {
auto model = manager.getModel(uuid);
@@ -1181,7 +1234,7 @@ bool Material::isPhysicalModelComplete(const QString& uuid) const
return false;
}
ModelManager manager;
auto manager = ModelManager::getManager();
try {
auto model = manager.getModel(uuid);
@@ -1207,7 +1260,7 @@ bool Material::isAppearanceModelComplete(const QString& uuid) const
return false;
}
ModelManager manager;
auto manager = ModelManager::getManager();
try {
auto model = manager.getModel(uuid);
@@ -1252,10 +1305,8 @@ void Material::saveGeneral(QTextStream& stream) const
void Material::saveInherits(QTextStream& stream) const
{
if (!_parentUuid.isEmpty()) {
MaterialManager manager;
try {
auto material = manager.getMaterial(_parentUuid);
auto material = MaterialManager::getManager().getMaterial(_parentUuid);
stream << "Inherits:\n";
stream << " " << material->getName() << ":\n";
@@ -1314,8 +1365,8 @@ void Material::saveModels(QTextStream& stream, bool saveInherited) const
return;
}
ModelManager modelManager;
MaterialManager materialManager;
auto modelManager = ModelManager::getManager();
auto materialManager = MaterialManager::getManager();
bool inherited = saveInherited && (_parentUuid.size() > 0);
std::shared_ptr<Material> parent;
@@ -1368,8 +1419,8 @@ void Material::saveAppearanceModels(QTextStream& stream, bool saveInherited) con
return;
}
ModelManager modelManager;
MaterialManager materialManager;
auto modelManager = ModelManager::getManager();
auto materialManager = MaterialManager::getManager();
bool inherited = saveInherited && (_parentUuid.size() > 0);
std::shared_ptr<Material> parent;
@@ -1421,7 +1472,7 @@ void Material::newUuid()
QString Material::getModelByName(const QString& name) const
{
ModelManager manager;
auto manager = ModelManager::getManager();
for (auto& it : _allUuids) {
try {
@@ -1442,8 +1493,7 @@ void Material::save(QTextStream& stream, bool overwrite, bool saveAsCopy, bool s
if (saveInherited && !saveAsCopy) {
// Check to see if we're an original or if we're already in the list of
// models
MaterialManager materialManager;
if (materialManager.exists(_uuid) && !overwrite) {
if (MaterialManager::getManager().exists(_uuid) && !overwrite) {
// Make a new version based on the current
setParentUUID(_uuid);
}
@@ -1494,6 +1544,7 @@ Material& Material::operator=(const Material& other)
_library = other._library;
_directory = other._directory;
_filename = other._filename;
_uuid = other._uuid;
_name = other._name;
_author = other._author;
@@ -1548,20 +1599,20 @@ Material& Material::operator=(const App::Material& other)
addAppearance(ModelUUIDs::ModelUUID_Rendering_Basic);
}
getAppearanceProperty(QLatin1String("AmbientColor"))->setColor(other.ambientColor);
getAppearanceProperty(QLatin1String("DiffuseColor"))->setColor(other.diffuseColor);
getAppearanceProperty(QLatin1String("SpecularColor"))->setColor(other.specularColor);
getAppearanceProperty(QLatin1String("EmissiveColor"))->setColor(other.emissiveColor);
getAppearanceProperty(QLatin1String("Shininess"))->setFloat(other.shininess);
getAppearanceProperty(QLatin1String("Transparency"))->setFloat(other.transparency);
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(QLatin1String("TextureImage"))->setString(other.image);
getAppearanceProperty(QLatin1String("TexturePath"))->setString(other.imagePath);
getAppearanceProperty(QStringLiteral("TextureImage"))->setString(other.image);
getAppearanceProperty(QStringLiteral("TexturePath"))->setString(other.imagePath);
}
return *this;
@@ -1574,7 +1625,7 @@ QStringList Material::normalizeModels(const QStringList& models)
{
QStringList normalized;
ModelManager manager;
auto manager = ModelManager::getManager();
for (auto& uuid : models) {
auto model = manager.getModel(uuid);
@@ -1652,32 +1703,32 @@ App::Material Material::getMaterialAppearance() const
App::Material material(App::Material::DEFAULT);
bool custom = false;
if (hasAppearanceProperty(QLatin1String("AmbientColor"))) {
material.ambientColor = getAppearanceProperty(QLatin1String("AmbientColor"))->getColor();
if (hasAppearanceProperty(QStringLiteral("AmbientColor"))) {
material.ambientColor = getAppearanceProperty(QStringLiteral("AmbientColor"))->getColor();
custom = true;
}
if (hasAppearanceProperty(QLatin1String("DiffuseColor"))) {
material.diffuseColor = getAppearanceProperty(QLatin1String("DiffuseColor"))->getColor();
if (hasAppearanceProperty(QStringLiteral("DiffuseColor"))) {
material.diffuseColor = getAppearanceProperty(QStringLiteral("DiffuseColor"))->getColor();
custom = true;
}
if (hasAppearanceProperty(QLatin1String("SpecularColor"))) {
material.specularColor = getAppearanceProperty(QLatin1String("SpecularColor"))->getColor();
if (hasAppearanceProperty(QStringLiteral("SpecularColor"))) {
material.specularColor = getAppearanceProperty(QStringLiteral("SpecularColor"))->getColor();
custom = true;
}
if (hasAppearanceProperty(QLatin1String("EmissiveColor"))) {
material.emissiveColor = getAppearanceProperty(QLatin1String("EmissiveColor"))->getColor();
if (hasAppearanceProperty(QStringLiteral("EmissiveColor"))) {
material.emissiveColor = getAppearanceProperty(QStringLiteral("EmissiveColor"))->getColor();
custom = true;
}
if (hasAppearanceProperty(QLatin1String("Shininess"))) {
material.shininess = getAppearanceProperty(QLatin1String("Shininess"))->getFloat();
if (hasAppearanceProperty(QStringLiteral("Shininess"))) {
material.shininess = getAppearanceProperty(QStringLiteral("Shininess"))->getFloat();
custom = true;
}
if (hasAppearanceProperty(QLatin1String("Transparency"))) {
material.transparency = getAppearanceProperty(QLatin1String("Transparency"))->getFloat();
if (hasAppearanceProperty(QStringLiteral("Transparency"))) {
material.transparency = getAppearanceProperty(QStringLiteral("Transparency"))->getFloat();
custom = true;
}
if (hasAppearanceProperty(QLatin1String("TextureImage"))) {
auto property = getAppearanceProperty(QLatin1String("TextureImage"));
if (hasAppearanceProperty(QStringLiteral("TextureImage"))) {
auto property = getAppearanceProperty(QStringLiteral("TextureImage"));
if (!property->isNull()) {
Base::Console().Log("Has 'TextureImage'\n");
material.image = property->getString().toStdString();
@@ -1685,8 +1736,8 @@ App::Material Material::getMaterialAppearance() const
custom = true;
}
else if (hasAppearanceProperty(QLatin1String("TexturePath"))) {
auto property = getAppearanceProperty(QLatin1String("TexturePath"));
else if (hasAppearanceProperty(QStringLiteral("TexturePath"))) {
auto property = getAppearanceProperty(QStringLiteral("TexturePath"));
if (!property->isNull()) {
Base::Console().Log("Has 'TexturePath'\n");
material.imagePath = property->getString().toStdString();
@@ -1702,3 +1753,98 @@ App::Material Material::getMaterialAppearance() const
return material;
}
void Material::validate(const std::shared_ptr<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);
}
}