This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/blender/asset_system/intern/asset_library_service.cc
Jacques Lucke b3fb73f325 Assets: bundle Essentials with Blender
This patch adds an "Essentials" asset library that is bundled with Blender.
Also see #103620. At build time, the `lib/assets/publish` folder is copied
to `datafiles/assets` in the build directory.

In the UI, the "Essentials" library can be accessed like other custom asset
libraries with the exception that assets from that library cannot be linked.

The immediate impact of this is that Blender now comes with some geometry
node groups for procedural hair grooming.

Pull Request #104474
2023-02-14 17:35:29 +01:00

281 lines
8.3 KiB
C++

/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup asset_system
*/
#include "BKE_blender.h"
#include "BKE_preferences.h"
#include "BLI_string_ref.hh"
#include "DNA_asset_types.h"
#include "DNA_userdef_types.h"
#include "CLG_log.h"
#include "AS_asset_catalog_tree.hh"
#include "AS_asset_library.hh"
#include "AS_essentials_library.hh"
#include "asset_library_service.hh"
#include "utils.hh"
/* When enabled, use a pre file load handler (#BKE_CB_EVT_LOAD_PRE) callback to destroy the asset
* library service. Without this an explicit call from the file loading code is needed to do this,
* which is not as nice.
*
* TODO Currently disabled because UI data depends on asset library data, so we have to make sure
* it's freed in the right order (UI first). Pre-load handlers don't give us this order.
* Should be addressed with a proper ownership model for the asset system:
* https://wiki.blender.org/wiki/Source/Architecture/Asset_System/Back_End#Ownership_Model
*/
//#define WITH_DESTROY_VIA_LOAD_HANDLER
static CLG_LogRef LOG = {"asset_system.asset_library_service"};
namespace blender::asset_system {
std::unique_ptr<AssetLibraryService> AssetLibraryService::instance_;
bool AssetLibraryService::atexit_handler_registered_ = false;
AssetLibraryService *AssetLibraryService::get()
{
if (!instance_) {
allocate_service_instance();
}
return instance_.get();
}
void AssetLibraryService::destroy()
{
if (!instance_) {
return;
}
instance_->app_handler_unregister();
instance_.reset();
}
AssetLibrary *AssetLibraryService::get_asset_library(
const Main *bmain, const AssetLibraryReference &library_reference)
{
const eAssetLibraryType type = eAssetLibraryType(library_reference.type);
switch (type) {
case ASSET_LIBRARY_ESSENTIALS: {
const StringRefNull root_path = essentials_directory_path();
AssetLibrary *asset_library = get_asset_library_on_disk(root_path);
asset_library->never_link = true;
return asset_library;
}
case ASSET_LIBRARY_LOCAL: {
/* For the "Current File" library we get the asset library root path based on main. */
std::string root_path = bmain ? AS_asset_library_find_suitable_root_path_from_main(bmain) :
"";
if (root_path.empty()) {
/* File wasn't saved yet. */
return get_asset_library_current_file();
}
return get_asset_library_on_disk(root_path);
}
case ASSET_LIBRARY_ALL:
return get_asset_library_all(bmain);
case ASSET_LIBRARY_CUSTOM: {
std::string root_path = root_path_from_library_ref(library_reference);
if (!root_path.empty()) {
return get_asset_library_on_disk(root_path);
}
break;
}
}
return nullptr;
}
AssetLibrary *AssetLibraryService::get_asset_library_on_disk(StringRefNull root_path)
{
BLI_assert_msg(!root_path.is_empty(),
"top level directory must be given for on-disk asset library");
std::string normalized_root_path = utils::normalize_directory_path(root_path);
std::unique_ptr<AssetLibrary> *lib_uptr_ptr = on_disk_libraries_.lookup_ptr(
normalized_root_path);
if (lib_uptr_ptr != nullptr) {
CLOG_INFO(&LOG, 2, "get \"%s\" (cached)", normalized_root_path.c_str());
AssetLibrary *lib = lib_uptr_ptr->get();
lib->refresh();
return lib;
}
std::unique_ptr lib_uptr = std::make_unique<AssetLibrary>(normalized_root_path);
AssetLibrary *lib = lib_uptr.get();
lib->on_blend_save_handler_register();
lib->load_catalogs();
/* Reload catalogs on refresh. */
lib->on_refresh_ = [](AssetLibrary &self) { self.catalog_service->reload_catalogs(); };
on_disk_libraries_.add_new(normalized_root_path, std::move(lib_uptr));
CLOG_INFO(&LOG, 2, "get \"%s\" (loaded)", normalized_root_path.c_str());
return lib;
}
AssetLibrary *AssetLibraryService::get_asset_library_current_file()
{
if (current_file_library_) {
CLOG_INFO(&LOG, 2, "get current file lib (cached)");
current_file_library_->refresh();
}
else {
CLOG_INFO(&LOG, 2, "get current file lib (loaded)");
current_file_library_ = std::make_unique<AssetLibrary>();
current_file_library_->on_blend_save_handler_register();
}
AssetLibrary *lib = current_file_library_.get();
return lib;
}
static void rebuild_all_library(AssetLibrary &all_library, const bool reload_catalogs)
{
/* Start with empty catalog storage. */
all_library.catalog_service = std::make_unique<AssetCatalogService>(
AssetCatalogService::read_only_tag());
AssetLibrary::foreach_loaded(
[&](AssetLibrary &nested) {
if (reload_catalogs) {
nested.catalog_service->reload_catalogs();
}
all_library.catalog_service->add_from_existing(*nested.catalog_service);
},
false);
all_library.catalog_service->rebuild_tree();
}
AssetLibrary *AssetLibraryService::get_asset_library_all(const Main *bmain)
{
/* (Re-)load all other asset libraries. */
for (AssetLibraryReference &library_ref : all_valid_asset_library_refs()) {
/* Skip self :) */
if (library_ref.type == ASSET_LIBRARY_ALL) {
continue;
}
/* Ensure all asset libraries are loaded. */
get_asset_library(bmain, library_ref);
}
if (all_library_) {
CLOG_INFO(&LOG, 2, "get all lib (cached)");
all_library_->refresh();
return all_library_.get();
}
CLOG_INFO(&LOG, 2, "get all lib (loaded)");
all_library_ = std::make_unique<AssetLibrary>();
/* Don't reload catalogs on this initial read, they've just been loaded above. */
rebuild_all_library(*all_library_, /*reload_catlogs=*/false);
all_library_->on_refresh_ = [](AssetLibrary &all_library) {
rebuild_all_library(all_library, /*reload_catalogs=*/true);
};
return all_library_.get();
}
std::string AssetLibraryService::root_path_from_library_ref(
const AssetLibraryReference &library_reference)
{
if (ELEM(library_reference.type, ASSET_LIBRARY_ALL, ASSET_LIBRARY_LOCAL)) {
return "";
}
BLI_assert(library_reference.type == ASSET_LIBRARY_CUSTOM);
BLI_assert(library_reference.custom_library_index >= 0);
bUserAssetLibrary *user_library = BKE_preferences_asset_library_find_from_index(
&U, library_reference.custom_library_index);
if (!user_library || !user_library->path[0]) {
return "";
}
return user_library->path;
}
void AssetLibraryService::allocate_service_instance()
{
instance_ = std::make_unique<AssetLibraryService>();
instance_->app_handler_register();
if (!atexit_handler_registered_) {
/* Ensure the instance gets freed before Blender's memory leak detector runs. */
BKE_blender_atexit_register([](void * /*user_data*/) { AssetLibraryService::destroy(); },
nullptr);
atexit_handler_registered_ = true;
}
}
static void on_blendfile_load(struct Main * /*bMain*/,
struct PointerRNA ** /*pointers*/,
const int /*num_pointers*/,
void * /*arg*/)
{
#ifdef WITH_DESTROY_VIA_LOAD_HANDLER
AssetLibraryService::destroy();
#endif
}
void AssetLibraryService::app_handler_register()
{
/* The callback system doesn't own `on_load_callback_store_`. */
on_load_callback_store_.alloc = false;
on_load_callback_store_.func = &on_blendfile_load;
on_load_callback_store_.arg = this;
BKE_callback_add(&on_load_callback_store_, BKE_CB_EVT_LOAD_PRE);
}
void AssetLibraryService::app_handler_unregister()
{
BKE_callback_remove(&on_load_callback_store_, BKE_CB_EVT_LOAD_PRE);
on_load_callback_store_.func = nullptr;
on_load_callback_store_.arg = nullptr;
}
bool AssetLibraryService::has_any_unsaved_catalogs() const
{
bool has_unsaved_changes = false;
foreach_loaded_asset_library(
[&has_unsaved_changes](AssetLibrary &library) {
if (library.catalog_service->has_unsaved_changes()) {
has_unsaved_changes = true;
}
},
true);
return has_unsaved_changes;
}
void AssetLibraryService::foreach_loaded_asset_library(FunctionRef<void(AssetLibrary &)> fn,
const bool include_all_library) const
{
if (include_all_library && all_library_) {
fn(*all_library_);
}
if (current_file_library_) {
fn(*current_file_library_);
}
for (const auto &asset_lib_uptr : on_disk_libraries_.values()) {
fn(*asset_lib_uptr);
}
}
} // namespace blender::asset_system