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
diff options
context:
space:
mode:
-rw-r--r--source/blender/blenkernel/BKE_asset_catalog.hh28
-rw-r--r--source/blender/blenkernel/BKE_asset_library.hh9
-rw-r--r--source/blender/blenkernel/intern/asset_catalog.cc62
-rw-r--r--source/blender/blenkernel/intern/asset_catalog_test.cc165
-rw-r--r--source/blender/blenkernel/intern/asset_library.cc42
5 files changed, 298 insertions, 8 deletions
diff --git a/source/blender/blenkernel/BKE_asset_catalog.hh b/source/blender/blenkernel/BKE_asset_catalog.hh
index 07373caf701..7b54d7cf572 100644
--- a/source/blender/blenkernel/BKE_asset_catalog.hh
+++ b/source/blender/blenkernel/BKE_asset_catalog.hh
@@ -74,6 +74,25 @@ class AssetCatalogService {
bool write_to_disk(const CatalogFilePath &directory_for_new_files);
/**
+ * Write the catalog definitions to disk in response to the blend file being saved.
+ *
+ * The location where the catalogs are saved is variable, and depends on the location of the
+ * blend file. The first matching rule wins:
+ *
+ * - Already loaded a CDF from disk?
+ * -> Always write to that file.
+ * - The directory containing the blend file has a blender_assets.cats.txt file?
+ * -> Merge with & write to that file.
+ * - The directory containing the blend file is part of an asset library, as per
+ * the user's preferences?
+ * -> Merge with & write to ${ASSET_LIBRARY_ROOT}/blender_assets.cats.txt
+ * - Create a new file blender_assets.cats.txt next to the blend file.
+ *
+ * Return true on success, which either means there were no in-memory categories to save,
+ * or the save was successful. */
+ bool write_to_disk_on_blendfile_save(const char *blend_file_path);
+
+ /**
* Merge on-disk changes into the in-memory asset catalogs.
* This should be called before writing the asset catalogs to disk.
*
@@ -124,6 +143,15 @@ class AssetCatalogService {
std::unique_ptr<AssetCatalogDefinitionFile> construct_cdf_in_memory(
const CatalogFilePath &file_path);
+ /**
+ * Find a suitable path to write a CDF to.
+ *
+ * This depends on the location of the blend file, and on whether a CDF already exists next to it
+ * or whether the blend file is saved inside an asset library.
+ */
+ static CatalogFilePath find_suitable_cdf_path_for_writing(
+ const CatalogFilePath &blend_file_path);
+
std::unique_ptr<AssetCatalogTree> read_into_tree();
void rebuild_tree();
};
diff --git a/source/blender/blenkernel/BKE_asset_library.hh b/source/blender/blenkernel/BKE_asset_library.hh
index 68f7481574e..fc5e137dd3e 100644
--- a/source/blender/blenkernel/BKE_asset_library.hh
+++ b/source/blender/blenkernel/BKE_asset_library.hh
@@ -27,6 +27,7 @@
#include "BKE_asset_library.h"
#include "BKE_asset_catalog.hh"
+#include "BKE_callbacks.h"
#include <memory>
@@ -36,6 +37,14 @@ struct AssetLibrary {
std::unique_ptr<AssetCatalogService> catalog_service;
void load(StringRefNull library_root_directory);
+
+ void on_save_handler_register();
+ void on_save_handler_unregister();
+
+ void on_save_post(struct Main *, struct PointerRNA **pointers, const int num_pointers);
+
+ private:
+ bCallbackFuncStore on_save_callback_store_;
};
} // namespace blender::bke
diff --git a/source/blender/blenkernel/intern/asset_catalog.cc b/source/blender/blenkernel/intern/asset_catalog.cc
index b65ae12e5a7..ee22e25a6ab 100644
--- a/source/blender/blenkernel/intern/asset_catalog.cc
+++ b/source/blender/blenkernel/intern/asset_catalog.cc
@@ -19,11 +19,14 @@
*/
#include "BKE_asset_catalog.hh"
+#include "BKE_preferences.h"
#include "BLI_fileops.h"
#include "BLI_path_util.h"
#include "BLI_string_ref.hh"
+#include "DNA_userdef_types.h"
+
/* For S_ISREG() and S_ISDIR() on Windows. */
#ifdef WIN32
# include "BLI_winstuff.h"
@@ -266,6 +269,65 @@ bool AssetCatalogService::write_to_disk(const CatalogFilePath &directory_for_new
return catalog_definition_file_->write_to_disk();
}
+bool AssetCatalogService::write_to_disk_on_blendfile_save(const char *blend_file_path)
+{
+ /* TODO(Sybren): deduplicate this and write_to_disk(...); maybe the latter function isn't even
+ * necessary any more. */
+
+ /* - Already loaded a CDF from disk? -> Always write to that file. */
+ if (this->catalog_definition_file_) {
+ merge_from_disk_before_writing();
+ return catalog_definition_file_->write_to_disk();
+ }
+
+ if (catalogs_.is_empty() && deleted_catalogs_.is_empty()) {
+ /* Avoid saving anything, when there is nothing to save. */
+ return true; /* Writing nothing when there is nothing to write is still a success. */
+ }
+
+ const CatalogFilePath cdf_path_to_write = find_suitable_cdf_path_for_writing(blend_file_path);
+ this->catalog_definition_file_ = construct_cdf_in_memory(cdf_path_to_write);
+ merge_from_disk_before_writing();
+ return catalog_definition_file_->write_to_disk();
+}
+
+CatalogFilePath AssetCatalogService::find_suitable_cdf_path_for_writing(
+ const CatalogFilePath &blend_file_path)
+{
+ /* Determine the default CDF path in the same directory of the blend file. */
+ char blend_dir_path[PATH_MAX];
+ BLI_split_dir_part(blend_file_path.c_str(), blend_dir_path, sizeof(blend_dir_path));
+ const CatalogFilePath cdf_path_next_to_blend = asset_definition_default_file_path_from_dir(
+ blend_dir_path);
+
+ if (BLI_exists(cdf_path_next_to_blend.c_str())) {
+ /* - The directory containing the blend file has a blender_assets.cats.txt file?
+ * -> Merge with & write to that file. */
+ return cdf_path_next_to_blend;
+ }
+
+ const bUserAssetLibrary *asset_lib_pref = BKE_preferences_asset_library_containing_path(
+ &U, blend_file_path.c_str());
+ if (asset_lib_pref) {
+ /* - The directory containing the blend file is part of an asset library, as per
+ * the user's preferences?
+ * -> Merge with & write to ${ASSET_LIBRARY_ROOT}/blender_assets.cats.txt */
+
+ char asset_lib_cdf_path[PATH_MAX];
+ BLI_path_join(asset_lib_cdf_path,
+ sizeof(asset_lib_cdf_path),
+ asset_lib_pref->path,
+ DEFAULT_CATALOG_FILENAME.c_str(),
+ NULL);
+
+ return asset_lib_cdf_path;
+ }
+
+ /* - Otherwise
+ * -> Create a new file blender_assets.cats.txt next to the blend file. */
+ return cdf_path_next_to_blend;
+}
+
std::unique_ptr<AssetCatalogDefinitionFile> AssetCatalogService::construct_cdf_in_memory(
const CatalogFilePath &file_path)
{
diff --git a/source/blender/blenkernel/intern/asset_catalog_test.cc b/source/blender/blenkernel/intern/asset_catalog_test.cc
index 87ee61b015f..5177abd2820 100644
--- a/source/blender/blenkernel/intern/asset_catalog_test.cc
+++ b/source/blender/blenkernel/intern/asset_catalog_test.cc
@@ -19,10 +19,13 @@
#include "BKE_appdir.h"
#include "BKE_asset_catalog.hh"
+#include "BKE_preferences.h"
#include "BLI_fileops.h"
#include "BLI_path_util.h"
+#include "DNA_userdef_types.h"
+
#include "testing/testing.h"
namespace blender::bke::tests {
@@ -43,6 +46,8 @@ const bUUID UUID_AGENT_47("c5744ba5-43f5-4f73-8e52-010ad4a61b34");
/* Subclass that adds accessors such that protected fields can be used in tests. */
class TestableAssetCatalogService : public AssetCatalogService {
public:
+ TestableAssetCatalogService() = default;
+
explicit TestableAssetCatalogService(const CatalogFilePath &asset_library_root)
: AssetCatalogService(asset_library_root)
{
@@ -405,6 +410,152 @@ TEST_F(AssetCatalogTest, no_writing_empty_files)
EXPECT_FALSE(BLI_exists(default_cdf_path.c_str()));
}
+/* Already loaded a CDF, saving to some unrelated directory. */
+TEST_F(AssetCatalogTest, on_blendfile_save__with_existing_cdf)
+{
+ const CatalogFilePath top_level_dir = create_temp_path(); // Has trailing slash.
+
+ /* Create a copy of the CDF in SVN, so we can safely write to it. */
+ const CatalogFilePath original_cdf_file = asset_library_root_ + "/blender_assets.cats.txt";
+ const CatalogFilePath cdf_dirname = top_level_dir + "other_dir/";
+ const CatalogFilePath cdf_filename = cdf_dirname + AssetCatalogService::DEFAULT_CATALOG_FILENAME;
+ ASSERT_TRUE(BLI_dir_create_recursive(cdf_dirname.c_str()));
+ ASSERT_EQ(0, BLI_copy(original_cdf_file.c_str(), cdf_filename.c_str()))
+ << "Unable to copy " << original_cdf_file << " to " << cdf_filename;
+
+ /* Load the CDF, add a catalog, and trigger a write. This should write to the loaded CDF. */
+ TestableAssetCatalogService service(cdf_filename);
+ service.load_from_disk();
+ const AssetCatalog *cat = service.create_catalog("some/catalog/path");
+
+ const CatalogFilePath blendfilename = top_level_dir + "subdir/some_file.blend";
+ ASSERT_TRUE(service.write_to_disk_on_blendfile_save(blendfilename.c_str()));
+ EXPECT_EQ(cdf_filename, service.get_catalog_definition_file()->file_path);
+
+ /* Test that the CDF was created in the expected location. */
+ const CatalogFilePath backup_filename = cdf_filename + "~";
+ EXPECT_TRUE(BLI_exists(cdf_filename.c_str()));
+ EXPECT_TRUE(BLI_exists(backup_filename.c_str()))
+ << "Overwritten CDF should have been backed up.";
+
+ /* Test that the on-disk CDF contains the expected catalogs. */
+ AssetCatalogService loaded_service(cdf_filename);
+ loaded_service.load_from_disk();
+ EXPECT_NE(nullptr, loaded_service.find_catalog(cat->catalog_id))
+ << "Expected to see the newly-created catalog.";
+ EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE))
+ << "Expected to see the already-existing catalog.";
+}
+
+/* Create some catalogs in memory, save to directory that doesn't contain anything else. */
+TEST_F(AssetCatalogTest, on_blendfile_save__from_memory_into_empty_directory)
+{
+ const CatalogFilePath target_dir = create_temp_path(); // Has trailing slash.
+
+ TestableAssetCatalogService service;
+ const AssetCatalog *cat = service.create_catalog("some/catalog/path");
+
+ const CatalogFilePath blendfilename = target_dir + "some_file.blend";
+ ASSERT_TRUE(service.write_to_disk_on_blendfile_save(blendfilename.c_str()));
+
+ /* Test that the CDF was created in the expected location. */
+ const CatalogFilePath expected_cdf_path = target_dir +
+ AssetCatalogService::DEFAULT_CATALOG_FILENAME;
+ EXPECT_TRUE(BLI_exists(expected_cdf_path.c_str()));
+
+ /* Test that the in-memory CDF has been created, and contains the expected catalog. */
+ AssetCatalogDefinitionFile *cdf = service.get_catalog_definition_file();
+ ASSERT_NE(nullptr, cdf);
+ EXPECT_TRUE(cdf->contains(cat->catalog_id));
+
+ /* Test that the on-disk CDF contains the expected catalog. */
+ AssetCatalogService loaded_service(expected_cdf_path);
+ loaded_service.load_from_disk();
+ EXPECT_NE(nullptr, loaded_service.find_catalog(cat->catalog_id));
+}
+
+/* Create some catalogs in memory, save to directory that contains a default CDF. */
+TEST_F(AssetCatalogTest, on_blendfile_save__from_memory_into_existing_cdf_and_merge)
+{
+ const CatalogFilePath target_dir = create_temp_path(); // Has trailing slash.
+ const CatalogFilePath original_cdf_file = asset_library_root_ + "/blender_assets.cats.txt";
+ const CatalogFilePath writable_cdf_file = target_dir +
+ AssetCatalogService::DEFAULT_CATALOG_FILENAME;
+ ASSERT_EQ(0, BLI_copy(original_cdf_file.c_str(), writable_cdf_file.c_str()));
+
+ /* Create the catalog service without loading the already-existing CDF. */
+ TestableAssetCatalogService service;
+ const AssetCatalog *cat = service.create_catalog("some/catalog/path");
+
+ /* Mock that the blend file is written to a subdirectory of the asset library. */
+ const CatalogFilePath blendfilename = target_dir + "some_file.blend";
+ ASSERT_TRUE(service.write_to_disk_on_blendfile_save(blendfilename.c_str()));
+
+ /* Test that the CDF still exists in the expected location. */
+ const CatalogFilePath backup_filename = writable_cdf_file + "~";
+ EXPECT_TRUE(BLI_exists(writable_cdf_file.c_str()));
+ EXPECT_TRUE(BLI_exists(backup_filename.c_str()))
+ << "Overwritten CDF should have been backed up.";
+
+ /* Test that the in-memory CDF has the expected file path. */
+ AssetCatalogDefinitionFile *cdf = service.get_catalog_definition_file();
+ ASSERT_NE(nullptr, cdf);
+ EXPECT_EQ(writable_cdf_file, cdf->file_path);
+
+ /* Test that the in-memory catalogs have been merged with the on-disk one. */
+ AssetCatalogService loaded_service(writable_cdf_file);
+ loaded_service.load_from_disk();
+ EXPECT_NE(nullptr, loaded_service.find_catalog(cat->catalog_id));
+ EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE));
+}
+
+/* Create some catalogs in memory, save to subdirectory of a registered asset library. */
+TEST_F(AssetCatalogTest, on_blendfile_save__from_memory_into_existing_asset_lib)
+{
+ const CatalogFilePath target_dir = create_temp_path(); // Has trailing slash.
+ const CatalogFilePath original_cdf_file = asset_library_root_ + "/blender_assets.cats.txt";
+ const CatalogFilePath registered_asset_lib = target_dir + "my_asset_library/";
+ CatalogFilePath writable_cdf_file = registered_asset_lib +
+ AssetCatalogService::DEFAULT_CATALOG_FILENAME;
+ BLI_path_slash_native(writable_cdf_file.data());
+
+ /* Set up a temporary asset library for testing. */
+ bUserAssetLibrary *asset_lib_pref = BKE_preferences_asset_library_add(
+ &U, "Test", registered_asset_lib.c_str());
+ ASSERT_NE(nullptr, asset_lib_pref);
+ ASSERT_TRUE(BLI_dir_create_recursive(registered_asset_lib.c_str()));
+ ASSERT_EQ(0, BLI_copy(original_cdf_file.c_str(), writable_cdf_file.c_str()));
+
+ /* Create the catalog service without loading the already-existing CDF. */
+ TestableAssetCatalogService service;
+ const CatalogFilePath blenddirname = registered_asset_lib + "subdirectory/";
+ const CatalogFilePath blendfilename = blenddirname + "some_file.blend";
+ ASSERT_TRUE(BLI_dir_create_recursive(blenddirname.c_str()));
+ const AssetCatalog *cat = service.create_catalog("some/catalog/path");
+
+ /* Mock that the blend file is written to the directory already containing a CDF. */
+ ASSERT_TRUE(service.write_to_disk_on_blendfile_save(blendfilename.c_str()));
+
+ /* Test that the CDF still exists in the expected location. */
+ EXPECT_TRUE(BLI_exists(writable_cdf_file.c_str()));
+ const CatalogFilePath backup_filename = writable_cdf_file + "~";
+ EXPECT_TRUE(BLI_exists(backup_filename.c_str()))
+ << "Overwritten CDF should have been backed up.";
+
+ /* Test that the in-memory CDF has the expected file path. */
+ AssetCatalogDefinitionFile *cdf = service.get_catalog_definition_file();
+ BLI_path_slash_native(cdf->file_path.data());
+ EXPECT_EQ(writable_cdf_file, cdf->file_path);
+
+ /* Test that the in-memory catalogs have been merged with the on-disk one. */
+ AssetCatalogService loaded_service(writable_cdf_file);
+ loaded_service.load_from_disk();
+ EXPECT_NE(nullptr, loaded_service.find_catalog(cat->catalog_id));
+ EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE));
+
+ BKE_preferences_asset_library_remove(&U, asset_lib_pref);
+}
+
TEST_F(AssetCatalogTest, create_first_catalog_from_scratch)
{
/* Even from scratch a root directory should be known. */
@@ -450,7 +601,7 @@ TEST_F(AssetCatalogTest, create_catalog_after_loading_file)
AssetCatalogService::DEFAULT_CATALOG_FILENAME;
const CatalogFilePath writable_catalog_path = temp_lib_root +
AssetCatalogService::DEFAULT_CATALOG_FILENAME;
- BLI_copy(default_catalog_path.c_str(), writable_catalog_path.c_str());
+ ASSERT_EQ(0, BLI_copy(default_catalog_path.c_str(), writable_catalog_path.c_str()));
EXPECT_TRUE(BLI_is_dir(temp_lib_root.c_str()));
EXPECT_TRUE(BLI_is_file(writable_catalog_path.c_str()));
@@ -485,8 +636,7 @@ TEST_F(AssetCatalogTest, create_catalog_after_loading_file)
TEST_F(AssetCatalogTest, create_catalog_path_cleanup)
{
- const CatalogFilePath temp_lib_root = use_temp_path();
- AssetCatalogService service(temp_lib_root);
+ AssetCatalogService service;
AssetCatalog *cat = service.create_catalog(" /some/path / ");
EXPECT_FALSE(BLI_uuid_is_nil(cat->catalog_id));
@@ -496,8 +646,7 @@ TEST_F(AssetCatalogTest, create_catalog_path_cleanup)
TEST_F(AssetCatalogTest, create_catalog_simple_name)
{
- const CatalogFilePath temp_lib_root = use_temp_path();
- AssetCatalogService service(temp_lib_root);
+ AssetCatalogService service;
AssetCatalog *cat = service.create_catalog(
"production/Spite Fright/Characters/Victora/Pose Library/Approved/Body Parts/Hands");
@@ -568,14 +717,14 @@ TEST_F(AssetCatalogTest, merge_catalog_files)
const CatalogFilePath original_cdf_file = asset_library_root_ + "/blender_assets.cats.txt";
const CatalogFilePath modified_cdf_file = asset_library_root_ + "/modified_assets.cats.txt";
const CatalogFilePath temp_cdf_file = cdf_dir + "blender_assets.cats.txt";
- BLI_copy(original_cdf_file.c_str(), temp_cdf_file.c_str());
+ ASSERT_EQ(0, BLI_copy(original_cdf_file.c_str(), temp_cdf_file.c_str()));
// Load the unmodified, original CDF.
TestableAssetCatalogService service(asset_library_root_);
service.load_from_disk(cdf_dir);
// Copy a modified file, to mimick a situation where someone changed the CDF after we loaded it.
- BLI_copy(modified_cdf_file.c_str(), temp_cdf_file.c_str());
+ ASSERT_EQ(0, BLI_copy(modified_cdf_file.c_str(), temp_cdf_file.c_str()));
// Overwrite the modified file. This should merge the on-disk file with our catalogs.
service.write_to_disk(cdf_dir);
@@ -602,7 +751,7 @@ TEST_F(AssetCatalogTest, backups)
const CatalogFilePath cdf_dir = create_temp_path();
const CatalogFilePath original_cdf_file = asset_library_root_ + "/blender_assets.cats.txt";
const CatalogFilePath writable_cdf_file = cdf_dir + "/blender_assets.cats.txt";
- BLI_copy(original_cdf_file.c_str(), writable_cdf_file.c_str());
+ ASSERT_EQ(0, BLI_copy(original_cdf_file.c_str(), writable_cdf_file.c_str()));
/* Read a CDF, modify, and write it. */
AssetCatalogService service(cdf_dir);
diff --git a/source/blender/blenkernel/intern/asset_library.cc b/source/blender/blenkernel/intern/asset_library.cc
index 1153e7b29f5..6fed355c41b 100644
--- a/source/blender/blenkernel/intern/asset_library.cc
+++ b/source/blender/blenkernel/intern/asset_library.cc
@@ -19,6 +19,8 @@
*/
#include "BKE_asset_library.hh"
+#include "BKE_callbacks.h"
+#include "BKE_main.h"
#include "MEM_guardedalloc.h"
@@ -31,6 +33,7 @@
struct AssetLibrary *BKE_asset_library_load(const char *library_path)
{
blender::bke::AssetLibrary *lib = new blender::bke::AssetLibrary();
+ lib->on_save_handler_register();
lib->load(library_path);
return reinterpret_cast<struct AssetLibrary *>(lib);
}
@@ -38,6 +41,7 @@ struct AssetLibrary *BKE_asset_library_load(const char *library_path)
void BKE_asset_library_free(struct AssetLibrary *asset_library)
{
blender::bke::AssetLibrary *lib = reinterpret_cast<blender::bke::AssetLibrary *>(asset_library);
+ lib->on_save_handler_unregister();
delete lib;
}
@@ -50,4 +54,42 @@ void AssetLibrary::load(StringRefNull library_root_directory)
this->catalog_service = std::move(catalog_service);
}
+namespace {
+void asset_library_on_save_post(struct Main *main,
+ struct PointerRNA **pointers,
+ const int num_pointers,
+ void *arg)
+{
+ AssetLibrary *asset_lib = static_cast<AssetLibrary *>(arg);
+ asset_lib->on_save_post(main, pointers, num_pointers);
+}
+} // namespace
+
+void AssetLibrary::on_save_handler_register()
+{
+ /* The callback system doesn't own `on_save_callback_store_`. */
+ on_save_callback_store_.alloc = false;
+
+ on_save_callback_store_.func = asset_library_on_save_post;
+ on_save_callback_store_.arg = this;
+
+ BKE_callback_add(&on_save_callback_store_, BKE_CB_EVT_SAVE_POST);
+}
+
+void AssetLibrary::on_save_handler_unregister()
+{
+ BKE_callback_remove(&on_save_callback_store_, BKE_CB_EVT_SAVE_POST);
+}
+
+void AssetLibrary::on_save_post(struct Main *main,
+ struct PointerRNA ** /*pointers*/,
+ const int /*num_pointers*/)
+{
+ if (this->catalog_service) {
+ return;
+ }
+
+ this->catalog_service->write_to_disk_on_blendfile_save(main->name);
+}
+
} // namespace blender::bke