/*************************************************************************** * Copyright (c) 2023 David Carter * * * * 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 * * . * * * **************************************************************************/ #include "PreCompiled.h" #ifndef _PreComp_ #endif #include #include #include #include #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 ModelManager::_localManager; #if defined(BUILD_MATERIAL_EXTERNAL) std::unique_ptr 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(); } #if defined(BUILD_MATERIAL_EXTERNAL) if (!_externalManager) { _externalManager = std::make_unique(); } #endif } void ModelManager::OnChange(ParameterGrp::SubjectType& rCaller, ParameterGrp::MessageType Reason) { const ParameterGrp& rGrp = static_cast(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>> ModelManager::getLibraries() { // External libraries take precedence over local libraries auto libMap = std::map>(); #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>>(); for (auto libEntry : libMap) { libraries->push_back(libEntry.second); } return libraries; } std::shared_ptr>> 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 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>> 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>> ModelManager::getModels() { // External libraries take precedence over local libraries auto modelMap = std::make_shared>>(); #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>> ModelManager::getLocalModels() { return _localManager->getModels(); } std::shared_ptr ModelManager::getModel(const QString& /*libraryName*/, const QString& uuid) const { // TODO: Search a specific library return getModel(uuid); } std::shared_ptr 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 ModelManager::getModelByPath(const QString& path) const { return _localManager->getModelByPath(path); } std::shared_ptr 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& 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& 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