| | |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | #include <QDirIterator> |
| | #include <QFileInfo> |
| | #include <QList> |
| | #include <QMetaType> |
| | #include <QRegularExpression> |
| | #include <QString> |
| |
|
| |
|
| | #include <App/Application.h> |
| | #include <Base/Interpreter.h> |
| | #include <Base/Stream.h> |
| | #include <Gui/MetaTypes.h> |
| |
|
| | #include "Materials.h" |
| |
|
| | #include "MaterialConfigLoader.h" |
| | #include "MaterialLibrary.h" |
| | #include "MaterialLoader.h" |
| | #include "Model.h" |
| | #include "ModelManager.h" |
| |
|
| |
|
| | using namespace Materials; |
| |
|
| | MaterialEntry::MaterialEntry(const std::shared_ptr<MaterialLibraryLocal>& library, |
| | const QString& modelName, |
| | const QString& dir, |
| | const QString& modelUuid) |
| | : _library(library) |
| | , _name(modelName) |
| | , _directory(dir) |
| | , _uuid(modelUuid) |
| | {} |
| |
|
| | MaterialYamlEntry::MaterialYamlEntry(const std::shared_ptr<MaterialLibraryLocal>& library, |
| | const QString& modelName, |
| | const QString& dir, |
| | const QString& modelUuid, |
| | const YAML::Node& modelData) |
| | : MaterialEntry(library, modelName, dir, modelUuid) |
| | , _model(modelData) |
| | {} |
| |
|
| | QString MaterialYamlEntry::yamlValue(const YAML::Node& node, |
| | const std::string& key, |
| | const std::string& defaultValue) |
| | { |
| | if (node[key]) { |
| | return QString::fromStdString(node[key].as<std::string>()); |
| | } |
| | return QString::fromStdString(defaultValue); |
| | } |
| |
|
| | std::shared_ptr<QList<QVariant>> MaterialYamlEntry::readList(const YAML::Node& node, |
| | bool isImageList) |
| | { |
| | auto list = std::make_shared<QList<QVariant>>(); |
| | for (auto it = node.begin(); it != node.end(); it++) { |
| | QVariant nodeValue; |
| | if (isImageList) { |
| | nodeValue = QString::fromStdString(it->as<std::string>()) |
| | .remove(QRegularExpression(QStringLiteral("[\r\n]"))); |
| | } |
| | else { |
| | nodeValue = QString::fromStdString(it->as<std::string>()); |
| | } |
| | list->append(nodeValue); |
| | } |
| |
|
| | return list; |
| | } |
| |
|
| | std::shared_ptr<QList<QVariant>> MaterialYamlEntry::readImageList(const YAML::Node& node) |
| | { |
| | return readList(node, true); |
| | } |
| |
|
| | std::shared_ptr<Array2D> MaterialYamlEntry::read2DArray(const YAML::Node& node, int columns) |
| | { |
| | auto array2d = std::make_shared<Array2D>(); |
| | array2d->setColumns(columns); |
| |
|
| | if (node.size() == 1 || node.size() == 2) { |
| | |
| | auto yamlArray = node[0]; |
| | if (node.size() == 2) { |
| | yamlArray = node[1]; |
| | } |
| |
|
| | for (std::size_t i = 0; i < yamlArray.size(); i++) { |
| | auto yamlRow = yamlArray[i]; |
| |
|
| | auto row = std::make_shared<QList<QVariant>>(); |
| | for (std::size_t j = 0; j < yamlRow.size(); j++) { |
| | Base::Quantity qq = Base::Quantity::parse(yamlRow[j].as<std::string>()); |
| | qq.setFormat(MaterialValue::getQuantityFormat()); |
| | row->push_back(QVariant::fromValue(qq)); |
| | } |
| | array2d->addRow(row); |
| | } |
| | } |
| |
|
| | return array2d; |
| | } |
| |
|
| | std::shared_ptr<Array3D> MaterialYamlEntry::read3DArray(const YAML::Node& node, int columns) |
| | { |
| | auto array3d = std::make_shared<Array3D>(); |
| | array3d->setColumns(columns - 1); |
| |
|
| | if (node.size() == 1 || node.size() == 2) { |
| | |
| | auto yamlArray = node[0]; |
| | if (node.size() == 2) { |
| | yamlArray = node[1]; |
| | } |
| |
|
| | for (std::size_t depth = 0; depth < yamlArray.size(); depth++) { |
| | auto yamlDepth = yamlArray[depth]; |
| | for (auto it = yamlDepth.begin(); it != yamlDepth.end(); it++) { |
| | auto depthValue = Base::Quantity::parse(it->first.as<std::string>()); |
| | depthValue.setFormat(MaterialValue::getQuantityFormat()); |
| | array3d->addDepth(depth, depthValue); |
| |
|
| | auto yamlTable = it->second; |
| | for (std::size_t i = 0; i < yamlTable.size(); i++) { |
| | auto yamlRow = yamlTable[i]; |
| |
|
| | auto row = std::make_shared<QList<Base::Quantity>>(); |
| | for (std::size_t j = 0; j < yamlRow.size(); j++) { |
| | auto qq = Base::Quantity::parse(yamlRow[j].as<std::string>()); |
| | qq.setFormat(MaterialValue::getQuantityFormat()); |
| | row->push_back(qq); |
| | } |
| | array3d->addRow(depth, row); |
| | } |
| | } |
| | } |
| | } |
| |
|
| | return array3d; |
| | } |
| |
|
| | void MaterialYamlEntry::addToTree( |
| | std::shared_ptr<std::map<QString, std::shared_ptr<Material>>> materialMap) |
| | { |
| | std::set<QString> exclude; |
| | exclude.insert(QStringLiteral("General")); |
| | exclude.insert(QStringLiteral("Inherits")); |
| |
|
| | auto yamlModel = getModel(); |
| | auto library = getLibrary(); |
| | auto name = getName(); |
| | auto directory = getDirectory(); |
| | QString uuid = getUUID(); |
| |
|
| | QString author = yamlValue(yamlModel["General"], "Author", ""); |
| | QString license = yamlValue(yamlModel["General"], "License", ""); |
| | QString description = yamlValue(yamlModel["General"], "Description", ""); |
| | QString sourceReference = yamlValue(yamlModel["General"], "ReferenceSource", ""); |
| | QString sourceURL = yamlValue(yamlModel["General"], "SourceURL", ""); |
| |
|
| | std::shared_ptr<Material> finalModel = |
| | std::make_shared<Material>(library, directory, uuid, name); |
| | finalModel->setAuthor(author); |
| | finalModel->setLicense(license); |
| | finalModel->setDescription(description); |
| | finalModel->setReference(sourceReference); |
| | finalModel->setURL(sourceURL); |
| |
|
| | if (yamlModel["General"]["Tags"]) { |
| | auto tags = readList(yamlModel["General"]["Tags"]); |
| | for (auto tag : *tags) { |
| | finalModel->addTag(tag.toString()); |
| | } |
| | } |
| |
|
| | |
| | if (yamlModel["Inherits"]) { |
| | auto inherits = yamlModel["Inherits"]; |
| | for (auto it = inherits.begin(); it != inherits.end(); it++) { |
| | auto nodeName = it->second["UUID"].as<std::string>(); |
| |
|
| | finalModel->setParentUUID( |
| | QString::fromStdString(nodeName)); |
| | } |
| | } |
| |
|
| | |
| | if (yamlModel["Models"]) { |
| | auto models = yamlModel["Models"]; |
| | for (auto it = models.begin(); it != models.end(); it++) { |
| | auto modelName = (it->first).as<std::string>(); |
| |
|
| | |
| | auto modelNode = models[modelName]; |
| | auto modelUUID = modelNode["UUID"].as<std::string>(); |
| | finalModel->addPhysical(QString::fromStdString(modelUUID)); |
| |
|
| | |
| | auto properties = yamlModel["Models"][modelName]; |
| | for (auto itp = properties.begin(); itp != properties.end(); itp++) { |
| | auto propertyName = (itp->first).as<std::string>(); |
| | if (finalModel->hasPhysicalProperty(QString::fromStdString(propertyName))) { |
| | auto prop = |
| | finalModel->getPhysicalProperty(QString::fromStdString(propertyName)); |
| | auto type = prop->getType(); |
| |
|
| | try { |
| | if (type == MaterialValue::List || type == MaterialValue::FileList) { |
| | auto list = readList(itp->second); |
| | finalModel->setPhysicalValue(QString::fromStdString(propertyName), |
| | list); |
| | } |
| | else if (type == MaterialValue::ImageList) { |
| | auto list = readImageList(itp->second); |
| | finalModel->setPhysicalValue(QString::fromStdString(propertyName), |
| | list); |
| | } |
| | else if (type == MaterialValue::Array2D) { |
| | auto array2d = read2DArray(itp->second, prop->columns()); |
| | finalModel->setPhysicalValue(QString::fromStdString(propertyName), |
| | array2d); |
| | } |
| | else if (type == MaterialValue::Array3D) { |
| | auto array3d = read3DArray(itp->second, prop->columns()); |
| | finalModel->setPhysicalValue(QString::fromStdString(propertyName), |
| | array3d); |
| | } |
| | else { |
| | QString propertyValue = |
| | QString::fromStdString((itp->second).as<std::string>()); |
| | if (type == MaterialValue::Image) { |
| | propertyValue = propertyValue.remove( |
| | QRegularExpression(QStringLiteral("[\r\n]"))); |
| | } |
| | try { |
| | finalModel->setPhysicalValue(QString::fromStdString(propertyName), |
| | propertyValue); |
| | } |
| | catch (const Base::ValueError&) { |
| | |
| | Base::Console().log("Units mismatch in material '%s':'%s' = '%s', " |
| | "setting to default property units '%s'\n", |
| | name.toStdString().c_str(), |
| | propertyName, |
| | propertyValue.toStdString().c_str(), |
| | prop->getUnits().toStdString().c_str()); |
| | auto quantity = Base::Quantity::parse(propertyValue.toStdString()); |
| | finalModel->setPhysicalValue( |
| | QString::fromStdString(propertyName), |
| | Base::Quantity(quantity.getValue(), |
| | prop->getUnits().toStdString())); |
| | } |
| | } |
| | } |
| | catch (const YAML::BadConversion& e) { |
| | Base::Console().log("Exception %s <%s:%s> - ignored\n", |
| | e.what(), |
| | name.toStdString().c_str(), |
| | propertyName.c_str()); |
| | } |
| | } |
| | else if (propertyName != "UUID") { |
| | Base::Console().log("\tProperty '%s' is not described by any model. Ignored\n", |
| | propertyName.c_str()); |
| | } |
| | } |
| | } |
| | } |
| |
|
| | |
| | if (yamlModel["AppearanceModels"]) { |
| | auto models = yamlModel["AppearanceModels"]; |
| | for (auto it = models.begin(); it != models.end(); it++) { |
| | auto modelName = (it->first).as<std::string>(); |
| |
|
| | |
| | auto modelNode = models[modelName]; |
| | auto modelUUID = modelNode["UUID"].as<std::string>(); |
| | finalModel->addAppearance(QString::fromStdString(modelUUID)); |
| |
|
| | |
| | auto properties = yamlModel["AppearanceModels"][modelName]; |
| | for (auto itp = properties.begin(); itp != properties.end(); itp++) { |
| | auto propertyName = (itp->first).as<std::string>(); |
| | if (finalModel->hasAppearanceProperty(QString::fromStdString(propertyName))) { |
| | auto prop = |
| | finalModel->getAppearanceProperty(QString::fromStdString(propertyName)); |
| | auto type = prop->getType(); |
| |
|
| | try { |
| | if (type == MaterialValue::List || type == MaterialValue::FileList) { |
| | auto list = readList(itp->second); |
| | finalModel->setAppearanceValue(QString::fromStdString(propertyName), |
| | list); |
| | } |
| | else if (type == MaterialValue::ImageList) { |
| | auto list = readImageList(itp->second); |
| | finalModel->setAppearanceValue(QString::fromStdString(propertyName), |
| | list); |
| | } |
| | else if (type == MaterialValue::Array2D) { |
| | auto array2d = read2DArray(itp->second, prop->columns()); |
| | finalModel->setAppearanceValue(QString::fromStdString(propertyName), |
| | array2d); |
| | } |
| | else if (type == MaterialValue::Array3D) { |
| | auto array3d = read3DArray(itp->second, prop->columns()); |
| | finalModel->setAppearanceValue(QString::fromStdString(propertyName), |
| | array3d); |
| | } |
| | else { |
| | QString propertyValue = |
| | QString::fromStdString((itp->second).as<std::string>()); |
| | if (type == MaterialValue::Image) { |
| | propertyValue = propertyValue.remove( |
| | QRegularExpression(QStringLiteral("[\r\n]"))); |
| | } |
| | finalModel->setAppearanceValue(QString::fromStdString(propertyName), |
| | propertyValue); |
| | } |
| | } |
| | catch (const YAML::BadConversion& e) { |
| | Base::Console().log("Exception %s <%s:%s> - ignored\n", |
| | e.what(), |
| | name.toStdString().c_str(), |
| | propertyName.c_str()); |
| | } |
| | } |
| | else if (propertyName != "UUID") { |
| | Base::Console().log("\tProperty '%s' is not described by any model. Ignored\n", |
| | propertyName.c_str()); |
| | } |
| | } |
| | } |
| | } |
| |
|
| | QString path = QDir(directory).absolutePath(); |
| | (*materialMap)[uuid] = library->addMaterial(finalModel, path); |
| | } |
| |
|
| | |
| |
|
| | std::unique_ptr<std::map<QString, std::shared_ptr<MaterialEntry>>> |
| | MaterialLoader::_materialEntryMap = nullptr; |
| |
|
| | MaterialLoader::MaterialLoader( |
| | const std::shared_ptr<std::map<QString, std::shared_ptr<Material>>>& materialMap, |
| | const std::shared_ptr<std::list<std::shared_ptr<MaterialLibrary>>>& libraryList) |
| | : _materialMap(materialMap) |
| | , _libraryList(libraryList) |
| | { |
| | loadLibraries(libraryList); |
| | } |
| |
|
| | void MaterialLoader::addLibrary(const std::shared_ptr<MaterialLibraryLocal>& model) |
| | { |
| | _libraryList->push_back(model); |
| | } |
| |
|
| | std::shared_ptr<MaterialEntry> |
| | MaterialLoader::getMaterialFromYAML(const std::shared_ptr<MaterialLibraryLocal>& library, |
| | YAML::Node& yamlroot, |
| | const QString& path) |
| | { |
| | std::shared_ptr<MaterialEntry> model = nullptr; |
| |
|
| | try { |
| | auto uuid = yamlroot["General"]["UUID"].as<std::string>(); |
| |
|
| | |
| | QFileInfo filepath(path); |
| | QString name = |
| | filepath.fileName().remove(QStringLiteral(".FCMat"), Qt::CaseInsensitive); |
| |
|
| | model = std::make_shared<MaterialYamlEntry>(library, |
| | name, |
| | path, |
| | QString::fromStdString(uuid), |
| | yamlroot); |
| | } |
| | catch (YAML::Exception const& e) { |
| | Base::Console().error("YAML parsing error: '%s'\n", path.toStdString().c_str()); |
| | Base::Console().error("\t'%s'\n", e.what()); |
| | showYaml(yamlroot); |
| | } |
| |
|
| |
|
| | return model; |
| | } |
| |
|
| | std::shared_ptr<MaterialEntry> |
| | MaterialLoader::getMaterialFromPath(const std::shared_ptr<MaterialLibraryLocal>& library, |
| | const QString& path) const |
| | { |
| | std::shared_ptr<MaterialEntry> model = nullptr; |
| | auto materialLibrary = |
| | reinterpret_cast<const std::shared_ptr<Materials::MaterialLibraryLocal>&>(library); |
| |
|
| | |
| | std::string pathName = path.toStdString(); |
| |
|
| | if (MaterialConfigLoader::isConfigStyle(path)) { |
| | auto material = MaterialConfigLoader::getMaterialFromPath(materialLibrary, path); |
| | if (material) { |
| | (*_materialMap)[material->getUUID()] = materialLibrary->addMaterial(material, path); |
| | } |
| |
|
| | |
| | |
| | return model; |
| | } |
| |
|
| | Base::FileInfo info(pathName); |
| | Base::ifstream fin(info); |
| | if (!fin) { |
| | Base::Console().error("YAML file open error: '%s'\n", pathName.c_str()); |
| | return model; |
| | } |
| |
|
| | YAML::Node yamlroot; |
| | try { |
| | yamlroot = YAML::Load(fin); |
| |
|
| | model = getMaterialFromYAML(materialLibrary, yamlroot, path); |
| | } |
| | catch (YAML::Exception const& e) { |
| | Base::Console().error("YAML parsing error: '%s'\n", pathName.c_str()); |
| | Base::Console().error("\t'%s'\n", e.what()); |
| | showYaml(yamlroot); |
| | } |
| |
|
| |
|
| | return model; |
| | } |
| |
|
| | void MaterialLoader::showYaml(const YAML::Node& yaml) |
| | { |
| | std::stringstream out; |
| |
|
| | out << yaml; |
| | std::string logData = out.str(); |
| | Base::Console().log("%s\n", logData.c_str()); |
| | } |
| |
|
| |
|
| | void MaterialLoader::dereference( |
| | const std::shared_ptr<std::map<QString, std::shared_ptr<Material>>>& materialMap, |
| | const std::shared_ptr<Material>& material) |
| | { |
| | |
| | if (material->getDereferenced()) { |
| | return; |
| | } |
| |
|
| | auto parentUUID = material->getParentUUID(); |
| | if (parentUUID.size() > 0) { |
| | std::shared_ptr<Material> parent; |
| | try { |
| | parent = materialMap->at(parentUUID); |
| | } |
| | catch (std::out_of_range&) { |
| | Base::Console().log( |
| | "Unable to apply inheritance for material '%s', parent '%s' not found.\n", |
| | material->getName().toStdString().c_str(), |
| | parentUUID.toStdString().c_str()); |
| | return; |
| | } |
| |
|
| | |
| | dereference(materialMap, parent); |
| |
|
| | |
| | auto modelVector = parent->getPhysicalModels(); |
| | for (auto& model : *modelVector) { |
| | if (!material->hasPhysicalModel(model)) { |
| | material->addPhysical(model); |
| | } |
| | } |
| |
|
| | |
| | modelVector = parent->getAppearanceModels(); |
| | for (auto& model : *modelVector) { |
| | if (!material->hasAppearanceModel(model)) { |
| | material->addAppearance(model); |
| | } |
| | } |
| |
|
| | |
| | auto properties = parent->getPhysicalProperties(); |
| | for (auto& itp : properties) { |
| | auto name = itp.first; |
| | auto property = itp.second; |
| |
|
| | if (material->getPhysicalProperty(name)->isNull()) { |
| | material->getPhysicalProperty(name)->setValue(property->getValue()); |
| | } |
| | } |
| |
|
| | properties = parent->getAppearanceProperties(); |
| | for (auto& itp : properties) { |
| | auto name = itp.first; |
| | auto property = itp.second; |
| |
|
| | if (material->getAppearanceProperty(name)->isNull()) { |
| | material->getAppearanceProperty(name)->setValue(property->getValue()); |
| | } |
| | } |
| | } |
| |
|
| | material->markDereferenced(); |
| | } |
| |
|
| | void MaterialLoader::dereference(const std::shared_ptr<Material>& material) |
| | { |
| | dereference(_materialMap, material); |
| | } |
| |
|
| | void MaterialLoader::loadLibrary(const std::shared_ptr<MaterialLibraryLocal>& library) |
| | { |
| | if (_materialEntryMap == nullptr) { |
| | _materialEntryMap = std::make_unique<std::map<QString, std::shared_ptr<MaterialEntry>>>(); |
| | } |
| |
|
| | QDirIterator it(library->getDirectory(), QDirIterator::Subdirectories); |
| | while (it.hasNext()) { |
| | auto pathname = it.next(); |
| | QFileInfo file(pathname); |
| | if (file.isFile()) { |
| | if (file.suffix().toStdString() == "FCMat") { |
| | try { |
| | auto model = getMaterialFromPath(library, file.canonicalFilePath()); |
| | if (model) { |
| | (*_materialEntryMap)[model->getUUID()] = model; |
| | } |
| | } |
| | catch (const MaterialReadError&) { |
| | |
| | } |
| | } |
| | } |
| | } |
| |
|
| | for (auto& it : *_materialEntryMap) { |
| | it.second->addToTree(_materialMap); |
| | } |
| | } |
| |
|
| | void MaterialLoader::loadLibraries( |
| | const std::shared_ptr<std::list<std::shared_ptr<MaterialLibrary>>>& libraryList) |
| | { |
| | if (libraryList) { |
| | for (auto& it : *libraryList) { |
| | if (it->isLocal()) { |
| | auto materialLibrary = |
| | reinterpret_cast<const std::shared_ptr<Materials::MaterialLibraryLocal>&>(it); |
| | loadLibrary(materialLibrary); |
| | } |
| | } |
| | } |
| |
|
| | for (auto& it : *_materialMap) { |
| | dereference(it.second); |
| | } |
| | } |
| |
|
| | std::shared_ptr<std::list<QString>> |
| | MaterialLoader::getMaterialFolders(const MaterialLibraryLocal& library) |
| | { |
| | std::shared_ptr<std::list<QString>> pathList = std::make_shared<std::list<QString>>(); |
| | QDirIterator it(library.getDirectory(), QDirIterator::Subdirectories); |
| | while (it.hasNext()) { |
| | auto pathname = it.next(); |
| | QFileInfo file(pathname); |
| | if (file.isDir()) { |
| | QString path = QDir(library.getDirectory()).relativeFilePath(file.absoluteFilePath()); |
| | if (!path.startsWith(QStringLiteral("."))) { |
| | pathList->push_back(path); |
| | } |
| | } |
| | } |
| |
|
| | return pathList; |
| | } |
| |
|