Blender V5.0
asset_library.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <memory>
10
12#include "AS_asset_library.hh"
14
15#include "BKE_lib_remap.hh"
16#include "BKE_main.hh"
17#include "BKE_preferences.h"
18
19#include "BLI_fileops.h"
20#include "BLI_listbase.h"
21#include "BLI_path_utils.hh"
22#include "BLI_string.h"
23
24#include "DNA_space_types.h"
25#include "DNA_userdef_types.h"
27
31#include "essentials_library.hh"
32#include "runtime_library.hh"
33#include "utils.hh"
34
35using namespace blender;
36using namespace blender::asset_system;
37
39
41{
42 /* NOTE: Can probably removed once #WITH_DESTROY_VIA_LOAD_HANDLER gets enabled by default. */
43
45}
46
48 const AssetLibraryReference &library_reference)
49{
51 return service->get_asset_library(bmain, library_reference);
52}
53
54AssetLibrary *AS_asset_library_load_from_directory(const char *name, const char *library_dirpath)
55{
56 /* NOTE: Loading an asset library at this point only means loading the catalogs.
57 * Later on this should invoke reading of asset representations too. */
58
61 if (library_dirpath == nullptr || library_dirpath[0] == '\0') {
63 }
64 else {
65 lib = service->get_asset_library_on_disk_custom(name, library_dirpath);
66 }
67 return lib;
68}
69
75
77 const AssetLibraryReference &library_reference)
78{
80}
81
83 const blender::StringRefNull input_path)
84{
86 &U, input_path.c_str()))
87 {
88 return preferences_lib->dirpath;
89 }
90
91 char buffer[FILE_MAXDIR];
92 BLI_path_split_dir_part(input_path.c_str(), buffer, FILE_MAXDIR);
93 return buffer;
94}
95
100
102{
105 [mappings](AssetLibrary &library) { library.remap_ids_and_remove_invalid(mappings); }, true);
106}
107
109 char r_path_buffer[/*FILE_MAX_LIBEXTRA*/ 1282],
110 char **r_dir,
111 char **r_group,
112 char **r_name)
113{
115 std::optional<AssetLibraryService::ExplodedPath> exploded =
116 service->resolve_asset_weak_reference_to_exploded_path(*asset_reference);
117
118 if (!exploded) {
119 if (r_dir) {
120 *r_dir = nullptr;
121 }
122 if (r_group) {
123 *r_group = nullptr;
124 }
125 if (r_name) {
126 *r_name = nullptr;
127 }
128 r_path_buffer[0] = '\0';
129 return;
130 }
131
132 BLI_assert(!exploded->group_component.is_empty());
133 BLI_assert(!exploded->name_component.is_empty());
134
135 BLI_strncpy(r_path_buffer, exploded->full_path->c_str(), /*FILE_MAX_LIBEXTRA*/ 1282);
136
137 if (!exploded->dir_component.is_empty()) {
138 r_path_buffer[exploded->dir_component.size()] = '\0';
139 r_path_buffer[exploded->dir_component.size() + 1 + exploded->group_component.size()] = '\0';
140
141 if (r_dir) {
142 *r_dir = r_path_buffer;
143 }
144 if (r_group) {
145 *r_group = r_path_buffer + exploded->dir_component.size() + 1;
146 }
147 if (r_name) {
148 *r_name = r_path_buffer + exploded->dir_component.size() + 1 +
149 exploded->group_component.size() + 1;
150 }
151 }
152 else {
153 r_path_buffer[exploded->group_component.size()] = '\0';
154
155 if (r_dir) {
156 *r_dir = nullptr;
157 }
158 if (r_group) {
159 *r_group = r_path_buffer;
160 }
161 if (r_name) {
162 *r_name = r_path_buffer + exploded->group_component.size() + 1;
163 }
164 }
165}
166
168{
169 LISTBASE_FOREACH (bUserAssetLibrary *, library, &U.asset_libraries) {
170 if (U.experimental.no_data_block_packing) {
171 if (library->import_method == ASSET_IMPORT_PACK) {
172 library->import_method = ASSET_IMPORT_APPEND_REUSE;
173 }
174 }
175 else {
176 if (library->import_method == ASSET_IMPORT_APPEND_REUSE) {
177 library->import_method = ASSET_IMPORT_PACK;
178 }
179 }
180 }
181}
182
184{
185 LISTBASE_FOREACH (bScreen *, screen, &bmain.screens) {
186 LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
187 LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
188 if (sl->spacetype != SPACE_FILE) {
189 continue;
190 }
191 SpaceFile *sfile = reinterpret_cast<SpaceFile *>(sl);
192 if (!sfile->asset_params) {
193 continue;
194 }
195 if (U.experimental.no_data_block_packing) {
198 }
199 }
200 else {
203 }
204 }
205 }
206 }
207 }
208}
209
215
217{
218 AssetLibraryReference library_ref{};
219 library_ref.custom_library_index = -1;
220 library_ref.type = ASSET_LIBRARY_ESSENTIALS;
221 EssentialsAssetLibrary *library = dynamic_cast<EssentialsAssetLibrary *>(
222 AS_asset_library_load(nullptr, library_ref));
223 if (library) {
225 }
226}
227
228namespace blender::asset_system {
229
231 : library_type_(library_type),
232 name_(name),
233 root_path_(std::make_shared<std::string>(utils::normalize_directory_path(root_path))),
234 catalog_service_(std::make_unique<AssetCatalogService>(*root_path_))
235{
236}
237
244
246 const bool include_all_library)
247{
249 service->foreach_loaded_asset_library(fn, include_all_library);
250}
251
253{
254 {
255 std::lock_guard lock{catalog_service_mutex_};
256 /* Should never actually be the case, catalog service gets allocated with the asset library. */
257 if (catalog_service_ == nullptr) {
258 auto catalog_service = std::make_unique<AssetCatalogService>(root_path());
259 catalog_service->load_from_disk();
261 return;
262 }
263 }
264
265 /* The catalog service was created before without being associated with a definition file. */
266 if (catalog_service_->get_catalog_definition_file() == nullptr) {
267 catalog_service_->load_from_disk();
268 }
269 else {
270 this->refresh_catalogs();
271 }
272}
273
278
280
281std::weak_ptr<AssetRepresentation> AssetLibrary::add_external_asset(
282 StringRef relative_asset_path,
284 const int id_type,
285 std::unique_ptr<AssetMetaData> metadata)
286{
287 return asset_storage_.external_assets.lookup_key_or_add(std::make_shared<AssetRepresentation>(
288 relative_asset_path, name, id_type, std::move(metadata), *this));
289}
290
291std::weak_ptr<AssetRepresentation> AssetLibrary::add_local_id_asset(ID &id)
292{
293 return asset_storage_.local_id_assets.lookup_key_or_add(
294 std::make_shared<AssetRepresentation>(id, *this));
295}
296
298{
299 /* Make sure this is forwarded to the library actually owning the asset if needed. For example
300 * the "All Libraries" library doesn't own the assets itself. */
301 if (&asset.owner_asset_library_ != this) {
302 return asset.owner_asset_library_.remove_asset(asset);
303 }
304
305 BLI_assert(asset_storage_.local_id_assets.contains_as(&asset) ||
306 asset_storage_.external_assets.contains_as(&asset));
307
308 if (asset_storage_.local_id_assets.remove_as(&asset)) {
309 return true;
310 }
311 return asset_storage_.external_assets.remove_as(&asset);
312}
313
315{
316 Set<AssetRepresentation *> removed_assets;
317
318 for (const auto &asset_ptr : asset_storage_.local_id_assets) {
319 AssetRepresentation &asset = *asset_ptr;
320 BLI_assert(asset.is_local_id());
321
322 const IDRemapperApplyResult result = mappings.apply(&std::get<ID *>(asset.asset_),
324
325 /* Entirely remove assets whose ID is unset. We don't want assets with a null ID pointer. */
327 removed_assets.add(&asset);
328 }
329 }
330
331 for (AssetRepresentation *asset : removed_assets) {
332 this->remove_asset(*asset);
333 }
334}
335
336namespace {
337void asset_library_on_save_post(Main *bmain,
338 PointerRNA **pointers,
339 const int num_pointers,
340 void *arg)
341{
342 AssetLibrary *asset_lib = static_cast<AssetLibrary *>(arg);
343
344 /* Transform 'runtime' current file library into 'on-disk' current file library. */
345 if (asset_lib->library_type() == ASSET_LIBRARY_LOCAL && asset_lib->root_path().is_empty()) {
346 BLI_assert(dynamic_cast<RuntimeAssetLibrary *>(asset_lib) != nullptr);
347
348 if (AssetLibrary *on_disk_lib =
350 {
351 /* Allow undoing to the state before merging in catalogs from disk. */
352 on_disk_lib->catalog_service().undo_push();
353
354 /* Force refresh to merge on-disk catalogs with the ones stolen from the runtime library. */
356 ASSET_LIBRARY_LOCAL, on_disk_lib->root_path());
357 BLI_assert(asset_lib == on_disk_lib);
358 }
359 }
360
361 asset_lib->on_blend_save_post(bmain, pointers, num_pointers);
362}
363
364} // namespace
365
367{
368 /* The callback system doesn't own `on_save_callback_store_`. */
369 on_save_callback_store_.alloc = false;
370
371 on_save_callback_store_.func = asset_library_on_save_post;
372 on_save_callback_store_.arg = this;
373
375}
376
383
385 PointerRNA ** /*pointers*/,
386 const int /*num_pointers*/)
387{
389 this->catalog_service().write_to_disk(bmain->filepath);
390 }
391}
392
394 const AssetWeakReference &asset_reference)
395{
397 return service->resolve_asset_weak_reference_to_full_path(asset_reference);
398}
399
401{
402 if (BLI_uuid_is_nil(asset_data->catalog_id)) {
403 asset_data->catalog_simple_name[0] = '\0';
404 return;
405 }
406 const AssetCatalog *catalog = this->catalog_service().find_catalog(asset_data->catalog_id);
407 if (catalog == nullptr) {
408 /* No-op if the catalog cannot be found. This could be the kind of "the catalog definition file
409 * is corrupt/lost" scenario that the simple name is meant to help recover from. */
410 return;
411 }
412 STRNCPY(asset_data->catalog_simple_name, catalog->simple_name.c_str());
413}
414
416{
417 return library_type_;
418}
419
421{
422 return name_;
423}
424
426{
427 return *root_path_;
428}
429
431{
433 {
434 AssetLibraryReference library_ref{};
435 library_ref.custom_library_index = -1;
436 library_ref.type = ASSET_LIBRARY_ESSENTIALS;
437 result.append(library_ref);
438 }
439 int i;
440 LISTBASE_FOREACH_INDEX (const bUserAssetLibrary *, asset_library, &U.asset_libraries, i) {
441 if (!BLI_is_dir(asset_library->dirpath)) {
442 continue;
443 }
444 AssetLibraryReference library_ref{};
445 library_ref.custom_library_index = i;
446 library_ref.type = ASSET_LIBRARY_CUSTOM;
447 result.append(library_ref);
448 }
449
450 AssetLibraryReference library_ref{};
451 library_ref.custom_library_index = -1;
452 library_ref.type = ASSET_LIBRARY_LOCAL;
453 result.append(library_ref);
454 return result;
455}
456
458{
459 AssetLibraryReference all_library_ref{};
460 all_library_ref.custom_library_index = -1;
461 all_library_ref.type = ASSET_LIBRARY_ALL;
462 return all_library_ref;
463}
464
466{
467 AssetLibraryReference library_ref{};
468 library_ref.custom_library_index = -1;
469 library_ref.type = ASSET_LIBRARY_LOCAL;
470 return library_ref;
471}
472
478
479} // namespace blender::asset_system
Main runtime representation of an asset.
void BKE_callback_add(bCallbackFuncStore *funcstore, eCbEvent evt)
Definition callbacks.cc:74
void BKE_callback_remove(bCallbackFuncStore *funcstore, eCbEvent evt)
Definition callbacks.cc:81
@ BKE_CB_EVT_SAVE_POST
IDRemapperApplyResult
@ ID_REMAP_RESULT_SOURCE_UNASSIGNED
@ ID_REMAP_APPLY_DEFAULT
struct bUserAssetLibrary * BKE_preferences_asset_library_containing_path(const struct UserDef *userdef, const char *path) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT
#define BLI_assert(a)
Definition BLI_assert.h:46
File and directory operations.
bool BLI_is_dir(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:443
#define LISTBASE_FOREACH(type, var, list)
#define LISTBASE_FOREACH_INDEX(type, var, list, index_var)
void void BLI_path_split_dir_part(const char *filepath, char *dir, size_t dir_maxncpy) ATTR_NONNULL(1
#define FILE_MAXDIR
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:693
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
bool BLI_uuid_is_nil(bUUID uuid)
Definition uuid.cc:79
@ ASSET_IMPORT_PACK
@ ASSET_IMPORT_APPEND_REUSE
eAssetLibraryType
@ ASSET_LIBRARY_CUSTOM
@ ASSET_LIBRARY_ESSENTIALS
@ ASSET_LIBRARY_LOCAL
@ ASSET_LIBRARY_ALL
@ SPACE_FILE
@ FILE_ASSET_IMPORT_APPEND_REUSE
@ FILE_ASSET_IMPORT_PACK
void AS_asset_library_remap_ids(const bke::id::IDRemapper &mappings)
std::string AS_asset_library_find_suitable_root_path_from_main(const Main *bmain)
void AS_asset_libraries_exit()
AssetLibrary * AS_asset_library_load(const Main *bmain, const AssetLibraryReference &library_reference)
void AS_asset_library_essential_import_method_update()
AssetLibrary * AS_asset_library_load_from_directory(const char *name, const char *library_dirpath)
std::string AS_asset_library_find_suitable_root_path_from_path(const blender::StringRefNull input_path)
void AS_asset_library_import_method_ensure_valid(Main &bmain)
static void update_import_method_for_user_libraries()
std::string AS_asset_library_root_path_from_library_ref(const AssetLibraryReference &library_reference)
bool AS_asset_library_has_any_unsaved_catalogs()
void AS_asset_full_path_explode_from_weak_ref(const AssetWeakReference *asset_reference, char r_path_buffer[1282], char **r_dir, char **r_group, char **r_name)
static void update_import_method_for_asset_browsers(Main &bmain)
volatile int lock
#define U
bool add(const Key &key)
Definition BLI_set.hh:248
constexpr bool is_empty() const
constexpr const char * c_str() const
AssetCatalog * find_catalog(CatalogID catalog_id) const
bool write_to_disk(const CatalogFilePath &blend_file_path)
void foreach_loaded_asset_library(FunctionRef< void(AssetLibrary &)> fn, bool include_all_library) const
AssetLibrary * get_asset_library_on_disk_builtin(eAssetLibraryType type, StringRefNull root_path)
AssetLibrary * get_asset_library(const Main *bmain, const AssetLibraryReference &library_reference)
std::optional< ExplodedPath > resolve_asset_weak_reference_to_exploded_path(const AssetWeakReference &asset_reference)
static AssetLibrary * move_runtime_current_file_into_on_disk_library(const Main &bmain)
std::string resolve_asset_weak_reference_to_full_path(const AssetWeakReference &asset_reference)
AssetLibrary * get_asset_library_on_disk_custom(StringRef name, StringRefNull root_path)
static std::string root_path_from_library_ref(const AssetLibraryReference &library_reference)
void on_blend_save_post(Main *bmain, PointerRNA **pointers, int num_pointers)
eAssetLibraryType library_type() const
std::unique_ptr< AssetCatalogService > catalog_service_
void remap_ids_and_remove_invalid(const blender::bke::id::IDRemapper &mappings)
std::weak_ptr< AssetRepresentation > add_local_id_asset(ID &id)
AssetCatalogService & catalog_service() const
static void foreach_loaded(FunctionRef< void(AssetLibrary &)> fn, bool include_all_library)
AssetLibrary(eAssetLibraryType library_type, StringRef name="", StringRef root_path="")
bool remove_asset(AssetRepresentation &asset)
void refresh_catalog_simplename(AssetMetaData *asset_data)
std::string resolve_asset_weak_reference_to_full_path(const AssetWeakReference &asset_reference)
std::weak_ptr< AssetRepresentation > add_external_asset(StringRef relative_asset_path, StringRef name, int id_type, std::unique_ptr< AssetMetaData > metadata)
IDRemapperApplyResult apply(ID **r_id_ptr, IDRemapperApplyOptions options, ID *id_self=nullptr) const
AssetLibraryReference all_library_reference()
AssetLibraryReference current_file_library_reference()
void all_library_reload_catalogs_if_dirty()
Vector< AssetLibraryReference > all_valid_asset_library_refs()
const char * name
The meta-data of an asset. By creating and giving this for a data-block (ID.asset_data),...
char catalog_simple_name[64]
struct bUUID catalog_id
Definition DNA_ID.h:414
char filepath[1024]
Definition BKE_main.hh:179
ListBase screens
Definition BKE_main.hh:292
FileAssetSelectParams * asset_params
i
Definition text_draw.cc:230
static DynamicLibrary lib