Files
create/src/Mod/Material/App/ModelManager.cpp
David Carter a6ffcf63b6 Materials: Model Manager External Interface (#20825)
* Materials: Model Manager External Interface

Implement the ModelManagerExternal class for the external Materials
interface.

This is part of the ongoing merges of code to support external material
interfaces. In this PR the ModelManagerExternal class is implemented,
along with changes to supporting classes required for this class.

* Apply reviewer feedback
2025-04-28 10:29:04 -05:00

365 lines
11 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 <Base/Console.h>
#include "Model.h"
#include "ModelLoader.h"
#include "ModelManager.h"
#include "ModelManagerLocal.h"
#if defined(BUILD_MATERIAL_EXTERNAL)
#include "ModelManagerExternal.h"
#endif
using namespace Materials;
TYPESYSTEM_SOURCE(Materials::ModelManager, Base::BaseClass)
QMutex ModelManager::_mutex;
bool ModelManager::_useExternal = false;
ModelManager* ModelManager::_manager = nullptr;
std::unique_ptr<ModelManagerLocal> ModelManager::_localManager;
#if defined(BUILD_MATERIAL_EXTERNAL)
std::unique_ptr<ModelManagerExternal> ModelManager::_externalManager;
#endif
ModelManager::ModelManager()
{
_hGrp = App::GetApplication().GetParameterGroupByPath(
"User parameter:BaseApp/Preferences/Mod/Material/ExternalInterface");
_useExternal = _hGrp->GetBool("UseExternal", false);
_hGrp->Attach(this);
}
ModelManager::~ModelManager()
{
_hGrp->Detach(this);
}
ModelManager& ModelManager::getManager()
{
if (!_manager) {
initManagers();
}
return *_manager;
}
void ModelManager::initManagers()
{
QMutexLocker locker(&_mutex);
if (!_manager) {
// Can't use smart pointers for this since the constructor is private
_manager = new ModelManager();
}
if (!_localManager) {
_localManager = std::make_unique<ModelManagerLocal>();
}
#if defined(BUILD_MATERIAL_EXTERNAL)
if (!_externalManager) {
_externalManager = std::make_unique<ModelManagerExternal>();
}
#endif
}
void ModelManager::OnChange(ParameterGrp::SubjectType& rCaller, ParameterGrp::MessageType Reason)
{
const ParameterGrp& rGrp = static_cast<ParameterGrp&>(rCaller);
if (strcmp(Reason, "UseExternal") == 0) {
Base::Console().Log("Use external changed\n");
_useExternal = rGrp.GetBool("UseExternal", false);
// _dbManager->refresh();
}
}
bool ModelManager::isModel(const QString& file)
{
return ModelManagerLocal::isModel(file);
}
void ModelManager::cleanup()
{
return ModelManagerLocal::cleanup();
#if defined(BUILD_MATERIAL_EXTERNAL)
if (_externalManager) {
_externalManager->cleanup();
}
#endif
}
void ModelManager::refresh()
{
_localManager->refresh();
}
//=====
//
// Library management
//
//=====
std::shared_ptr<std::list<std::shared_ptr<ModelLibrary>>> ModelManager::getLibraries()
{
// External libraries take precedence over local libraries
auto libMap = std::map<QString, std::shared_ptr<ModelLibrary>>();
#if defined(BUILD_MATERIAL_EXTERNAL)
if (_useExternal) {
auto remoteLibraries = _externalManager->getLibraries();
for (auto& remote : *remoteLibraries) {
libMap.try_emplace(remote->getName(), remote);
}
}
#endif
auto localLibraries = _localManager->getLibraries();
for (auto& local : *localLibraries) {
libMap.try_emplace(local->getName(), local);
}
// Consolidate into a single list
auto libraries = std::make_shared<std::list<std::shared_ptr<ModelLibrary>>>();
for (auto libEntry : libMap) {
libraries->push_back(libEntry.second);
}
return libraries;
}
std::shared_ptr<std::list<std::shared_ptr<ModelLibrary>>> ModelManager::getLocalLibraries()
{
return _localManager->getLibraries();
}
void ModelManager::createLibrary(const QString& libraryName, const QString& icon, bool readOnly)
{
#if defined(BUILD_MATERIAL_EXTERNAL)
_externalManager->createLibrary(libraryName, icon, readOnly);
#endif
}
std::shared_ptr<ModelLibrary> ModelManager::getLibrary(const QString& name) const
{
#if defined(BUILD_MATERIAL_EXTERNAL)
if (_useExternal) {
auto library = _externalManager->getLibrary(name);
if (library) {
return library;
}
}
#endif
return _localManager->getLibrary(name);
}
void ModelManager::createLocalLibrary(const QString& libraryName,
const QString& directory,
const QString& icon,
bool readOnly)
{
_localManager->createLibrary(libraryName, directory, icon, readOnly);
}
void ModelManager::renameLibrary(const QString& libraryName, const QString& newName)
{
_localManager->renameLibrary(libraryName, newName);
}
void ModelManager::changeIcon(const QString& libraryName, const QString& icon)
{
_localManager->changeIcon(libraryName, icon);
}
void ModelManager::removeLibrary(const QString& libraryName)
{
_localManager->removeLibrary(libraryName);
}
std::shared_ptr<std::vector<std::tuple<QString, QString, QString>>>
ModelManager::libraryModels(const QString& libraryName)
{
#if defined(BUILD_MATERIAL_EXTERNAL)
if (_useExternal) {
try {
auto models = _externalManager->libraryModels(libraryName);
if (models) {
return models;
}
}
catch (const LibraryNotFound& e) {
}
catch (const InvalidModel& e) {
}
}
#endif
return _localManager->libraryModels(libraryName);
}
bool ModelManager::isLocalLibrary(const QString& libraryName)
{
#if defined(BUILD_MATERIAL_EXTERNAL)
if (_useExternal) {
try {
auto lib = _externalManager->getLibrary(libraryName);
if (lib) {
return false;
}
}
catch (const LibraryNotFound& e) {
}
}
#endif
return true;
}
//=====
//
// Model management
//
//=====
std::shared_ptr<std::map<QString, std::shared_ptr<Model>>> ModelManager::getModels()
{
// External libraries take precedence over local libraries
auto modelMap = std::make_shared<std::map<QString, std::shared_ptr<Model>>>();
#if defined(BUILD_MATERIAL_EXTERNAL)
if (_useExternal) {
auto remoteModels = _externalManager->getModels();
for (auto& remote : *remoteModels) {
modelMap->try_emplace(remote.first, remote.second);
}
}
#endif
auto localModels = _localManager->getModels();
for (auto& local : *localModels) {
modelMap->try_emplace(local.first, local.second);
}
return modelMap;
}
std::shared_ptr<std::map<QString, std::shared_ptr<Model>>> ModelManager::getLocalModels()
{
return _localManager->getModels();
}
std::shared_ptr<Model> ModelManager::getModel(const QString& /*libraryName*/, const QString& uuid) const
{
// TODO: Search a specific library
return getModel(uuid);
}
std::shared_ptr<Model> ModelManager::getModel(const QString& uuid) const
{
#if defined(BUILD_MATERIAL_EXTERNAL)
if (_useExternal) {
auto model = _externalManager->getModel(uuid);
if (model) {
return model;
}
}
#endif
// We really want to return the local model if not found, such as for User folder models
return _localManager->getModel(uuid);
}
std::shared_ptr<Model> ModelManager::getModelByPath(const QString& path) const
{
return _localManager->getModelByPath(path);
}
std::shared_ptr<Model> ModelManager::getModelByPath(const QString& path, const QString& lib) const
{
return _localManager->getModelByPath(path, lib);
}
bool ModelManager::passFilter(ModelFilter filter, Model::ModelType modelType)
{
switch (filter) {
case ModelFilter_None:
return true;
case ModelFilter_Physical:
return (modelType == Model::ModelType_Physical);
case ModelFilter_Appearance:
return (modelType == Model::ModelType_Appearance);
}
return false;
}
#if defined(BUILD_MATERIAL_EXTERNAL)
void ModelManager::migrateToExternal(const std::shared_ptr<Materials::ModelLibrary>& library)
{
_externalManager->createLibrary(library->getName(),
library->getIconPath(),
library->isReadOnly());
auto models = _localManager->libraryModels(library->getName());
for (auto& tuple : *models) {
auto uuid = std::get<0>(tuple);
auto path = std::get<1>(tuple);
auto name = std::get<2>(tuple);
Base::Console().Log("\t('%s', '%s', '%s')\n",
uuid.toStdString().c_str(),
path.toStdString().c_str(),
name.toStdString().c_str());
auto model = _localManager->getModel(uuid);
_externalManager->migrateModel(library->getName(), path, model);
}
}
void ModelManager::validateMigration(const std::shared_ptr<Materials::ModelLibrary>& library)
{
auto models = _localManager->libraryModels(library->getName());
for (auto& tuple : *models) {
auto uuid = std::get<0>(tuple);
auto path = std::get<1>(tuple);
auto name = std::get<2>(tuple);
Base::Console().Log("\t('%s', '%s', '%s')\n",
uuid.toStdString().c_str(),
path.toStdString().c_str(),
name.toStdString().c_str());
auto model = _localManager->getModel(uuid);
auto externalModel = _externalManager->getModel(uuid);
model->validate(externalModel);
}
}
// Cache stats
double ModelManager::modelHitRate()
{
initManagers();
return _externalManager->modelHitRate();
}
#endif