Compare commits
23 Commits
tmp-volume
...
temp-asset
Author | SHA1 | Date | |
---|---|---|---|
59eec2f67d | |||
a83d40c497 | |||
3af1113a96 | |||
a683d5f934 | |||
3cd93ace24 | |||
e8575bfd4a | |||
747a9ea263 | |||
af0c1d72a2 | |||
11abc1be39 | |||
ecc25bc62e | |||
a07a2e2369 | |||
af5d225653 | |||
1dc8305213 | |||
fb2303fb73 | |||
5186c9c9c6 | |||
126136baab | |||
3f1e4f6f56 | |||
03bd437170 | |||
2c2515d465 | |||
d51212c4f0 | |||
86b9b1df22 | |||
ca8fa2f7d6 | |||
33bcc4f430 |
@@ -45,11 +45,17 @@ class AssetCatalogService {
|
||||
Vector<std::unique_ptr<AssetCatalogCollection>> undo_snapshots_;
|
||||
Vector<std::unique_ptr<AssetCatalogCollection>> redo_snapshots_;
|
||||
|
||||
const bool is_read_only_ = false;
|
||||
|
||||
public:
|
||||
static const CatalogFilePath DEFAULT_CATALOG_FILENAME;
|
||||
|
||||
struct read_only_tag {
|
||||
};
|
||||
|
||||
public:
|
||||
AssetCatalogService();
|
||||
explicit AssetCatalogService(read_only_tag);
|
||||
explicit AssetCatalogService(const CatalogFilePath &asset_library_root);
|
||||
|
||||
/**
|
||||
@@ -62,11 +68,24 @@ class AssetCatalogService {
|
||||
void tag_has_unsaved_changes(AssetCatalog *edited_catalog);
|
||||
bool has_unsaved_changes() const;
|
||||
|
||||
/**
|
||||
* Check if this is a read-only service meaning the user shouldn't be able to do edits. This is
|
||||
* not enforced by internal catalog code, the catalog service user is responsible for it. For
|
||||
* example the UI should disallow edits.
|
||||
*/
|
||||
bool is_read_only() const;
|
||||
|
||||
/** Load asset catalog definitions from the files found in the asset library. */
|
||||
void load_from_disk();
|
||||
/** Load asset catalog definitions from the given file or directory. */
|
||||
void load_from_disk(const CatalogFilePath &file_or_directory_path);
|
||||
|
||||
/**
|
||||
* Duplicate the catalogs from \a other_service into this one. Does not rebuild the tree, this
|
||||
* needs to be done by the caller (call #rebuild_tree()!).
|
||||
*/
|
||||
void add_from_existing(const AssetCatalogService &other_service);
|
||||
|
||||
/**
|
||||
* Write the catalog definitions to disk.
|
||||
*
|
||||
@@ -105,6 +124,15 @@ class AssetCatalogService {
|
||||
*/
|
||||
void reload_catalogs();
|
||||
|
||||
/**
|
||||
* Make sure the tree is updated to the latest collection of catalogs stored in this service.
|
||||
* Does not depend on a CDF file being available so this can be called on a service that stores
|
||||
* catalogs that are not stored in a CDF.
|
||||
* Most API functions that modify catalog data will trigger this, unless otherwise specified (for
|
||||
* batch operations).
|
||||
*/
|
||||
void rebuild_tree();
|
||||
|
||||
/** Return catalog with the given ID. Return nullptr if not found. */
|
||||
AssetCatalog *find_catalog(CatalogID catalog_id) const;
|
||||
|
||||
@@ -222,7 +250,6 @@ class AssetCatalogService {
|
||||
const CatalogFilePath &blend_file_path);
|
||||
|
||||
std::unique_ptr<AssetCatalogTree> read_into_tree();
|
||||
void rebuild_tree();
|
||||
|
||||
/**
|
||||
* For every catalog, ensure that its parent path also has a known catalog.
|
||||
@@ -270,6 +297,11 @@ class AssetCatalogCollection {
|
||||
AssetCatalogCollection(AssetCatalogCollection &&other) noexcept = default;
|
||||
|
||||
std::unique_ptr<AssetCatalogCollection> deep_copy() const;
|
||||
/**
|
||||
* Copy the catalogs from \a other and append them to this collection. Copies no other data
|
||||
* otherwise.
|
||||
*/
|
||||
void add_catalogs_from_existing(const AssetCatalogCollection &other);
|
||||
|
||||
protected:
|
||||
static OwningAssetCatalogMap copy_catalog_map(const OwningAssetCatalogMap &orig);
|
||||
|
@@ -56,6 +56,8 @@ class AssetLibrary {
|
||||
*/
|
||||
std::unique_ptr<AssetStorage> asset_storage_;
|
||||
|
||||
std::function<void(AssetLibrary &self)> on_refresh_;
|
||||
|
||||
bCallbackFuncStore on_save_callback_store_{};
|
||||
|
||||
public:
|
||||
@@ -65,6 +67,8 @@ class AssetLibrary {
|
||||
|
||||
std::unique_ptr<AssetCatalogService> catalog_service;
|
||||
|
||||
friend class AssetLibraryService;
|
||||
|
||||
public:
|
||||
/**
|
||||
* \param root_path: If this is an asset library on disk, the top-level directory path.
|
||||
@@ -72,6 +76,16 @@ class AssetLibrary {
|
||||
AssetLibrary(StringRef root_path = "");
|
||||
~AssetLibrary();
|
||||
|
||||
/**
|
||||
* Execute \a fn for every asset library that is loaded. The asset library is passed to the
|
||||
* \a fn call.
|
||||
*
|
||||
* \param skip_all_library: When true, the \a fn will also be executed for the "All" asset
|
||||
* library. This is just a combination of the other ones, so usually
|
||||
* iterating over it is redundant.
|
||||
*/
|
||||
static void foreach_loaded(FunctionRef<void(AssetLibrary &)> fn, bool include_all_library);
|
||||
|
||||
void load_catalogs();
|
||||
|
||||
/** Load catalogs that have changed on disk. */
|
||||
@@ -128,9 +142,6 @@ class AssetLibrary {
|
||||
AssetIdentifier asset_identifier_from_library(StringRef relative_asset_path);
|
||||
|
||||
StringRefNull root_path() const;
|
||||
|
||||
private:
|
||||
std::optional<int> find_asset_index(const AssetRepresentation &asset);
|
||||
};
|
||||
|
||||
Vector<AssetLibraryReference> all_valid_asset_library_refs();
|
||||
@@ -138,12 +149,22 @@ Vector<AssetLibraryReference> all_valid_asset_library_refs();
|
||||
} // namespace blender::asset_system
|
||||
|
||||
/**
|
||||
* Load the data for an asset library, but not the asset representations themselves (loading these
|
||||
* is currently not done in the asset system).
|
||||
*
|
||||
* For the "All" asset library (#ASSET_LIBRARY_ALL), every other known asset library will be
|
||||
* loaded as well. So a call to #AssetLibrary::foreach_loaded() can be expected to iterate over all
|
||||
* libraries.
|
||||
*
|
||||
* \warning Catalogs are reloaded, invalidating catalog pointers. Do not store catalog pointers,
|
||||
* store CatalogIDs instead and lookup the catalog where needed.
|
||||
*/
|
||||
blender::asset_system::AssetLibrary *AS_asset_library_load(
|
||||
const Main *bmain, const AssetLibraryReference &library_reference);
|
||||
|
||||
std::string AS_asset_library_root_path_from_library_ref(
|
||||
const AssetLibraryReference &library_reference);
|
||||
|
||||
/**
|
||||
* Try to find an appropriate location for an asset library root from a file or directory path.
|
||||
* Does not check if \a input_path exists.
|
||||
|
@@ -43,6 +43,11 @@ AssetCatalogService::AssetCatalogService()
|
||||
{
|
||||
}
|
||||
|
||||
AssetCatalogService::AssetCatalogService(read_only_tag) : AssetCatalogService()
|
||||
{
|
||||
const_cast<bool &>(is_read_only_) = true;
|
||||
}
|
||||
|
||||
AssetCatalogService::AssetCatalogService(const CatalogFilePath &asset_library_root)
|
||||
: AssetCatalogService()
|
||||
{
|
||||
@@ -51,6 +56,8 @@ AssetCatalogService::AssetCatalogService(const CatalogFilePath &asset_library_ro
|
||||
|
||||
void AssetCatalogService::tag_has_unsaved_changes(AssetCatalog *edited_catalog)
|
||||
{
|
||||
BLI_assert(!is_read_only_);
|
||||
|
||||
if (edited_catalog) {
|
||||
edited_catalog->flags.has_unsaved_changes = true;
|
||||
}
|
||||
@@ -85,6 +92,11 @@ bool AssetCatalogService::has_unsaved_changes() const
|
||||
return catalog_collection_->has_unsaved_changes_;
|
||||
}
|
||||
|
||||
bool AssetCatalogService::is_read_only() const
|
||||
{
|
||||
return is_read_only_;
|
||||
}
|
||||
|
||||
void AssetCatalogService::tag_all_catalogs_as_unsaved_changes()
|
||||
{
|
||||
for (auto &catalog : catalog_collection_->catalogs_.values()) {
|
||||
@@ -322,6 +334,11 @@ void AssetCatalogService::load_from_disk(const CatalogFilePath &file_or_director
|
||||
rebuild_tree();
|
||||
}
|
||||
|
||||
void AssetCatalogService::add_from_existing(const AssetCatalogService &other_service)
|
||||
{
|
||||
catalog_collection_->add_catalogs_from_existing(*other_service.catalog_collection_);
|
||||
}
|
||||
|
||||
void AssetCatalogService::load_directory_recursive(const CatalogFilePath &directory_path)
|
||||
{
|
||||
/* TODO(@sybren): implement proper multi-file support. For now, just load
|
||||
@@ -452,6 +469,8 @@ bool AssetCatalogService::is_catalog_known_with_unsaved_changes(const CatalogID
|
||||
|
||||
bool AssetCatalogService::write_to_disk(const CatalogFilePath &blend_file_path)
|
||||
{
|
||||
BLI_assert(!is_read_only_);
|
||||
|
||||
if (!write_to_disk_ex(blend_file_path)) {
|
||||
return false;
|
||||
}
|
||||
@@ -625,6 +644,7 @@ void AssetCatalogService::undo()
|
||||
|
||||
void AssetCatalogService::redo()
|
||||
{
|
||||
BLI_assert(!is_read_only_);
|
||||
BLI_assert_msg(is_redo_possbile(), "Redo stack is empty");
|
||||
|
||||
undo_snapshots_.append(std::move(catalog_collection_));
|
||||
@@ -634,6 +654,7 @@ void AssetCatalogService::redo()
|
||||
|
||||
void AssetCatalogService::undo_push()
|
||||
{
|
||||
BLI_assert(!is_read_only_);
|
||||
std::unique_ptr<AssetCatalogCollection> snapshot = catalog_collection_->deep_copy();
|
||||
undo_snapshots_.append(std::move(snapshot));
|
||||
redo_snapshots_.clear();
|
||||
@@ -657,15 +678,24 @@ std::unique_ptr<AssetCatalogCollection> AssetCatalogCollection::deep_copy() cons
|
||||
return copy;
|
||||
}
|
||||
|
||||
static void copy_catalog_map_into_existing(const OwningAssetCatalogMap &source,
|
||||
OwningAssetCatalogMap &dest)
|
||||
{
|
||||
for (const auto &orig_catalog_uptr : source.values()) {
|
||||
auto copy_catalog_uptr = std::make_unique<AssetCatalog>(*orig_catalog_uptr);
|
||||
dest.add_new(copy_catalog_uptr->catalog_id, std::move(copy_catalog_uptr));
|
||||
}
|
||||
}
|
||||
|
||||
void AssetCatalogCollection::add_catalogs_from_existing(const AssetCatalogCollection &other)
|
||||
{
|
||||
copy_catalog_map_into_existing(other.catalogs_, catalogs_);
|
||||
}
|
||||
|
||||
OwningAssetCatalogMap AssetCatalogCollection::copy_catalog_map(const OwningAssetCatalogMap &orig)
|
||||
{
|
||||
OwningAssetCatalogMap copy;
|
||||
|
||||
for (const auto &orig_catalog_uptr : orig.values()) {
|
||||
auto copy_catalog_uptr = std::make_unique<AssetCatalog>(*orig_catalog_uptr);
|
||||
copy.add_new(copy_catalog_uptr->catalog_id, std::move(copy_catalog_uptr));
|
||||
}
|
||||
|
||||
copy_catalog_map_into_existing(orig, copy);
|
||||
return copy;
|
||||
}
|
||||
|
||||
|
@@ -65,6 +65,12 @@ bool AS_asset_library_has_any_unsaved_catalogs()
|
||||
return service->has_any_unsaved_catalogs();
|
||||
}
|
||||
|
||||
std::string AS_asset_library_root_path_from_library_ref(
|
||||
const AssetLibraryReference &library_reference)
|
||||
{
|
||||
return AssetLibraryService::root_path_from_library_ref(library_reference);
|
||||
}
|
||||
|
||||
std::string AS_asset_library_find_suitable_root_path_from_path(
|
||||
const blender::StringRefNull input_path)
|
||||
{
|
||||
@@ -114,9 +120,11 @@ void AS_asset_library_refresh_catalog_simplename(struct ::AssetLibrary *asset_li
|
||||
void AS_asset_library_remap_ids(const IDRemapper *mappings)
|
||||
{
|
||||
AssetLibraryService *service = AssetLibraryService::get();
|
||||
service->foreach_loaded_asset_library([mappings](asset_system::AssetLibrary &library) {
|
||||
library.remap_ids_and_remove_invalid(*mappings);
|
||||
});
|
||||
service->foreach_loaded_asset_library(
|
||||
[mappings](asset_system::AssetLibrary &library) {
|
||||
library.remap_ids_and_remove_invalid(*mappings);
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
||||
namespace blender::asset_system {
|
||||
@@ -135,6 +143,13 @@ AssetLibrary::~AssetLibrary()
|
||||
}
|
||||
}
|
||||
|
||||
void AssetLibrary::foreach_loaded(FunctionRef<void(AssetLibrary &)> fn,
|
||||
const bool include_all_library)
|
||||
{
|
||||
AssetLibraryService *service = AssetLibraryService::get();
|
||||
service->foreach_loaded_asset_library(fn, include_all_library);
|
||||
}
|
||||
|
||||
void AssetLibrary::load_catalogs()
|
||||
{
|
||||
auto catalog_service = std::make_unique<AssetCatalogService>(root_path());
|
||||
@@ -144,7 +159,9 @@ void AssetLibrary::load_catalogs()
|
||||
|
||||
void AssetLibrary::refresh()
|
||||
{
|
||||
this->catalog_service->reload_catalogs();
|
||||
if (on_refresh_) {
|
||||
on_refresh_(*this);
|
||||
}
|
||||
}
|
||||
|
||||
AssetRepresentation &AssetLibrary::add_external_asset(StringRef relative_asset_path,
|
||||
|
@@ -14,6 +14,7 @@
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
||||
#include "AS_asset_catalog_tree.hh"
|
||||
#include "AS_asset_library.hh"
|
||||
#include "asset_library_service.hh"
|
||||
#include "utils.hh"
|
||||
@@ -56,23 +57,29 @@ void AssetLibraryService::destroy()
|
||||
AssetLibrary *AssetLibraryService::get_asset_library(
|
||||
const Main *bmain, const AssetLibraryReference &library_reference)
|
||||
{
|
||||
if (library_reference.type == 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) : "";
|
||||
const eAssetLibraryType type = eAssetLibraryType(library_reference.type);
|
||||
|
||||
if (root_path.empty()) {
|
||||
/* File wasn't saved yet. */
|
||||
return get_asset_library_current_file();
|
||||
switch (type) {
|
||||
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);
|
||||
|
||||
return get_asset_library_on_disk(root_path);
|
||||
}
|
||||
if (library_reference.type == ASSET_LIBRARY_CUSTOM) {
|
||||
bUserAssetLibrary *user_library = BKE_preferences_asset_library_find_from_index(
|
||||
&U, library_reference.custom_library_index);
|
||||
|
||||
if (user_library) {
|
||||
return get_asset_library_on_disk(user_library->path);
|
||||
if (!root_path.empty()) {
|
||||
return get_asset_library_on_disk(root_path);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,6 +107,8 @@ AssetLibrary *AssetLibraryService::get_asset_library_on_disk(StringRefNull root_
|
||||
|
||||
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());
|
||||
@@ -110,6 +119,7 @@ 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)");
|
||||
@@ -121,6 +131,74 @@ AssetLibrary *AssetLibraryService::get_asset_library_current_file()
|
||||
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>();
|
||||
@@ -164,21 +242,25 @@ void AssetLibraryService::app_handler_unregister()
|
||||
|
||||
bool AssetLibraryService::has_any_unsaved_catalogs() const
|
||||
{
|
||||
if (current_file_library_ && current_file_library_->catalog_service->has_unsaved_changes()) {
|
||||
return true;
|
||||
}
|
||||
bool has_unsaved_changes = false;
|
||||
|
||||
for (const auto &asset_lib_uptr : on_disk_libraries_.values()) {
|
||||
if (asset_lib_uptr->catalog_service->has_unsaved_changes()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return 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
|
||||
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_);
|
||||
}
|
||||
|
@@ -40,6 +40,8 @@ class AssetLibraryService {
|
||||
* the file was saved, a valid path for the library can be determined and #on_disk_libraries_
|
||||
* above should be used. */
|
||||
std::unique_ptr<AssetLibrary> current_file_library_;
|
||||
/** The "all" asset library, merging all other libraries into one. */
|
||||
std::unique_ptr<AssetLibrary> all_library_;
|
||||
|
||||
/* Handlers for managing the life cycle of the AssetLibraryService instance. */
|
||||
bCallbackFuncStore on_load_callback_store_;
|
||||
@@ -55,6 +57,8 @@ class AssetLibraryService {
|
||||
/** Destroy the AssetLibraryService singleton. It will be reallocated by #get() if necessary. */
|
||||
static void destroy();
|
||||
|
||||
static std::string root_path_from_library_ref(const AssetLibraryReference &library_reference);
|
||||
|
||||
AssetLibrary *get_asset_library(const Main *bmain,
|
||||
const AssetLibraryReference &library_reference);
|
||||
|
||||
@@ -66,10 +70,15 @@ class AssetLibraryService {
|
||||
/** Get the "Current File" asset library. */
|
||||
AssetLibrary *get_asset_library_current_file();
|
||||
|
||||
/** Get the "All" asset library, which loads all others and merges them into one. */
|
||||
AssetLibrary *get_asset_library_all(const Main *bmain);
|
||||
|
||||
/** Returns whether there are any known asset libraries with unsaved catalog edits. */
|
||||
bool has_any_unsaved_catalogs() const;
|
||||
|
||||
void foreach_loaded_asset_library(FunctionRef<void(AssetLibrary &)> fn) const;
|
||||
/** See AssetLibrary::foreach_loaded(). */
|
||||
void foreach_loaded_asset_library(FunctionRef<void(AssetLibrary &)> fn,
|
||||
bool include_all_library) const;
|
||||
|
||||
protected:
|
||||
/** Allocate a new instance of the service and assign it to `instance_`. */
|
||||
|
@@ -19,6 +19,8 @@ struct Main;
|
||||
|
||||
void ED_asset_catalogs_save_from_main_path(struct AssetLibrary *library, const struct Main *bmain);
|
||||
|
||||
/** Saving catalog edits when the file is saved is a global option shared for each asset library,
|
||||
* and as such ignores the per asset library #ED_asset_catalogs_read_only(). */
|
||||
void ED_asset_catalogs_set_save_catalogs_when_file_is_saved(bool should_save);
|
||||
bool ED_asset_catalogs_get_save_catalogs_when_file_is_saved(void);
|
||||
|
||||
|
@@ -6,6 +6,10 @@
|
||||
* UI/Editor level API for catalog operations, creating richer functionality than the asset system
|
||||
* catalog API provides (which this uses internally).
|
||||
*
|
||||
* Functions can be expected to not perform any change when #ED_asset_catalogs_read_only() returns
|
||||
* true. Generally UI code should disable such functionality in this case, so these functions are
|
||||
* not called at all.
|
||||
*
|
||||
* Note that `ED_asset_catalog.h` is part of this API.
|
||||
*/
|
||||
|
||||
@@ -19,6 +23,12 @@
|
||||
|
||||
struct AssetLibrary;
|
||||
|
||||
/**
|
||||
* Returns if the catalogs of \a library are allowed to be editable, or if the UI should forbid
|
||||
* edits.
|
||||
*/
|
||||
[[nodiscard]] bool ED_asset_catalogs_read_only(const AssetLibrary &library);
|
||||
|
||||
blender::asset_system::AssetCatalog *ED_asset_catalog_add(
|
||||
AssetLibrary *library, blender::StringRefNull name, blender::StringRef parent_path = nullptr);
|
||||
void ED_asset_catalog_remove(AssetLibrary *library,
|
||||
|
@@ -29,10 +29,13 @@ AssetLibraryReference ED_asset_library_reference_from_enum_value(int value);
|
||||
* Since this is meant for UI display, skips non-displayable libraries, that is, libraries with an
|
||||
* empty name or path.
|
||||
*
|
||||
* \param include_local_library: Whether to include the "Current File" library or not.
|
||||
* \param include_generated: Whether to include libraries that are generated and thus cannot be
|
||||
* written to. Setting this to false means only custom libraries will be
|
||||
* included, since they are stored on disk with a single root directory,
|
||||
* thus have a well defined location that can be written to.
|
||||
*/
|
||||
const struct EnumPropertyItem *ED_asset_library_reference_to_rna_enum_itemf(
|
||||
bool include_local_library);
|
||||
bool include_generated);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@@ -15,6 +15,21 @@ struct AssetLibraryReference;
|
||||
struct FileDirEntry;
|
||||
struct bContext;
|
||||
|
||||
namespace blender::asset_system {
|
||||
class AssetLibrary;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the asset library being read into an asset-list and identified using \a library_reference.
|
||||
*
|
||||
* \note The asset library may be allocated and loaded asynchronously, so it's not available right
|
||||
* after fetching, and this function will return null. The asset list code sends `NC_ASSET |
|
||||
* ND_ASSET_LIST_READING` notifiers until loading is done, they can be used to continuously
|
||||
* call this function to retrieve the asset library once available.
|
||||
*/
|
||||
blender::asset_system::AssetLibrary *ED_assetlist_library_get_once_available(
|
||||
const AssetLibraryReference &library_reference);
|
||||
|
||||
/* Can return false to stop iterating. */
|
||||
using AssetListIterFn = blender::FunctionRef<bool(AssetHandle)>;
|
||||
void ED_assetlist_iterate(const AssetLibraryReference &library_reference, AssetListIterFn fn);
|
||||
|
@@ -19,6 +19,13 @@
|
||||
using namespace blender;
|
||||
using namespace blender::asset_system;
|
||||
|
||||
bool ED_asset_catalogs_read_only(const ::AssetLibrary &library)
|
||||
{
|
||||
asset_system::AssetCatalogService *catalog_service = AS_asset_library_get_catalog_service(
|
||||
&library);
|
||||
return catalog_service->is_read_only();
|
||||
}
|
||||
|
||||
struct CatalogUniqueNameFnData {
|
||||
const AssetCatalogService &catalog_service;
|
||||
StringRef parent_path;
|
||||
@@ -53,6 +60,9 @@ asset_system::AssetCatalog *ED_asset_catalog_add(::AssetLibrary *library,
|
||||
if (!catalog_service) {
|
||||
return nullptr;
|
||||
}
|
||||
if (ED_asset_catalogs_read_only(*library)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string unique_name = catalog_name_ensure_unique(*catalog_service, name, parent_path);
|
||||
AssetCatalogPath fullpath = AssetCatalogPath(parent_path) / unique_name;
|
||||
@@ -76,6 +86,9 @@ void ED_asset_catalog_remove(::AssetLibrary *library, const CatalogID &catalog_i
|
||||
BLI_assert_unreachable();
|
||||
return;
|
||||
}
|
||||
if (ED_asset_catalogs_read_only(*library)) {
|
||||
return;
|
||||
}
|
||||
|
||||
catalog_service->undo_push();
|
||||
catalog_service->tag_has_unsaved_changes(nullptr);
|
||||
@@ -93,6 +106,9 @@ void ED_asset_catalog_rename(::AssetLibrary *library,
|
||||
BLI_assert_unreachable();
|
||||
return;
|
||||
}
|
||||
if (ED_asset_catalogs_read_only(*library)) {
|
||||
return;
|
||||
}
|
||||
|
||||
AssetCatalog *catalog = catalog_service->find_catalog(catalog_id);
|
||||
|
||||
@@ -120,6 +136,9 @@ void ED_asset_catalog_move(::AssetLibrary *library,
|
||||
BLI_assert_unreachable();
|
||||
return;
|
||||
}
|
||||
if (ED_asset_catalogs_read_only(*library)) {
|
||||
return;
|
||||
}
|
||||
|
||||
AssetCatalog *src_catalog = catalog_service->find_catalog(src_catalog_id);
|
||||
if (!src_catalog) {
|
||||
@@ -161,6 +180,9 @@ void ED_asset_catalogs_save_from_main_path(::AssetLibrary *library, const Main *
|
||||
BLI_assert_unreachable();
|
||||
return;
|
||||
}
|
||||
if (ED_asset_catalogs_read_only(*library)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Since writing to disk also means loading any on-disk changes, it may be a good idea to store
|
||||
* an undo step. */
|
||||
|
@@ -47,7 +47,7 @@ AssetLibraryReference ED_asset_library_reference_from_enum_value(int value)
|
||||
if (value < ASSET_LIBRARY_CUSTOM) {
|
||||
library.type = value;
|
||||
library.custom_library_index = -1;
|
||||
BLI_assert(ELEM(value, ASSET_LIBRARY_LOCAL));
|
||||
BLI_assert(ELEM(value, ASSET_LIBRARY_ALL, ASSET_LIBRARY_LOCAL));
|
||||
return library;
|
||||
}
|
||||
|
||||
@@ -70,16 +70,18 @@ AssetLibraryReference ED_asset_library_reference_from_enum_value(int value)
|
||||
return library;
|
||||
}
|
||||
|
||||
const EnumPropertyItem *ED_asset_library_reference_to_rna_enum_itemf(
|
||||
const bool include_local_library)
|
||||
const EnumPropertyItem *ED_asset_library_reference_to_rna_enum_itemf(const bool include_generated)
|
||||
{
|
||||
EnumPropertyItem *item = nullptr;
|
||||
int totitem = 0;
|
||||
|
||||
if (include_local_library) {
|
||||
const EnumPropertyItem predefined_items[] = {
|
||||
/* For the future. */
|
||||
// {ASSET_REPO_BUNDLED, "BUNDLED", 0, "Bundled", "Show the default user assets"},
|
||||
if (include_generated) {
|
||||
const EnumPropertyItem generated_items[] = {
|
||||
{ASSET_LIBRARY_ALL,
|
||||
"ALL",
|
||||
ICON_NONE,
|
||||
"All",
|
||||
"Show assets from all of the listed asset libraries"},
|
||||
{ASSET_LIBRARY_LOCAL,
|
||||
"LOCAL",
|
||||
ICON_CURRENT_FILE,
|
||||
@@ -88,8 +90,9 @@ const EnumPropertyItem *ED_asset_library_reference_to_rna_enum_itemf(
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
|
||||
/* Add predefined items. */
|
||||
RNA_enum_items_add(&item, &totitem, predefined_items);
|
||||
/* Add predefined libraries that are generated and not simple directories that can be written
|
||||
* to. */
|
||||
RNA_enum_items_add(&item, &totitem, generated_items);
|
||||
}
|
||||
|
||||
/* Add separator if needed. */
|
||||
|
@@ -12,6 +12,8 @@
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "AS_asset_library.hh"
|
||||
|
||||
#include "BKE_context.h"
|
||||
|
||||
#include "BLI_map.hh"
|
||||
@@ -112,6 +114,7 @@ class AssetList : NonCopyable {
|
||||
|
||||
bool needsRefetch() const;
|
||||
bool isLoaded() const;
|
||||
asset_system::AssetLibrary *asset_library() const;
|
||||
void iterate(AssetListIterFn fn) const;
|
||||
bool listen(const wmNotifier ¬ifier) const;
|
||||
int size() const;
|
||||
@@ -127,16 +130,7 @@ AssetList::AssetList(eFileSelectType filesel_type, const AssetLibraryReference &
|
||||
void AssetList::setup()
|
||||
{
|
||||
FileList *files = filelist_;
|
||||
|
||||
bUserAssetLibrary *user_library = nullptr;
|
||||
|
||||
/* Ensure valid repository, or fall-back to local one. */
|
||||
if (library_ref_.type == ASSET_LIBRARY_CUSTOM) {
|
||||
BLI_assert(library_ref_.custom_library_index >= 0);
|
||||
|
||||
user_library = BKE_preferences_asset_library_find_from_index(
|
||||
&U, library_ref_.custom_library_index);
|
||||
}
|
||||
std::string asset_lib_path = AS_asset_library_root_path_from_library_ref(library_ref_);
|
||||
|
||||
/* Relevant bits from file_refresh(). */
|
||||
/* TODO pass options properly. */
|
||||
@@ -158,13 +152,10 @@ void AssetList::setup()
|
||||
filelist_setindexer(files, use_asset_indexer ? &file_indexer_asset : &file_indexer_noop);
|
||||
|
||||
char path[FILE_MAXDIR] = "";
|
||||
if (user_library) {
|
||||
BLI_strncpy(path, user_library->path, sizeof(path));
|
||||
filelist_setdir(files, path);
|
||||
}
|
||||
else {
|
||||
filelist_setdir(files, path);
|
||||
if (!asset_lib_path.empty()) {
|
||||
BLI_strncpy(path, asset_lib_path.c_str(), sizeof(path));
|
||||
}
|
||||
filelist_setdir(files, path);
|
||||
}
|
||||
|
||||
void AssetList::fetch(const bContext &C)
|
||||
@@ -195,6 +186,11 @@ bool AssetList::isLoaded() const
|
||||
return filelist_is_ready(filelist_);
|
||||
}
|
||||
|
||||
asset_system::AssetLibrary *AssetList::asset_library() const
|
||||
{
|
||||
return reinterpret_cast<asset_system::AssetLibrary *>(filelist_asset_library(filelist_));
|
||||
}
|
||||
|
||||
void AssetList::iterate(AssetListIterFn fn) const
|
||||
{
|
||||
FileList *files = filelist_;
|
||||
@@ -373,7 +369,9 @@ void AssetListStorage::remapID(ID *id_new, ID *id_old)
|
||||
std::optional<eFileSelectType> AssetListStorage::asset_library_reference_to_fileselect_type(
|
||||
const AssetLibraryReference &library_reference)
|
||||
{
|
||||
switch (library_reference.type) {
|
||||
switch (eAssetLibraryType(library_reference.type)) {
|
||||
case ASSET_LIBRARY_ALL:
|
||||
return FILE_ASSET_LIBRARY_ALL;
|
||||
case ASSET_LIBRARY_CUSTOM:
|
||||
return FILE_ASSET_LIBRARY;
|
||||
case ASSET_LIBRARY_LOCAL:
|
||||
@@ -412,6 +410,7 @@ AssetListStorage::AssetListMap &AssetListStorage::global_storage()
|
||||
/** \name C-API
|
||||
* \{ */
|
||||
|
||||
using namespace blender;
|
||||
using namespace blender::ed::asset;
|
||||
|
||||
void ED_assetlist_storage_fetch(const AssetLibraryReference *library_reference, const bContext *C)
|
||||
@@ -462,6 +461,16 @@ void ED_assetlist_iterate(const AssetLibraryReference &library_reference, AssetL
|
||||
}
|
||||
}
|
||||
|
||||
asset_system::AssetLibrary *ED_assetlist_library_get_once_available(
|
||||
const AssetLibraryReference &library_reference)
|
||||
{
|
||||
const AssetList *list = AssetListStorage::lookup_list(library_reference);
|
||||
if (!list) {
|
||||
return nullptr;
|
||||
}
|
||||
return list->asset_library();
|
||||
}
|
||||
|
||||
ImBuf *ED_assetlist_asset_image_get(const AssetHandle *asset_handle)
|
||||
{
|
||||
ImBuf *imbuf = filelist_file_getimage(asset_handle->file_data);
|
||||
|
@@ -435,7 +435,18 @@ static void ASSET_OT_library_refresh(struct wmOperatorType *ot)
|
||||
static bool asset_catalog_operator_poll(bContext *C)
|
||||
{
|
||||
const SpaceFile *sfile = CTX_wm_space_file(C);
|
||||
return sfile && ED_fileselect_active_asset_library_get(sfile);
|
||||
if (!sfile) {
|
||||
return false;
|
||||
}
|
||||
const AssetLibrary *asset_library = ED_fileselect_active_asset_library_get(sfile);
|
||||
if (!asset_library) {
|
||||
return false;
|
||||
}
|
||||
if (ED_asset_catalogs_read_only(*asset_library)) {
|
||||
CTX_wm_operator_poll_msg_set(C, "Asset catalogs cannot be edited in this asset library");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static int asset_catalog_new_exec(bContext *C, wmOperator *op)
|
||||
|
@@ -119,6 +119,8 @@ class AssetCatalogDropController : public ui::AbstractViewItemDropController {
|
||||
|
||||
static AssetCatalog *get_drag_catalog(const wmDrag &drag, const ::AssetLibrary &asset_library);
|
||||
static bool has_droppable_asset(const wmDrag &drag, const char **r_disabled_hint);
|
||||
static bool can_modify_catalogs(const ::AssetLibrary &asset_library,
|
||||
const char **r_disabled_hint);
|
||||
static bool drop_assets_into_catalog(struct bContext *C,
|
||||
const AssetCatalogTreeView &tree_view,
|
||||
const wmDrag &drag,
|
||||
@@ -321,7 +323,9 @@ void AssetCatalogTreeViewItem::build_context_menu(bContext &C, uiLayout &column)
|
||||
|
||||
bool AssetCatalogTreeViewItem::supports_renaming() const
|
||||
{
|
||||
return true;
|
||||
const AssetCatalogTreeView &tree_view = static_cast<const AssetCatalogTreeView &>(
|
||||
get_tree_view());
|
||||
return !ED_asset_catalogs_read_only(*tree_view.asset_library_);
|
||||
}
|
||||
|
||||
bool AssetCatalogTreeViewItem::rename(StringRefNull new_name)
|
||||
@@ -360,7 +364,12 @@ AssetCatalogDropController::AssetCatalogDropController(AssetCatalogTreeView &tre
|
||||
bool AssetCatalogDropController::can_drop(const wmDrag &drag, const char **r_disabled_hint) const
|
||||
{
|
||||
if (drag.type == WM_DRAG_ASSET_CATALOG) {
|
||||
const AssetCatalog *drag_catalog = get_drag_catalog(drag, get_asset_library());
|
||||
const ::AssetLibrary &library = get_asset_library();
|
||||
if (!can_modify_catalogs(library, r_disabled_hint)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const AssetCatalog *drag_catalog = get_drag_catalog(drag, library);
|
||||
/* NOTE: Technically it's not an issue to allow this (the catalog will just receive a new
|
||||
* path and the catalog system will generate missing parents from the path). But it does
|
||||
* appear broken to users, so disabling entirely. */
|
||||
@@ -512,6 +521,16 @@ bool AssetCatalogDropController::has_droppable_asset(const wmDrag &drag,
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AssetCatalogDropController::can_modify_catalogs(const ::AssetLibrary &library,
|
||||
const char **r_disabled_hint)
|
||||
{
|
||||
if (ED_asset_catalogs_read_only(library)) {
|
||||
*r_disabled_hint = "Catalogs cannot be edited in this asset library";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
::AssetLibrary &AssetCatalogDropController::get_asset_library() const
|
||||
{
|
||||
return *get_view<AssetCatalogTreeView>().asset_library_;
|
||||
@@ -579,9 +598,12 @@ bool AssetCatalogTreeViewAllItem::DropController::can_drop(const wmDrag &drag,
|
||||
if (drag.type != WM_DRAG_ASSET_CATALOG) {
|
||||
return false;
|
||||
}
|
||||
::AssetLibrary &library = *get_view<AssetCatalogTreeView>().asset_library_;
|
||||
if (!AssetCatalogDropController::can_modify_catalogs(library, r_disabled_hint)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const AssetCatalog *drag_catalog = AssetCatalogDropController::get_drag_catalog(
|
||||
drag, *get_view<AssetCatalogTreeView>().asset_library_);
|
||||
const AssetCatalog *drag_catalog = AssetCatalogDropController::get_drag_catalog(drag, library);
|
||||
if (drag_catalog->path.parent() == "") {
|
||||
*r_disabled_hint = "Catalog is already placed at the highest level";
|
||||
return false;
|
||||
|
@@ -320,6 +320,10 @@ static void filelist_readjob_main_assets(FileListReadJob *job_params,
|
||||
bool *stop,
|
||||
bool *do_update,
|
||||
float *progress);
|
||||
static void filelist_readjob_all_asset_library(FileListReadJob *job_params,
|
||||
bool *stop,
|
||||
bool *do_update,
|
||||
float *progress);
|
||||
|
||||
/* helper, could probably go in BKE actually? */
|
||||
static int groupname_to_code(const char *group);
|
||||
@@ -856,6 +860,7 @@ static bool is_filtered_lib_type(FileListInternEntry *file,
|
||||
if (file->typeflag & FILE_TYPE_BLENDERLIB) {
|
||||
return is_filtered_id_file_type(file, file->blentype, file->name, filter);
|
||||
}
|
||||
|
||||
return is_filtered_file_type(file, filter);
|
||||
}
|
||||
|
||||
@@ -1362,11 +1367,10 @@ static bool filelist_checkdir_main(FileList *filelist, char *r_dir, const bool d
|
||||
return filelist_checkdir_lib(filelist, r_dir, do_change);
|
||||
}
|
||||
|
||||
static bool filelist_checkdir_main_assets(FileList * /*filelist*/,
|
||||
char * /*r_dir*/,
|
||||
const bool /*do_change*/)
|
||||
static bool filelist_checkdir_return_always_valid(struct FileList * /*filelist*/,
|
||||
char * /*r_dir*/,
|
||||
const bool /*do_change*/)
|
||||
{
|
||||
/* Main is always valid. */
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1768,12 +1772,19 @@ void filelist_settype(FileList *filelist, short type)
|
||||
filelist->tags |= FILELIST_TAGS_USES_MAIN_DATA;
|
||||
break;
|
||||
case FILE_MAIN_ASSET:
|
||||
filelist->check_dir_fn = filelist_checkdir_main_assets;
|
||||
filelist->check_dir_fn = filelist_checkdir_return_always_valid;
|
||||
filelist->read_job_fn = filelist_readjob_main_assets;
|
||||
filelist->prepare_filter_fn = prepare_filter_asset_library;
|
||||
filelist->filter_fn = is_filtered_main_assets;
|
||||
filelist->tags |= FILELIST_TAGS_USES_MAIN_DATA | FILELIST_TAGS_NO_THREADS;
|
||||
break;
|
||||
case FILE_ASSET_LIBRARY_ALL:
|
||||
filelist->check_dir_fn = filelist_checkdir_return_always_valid;
|
||||
filelist->read_job_fn = filelist_readjob_all_asset_library;
|
||||
filelist->prepare_filter_fn = prepare_filter_asset_library;
|
||||
filelist->filter_fn = is_filtered_asset_library;
|
||||
filelist->tags |= FILELIST_TAGS_USES_MAIN_DATA;
|
||||
break;
|
||||
default:
|
||||
filelist->check_dir_fn = filelist_checkdir_dir;
|
||||
filelist->read_job_fn = filelist_readjob_dir;
|
||||
@@ -2852,6 +2863,9 @@ void filelist_entry_parent_select_set(FileList *filelist,
|
||||
|
||||
bool filelist_islibrary(FileList *filelist, char *dir, char **r_group)
|
||||
{
|
||||
if (filelist->asset_library) {
|
||||
return true;
|
||||
}
|
||||
return BLO_library_path_explode(filelist->filelist.root, dir, r_group, nullptr);
|
||||
}
|
||||
|
||||
@@ -2893,6 +2907,11 @@ struct FileListReadJob {
|
||||
* `Materials/Material.001`). */
|
||||
char cur_relbase[FILE_MAX_LIBEXTRA];
|
||||
|
||||
/** The current asset library to load. Usually the same as #FileList.asset_library, however
|
||||
* sometimes the #FileList one is a combination of multiple other ones ("All" asset library),
|
||||
* which need to be loaded individually. Then this can be set to override the #FileList library.
|
||||
* Use this in all loading code. */
|
||||
asset_system::AssetLibrary *load_asset_library;
|
||||
/** Set to request a partial read that only adds files representing #Main data (IDs). Used when
|
||||
* #Main may have received changes of interest (e.g. asset removed or renamed). */
|
||||
bool only_main_data;
|
||||
@@ -3070,7 +3089,6 @@ static void filelist_readjob_list_lib_add_datablock(FileListReadJob *job_params,
|
||||
const int idcode,
|
||||
const char *group_name)
|
||||
{
|
||||
FileList *filelist = job_params->tmp_filelist; /* Use the thread-safe filelist queue. */
|
||||
FileListInternEntry *entry = MEM_cnew<FileListInternEntry>(__func__);
|
||||
if (prefix_relpath_with_group_name) {
|
||||
std::string datablock_path = StringRef(group_name) + "/" + datablock_info->name;
|
||||
@@ -3086,7 +3104,7 @@ static void filelist_readjob_list_lib_add_datablock(FileListReadJob *job_params,
|
||||
if (datablock_info->asset_data) {
|
||||
entry->typeflag |= FILE_TYPE_ASSET;
|
||||
|
||||
if (filelist->asset_library) {
|
||||
if (job_params->load_asset_library) {
|
||||
/* Take ownership over the asset data (shallow copies into unique_ptr managed memory) to
|
||||
* pass it on to the asset system. */
|
||||
std::unique_ptr metadata = std::make_unique<AssetMetaData>(*datablock_info->asset_data);
|
||||
@@ -3096,7 +3114,7 @@ static void filelist_readjob_list_lib_add_datablock(FileListReadJob *job_params,
|
||||
datablock_info->asset_data = metadata.get();
|
||||
datablock_info->free_asset_data = false;
|
||||
|
||||
entry->asset = &filelist->asset_library->add_external_asset(
|
||||
entry->asset = &job_params->load_asset_library->add_external_asset(
|
||||
entry->relpath, datablock_info->name, std::move(metadata));
|
||||
}
|
||||
}
|
||||
@@ -3600,7 +3618,7 @@ static void filelist_readjob_recursive_dir_add_items(const bool do_lib,
|
||||
}
|
||||
/* Only load assets when browsing an asset library. For normal file browsing we return all
|
||||
* entries. `FLF_ASSETS_ONLY` filter can be enabled/disabled by the user. */
|
||||
if (filelist->asset_library) {
|
||||
if (job_params->load_asset_library) {
|
||||
list_lib_options |= LIST_LIB_ASSETS_ONLY;
|
||||
}
|
||||
std::optional<int> lib_entries_num = filelist_readjob_list_lib(
|
||||
@@ -3715,6 +3733,8 @@ static void filelist_readjob_load_asset_library_data(FileListReadJob *job_params
|
||||
* #filelist_readjob_endjob() will move it into the real filelist. */
|
||||
tmp_filelist->asset_library = AS_asset_library_load(job_params->current_main,
|
||||
*job_params->filelist->asset_library_ref);
|
||||
/* Set asset library to load (may be overridden later for loading nested ones). */
|
||||
job_params->load_asset_library = tmp_filelist->asset_library;
|
||||
*do_update = true;
|
||||
}
|
||||
|
||||
@@ -3752,8 +3772,8 @@ static void filelist_readjob_main_assets_add_items(FileListReadJob *job_params,
|
||||
entry->local_data.preview_image = BKE_asset_metadata_preview_get_from_id(id_iter->asset_data,
|
||||
id_iter);
|
||||
entry->local_data.id = id_iter;
|
||||
if (filelist->asset_library) {
|
||||
entry->asset = &filelist->asset_library->add_local_id_asset(entry->relpath, *id_iter);
|
||||
if (job_params->load_asset_library) {
|
||||
entry->asset = &job_params->load_asset_library->add_local_id_asset(entry->relpath, *id_iter);
|
||||
}
|
||||
entries_num++;
|
||||
BLI_addtail(&tmp_entries, entry);
|
||||
@@ -3780,6 +3800,10 @@ static void filelist_readjob_main_assets_add_items(FileListReadJob *job_params,
|
||||
*/
|
||||
static bool filelist_contains_main(const FileList *filelist, const Main *bmain)
|
||||
{
|
||||
if (filelist->asset_library_ref && (filelist->asset_library_ref->type == ASSET_LIBRARY_ALL)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const char *blendfile_path = BKE_main_blendfile_path(bmain);
|
||||
return blendfile_path[0] && BLI_path_contains(filelist->filelist.root, blendfile_path);
|
||||
}
|
||||
@@ -3797,6 +3821,8 @@ static void filelist_readjob_asset_library(FileListReadJob *job_params,
|
||||
/* A valid, but empty file-list from now. */
|
||||
filelist->filelist.entries_num = 0;
|
||||
|
||||
BLI_assert(job_params->filelist->asset_library_ref != nullptr);
|
||||
|
||||
/* NOP if already read. */
|
||||
filelist_readjob_load_asset_library_data(job_params, do_update);
|
||||
|
||||
@@ -3834,6 +3860,57 @@ static void filelist_readjob_main_assets(FileListReadJob *job_params,
|
||||
filelist_readjob_main_assets_add_items(job_params, stop, do_update, progress);
|
||||
}
|
||||
|
||||
static void filelist_readjob_all_asset_library(FileListReadJob *job_params,
|
||||
bool *stop,
|
||||
bool *do_update,
|
||||
float *progress)
|
||||
{
|
||||
FileList *filelist = job_params->tmp_filelist; /* Use the thread-safe filelist queue. */
|
||||
BLI_assert(BLI_listbase_is_empty(&filelist->filelist.entries) &&
|
||||
(filelist->filelist.entries_num == FILEDIR_NBR_ENTRIES_UNSET));
|
||||
|
||||
filelist_readjob_load_asset_library_data(job_params, do_update);
|
||||
|
||||
/* A valid, but empty file-list from now. */
|
||||
filelist->filelist.entries_num = 0;
|
||||
|
||||
filelist_readjob_main_assets_add_items(job_params, stop, do_update, progress);
|
||||
|
||||
/* When only doing partialy reload for main data, we're done. */
|
||||
if (job_params->only_main_data) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Count how many asset libraries need to be loaded, for progress reporting. Not very precise. */
|
||||
int library_count = 0;
|
||||
asset_system::AssetLibrary::foreach_loaded([&library_count](auto &) { library_count++; }, false);
|
||||
|
||||
BLI_assert(filelist->asset_library != nullptr);
|
||||
|
||||
int libraries_done_count = 0;
|
||||
/* The "All" asset library was loaded, which means all other asset libraries are also loaded.
|
||||
* Load their assets from disk into the "All" library. */
|
||||
asset_system::AssetLibrary::foreach_loaded(
|
||||
[&](asset_system::AssetLibrary &nested_library) {
|
||||
StringRefNull root_path = nested_library.root_path();
|
||||
if (root_path.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Override library info to read this library. */
|
||||
job_params->load_asset_library = &nested_library;
|
||||
BLI_strncpy(filelist->filelist.root, root_path.c_str(), sizeof(filelist->filelist.root));
|
||||
|
||||
float progress_this = 0.0f;
|
||||
filelist_readjob_recursive_dir_add_items(
|
||||
true, job_params, stop, do_update, &progress_this);
|
||||
|
||||
libraries_done_count++;
|
||||
*progress = float(libraries_done_count) / library_count;
|
||||
},
|
||||
false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the read-job is requesting a partial reread of the file list only.
|
||||
*/
|
||||
|
@@ -423,16 +423,20 @@ static void fileselect_refresh_asset_params(FileAssetSelectParams *asset_params)
|
||||
}
|
||||
|
||||
switch (library->type) {
|
||||
case ASSET_LIBRARY_ALL:
|
||||
base_params->dir[0] = '\0';
|
||||
base_params->type = FILE_ASSET_LIBRARY_ALL;
|
||||
break;
|
||||
case ASSET_LIBRARY_LOCAL:
|
||||
base_params->dir[0] = '\0';
|
||||
base_params->type = FILE_MAIN_ASSET;
|
||||
break;
|
||||
case ASSET_LIBRARY_CUSTOM:
|
||||
BLI_assert(user_library);
|
||||
BLI_strncpy(base_params->dir, user_library->path, sizeof(base_params->dir));
|
||||
base_params->type = FILE_ASSET_LIBRARY;
|
||||
break;
|
||||
}
|
||||
base_params->type = (library->type == ASSET_LIBRARY_LOCAL) ? FILE_MAIN_ASSET :
|
||||
FILE_ASSET_LIBRARY;
|
||||
}
|
||||
|
||||
void fileselect_refresh_params(SpaceFile *sfile)
|
||||
|
@@ -49,13 +49,6 @@ struct LibraryAsset {
|
||||
AssetHandle handle;
|
||||
};
|
||||
|
||||
struct LibraryCatalog {
|
||||
asset_system::AssetLibrary *library;
|
||||
/* Catalog pointers are not save to store. Use the catalog ID instead and lookup the catalog when
|
||||
* needed. */
|
||||
const asset_system::CatalogID catalog_id;
|
||||
};
|
||||
|
||||
struct AssetItemTree {
|
||||
asset_system::AssetCatalogTree catalogs;
|
||||
MultiValueMap<asset_system::AssetCatalogPath, LibraryAsset> assets_per_path;
|
||||
@@ -63,14 +56,18 @@ struct AssetItemTree {
|
||||
full_catalog_per_tree_item;
|
||||
};
|
||||
|
||||
static AssetLibraryReference all_library_reference()
|
||||
{
|
||||
AssetLibraryReference all_library_ref{};
|
||||
all_library_ref.custom_library_index = -1;
|
||||
all_library_ref.type = ASSET_LIBRARY_ALL;
|
||||
return all_library_ref;
|
||||
}
|
||||
|
||||
static bool all_loading_finished()
|
||||
{
|
||||
for (const AssetLibraryReference &library : asset_system::all_valid_asset_library_refs()) {
|
||||
if (!ED_assetlist_is_loaded(&library)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
AssetLibraryReference all_library_ref = all_library_reference();
|
||||
return ED_assetlist_is_loaded(&all_library_ref);
|
||||
}
|
||||
|
||||
static AssetItemTree build_catalog_tree(const bContext &C, const bNodeTree *node_tree)
|
||||
@@ -78,68 +75,52 @@ static AssetItemTree build_catalog_tree(const bContext &C, const bNodeTree *node
|
||||
if (!node_tree) {
|
||||
return {};
|
||||
}
|
||||
const Main &bmain = *CTX_data_main(&C);
|
||||
const Vector<AssetLibraryReference> all_libraries = asset_system::all_valid_asset_library_refs();
|
||||
|
||||
/* Merge catalogs from all libraries to deduplicate menu items. Also store the catalog and
|
||||
* library for each asset ID in order to use them later when retrieving assets and removing
|
||||
* empty catalogs. */
|
||||
Map<asset_system::CatalogID, LibraryCatalog> id_to_catalog_map;
|
||||
asset_system::AssetCatalogTree catalogs_from_all_libraries;
|
||||
for (const AssetLibraryReference &library_ref : all_libraries) {
|
||||
if (asset_system::AssetLibrary *library = AS_asset_library_load(&bmain, library_ref)) {
|
||||
if (asset_system::AssetCatalogTree *tree = library->catalog_service->get_catalog_tree()) {
|
||||
tree->foreach_item([&](asset_system::AssetCatalogTreeItem &item) {
|
||||
const asset_system::CatalogID &id = item.get_catalog_id();
|
||||
asset_system::AssetCatalog *catalog = library->catalog_service->find_catalog(id);
|
||||
catalogs_from_all_libraries.insert_item(*catalog);
|
||||
id_to_catalog_map.add(item.get_catalog_id(), LibraryCatalog{library, id});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Find all the matching node group assets for every catalog path. */
|
||||
MultiValueMap<asset_system::AssetCatalogPath, LibraryAsset> assets_per_path;
|
||||
for (const AssetLibraryReference &library_ref : all_libraries) {
|
||||
AssetFilterSettings type_filter{};
|
||||
type_filter.id_types = FILTER_ID_NT;
|
||||
|
||||
ED_assetlist_storage_fetch(&library_ref, &C);
|
||||
ED_assetlist_ensure_previews_job(&library_ref, &C);
|
||||
ED_assetlist_iterate(library_ref, [&](AssetHandle asset) {
|
||||
if (!ED_asset_filter_matches_asset(&type_filter, &asset)) {
|
||||
return true;
|
||||
}
|
||||
const AssetMetaData &meta_data = *ED_asset_handle_get_metadata(&asset);
|
||||
const IDProperty *tree_type = BKE_asset_metadata_idprop_find(&meta_data, "type");
|
||||
if (tree_type == nullptr || IDP_Int(tree_type) != node_tree->type) {
|
||||
return true;
|
||||
}
|
||||
if (BLI_uuid_is_nil(meta_data.catalog_id)) {
|
||||
return true;
|
||||
}
|
||||
const LibraryCatalog *library_catalog = id_to_catalog_map.lookup_ptr(meta_data.catalog_id);
|
||||
if (library_catalog == nullptr) {
|
||||
return true;
|
||||
}
|
||||
const asset_system::AssetCatalog *catalog =
|
||||
library_catalog->library->catalog_service->find_catalog(library_catalog->catalog_id);
|
||||
assets_per_path.add(catalog->path, LibraryAsset{library_ref, asset});
|
||||
return true;
|
||||
});
|
||||
AssetFilterSettings type_filter{};
|
||||
type_filter.id_types = FILTER_ID_NT;
|
||||
|
||||
const AssetLibraryReference all_library_ref = all_library_reference();
|
||||
|
||||
ED_assetlist_storage_fetch(&all_library_ref, &C);
|
||||
ED_assetlist_ensure_previews_job(&all_library_ref, &C);
|
||||
|
||||
asset_system::AssetLibrary *all_library = ED_assetlist_library_get_once_available(all_library_ref);
|
||||
if (!all_library) {
|
||||
return {};
|
||||
}
|
||||
|
||||
/* Build the final tree without any of the catalogs that don't have proper node group assets. */
|
||||
asset_system::AssetCatalogTree catalogs_with_node_assets;
|
||||
catalogs_from_all_libraries.foreach_item([&](asset_system::AssetCatalogTreeItem &item) {
|
||||
if (!assets_per_path.lookup(item.catalog_path()).is_empty()) {
|
||||
const asset_system::CatalogID &id = item.get_catalog_id();
|
||||
const LibraryCatalog &library_catalog = id_to_catalog_map.lookup(id);
|
||||
asset_system::AssetCatalog *catalog = library_catalog.library->catalog_service->find_catalog(
|
||||
id);
|
||||
catalogs_with_node_assets.insert_item(*catalog);
|
||||
ED_assetlist_iterate(all_library_ref, [&](AssetHandle asset) {
|
||||
if (!ED_asset_filter_matches_asset(&type_filter, &asset)) {
|
||||
return true;
|
||||
}
|
||||
const AssetMetaData &meta_data = *ED_asset_handle_get_metadata(&asset);
|
||||
const IDProperty *tree_type = BKE_asset_metadata_idprop_find(&meta_data, "type");
|
||||
if (tree_type == nullptr || IDP_Int(tree_type) != node_tree->type) {
|
||||
return true;
|
||||
}
|
||||
if (BLI_uuid_is_nil(meta_data.catalog_id)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const asset_system::AssetCatalog *catalog = all_library->catalog_service->find_catalog(
|
||||
meta_data.catalog_id);
|
||||
assets_per_path.add(catalog->path, LibraryAsset{all_library_ref, asset});
|
||||
return true;
|
||||
});
|
||||
|
||||
/* Build an own tree without any of the catalogs that don't have proper node group assets. */
|
||||
asset_system::AssetCatalogTree catalogs_with_node_assets;
|
||||
asset_system::AssetCatalogTree &catalog_tree = *all_library->catalog_service->get_catalog_tree();
|
||||
catalog_tree.foreach_item([&](asset_system::AssetCatalogTreeItem &item) {
|
||||
if (assets_per_path.lookup(item.catalog_path()).is_empty()) {
|
||||
return;
|
||||
}
|
||||
asset_system::AssetCatalog *catalog = all_library->catalog_service->find_catalog(
|
||||
item.get_catalog_id());
|
||||
catalogs_with_node_assets.insert_item(*catalog);
|
||||
});
|
||||
|
||||
/* Build another map storing full asset paths for each tree item, in order to have stable
|
||||
|
@@ -93,12 +93,15 @@ static void search_items_for_asset_metadata(const bNodeTree &node_tree,
|
||||
search_items.append(std::move(item));
|
||||
}
|
||||
|
||||
static void gather_search_items_for_asset_library(const bContext &C,
|
||||
const bNodeTree &node_tree,
|
||||
const AssetLibraryReference &library_ref,
|
||||
Set<std::string> &r_added_assets,
|
||||
Vector<AddNodeItem> &search_items)
|
||||
static void gather_search_items_for_all_assets(const bContext &C,
|
||||
const bNodeTree &node_tree,
|
||||
Set<std::string> &r_added_assets,
|
||||
Vector<AddNodeItem> &search_items)
|
||||
{
|
||||
AssetLibraryReference library_ref{};
|
||||
library_ref.custom_library_index = -1;
|
||||
library_ref.type = ASSET_LIBRARY_ALL;
|
||||
|
||||
AssetFilterSettings filter_settings{};
|
||||
filter_settings.id_types = FILTER_ID_NT;
|
||||
|
||||
@@ -117,26 +120,6 @@ static void gather_search_items_for_asset_library(const bContext &C,
|
||||
});
|
||||
}
|
||||
|
||||
static void gather_search_items_for_all_assets(const bContext &C,
|
||||
const bNodeTree &node_tree,
|
||||
Set<std::string> &r_added_assets,
|
||||
Vector<AddNodeItem> &search_items)
|
||||
{
|
||||
int i;
|
||||
LISTBASE_FOREACH_INDEX (const bUserAssetLibrary *, asset_library, &U.asset_libraries, i) {
|
||||
AssetLibraryReference library_ref{};
|
||||
library_ref.custom_library_index = i;
|
||||
library_ref.type = ASSET_LIBRARY_CUSTOM;
|
||||
/* Skip local assets to avoid duplicates when the asset is part of the local file library. */
|
||||
gather_search_items_for_asset_library(C, node_tree, library_ref, r_added_assets, search_items);
|
||||
}
|
||||
|
||||
AssetLibraryReference library_ref{};
|
||||
library_ref.custom_library_index = -1;
|
||||
library_ref.type = ASSET_LIBRARY_LOCAL;
|
||||
gather_search_items_for_asset_library(C, node_tree, library_ref, r_added_assets, search_items);
|
||||
}
|
||||
|
||||
static void gather_search_items_for_node_groups(const bContext &C,
|
||||
const bNodeTree &node_tree,
|
||||
const Set<std::string> &local_assets,
|
||||
|
@@ -21,7 +21,7 @@
|
||||
#define _DNA_DEFAULT_AssetLibraryReference \
|
||||
{ \
|
||||
.type = ASSET_LIBRARY_LOCAL, \
|
||||
/* Not needed really (should be ignored for #ASSET_LIBRARY_LOCAL), but helps debugging. */ \
|
||||
/* Not needed really (should be ignored for anything but #ASSET_LIBRARY_CUSTOM), but helps debugging. */ \
|
||||
.custom_library_index = -1, \
|
||||
}
|
||||
|
||||
|
@@ -89,8 +89,7 @@ typedef enum eAssetLibraryType {
|
||||
// ASSET_LIBRARY_BUNDLED = 0,
|
||||
/** Display assets from the current session (current "Main"). */
|
||||
ASSET_LIBRARY_LOCAL = 1,
|
||||
/* For the future. Display assets for the current project. */
|
||||
// ASSET_LIBRARY_PROJECT = 2,
|
||||
ASSET_LIBRARY_ALL = 2,
|
||||
|
||||
/** Display assets from custom asset libraries, as defined in the preferences
|
||||
* (#bUserAssetLibrary). The name will be taken from #FileSelectParams.asset_library_ref.idname
|
||||
|
@@ -1001,6 +1001,8 @@ typedef enum eFileSelectType {
|
||||
FILE_MAIN_ASSET = 3,
|
||||
/** Load assets of an asset library containing external files. */
|
||||
FILE_ASSET_LIBRARY = 4,
|
||||
/** Load all asset libraries. */
|
||||
FILE_ASSET_LIBRARY_ALL = 5,
|
||||
|
||||
FILE_UNIX = 8,
|
||||
FILE_BLENDER = 8, /* don't display relative paths */
|
||||
|
Reference in New Issue
Block a user