Files
create/src/Mod/Material/App/MaterialManager.cpp
David Carter bc36c8dab5 Material: Compatibility with older FCMat files
Provides compatibility loading older files outside the context of
a library.

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

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

Fixes #13302
2024-04-15 18:05:18 +02:00

346 lines
10 KiB
C++

/***************************************************************************
* 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/>. *
* *
**************************************************************************/
#include "PreCompiled.h"
#ifndef _PreComp_
#endif
#include <QDirIterator>
#include <QMutexLocker>
#include <App/Application.h>
#include <App/Material.h>
#include "Exceptions.h"
#include "MaterialConfigLoader.h"
#include "MaterialLoader.h"
#include "MaterialManager.h"
#include "ModelManager.h"
#include "ModelUuids.h"
using namespace Materials;
/* TRANSLATOR Material::Materials */
std::shared_ptr<std::list<std::shared_ptr<MaterialLibrary>>> MaterialManager::_libraryList =
nullptr;
std::shared_ptr<std::map<QString, std::shared_ptr<Material>>> MaterialManager::_materialMap =
nullptr;
QMutex MaterialManager::_mutex;
TYPESYSTEM_SOURCE(Materials::MaterialManager, Base::BaseClass)
MaterialManager::MaterialManager()
{
// TODO: Add a mutex or similar
initLibraries();
}
void MaterialManager::initLibraries()
{
QMutexLocker locker(&_mutex);
if (_materialMap == nullptr) {
// Load the models first
auto manager = std::make_unique<ModelManager>();
Q_UNUSED(manager)
_materialMap = std::make_shared<std::map<QString, std::shared_ptr<Material>>>();
if (_libraryList == nullptr) {
_libraryList = std::make_shared<std::list<std::shared_ptr<MaterialLibrary>>>();
}
// Load the libraries
MaterialLoader loader(_materialMap, _libraryList);
}
}
void MaterialManager::saveMaterial(const std::shared_ptr<MaterialLibrary>& library,
const std::shared_ptr<Material>& material,
const QString& path,
bool overwrite,
bool saveAsCopy,
bool saveInherited) const
{
auto newMaterial = library->saveMaterial(material, path, overwrite, saveAsCopy, saveInherited);
(*_materialMap)[newMaterial->getUUID()] = newMaterial;
}
bool MaterialManager::isMaterial(const fs::path& p) const
{
if (!fs::is_regular_file(p)) {
return false;
}
// check file extension
if (p.extension() == ".FCMat") {
return true;
}
return false;
}
bool MaterialManager::isMaterial(const QFileInfo& file) const
{
if (!file.isFile()) {
return false;
}
// check file extension
if (file.suffix() == QString::fromStdString("FCMat")) {
return true;
}
return false;
}
std::shared_ptr<Material> MaterialManager::defaultMaterial()
{
MaterialManager manager;
ParameterGrp::handle hGrp =
App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/View");
bool randomColor = hGrp->GetBool("RandomColor", false);
float r, g, b;
if (randomColor) {
auto fMax = (float)RAND_MAX;
r = (float)rand() / fMax;
g = (float)rand() / fMax;
b = (float)rand() / fMax;
}
else {
unsigned long shcol = hGrp->GetUnsigned("DefaultShapeColor", 3435980543UL);
r = ((shcol >> 24) & 0xff) / 255.0;
g = ((shcol >> 16) & 0xff) / 255.0;
b = ((shcol >> 8) & 0xff) / 255.0;
}
int initialTransparency = hGrp->GetInt("DefaultShapeTransparency", 0);
auto material = manager.getMaterial(defaultMaterialUUID());
if (material->hasAppearanceModel(ModelUUIDs::ModelUUID_Rendering_Basic)) {
material->getAppearanceProperty(QString::fromLatin1("DiffuseColor"))
->setColor(App::Color(r, g, b));
material->getAppearanceProperty(QString::fromLatin1("Transparency"))
->setFloat((float)initialTransparency / 100.0f);
}
return material;
}
QString MaterialManager::defaultMaterialUUID()
{
// Make this a preference
return QString::fromLatin1("7f9fd73b-50c9-41d8-b7b2-575a030c1eeb");
}
std::shared_ptr<Material> MaterialManager::getMaterial(const QString& uuid) const
{
try {
return _materialMap->at(uuid);
}
catch (std::out_of_range&) {
throw MaterialNotFound();
}
}
std::shared_ptr<Material> MaterialManager::getMaterial(const App::Material& material)
{
MaterialManager manager;
return manager.getMaterial(QString::fromStdString(material.uuid));
}
std::shared_ptr<Material> MaterialManager::getMaterialByPath(const QString& path) const
{
QString cleanPath = QDir::cleanPath(path);
for (auto& library : *_libraryList) {
if (cleanPath.startsWith(library->getDirectory())) {
try {
return library->getMaterialByPath(cleanPath);
}
catch (const MaterialNotFound&) {
}
// See if it's a new file saved by the old editor
{
QMutexLocker locker(&_mutex);
if (MaterialConfigLoader::isConfigStyle(path)) {
auto material = MaterialConfigLoader::getMaterialFromPath(library, path);
if (material) {
(*_materialMap)[material->getUUID()] = library->addMaterial(material, path);
}
return material;
}
}
}
}
// Older workbenches may try files outside the context of a library
{
QMutexLocker locker(&_mutex);
if (MaterialConfigLoader::isConfigStyle(path)) {
auto material = MaterialConfigLoader::getMaterialFromPath(nullptr, path);
return material;
}
}
throw MaterialNotFound();
}
std::shared_ptr<Material> MaterialManager::getMaterialByPath(const QString& path,
const QString& lib) const
{
auto library = getLibrary(lib); // May throw LibraryNotFound
return library->getMaterialByPath(path); // May throw MaterialNotFound
}
bool MaterialManager::exists(const QString& uuid) const
{
try {
auto material = getMaterial(uuid);
if (material) {
return true;
}
}
catch (const MaterialNotFound&) {
}
return false;
}
std::shared_ptr<Material>
MaterialManager::getParent(const std::shared_ptr<Material>& material) const
{
if (material->getParentUUID().isEmpty()) {
throw MaterialNotFound();
}
return getMaterial(material->getParentUUID());
}
bool MaterialManager::exists(const std::shared_ptr<MaterialLibrary>& library,
const QString& uuid) const
{
try {
auto material = getMaterial(uuid);
if (material) {
return (*material->getLibrary() == *library);
}
}
catch (const MaterialNotFound&) {
}
return false;
}
std::shared_ptr<MaterialLibrary> MaterialManager::getLibrary(const QString& name) const
{
for (auto& library : *_libraryList) {
if (library->getName() == name) {
return library;
}
}
throw LibraryNotFound();
}
std::shared_ptr<std::list<std::shared_ptr<MaterialLibrary>>>
MaterialManager::getMaterialLibraries() const
{
if (_libraryList == nullptr) {
if (_materialMap == nullptr) {
_materialMap = std::make_shared<std::map<QString, std::shared_ptr<Material>>>();
}
_libraryList = std::make_shared<std::list<std::shared_ptr<MaterialLibrary>>>();
// Load the libraries
MaterialLoader loader(_materialMap, _libraryList);
}
return _libraryList;
}
std::shared_ptr<std::list<QString>>
MaterialManager::getMaterialFolders(const std::shared_ptr<MaterialLibrary>& library) const
{
return MaterialLoader::getMaterialFolders(*library);
}
std::shared_ptr<std::map<QString, std::shared_ptr<Material>>>
MaterialManager::materialsWithModel(const QString& uuid) const
{
std::shared_ptr<std::map<QString, std::shared_ptr<Material>>> dict =
std::make_shared<std::map<QString, std::shared_ptr<Material>>>();
for (auto& it : *_materialMap) {
QString key = it.first;
auto material = it.second;
if (material->hasModel(uuid)) {
(*dict)[key] = material;
}
}
return dict;
}
std::shared_ptr<std::map<QString, std::shared_ptr<Material>>>
MaterialManager::materialsWithModelComplete(const QString& uuid) const
{
std::shared_ptr<std::map<QString, std::shared_ptr<Material>>> dict =
std::make_shared<std::map<QString, std::shared_ptr<Material>>>();
for (auto& it : *_materialMap) {
QString key = it.first;
auto material = it.second;
if (material->isModelComplete(uuid)) {
(*dict)[key] = material;
}
}
return dict;
}
void MaterialManager::dereference() const
{
// First clear the inheritences
for (auto& it : *_materialMap) {
auto material = it.second;
material->clearDereferenced();
material->clearInherited();
}
// Run the dereference again
for (auto& it : *_materialMap) {
dereference(it.second);
}
}
void MaterialManager::dereference(std::shared_ptr<Material> material) const
{
MaterialLoader::dereference(_materialMap, material);
}