Blender V4.3
asset_system/intern/asset_catalog.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
9#include <iostream>
10#include <set>
11
12#include "AS_asset_catalog.hh"
14#include "AS_asset_library.hh"
17
18#include "BLI_fileops.h"
19#include "BLI_path_utils.hh"
20
21/* For S_ISREG() and S_ISDIR() on Windows. */
22#ifdef WIN32
23# include "BLI_winstuff.h"
24#endif
25
27
28#include "CLG_log.h"
29
30static CLG_LogRef LOG = {"asset_system.asset_catalog_service"};
31
32namespace blender::asset_system {
33
35
37 : catalog_collection_(std::make_unique<AssetCatalogCollection>()),
38 asset_library_root_(asset_library_root)
39{
40}
41
43{
44 const_cast<bool &>(is_read_only_) = true;
45}
46
48{
49 BLI_assert(!is_read_only_);
50
51 if (edited_catalog) {
52 edited_catalog->flags.has_unsaved_changes = true;
53 }
54 BLI_assert(catalog_collection_);
55 catalog_collection_->has_unsaved_changes_ = true;
56}
57
59{
60 BLI_assert(catalog_collection_);
61 catalog_collection_->has_unsaved_changes_ = false;
62
63 /* TODO(Sybren): refactor; this is more like "post-write cleanup" than "remove a tag" code. */
64
65 /* Forget about any deleted catalogs. */
66 if (catalog_collection_->catalog_definition_file_) {
67 for (CatalogID catalog_id : catalog_collection_->deleted_catalogs_.keys()) {
68 catalog_collection_->catalog_definition_file_->forget(catalog_id);
69 }
70 }
71 catalog_collection_->deleted_catalogs_.clear();
72
73 /* Mark all remaining catalogs as "without unsaved changes". */
74 for (auto &catalog_uptr : catalog_collection_->catalogs_.values()) {
75 catalog_uptr->flags.has_unsaved_changes = false;
76 }
77}
78
80{
81 BLI_assert(catalog_collection_);
82 return catalog_collection_->has_unsaved_changes_;
83}
84
86{
87 return is_read_only_;
88}
89
91{
92 for (auto &catalog : catalog_collection_->catalogs_.values()) {
93 catalog->flags.has_unsaved_changes = true;
94 }
95 catalog_collection_->has_unsaved_changes_ = true;
96}
97
99{
100 BLI_assert(catalog_collection_);
101 return catalog_collection_->catalogs_.is_empty();
102}
103
105{
106 return catalog_collection_->catalogs_;
107}
109{
110 return catalog_collection_->deleted_catalogs_;
111}
112
114{
115 return catalog_collection_->catalog_definition_file_.get();
116}
117
119{
120 const std::unique_ptr<AssetCatalog> *catalog_uptr_ptr =
121 catalog_collection_->catalogs_.lookup_ptr(catalog_id);
122 if (catalog_uptr_ptr == nullptr) {
123 return nullptr;
124 }
125 return catalog_uptr_ptr->get();
126}
127
129{
130 /* Use an AssetCatalogOrderedSet to find the 'best' catalog for this path. This will be the first
131 * one loaded from disk, or if that does not exist the one with the lowest UUID. This ensures
132 * stable, predictable results. */
133 MutableAssetCatalogOrderedSet ordered_catalogs;
134
135 for (const auto &catalog : catalog_collection_->catalogs_.values()) {
136 if (catalog->path == path) {
137 ordered_catalogs.insert(catalog.get());
138 }
139 }
140
141 if (ordered_catalogs.empty()) {
142 return nullptr;
143 }
144
145 MutableAssetCatalogOrderedSet::iterator best_choice_it = ordered_catalogs.begin();
146 return *best_choice_it;
147}
148
150{
151 BLI_assert(catalog_collection_);
152 return catalog_collection_->catalogs_.contains(catalog_id);
153}
154
156 const CatalogID active_catalog_id) const
157{
158 Set<CatalogID> matching_catalog_ids;
159 Set<CatalogID> known_catalog_ids;
160 matching_catalog_ids.add(active_catalog_id);
161
162 const AssetCatalog *active_catalog = this->find_catalog(active_catalog_id);
163
164 /* This cannot just iterate over tree items to get all the required data, because tree items only
165 * represent single UUIDs. It could be used to get the main UUIDs of the children, though, and
166 * then only do an exact match on the path (instead of the more complex `is_contained_in()`
167 * call). Without an extra indexed-by-path acceleration structure, this is still going to require
168 * a linear search, though. */
169 for (const auto &catalog_uptr : catalog_collection_->catalogs_.values()) {
170 if (active_catalog && catalog_uptr->path.is_contained_in(active_catalog->path)) {
171 matching_catalog_ids.add(catalog_uptr->catalog_id);
172 }
173 known_catalog_ids.add(catalog_uptr->catalog_id);
174 }
175
176 return AssetCatalogFilter(std::move(matching_catalog_ids), std::move(known_catalog_ids));
177}
178
180{
181 std::unique_ptr<AssetCatalog> *catalog_uptr_ptr = catalog_collection_->catalogs_.lookup_ptr(
182 catalog_id);
183 if (catalog_uptr_ptr == nullptr) {
184 /* Catalog cannot be found, which is fine. */
185 return;
186 }
187
188 /* Mark the catalog as deleted. */
189 AssetCatalog *catalog = catalog_uptr_ptr->get();
190 catalog->flags.is_deleted = true;
191
192 /* Move ownership from catalog_collection_->catalogs_ to catalog_collection_->deleted_catalogs_.
193 */
194 catalog_collection_->deleted_catalogs_.add(catalog_id, std::move(*catalog_uptr_ptr));
195
196 /* The catalog can now be removed from the map without freeing the actual AssetCatalog. */
197 catalog_collection_->catalogs_.remove(catalog_id);
198}
199
201{
202 catalog_collection_->catalogs_.remove(catalog_id);
203 catalog_collection_->deleted_catalogs_.remove(catalog_id);
204
205 /* TODO(@sybren): adjust this when supporting multiple CDFs. */
206 catalog_collection_->catalog_definition_file_->forget(catalog_id);
207}
208
210{
211 /* Build a collection of catalog IDs to delete. */
212 Set<CatalogID> catalogs_to_delete;
213 for (const auto &catalog_uptr : catalog_collection_->catalogs_.values()) {
214 const AssetCatalog *cat = catalog_uptr.get();
215 if (cat->path.is_contained_in(path)) {
216 catalogs_to_delete.add(cat->catalog_id);
217 }
218 }
219
220 /* Delete the catalogs. */
221 for (const CatalogID cat_id : catalogs_to_delete) {
222 this->delete_catalog_by_id_soft(cat_id);
223 }
224
227}
228
230{
231 const AssetCatalog *catalog = find_catalog(catalog_id);
232 BLI_assert_msg(catalog, "trying to prune asset catalogs by the path of a non-existent catalog");
233 if (!catalog) {
234 return;
235 }
236 this->prune_catalogs_by_path(catalog->path);
237}
238
240 const AssetCatalogPath &new_catalog_path)
241{
242 AssetCatalog *renamed_cat = this->find_catalog(catalog_id);
243 const AssetCatalogPath old_cat_path = renamed_cat->path;
244
245 for (auto &catalog_uptr : catalog_collection_->catalogs_.values()) {
246 AssetCatalog *cat = catalog_uptr.get();
247
248 const AssetCatalogPath new_path = cat->path.rebase(old_cat_path, new_catalog_path);
249 if (!new_path) {
250 continue;
251 }
252 cat->path = new_path;
253 cat->simple_name_refresh();
254 this->tag_has_unsaved_changes(cat);
255
256 /* TODO(Sybren): go over all assets that are assigned to this catalog, defined in the current
257 * blend file, and update the catalog simple name stored there. */
258 }
259
263}
264
266{
267 std::unique_ptr<AssetCatalog> catalog = AssetCatalog::from_path(catalog_path);
268 catalog->flags.has_unsaved_changes = true;
269
270 /* So we can std::move(catalog) and still use the non-owning pointer: */
271 AssetCatalog *const catalog_ptr = catalog.get();
272
273 /* TODO(@sybren): move the `AssetCatalog::from_path()` function to another place, that can reuse
274 * catalogs when a catalog with the given path is already known, and avoid duplicate catalog IDs.
275 */
276 BLI_assert_msg(!catalog_collection_->catalogs_.contains(catalog->catalog_id),
277 "duplicate catalog ID not supported");
278 catalog_collection_->catalogs_.add_new(catalog->catalog_id, std::move(catalog));
279
280 if (catalog_collection_->catalog_definition_file_) {
281 /* Ensure the new catalog gets written to disk at some point. If there is no CDF in memory yet,
282 * it's enough to have the catalog known to the service as it'll be saved to a new file. */
283 catalog_collection_->catalog_definition_file_->add_new(catalog_ptr);
284 }
285
288
289 return catalog_ptr;
290}
291
292static std::string asset_definition_default_file_path_from_dir(StringRef asset_library_root)
293{
294 char file_path[PATH_MAX];
295 BLI_path_join(file_path,
296 sizeof(file_path),
297 asset_library_root.data(),
299 return file_path;
300}
301
303{
304 this->load_from_disk(asset_library_root_);
305}
306
307void AssetCatalogService::load_from_disk(const CatalogFilePath &file_or_directory_path)
308{
309 BLI_stat_t status;
310 if (BLI_stat(file_or_directory_path.data(), &status) == -1) {
311 /* TODO(@sybren): throw an appropriate exception. */
312 CLOG_WARN(&LOG, "path not found: %s", file_or_directory_path.data());
313 return;
314 }
315
316 if (S_ISREG(status.st_mode)) {
317 this->load_single_file(file_or_directory_path);
318 }
319 else if (S_ISDIR(status.st_mode)) {
320 this->load_directory_recursive(file_or_directory_path);
321 }
322 else {
323 /* TODO(@sybren): throw an appropriate exception. */
324 }
325
326 /* TODO: Should there be a sanitize step? E.g. to remove catalogs with identical paths? */
327
330}
331
333 const AssetCatalogService &other_service,
335{
336 catalog_collection_->add_catalogs_from_existing(*other_service.catalog_collection_,
337 on_duplicate_items);
338}
339
341{
342 /* TODO(@sybren): implement proper multi-file support. For now, just load
343 * the default file if it is there. */
345
346 if (!BLI_exists(file_path.data())) {
347 /* No file to be loaded is perfectly fine. */
348 CLOG_INFO(&LOG, 2, "path not found: %s", file_path.data());
349 return;
350 }
351
352 this->load_single_file(file_path);
353}
354
355void AssetCatalogService::load_single_file(const CatalogFilePath &catalog_definition_file_path)
356{
357 /* TODO(@sybren): check that #catalog_definition_file_path is contained in #asset_library_root_,
358 * otherwise some assumptions may fail. */
359 std::unique_ptr<AssetCatalogDefinitionFile> cdf = parse_catalog_file(
360 catalog_definition_file_path);
361
362 BLI_assert_msg(!catalog_collection_->catalog_definition_file_,
363 "Only loading of a single catalog definition file is supported.");
364 catalog_collection_->catalog_definition_file_ = std::move(cdf);
365}
366
367std::unique_ptr<AssetCatalogDefinitionFile> AssetCatalogService::parse_catalog_file(
368 const CatalogFilePath &catalog_definition_file_path)
369{
370 auto cdf = std::make_unique<AssetCatalogDefinitionFile>(catalog_definition_file_path);
371
372 /* TODO(Sybren): this might have to move to a higher level when supporting multiple CDFs. */
373 Set<AssetCatalogPath> seen_paths;
374
375 auto catalog_parsed_callback = [this, catalog_definition_file_path, &seen_paths](
376 std::unique_ptr<AssetCatalog> catalog) {
377 if (catalog_collection_->catalogs_.contains(catalog->catalog_id)) {
378 /* TODO(@sybren): apparently another CDF was already loaded. This is not supported yet. */
379 std::cerr << catalog_definition_file_path << ": multiple definitions of catalog "
380 << catalog->catalog_id << " in multiple files, ignoring this one." << std::endl;
381 /* Don't store 'catalog'; unique_ptr will free its memory. */
382 return false;
383 }
384
385 catalog->flags.is_first_loaded = seen_paths.add(catalog->path);
386
387 /* The AssetCatalog pointer is now owned by the AssetCatalogService. */
388 catalog_collection_->catalogs_.add_new(catalog->catalog_id, std::move(catalog));
389 return true;
390 };
391
392 cdf->parse_catalog_file(cdf->file_path, catalog_parsed_callback);
393
394 return cdf;
395}
396
398{
399 /* TODO(Sybren): expand to support multiple CDFs. */
400 AssetCatalogDefinitionFile *const cdf = catalog_collection_->catalog_definition_file_.get();
401 if (!cdf || cdf->file_path.empty() || !BLI_is_file(cdf->file_path.c_str())) {
402 return;
403 }
404
405 /* Keeps track of the catalog IDs that are seen in the CDF, so that we also know what was deleted
406 * from the file on disk. */
407 Set<CatalogID> cats_in_file;
408
409 auto catalog_parsed_callback = [this, &cats_in_file](std::unique_ptr<AssetCatalog> catalog) {
410 const CatalogID catalog_id = catalog->catalog_id;
411 cats_in_file.add(catalog_id);
412
413 const bool should_skip = this->is_catalog_known_with_unsaved_changes(catalog_id);
414 if (should_skip) {
415 /* Do not overwrite unsaved local changes. */
416 return false;
417 }
418
419 /* This is either a new catalog, or we can just replace the in-memory one with the newly loaded
420 * one. */
421 catalog_collection_->catalogs_.add_overwrite(catalog_id, std::move(catalog));
422 return true;
423 };
424
425 cdf->parse_catalog_file(cdf->file_path, catalog_parsed_callback);
426 this->purge_catalogs_not_listed(cats_in_file);
429}
430
432{
433 Set<CatalogID> cats_to_remove;
434 for (CatalogID cat_id : this->catalog_collection_->catalogs_.keys()) {
435 if (catalogs_to_keep.contains(cat_id)) {
436 continue;
437 }
438 if (this->is_catalog_known_with_unsaved_changes(cat_id)) {
439 continue;
440 }
441 /* This catalog is not on disk, but also not modified, so get rid of it. */
442 cats_to_remove.add(cat_id);
443 }
444
445 for (CatalogID cat_id : cats_to_remove) {
446 this->delete_catalog_by_id_hard(cat_id);
447 }
448}
449
451{
452 if (catalog_collection_->deleted_catalogs_.contains(catalog_id)) {
453 /* Deleted catalogs are always considered modified, by definition. */
454 return true;
455 }
456
457 const std::unique_ptr<AssetCatalog> *catalog_uptr_ptr =
458 catalog_collection_->catalogs_.lookup_ptr(catalog_id);
459 if (!catalog_uptr_ptr) {
460 /* Catalog is unknown. */
461 return false;
462 }
463
464 const bool has_unsaved_changes = (*catalog_uptr_ptr)->flags.has_unsaved_changes;
465 return has_unsaved_changes;
466}
467
469{
470 BLI_assert(!is_read_only_);
471
472 if (!this->write_to_disk_ex(blend_file_path)) {
473 return false;
474 }
475
478 return true;
479}
480
482{
483 /* TODO(Sybren): expand to support multiple CDFs. */
484
485 /* - Already loaded a CDF from disk? -> Always write to that file. */
486 if (catalog_collection_->catalog_definition_file_) {
487 this->reload_catalogs();
488 return catalog_collection_->catalog_definition_file_->write_to_disk();
489 }
490
491 if (catalog_collection_->catalogs_.is_empty() &&
492 catalog_collection_->deleted_catalogs_.is_empty())
493 {
494 /* Avoid saving anything, when there is nothing to save. */
495 return true; /* Writing nothing when there is nothing to write is still a success. */
496 }
497
498 const CatalogFilePath cdf_path_to_write = this->find_suitable_cdf_path_for_writing(
499 blend_file_path);
500 catalog_collection_->catalog_definition_file_ = this->construct_cdf_in_memory(cdf_path_to_write);
501 this->reload_catalogs();
502 return catalog_collection_->catalog_definition_file_->write_to_disk();
503}
504
506{
507 /* TODO(Sybren): expand to support multiple CDFs. */
508
509 if (!catalog_collection_->catalog_definition_file_) {
510 /* There is no CDF connected, so it's a no-op. */
511 return;
512 }
513
514 /* Remove any association with the CDF, so that a new location will be chosen
515 * when the blend file is saved. */
516 catalog_collection_->catalog_definition_file_.reset();
517
518 /* Mark all in-memory catalogs as "dirty", to force them to be kept around on
519 * the next "load-merge-write" cycle. */
521}
522
524 const CatalogFilePath &blend_file_path)
525{
526 BLI_assert_msg(!blend_file_path.empty(),
527 "A non-empty .blend file path is required to be able to determine where the "
528 "catalog definition file should be put");
529
530 /* Ask the asset library API for an appropriate location. */
531 const std::string suitable_root_path = AS_asset_library_find_suitable_root_path_from_path(
532 blend_file_path);
533 if (!suitable_root_path.empty()) {
534 char asset_lib_cdf_path[PATH_MAX];
535 BLI_path_join(asset_lib_cdf_path,
536 sizeof(asset_lib_cdf_path),
537 suitable_root_path.c_str(),
539 return asset_lib_cdf_path;
540 }
541
542 /* Determine the default CDF path in the same directory of the blend file. */
543 char blend_dir_path[PATH_MAX];
544 BLI_path_split_dir_part(blend_file_path.c_str(), blend_dir_path, sizeof(blend_dir_path));
546 blend_dir_path);
547 return cdf_path_next_to_blend;
548}
549
550std::unique_ptr<AssetCatalogDefinitionFile> AssetCatalogService::construct_cdf_in_memory(
551 const CatalogFilePath &file_path) const
552{
553 auto cdf = std::make_unique<AssetCatalogDefinitionFile>(file_path);
554
555 for (auto &catalog : catalog_collection_->catalogs_.values()) {
556 cdf->add_new(catalog.get());
557 }
558
559 return cdf;
560}
561
562std::unique_ptr<AssetCatalogTree> AssetCatalogService::read_into_tree() const
563{
564 auto tree = std::make_unique<AssetCatalogTree>();
565
566 /* Go through the catalogs, insert each path component into the tree where needed. */
567 for (auto &catalog : catalog_collection_->catalogs_.values()) {
568 tree->insert_item(*catalog);
569 }
570
571 return tree;
572}
573
575{
576 std::lock_guard lock{catalog_tree_mutex_};
577 this->catalog_tree_ = nullptr;
578}
579
581{
582 std::lock_guard lock{catalog_tree_mutex_};
583 if (!catalog_tree_) {
584 /* Ensure all catalog paths lead to valid catalogs. This is important for the catalog tree to
585 * be usable, e.g. it makes sure every item in the tree maps to an actual catalog. */
587
588 catalog_tree_ = read_into_tree();
589 }
590 return *catalog_tree_;
591}
592
594{
595 /* Construct an ordered set of paths to check, so that parents are ordered before children. */
596 std::set<AssetCatalogPath> paths_to_check;
597 for (auto &catalog : catalog_collection_->catalogs_.values()) {
598 paths_to_check.insert(catalog->path);
599 }
600
601 std::set<AssetCatalogPath> seen_paths;
602 /* The empty parent should never be created, so always be considered "seen". */
603 seen_paths.insert(AssetCatalogPath(""));
604
605 /* Find and create missing direct parents (so ignoring parents-of-parents). */
606 while (!paths_to_check.empty()) {
607 /* Pop the first path of the queue. */
608 const AssetCatalogPath path = *paths_to_check.begin();
609 paths_to_check.erase(paths_to_check.begin());
610
611 if (seen_paths.find(path) != seen_paths.end()) {
612 /* This path has been seen already, so it can be ignored. */
613 continue;
614 }
615 seen_paths.insert(path);
616
617 const AssetCatalogPath parent_path = path.parent();
618 if (seen_paths.find(parent_path) != seen_paths.end()) {
619 /* The parent exists, continue to the next path. */
620 continue;
621 }
622
623 /* The parent doesn't exist, so create it and queue it up for checking its parent. */
624 AssetCatalog *parent_catalog = this->create_catalog(parent_path);
625 parent_catalog->flags.has_unsaved_changes = true;
626
627 paths_to_check.insert(parent_path);
628 }
629
630 /* TODO(Sybren): bind the newly created catalogs to a CDF, if we know about it. */
631}
632
634{
635 return !undo_snapshots_.is_empty();
636}
637
639{
640 return !redo_snapshots_.is_empty();
641}
642
644{
645 BLI_assert_msg(is_undo_possbile(), "Undo stack is empty");
646
647 redo_snapshots_.append(std::move(catalog_collection_));
648 catalog_collection_ = undo_snapshots_.pop_last();
652}
653
655{
656 BLI_assert(!is_read_only_);
657 BLI_assert_msg(is_redo_possbile(), "Redo stack is empty");
658
659 undo_snapshots_.append(std::move(catalog_collection_));
660 catalog_collection_ = redo_snapshots_.pop_last();
664}
665
667{
668 BLI_assert(!is_read_only_);
669 std::unique_ptr<AssetCatalogCollection> snapshot = catalog_collection_->deep_copy();
670 undo_snapshots_.append(std::move(snapshot));
671 redo_snapshots_.clear();
672}
673
674/* ---------------------------------------------------------------------- */
675
677 const AssetCatalogPath &path,
678 const std::string &simple_name)
679 : catalog_id(catalog_id), path(path), simple_name(simple_name)
680{
681}
682
683std::unique_ptr<AssetCatalog> AssetCatalog::from_path(const AssetCatalogPath &path)
684{
685 const AssetCatalogPath clean_path = path.cleanup();
686 const CatalogID cat_id = BLI_uuid_generate_random();
687 const std::string simple_name = sensible_simple_name_for_path(clean_path);
688 auto catalog = std::make_unique<AssetCatalog>(cat_id, clean_path, simple_name);
689 return catalog;
690}
691
696
698{
699 std::string name = path.str();
700 std::replace(name.begin(), name.end(), AssetCatalogPath::SEPARATOR, '-');
701 if (name.length() < MAX_NAME - 1) {
702 return name;
703 }
704
705 /* Trim off the start of the path, as that's the most generic part and thus contains the least
706 * information. */
707 return "..." + name.substr(name.length() - 60);
708}
709
710/* ---------------------------------------------------------------------- */
711
713 Set<CatalogID> &&known_catalog_ids)
714 : matching_catalog_ids_(std::move(matching_catalog_ids)),
715 known_catalog_ids_(std::move(known_catalog_ids))
716{
717}
718
719bool AssetCatalogFilter::contains(const CatalogID asset_catalog_id) const
720{
721 return matching_catalog_ids_.contains(asset_catalog_id);
722}
723
724bool AssetCatalogFilter::is_known(const CatalogID asset_catalog_id) const
725{
726 if (BLI_uuid_is_nil(asset_catalog_id)) {
727 return false;
728 }
729 return known_catalog_ids_.contains(asset_catalog_id);
730}
731
732} // namespace blender::asset_system
std::string AS_asset_library_find_suitable_root_path_from_path(blender::StringRefNull input_path)
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:57
File and directory operations.
int BLI_exists(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:350
int BLI_stat(const char *path, BLI_stat_t *buffer) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
bool BLI_is_file(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:438
struct stat BLI_stat_t
#define PATH_MAX
Definition BLI_fileops.h:30
#define BLI_path_join(...)
void void BLI_path_split_dir_part(const char *filepath, char *dir, size_t dir_maxncpy) ATTR_NONNULL(1
bool BLI_uuid_is_nil(bUUID uuid)
Definition uuid.cc:79
bUUID BLI_uuid_generate_random(void)
Definition uuid.cc:24
Compatibility-like things for windows.
#define S_ISDIR(x)
#define S_ISREG(x)
#define CLOG_WARN(clg_ref,...)
Definition CLG_log.h:181
#define CLOG_INFO(clg_ref, level,...)
Definition CLG_log.h:179
#define MAX_NAME
Definition DNA_defs.h:50
static CLG_LogRef LOG
volatile int lock
bool contains(const Key &key) const
Definition BLI_set.hh:291
bool add(const Key &key)
Definition BLI_set.hh:248
constexpr const char * data() const
void parse_catalog_file(const CatalogFilePath &catalog_definition_file_path, AssetCatalogParsedFn catalog_loaded_callback)
AssetCatalogFilter(Set< CatalogID > &&matching_catalog_ids, Set< CatalogID > &&known_catalog_ids)
AssetCatalogPath rebase(const AssetCatalogPath &from_path, const AssetCatalogPath &to_path) const
bool is_contained_in(const AssetCatalogPath &other_path) const
void load_single_file(const CatalogFilePath &catalog_definition_file_path)
AssetCatalogFilter create_catalog_filter(CatalogID active_catalog_id) const
AssetCatalog * find_catalog_by_path(const AssetCatalogPath &path) const
AssetCatalog * find_catalog(CatalogID catalog_id) const
bool write_to_disk(const CatalogFilePath &blend_file_path)
std::unique_ptr< AssetCatalogTree > read_into_tree() const
void purge_catalogs_not_listed(const Set< CatalogID > &catalogs_to_keep)
std::unique_ptr< AssetCatalogDefinitionFile > parse_catalog_file(const CatalogFilePath &catalog_definition_file_path)
std::unique_ptr< AssetCatalogDefinitionFile > construct_cdf_in_memory(const CatalogFilePath &file_path) const
AssetCatalogService(const CatalogFilePath &asset_library_root={})
AssetCatalog * create_catalog(const AssetCatalogPath &catalog_path)
void update_catalog_path(CatalogID catalog_id, const AssetCatalogPath &new_catalog_path)
void add_from_existing(const AssetCatalogService &other_service, FunctionRef< void(const AssetCatalog &existing, const AssetCatalog &to_be_ignored)> on_duplicate_items)
static CatalogFilePath find_suitable_cdf_path_for_writing(const CatalogFilePath &blend_file_path)
bool write_to_disk_ex(const CatalogFilePath &blend_file_path)
void load_directory_recursive(const CatalogFilePath &directory_path)
bool is_catalog_known_with_unsaved_changes(CatalogID catalog_id) const
static const CatalogFilePath DEFAULT_CATALOG_FILENAME
const AssetCatalogDefinitionFile * get_catalog_definition_file() const
static std::unique_ptr< AssetCatalog > from_path(const AssetCatalogPath &path)
static std::string sensible_simple_name_for_path(const AssetCatalogPath &path)
struct blender::asset_system::AssetCatalog::Flags flags
KDTree_3d * tree
#define LOG(severity)
Definition log.h:33
std::set< AssetCatalog *, AssetCatalogLessThan > MutableAssetCatalogOrderedSet
static std::string asset_definition_default_file_path_from_dir(StringRef asset_library_root)
Universally Unique Identifier according to RFC4122.