Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
authorSybren A. Stüvel <sybren@blender.org>2021-09-30 18:30:30 +0300
committerSybren A. Stüvel <sybren@blender.org>2021-09-30 18:34:58 +0300
commitdd3391dd996e90fba3227c1cc2b50f4ef490ccdb (patch)
tree5a60f7589bb36be75f7bd1624925ef3f281bd927 /source
parent4389067929d9a57923b7a85ec29b8ca9633fef29 (diff)
Asset Catalogs: create missing parent catalogs
For every known catalog, ensure its parent catalog also exists. This ensures that assets can be assigned to parent catalogs, even when they didn't exist in the Catalog Definition File yet.
Diffstat (limited to 'source')
-rw-r--r--source/blender/blenkernel/BKE_asset_catalog.hh5
-rw-r--r--source/blender/blenkernel/BKE_asset_catalog_path.hh5
-rw-r--r--source/blender/blenkernel/intern/asset_catalog.cc41
-rw-r--r--source/blender/blenkernel/intern/asset_catalog_path.cc12
-rw-r--r--source/blender/blenkernel/intern/asset_catalog_path_test.cc17
-rw-r--r--source/blender/blenkernel/intern/asset_catalog_test.cc43
6 files changed, 122 insertions, 1 deletions
diff --git a/source/blender/blenkernel/BKE_asset_catalog.hh b/source/blender/blenkernel/BKE_asset_catalog.hh
index 9d179011b25..a0bc1267826 100644
--- a/source/blender/blenkernel/BKE_asset_catalog.hh
+++ b/source/blender/blenkernel/BKE_asset_catalog.hh
@@ -150,6 +150,11 @@ class AssetCatalogService {
std::unique_ptr<AssetCatalogTree> read_into_tree();
void rebuild_tree();
+
+ /**
+ * For every catalog, ensure that its parent path also has a known catalog.
+ */
+ void create_missing_catalogs();
};
/**
diff --git a/source/blender/blenkernel/BKE_asset_catalog_path.hh b/source/blender/blenkernel/BKE_asset_catalog_path.hh
index 1e53df553a9..b150f805ed5 100644
--- a/source/blender/blenkernel/BKE_asset_catalog_path.hh
+++ b/source/blender/blenkernel/BKE_asset_catalog_path.hh
@@ -112,6 +112,11 @@ class AssetCatalogPath {
bool is_contained_in(const AssetCatalogPath &other_path) const;
/**
+ * \return the parent path, or an empty path if there is no parent.
+ */
+ AssetCatalogPath parent() const;
+
+ /**
* Change the initial part of the path from `from_path` to `to_path`.
* If this path does not start with `from_path`, return an empty path as result.
*
diff --git a/source/blender/blenkernel/intern/asset_catalog.cc b/source/blender/blenkernel/intern/asset_catalog.cc
index bb213877e05..a948c0f96ff 100644
--- a/source/blender/blenkernel/intern/asset_catalog.cc
+++ b/source/blender/blenkernel/intern/asset_catalog.cc
@@ -188,7 +188,7 @@ void AssetCatalogService::load_from_disk(const CatalogFilePath &file_or_director
/* TODO: Should there be a sanitize step? E.g. to remove catalogs with identical paths? */
- catalog_tree_ = read_into_tree();
+ rebuild_tree();
}
void AssetCatalogService::load_directory_recursive(const CatalogFilePath &directory_path)
@@ -356,9 +356,48 @@ std::unique_ptr<AssetCatalogTree> AssetCatalogService::read_into_tree()
void AssetCatalogService::rebuild_tree()
{
+ create_missing_catalogs();
this->catalog_tree_ = read_into_tree();
}
+void AssetCatalogService::create_missing_catalogs()
+{
+ /* Construct an ordered set of paths to check, so that parents are ordered before children. */
+ std::set<AssetCatalogPath> paths_to_check;
+ for (auto &catalog : catalogs_.values()) {
+ paths_to_check.insert(catalog->path);
+ }
+
+ std::set<AssetCatalogPath> seen_paths;
+ /* The empty parent should never be created, so always be considered "seen". */
+ seen_paths.insert(AssetCatalogPath(""));
+
+ /* Find and create missing direct parents (so ignoring parents-of-parents). */
+ while (!paths_to_check.empty()) {
+ /* Pop the first path of the queue. */
+ const AssetCatalogPath path = *paths_to_check.begin();
+ paths_to_check.erase(paths_to_check.begin());
+
+ if (seen_paths.find(path) != seen_paths.end()) {
+ /* This path has been seen already, so it can be ignored. */
+ continue;
+ }
+ seen_paths.insert(path);
+
+ const AssetCatalogPath parent_path = path.parent();
+ if (seen_paths.find(parent_path) != seen_paths.end()) {
+ /* The parent exists, continue to the next path. */
+ continue;
+ }
+
+ /* The parent doesn't exist, so create it and queue it up for checking its parent. */
+ create_catalog(parent_path);
+ paths_to_check.insert(parent_path);
+ }
+
+ /* TODO(Sybren): bind the newly created catalogs to a CDF, if we know about it. */
+}
+
/* ---------------------------------------------------------------------- */
AssetCatalogTreeItem::AssetCatalogTreeItem(StringRef name,
diff --git a/source/blender/blenkernel/intern/asset_catalog_path.cc b/source/blender/blenkernel/intern/asset_catalog_path.cc
index d8af7be4a02..cc6aceef8e7 100644
--- a/source/blender/blenkernel/intern/asset_catalog_path.cc
+++ b/source/blender/blenkernel/intern/asset_catalog_path.cc
@@ -172,6 +172,18 @@ bool AssetCatalogPath::is_contained_in(const AssetCatalogPath &other_path) const
return prefix_ok && next_char == SEPARATOR;
}
+AssetCatalogPath AssetCatalogPath::parent() const
+{
+ if (!*this) {
+ return AssetCatalogPath("");
+ }
+ std::string::size_type last_sep_index = this->path_.rfind(SEPARATOR);
+ if (last_sep_index == std::string::npos) {
+ return AssetCatalogPath("");
+ }
+ return AssetCatalogPath(this->path_.substr(0, last_sep_index));
+}
+
void AssetCatalogPath::iterate_components(ComponentIteratorFn callback) const
{
const char *next_slash_ptr;
diff --git a/source/blender/blenkernel/intern/asset_catalog_path_test.cc b/source/blender/blenkernel/intern/asset_catalog_path_test.cc
index 55919abbb8f..af15cbf405a 100644
--- a/source/blender/blenkernel/intern/asset_catalog_path_test.cc
+++ b/source/blender/blenkernel/intern/asset_catalog_path_test.cc
@@ -231,4 +231,21 @@ TEST(AssetCatalogPathTest, rebase)
EXPECT_EQ(empty.rebase("", ""), "");
}
+TEST(AssetCatalogPathTest, parent)
+{
+ const AssetCatalogPath ascii_path("path/with/missing/parents");
+ EXPECT_EQ(ascii_path.parent(), "path/with/missing");
+
+ const AssetCatalogPath path("путь/в/Пермь/долог/и/далек");
+ EXPECT_EQ(path.parent(), "путь/в/Пермь/долог/и");
+ EXPECT_EQ(path.parent().parent(), "путь/в/Пермь/долог");
+ EXPECT_EQ(path.parent().parent().parent(), "путь/в/Пермь");
+
+ const AssetCatalogPath one_level("one");
+ EXPECT_EQ(one_level.parent(), "");
+
+ const AssetCatalogPath empty("");
+ EXPECT_EQ(empty.parent(), "");
+}
+
} // namespace blender::bke::tests
diff --git a/source/blender/blenkernel/intern/asset_catalog_test.cc b/source/blender/blenkernel/intern/asset_catalog_test.cc
index d7c3e50cbdf..836b681c950 100644
--- a/source/blender/blenkernel/intern/asset_catalog_test.cc
+++ b/source/blender/blenkernel/intern/asset_catalog_test.cc
@@ -57,6 +57,11 @@ class TestableAssetCatalogService : public AssetCatalogService {
{
return catalog_definition_file_.get();
}
+
+ void create_missing_catalogs()
+ {
+ AssetCatalogService::create_missing_catalogs();
+ }
};
class AssetCatalogTest : public testing::Test {
@@ -846,4 +851,42 @@ TEST_F(AssetCatalogTest, order_by_path)
}
}
+TEST_F(AssetCatalogTest, create_missing_catalogs)
+{
+ TestableAssetCatalogService new_service;
+ new_service.create_catalog("path/with/missing/parents");
+
+ EXPECT_EQ(nullptr, new_service.find_catalog_by_path("path/with/missing"))
+ << "Missing parents should not be immediately created.";
+ EXPECT_EQ(nullptr, new_service.find_catalog_by_path("")) << "Empty path should never be valid";
+
+ new_service.create_missing_catalogs();
+
+ EXPECT_NE(nullptr, new_service.find_catalog_by_path("path/with/missing"));
+ EXPECT_NE(nullptr, new_service.find_catalog_by_path("path/with"));
+ EXPECT_NE(nullptr, new_service.find_catalog_by_path("path"));
+ EXPECT_EQ(nullptr, new_service.find_catalog_by_path(""))
+ << "Empty path should never be valid, even when after missing catalogs";
+}
+
+TEST_F(AssetCatalogTest, create_missing_catalogs_after_loading)
+{
+ TestableAssetCatalogService loaded_service(asset_library_root_);
+ loaded_service.load_from_disk();
+
+ const AssetCatalog *cat_char = loaded_service.find_catalog_by_path("character");
+ const AssetCatalog *cat_ellie = loaded_service.find_catalog_by_path("character/Ellie");
+ const AssetCatalog *cat_ruzena = loaded_service.find_catalog_by_path("character/Ružena");
+ ASSERT_NE(nullptr, cat_char) << "Missing parents should be created immediately after loading.";
+ ASSERT_NE(nullptr, cat_ellie) << "Missing parents should be created immediately after loading.";
+ ASSERT_NE(nullptr, cat_ruzena) << "Missing parents should be created immediately after loading.";
+
+ AssetCatalogDefinitionFile *cdf = loaded_service.get_catalog_definition_file();
+ ASSERT_NE(nullptr, cdf);
+ EXPECT_TRUE(cdf->contains(cat_char->catalog_id)) << "Missing parents should be saved to a CDF.";
+ EXPECT_TRUE(cdf->contains(cat_ellie->catalog_id)) << "Missing parents should be saved to a CDF.";
+ EXPECT_TRUE(cdf->contains(cat_ruzena->catalog_id))
+ << "Missing parents should be saved to a CDF.";
+}
+
} // namespace blender::bke::tests