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:
Diffstat (limited to 'source/blender')
-rw-r--r--source/blender/blenkernel/BKE_idtype.h6
-rw-r--r--source/blender/blenkernel/BKE_main.h26
-rw-r--r--source/blender/blenkernel/intern/armature.c2
-rw-r--r--source/blender/blenkernel/intern/bpath.c5
-rw-r--r--source/blender/blenkernel/intern/cachefile.c2
-rw-r--r--source/blender/blenkernel/intern/camera.c2
-rw-r--r--source/blender/blenkernel/intern/curve.c2
-rw-r--r--source/blender/blenkernel/intern/font.c2
-rw-r--r--source/blender/blenkernel/intern/hair.c2
-rw-r--r--source/blender/blenkernel/intern/idtype.c18
-rw-r--r--source/blender/blenkernel/intern/image.c2
-rw-r--r--source/blender/blenkernel/intern/lattice.c2
-rw-r--r--source/blender/blenkernel/intern/lib_id.c7
-rw-r--r--source/blender/blenkernel/intern/lib_id_delete.c4
-rw-r--r--source/blender/blenkernel/intern/light.c2
-rw-r--r--source/blender/blenkernel/intern/lightprobe.c2
-rw-r--r--source/blender/blenkernel/intern/linestyle.c2
-rw-r--r--source/blender/blenkernel/intern/main.c202
-rw-r--r--source/blender/blenkernel/intern/mask.c2
-rw-r--r--source/blender/blenkernel/intern/material.c2
-rw-r--r--source/blender/blenkernel/intern/mball.c2
-rw-r--r--source/blender/blenkernel/intern/mesh.c2
-rw-r--r--source/blender/blenkernel/intern/movieclip.c2
-rw-r--r--source/blender/blenkernel/intern/node.cc2
-rw-r--r--source/blender/blenkernel/intern/pointcloud.cc2
-rw-r--r--source/blender/blenkernel/intern/simulation.cc2
-rw-r--r--source/blender/blenkernel/intern/sound.c2
-rw-r--r--source/blender/blenkernel/intern/speaker.c2
-rw-r--r--source/blender/blenkernel/intern/text.c2
-rw-r--r--source/blender/blenkernel/intern/texture.c2
-rw-r--r--source/blender/blenkernel/intern/volume.cc2
-rw-r--r--source/blender/blenkernel/intern/world.c2
-rw-r--r--source/blender/blenloader/intern/readfile.c7
-rw-r--r--source/blender/makesdna/DNA_ID.h29
-rw-r--r--source/blender/makesrna/intern/rna_ID.c35
-rw-r--r--source/blender/windowmanager/intern/wm_files_link.c52
36 files changed, 412 insertions, 29 deletions
diff --git a/source/blender/blenkernel/BKE_idtype.h b/source/blender/blenkernel/BKE_idtype.h
index 7136a3fd7af..cd656d94fce 100644
--- a/source/blender/blenkernel/BKE_idtype.h
+++ b/source/blender/blenkernel/BKE_idtype.h
@@ -49,8 +49,11 @@ enum {
* appended.
* NOTE: Mutually exclusive with `IDTYPE_FLAGS_NO_LIBLINKING`. */
IDTYPE_FLAGS_ONLY_APPEND = 1 << 2,
+ /** Allow to re-use an existing local ID with matching weak library reference instead of creating
+ * a new copy of it, when appending. See also #LibraryWeakReference in `DNA_ID.h`. */
+ IDTYPE_FLAGS_APPEND_IS_REUSABLE = 1 << 3,
/** Indicates that the given IDType does not have animation data. */
- IDTYPE_FLAGS_NO_ANIMDATA = 1 << 3,
+ IDTYPE_FLAGS_NO_ANIMDATA = 1 << 4,
};
typedef struct IDCacheKey {
@@ -290,6 +293,7 @@ bool BKE_idtype_idcode_is_valid(const short idcode);
bool BKE_idtype_idcode_is_linkable(const short idcode);
bool BKE_idtype_idcode_is_only_appendable(const short idcode);
+bool BKE_idtype_idcode_append_is_reusable(const short idcode);
/* Macro currently, since any linkable IDtype should be localizable. */
#define BKE_idtype_idcode_is_localizable BKE_idtype_idcode_is_linkable
diff --git a/source/blender/blenkernel/BKE_main.h b/source/blender/blenkernel/BKE_main.h
index ae60a5563b5..93d5b5c5aa6 100644
--- a/source/blender/blenkernel/BKE_main.h
+++ b/source/blender/blenkernel/BKE_main.h
@@ -212,6 +212,32 @@ void BKE_main_relations_tag_set(struct Main *bmain,
struct GSet *BKE_main_gset_create(struct Main *bmain, struct GSet *gset);
+/*
+ * Temporary runtime API to allow re-using local (already appended) IDs instead of appending a new
+ * copy again.
+ */
+
+struct GHash *BKE_main_library_weak_reference_create(struct Main *bmain) ATTR_NONNULL();
+void BKE_main_library_weak_reference_destroy(struct GHash *library_weak_reference_mapping)
+ ATTR_NONNULL();
+struct ID *BKE_main_library_weak_reference_search_item(
+ struct GHash *library_weak_reference_mapping,
+ const char *library_filepath,
+ const char *library_id_name) ATTR_NONNULL();
+void BKE_main_library_weak_reference_add_item(struct GHash *library_weak_reference_mapping,
+ const char *library_filepath,
+ const char *library_id_name,
+ struct ID *new_id) ATTR_NONNULL();
+void BKE_main_library_weak_reference_update_item(struct GHash *library_weak_reference_mapping,
+ const char *library_filepath,
+ const char *library_id_name,
+ struct ID *old_id,
+ struct ID *new_id) ATTR_NONNULL();
+void BKE_main_library_weak_reference_remove_item(struct GHash *library_weak_reference_mapping,
+ const char *library_filepath,
+ const char *library_id_name,
+ struct ID *old_id) ATTR_NONNULL();
+
/* *** Generic utils to loop over whole Main database. *** */
#define FOREACH_MAIN_LISTBASE_ID_BEGIN(_lb, _id) \
diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c
index 87320c88b1b..a86f436185e 100644
--- a/source/blender/blenkernel/intern/armature.c
+++ b/source/blender/blenkernel/intern/armature.c
@@ -315,7 +315,7 @@ IDTypeInfo IDType_ID_AR = {
.name = "Armature",
.name_plural = "armatures",
.translation_context = BLT_I18NCONTEXT_ID_ARMATURE,
- .flags = 0,
+ .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE,
.init_data = armature_init_data,
.copy_data = armature_copy_data,
diff --git a/source/blender/blenkernel/intern/bpath.c b/source/blender/blenkernel/intern/bpath.c
index 1684e22dece..371ec14876b 100644
--- a/source/blender/blenkernel/intern/bpath.c
+++ b/source/blender/blenkernel/intern/bpath.c
@@ -586,6 +586,11 @@ void BKE_bpath_traverse_id(
return;
}
+ if (id->library_weak_reference != NULL) {
+ rewrite_path_fixed(
+ id->library_weak_reference->library_filepath, visit_cb, absbase, bpath_user_data);
+ }
+
switch (GS(id->name)) {
case ID_IM: {
Image *ima;
diff --git a/source/blender/blenkernel/intern/cachefile.c b/source/blender/blenkernel/intern/cachefile.c
index 87b1584d422..e642bbc9e06 100644
--- a/source/blender/blenkernel/intern/cachefile.c
+++ b/source/blender/blenkernel/intern/cachefile.c
@@ -133,7 +133,7 @@ IDTypeInfo IDType_ID_CF = {
.name = "CacheFile",
.name_plural = "cache_files",
.translation_context = BLT_I18NCONTEXT_ID_CACHEFILE,
- .flags = 0,
+ .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE,
.init_data = cache_file_init_data,
.copy_data = cache_file_copy_data,
diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c
index b77855f8f95..ed1f6fcb40a 100644
--- a/source/blender/blenkernel/intern/camera.c
+++ b/source/blender/blenkernel/intern/camera.c
@@ -182,7 +182,7 @@ IDTypeInfo IDType_ID_CA = {
.name = "Camera",
.name_plural = "cameras",
.translation_context = BLT_I18NCONTEXT_ID_CAMERA,
- .flags = 0,
+ .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE,
.init_data = camera_init_data,
.copy_data = camera_copy_data,
diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c
index f22c3b13efc..b0d196b2bb0 100644
--- a/source/blender/blenkernel/intern/curve.c
+++ b/source/blender/blenkernel/intern/curve.c
@@ -311,7 +311,7 @@ IDTypeInfo IDType_ID_CU = {
.name = "Curve",
.name_plural = "curves",
.translation_context = BLT_I18NCONTEXT_ID_CURVE,
- .flags = 0,
+ .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE,
.init_data = curve_init_data,
.copy_data = curve_copy_data,
diff --git a/source/blender/blenkernel/intern/font.c b/source/blender/blenkernel/intern/font.c
index 842a701f525..aa13f86523a 100644
--- a/source/blender/blenkernel/intern/font.c
+++ b/source/blender/blenkernel/intern/font.c
@@ -160,7 +160,7 @@ IDTypeInfo IDType_ID_VF = {
.name = "Font",
.name_plural = "fonts",
.translation_context = BLT_I18NCONTEXT_ID_VFONT,
- .flags = IDTYPE_FLAGS_NO_ANIMDATA,
+ .flags = IDTYPE_FLAGS_NO_ANIMDATA | IDTYPE_FLAGS_APPEND_IS_REUSABLE,
.init_data = vfont_init_data,
.copy_data = vfont_copy_data,
diff --git a/source/blender/blenkernel/intern/hair.c b/source/blender/blenkernel/intern/hair.c
index af7cc0acb57..cf346e9cac7 100644
--- a/source/blender/blenkernel/intern/hair.c
+++ b/source/blender/blenkernel/intern/hair.c
@@ -181,7 +181,7 @@ IDTypeInfo IDType_ID_HA = {
.name = "Hair",
.name_plural = "hairs",
.translation_context = BLT_I18NCONTEXT_ID_HAIR,
- .flags = 0,
+ .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE,
.init_data = hair_init_data,
.copy_data = hair_copy_data,
diff --git a/source/blender/blenkernel/intern/idtype.c b/source/blender/blenkernel/intern/idtype.c
index b2efccc53c4..d9dc68b1a4f 100644
--- a/source/blender/blenkernel/intern/idtype.c
+++ b/source/blender/blenkernel/intern/idtype.c
@@ -255,6 +255,24 @@ bool BKE_idtype_idcode_is_only_appendable(const short idcode)
}
/**
+ * Check if an ID type can try to reuse and existing matching local one when being appended again.
+ *
+ * \param idcode: The IDType code to check.
+ * \return Boolean, false when it cannot be re-used, true otherwise.
+ */
+bool BKE_idtype_idcode_append_is_reusable(const short idcode)
+{
+ const IDTypeInfo *id_type = BKE_idtype_get_info_from_idcode(idcode);
+ BLI_assert(id_type != NULL);
+ if (id_type != NULL && (id_type->flags & IDTYPE_FLAGS_APPEND_IS_REUSABLE) != 0) {
+ /* All appendable ID types should also always be linkable. */
+ BLI_assert((id_type->flags & IDTYPE_FLAGS_NO_LIBLINKING) == 0);
+ return true;
+ }
+ return false;
+}
+
+/**
* Convert an \a idcode into an \a idfilter (e.g. ID_OB -> FILTER_ID_OB).
*/
uint64_t BKE_idtype_idcode_to_idfilter(const short idcode)
diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c
index 33f007c6dee..b993d743044 100644
--- a/source/blender/blenkernel/intern/image.c
+++ b/source/blender/blenkernel/intern/image.c
@@ -345,7 +345,7 @@ IDTypeInfo IDType_ID_IM = {
.name = "Image",
.name_plural = "images",
.translation_context = BLT_I18NCONTEXT_ID_IMAGE,
- .flags = IDTYPE_FLAGS_NO_ANIMDATA,
+ .flags = IDTYPE_FLAGS_NO_ANIMDATA | IDTYPE_FLAGS_APPEND_IS_REUSABLE,
.init_data = image_init_data,
.copy_data = image_copy_data,
diff --git a/source/blender/blenkernel/intern/lattice.c b/source/blender/blenkernel/intern/lattice.c
index e804f32e5a6..9bca8172e64 100644
--- a/source/blender/blenkernel/intern/lattice.c
+++ b/source/blender/blenkernel/intern/lattice.c
@@ -196,7 +196,7 @@ IDTypeInfo IDType_ID_LT = {
.name = "Lattice",
.name_plural = "lattices",
.translation_context = BLT_I18NCONTEXT_ID_LATTICE,
- .flags = 0,
+ .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE,
.init_data = lattice_init_data,
.copy_data = lattice_copy_data,
diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c
index 60b6d7ad66d..18824e73ee5 100644
--- a/source/blender/blenkernel/intern/lib_id.c
+++ b/source/blender/blenkernel/intern/lib_id.c
@@ -1301,6 +1301,9 @@ void BKE_libblock_copy_ex(Main *bmain, const ID *id, ID **r_newid, const int ori
new_id->properties = IDP_CopyProperty_ex(id->properties, copy_data_flag);
}
+ /* This is never duplicated, only one existing ID should have a given weak ref to library/ID. */
+ new_id->library_weak_reference = NULL;
+
if ((orig_flag & LIB_ID_COPY_NO_LIB_OVERRIDE) == 0) {
if (ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
/* We do not want to copy existing override rules here, as they would break the proper
@@ -2440,6 +2443,10 @@ void BKE_id_blend_write(BlendWriter *writer, ID *id)
BKE_asset_metadata_write(writer, id->asset_data);
}
+ if (id->library_weak_reference != NULL) {
+ BLO_write_struct(writer, LibraryWeakReference, id->library_weak_reference);
+ }
+
/* ID_WM's id->properties are considered runtime only, and never written in .blend file. */
if (id->properties && !ELEM(GS(id->name), ID_WM)) {
IDP_BlendWrite(writer, id->properties);
diff --git a/source/blender/blenkernel/intern/lib_id_delete.c b/source/blender/blenkernel/intern/lib_id_delete.c
index 79717fe5f48..502a1197616 100644
--- a/source/blender/blenkernel/intern/lib_id_delete.c
+++ b/source/blender/blenkernel/intern/lib_id_delete.c
@@ -69,6 +69,10 @@ void BKE_libblock_free_data(ID *id, const bool do_id_user)
BKE_asset_metadata_free(&id->asset_data);
}
+ if (id->library_weak_reference != NULL) {
+ MEM_freeN(id->library_weak_reference);
+ }
+
BKE_animdata_free(id, do_id_user);
}
diff --git a/source/blender/blenkernel/intern/light.c b/source/blender/blenkernel/intern/light.c
index c2b71b85973..a6150028f46 100644
--- a/source/blender/blenkernel/intern/light.c
+++ b/source/blender/blenkernel/intern/light.c
@@ -193,7 +193,7 @@ IDTypeInfo IDType_ID_LA = {
.name = "Light",
.name_plural = "lights",
.translation_context = BLT_I18NCONTEXT_ID_LIGHT,
- .flags = 0,
+ .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE,
.init_data = light_init_data,
.copy_data = light_copy_data,
diff --git a/source/blender/blenkernel/intern/lightprobe.c b/source/blender/blenkernel/intern/lightprobe.c
index 15733af8ef0..1f4abf36426 100644
--- a/source/blender/blenkernel/intern/lightprobe.c
+++ b/source/blender/blenkernel/intern/lightprobe.c
@@ -91,7 +91,7 @@ IDTypeInfo IDType_ID_LP = {
.name = "LightProbe",
.name_plural = "lightprobes",
.translation_context = BLT_I18NCONTEXT_ID_LIGHTPROBE,
- .flags = 0,
+ .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE,
.init_data = lightprobe_init_data,
.copy_data = NULL,
diff --git a/source/blender/blenkernel/intern/linestyle.c b/source/blender/blenkernel/intern/linestyle.c
index 19030fca38b..f4e4dd9f1ab 100644
--- a/source/blender/blenkernel/intern/linestyle.c
+++ b/source/blender/blenkernel/intern/linestyle.c
@@ -751,7 +751,7 @@ IDTypeInfo IDType_ID_LS = {
.name = "FreestyleLineStyle",
.name_plural = "linestyles",
.translation_context = BLT_I18NCONTEXT_ID_FREESTYLELINESTYLE,
- .flags = 0,
+ .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE,
.init_data = linestyle_init_data,
.copy_data = linestyle_copy_data,
diff --git a/source/blender/blenkernel/intern/main.c b/source/blender/blenkernel/intern/main.c
index 981f1d4a623..26dcadcc77b 100644
--- a/source/blender/blenkernel/intern/main.c
+++ b/source/blender/blenkernel/intern/main.c
@@ -35,6 +35,7 @@
#include "DNA_ID.h"
#include "BKE_global.h"
+#include "BKE_idtype.h"
#include "BKE_lib_id.h"
#include "BKE_lib_query.h"
#include "BKE_main.h"
@@ -358,6 +359,207 @@ GSet *BKE_main_gset_create(Main *bmain, GSet *gset)
return gset;
}
+/* Utils for ID's library weak reference API. */
+typedef struct LibWeakRefKey {
+ char filepath[FILE_MAX];
+ char id_name[MAX_ID_NAME];
+} LibWeakRefKey;
+
+static LibWeakRefKey *lib_weak_key_create(LibWeakRefKey *key,
+ const char *lib_path,
+ const char *id_name)
+{
+ if (key == NULL) {
+ key = MEM_mallocN(sizeof(*key), __func__);
+ }
+ BLI_strncpy(key->filepath, lib_path, sizeof(key->filepath));
+ BLI_strncpy(key->id_name, id_name, sizeof(key->id_name));
+ return key;
+}
+
+static uint lib_weak_key_hash(const void *ptr)
+{
+ const LibWeakRefKey *string_pair = ptr;
+ uint hash = BLI_ghashutil_strhash_p_murmur(string_pair->filepath);
+ return hash ^ BLI_ghashutil_strhash_p_murmur(string_pair->id_name);
+}
+
+static bool lib_weak_key_cmp(const void *a, const void *b)
+{
+ const LibWeakRefKey *string_pair_a = a;
+ const LibWeakRefKey *string_pair_b = b;
+
+ return !(STREQ(string_pair_a->filepath, string_pair_b->filepath) &&
+ STREQ(string_pair_a->id_name, string_pair_b->id_name));
+}
+
+/**
+ * Generate a mapping between 'library path' of an ID (as a pair (relative blend file path, id
+ * name)), and a current local ID, if any.
+ *
+ * This uses the information stored in `ID.library_weak_reference`.
+ */
+GHash *BKE_main_library_weak_reference_create(Main *bmain)
+{
+ GHash *library_weak_reference_mapping = BLI_ghash_new(
+ lib_weak_key_hash, lib_weak_key_cmp, __func__);
+
+ ListBase *lb;
+ FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) {
+ ID *id_iter = lb->first;
+ if (id_iter == NULL) {
+ continue;
+ }
+ if (!BKE_idtype_idcode_append_is_reusable(GS(id_iter->name))) {
+ continue;
+ }
+ BLI_assert(BKE_idtype_idcode_is_linkable(GS(id_iter->name)));
+
+ FOREACH_MAIN_LISTBASE_ID_BEGIN (lb, id_iter) {
+ if (id_iter->library_weak_reference == NULL) {
+ continue;
+ }
+ LibWeakRefKey *key = lib_weak_key_create(NULL,
+ id_iter->library_weak_reference->library_filepath,
+ id_iter->library_weak_reference->library_id_name);
+ BLI_ghash_insert(library_weak_reference_mapping, key, id_iter);
+ }
+ FOREACH_MAIN_LISTBASE_ID_END;
+ }
+ FOREACH_MAIN_LISTBASE_END;
+
+ return library_weak_reference_mapping;
+}
+
+/**
+ * Destroy the data generated by #BKE_main_library_weak_reference_create.
+ */
+void BKE_main_library_weak_reference_destroy(GHash *library_weak_reference_mapping)
+{
+ BLI_ghash_free(library_weak_reference_mapping, MEM_freeN, NULL);
+}
+
+/**
+ * Search for a local ID matching the given linked ID reference.
+ *
+ * \param library_weak_reference_mapping: the mapping data generated by
+ * #BKE_main_library_weak_reference_create.
+ * \param library_relative_path: the path of a blend file library (relative to current working
+ * one).
+ * \param library_id_name: the full ID name, including the leading two chars encoding the ID
+ * type.
+ */
+ID *BKE_main_library_weak_reference_search_item(GHash *library_weak_reference_mapping,
+ const char *library_filepath,
+ const char *library_id_name)
+{
+ LibWeakRefKey key;
+ lib_weak_key_create(&key, library_filepath, library_id_name);
+ return (ID *)BLI_ghash_lookup(library_weak_reference_mapping, &key);
+}
+
+/**
+ * Add the given ID weak library reference to given local ID and the runtime mapping.
+ *
+ * \param library_weak_reference_mapping: the mapping data generated by
+ * #BKE_main_library_weak_reference_create.
+ * \param library_relative_path: the path of a blend file library (relative to current working
+ * one).
+ * \param library_id_name: the full ID name, including the leading two chars encoding the ID type.
+ * \param new_id: New local ID matching given weak reference.
+ */
+void BKE_main_library_weak_reference_add_item(GHash *library_weak_reference_mapping,
+ const char *library_filepath,
+ const char *library_id_name,
+ ID *new_id)
+{
+ BLI_assert(GS(library_id_name) == GS(new_id->name));
+ BLI_assert(new_id->library_weak_reference == NULL);
+ BLI_assert(BKE_idtype_idcode_append_is_reusable(GS(new_id->name)));
+
+ new_id->library_weak_reference = MEM_mallocN(sizeof(*(new_id->library_weak_reference)),
+ __func__);
+
+ LibWeakRefKey *key = lib_weak_key_create(NULL, library_filepath, library_id_name);
+ void **id_p;
+ const bool already_exist_in_mapping = BLI_ghash_ensure_p(
+ library_weak_reference_mapping, key, &id_p);
+ BLI_assert(!already_exist_in_mapping);
+
+ BLI_strncpy(new_id->library_weak_reference->library_filepath,
+ library_filepath,
+ sizeof(new_id->library_weak_reference->library_filepath));
+ BLI_strncpy(new_id->library_weak_reference->library_id_name,
+ library_id_name,
+ sizeof(new_id->library_weak_reference->library_id_name));
+ *id_p = new_id;
+}
+
+/**
+ * Update the status of the given ID weak library reference in current local IDs and the runtime
+ * mapping.
+ *
+ * This effectively transfers the 'ownership' of the given weak reference from `old_id` to
+ * `new_id`.
+ *
+ * \param library_weak_reference_mapping: the mapping data generated by
+ * #BKE_main_library_weak_reference_create.
+ * \param library_relative_path: the path of a blend file library (relative to current working
+ * one).
+ * \param library_id_name: the full ID name, including the leading two chars encoding the ID type.
+ * \param old_id: Existing local ID matching given weak reference.
+ * \param new_id: New local ID matching given weak reference.
+ */
+void BKE_main_library_weak_reference_update_item(GHash *library_weak_reference_mapping,
+ const char *library_filepath,
+ const char *library_id_name,
+ ID *old_id,
+ ID *new_id)
+{
+ BLI_assert(GS(library_id_name) == GS(old_id->name));
+ BLI_assert(GS(library_id_name) == GS(new_id->name));
+ BLI_assert(old_id->library_weak_reference != NULL);
+ BLI_assert(new_id->library_weak_reference == NULL);
+ BLI_assert(STREQ(old_id->library_weak_reference->library_filepath, library_filepath));
+ BLI_assert(STREQ(old_id->library_weak_reference->library_id_name, library_id_name));
+
+ LibWeakRefKey key;
+ lib_weak_key_create(&key, library_filepath, library_id_name);
+ void **id_p = BLI_ghash_lookup_p(library_weak_reference_mapping, &key);
+ BLI_assert(id_p != NULL && *id_p == old_id);
+
+ new_id->library_weak_reference = old_id->library_weak_reference;
+ old_id->library_weak_reference = NULL;
+ *id_p = new_id;
+}
+
+/**
+ * Remove the given ID weak library reference from the given local ID and the runtime mapping.
+ *
+ * \param library_weak_reference_mapping: the mapping data generated by
+ * #BKE_main_library_weak_reference_create.
+ * \param library_relative_path: the path of a blend file library (relative to current working
+ * one).
+ * \param library_id_name: the full ID name, including the leading two chars encoding the ID type.
+ * \param old_id: Existing local ID matching given weak reference.
+ */
+void BKE_main_library_weak_reference_remove_item(GHash *library_weak_reference_mapping,
+ const char *library_filepath,
+ const char *library_id_name,
+ ID *old_id)
+{
+ BLI_assert(GS(library_id_name) == GS(old_id->name));
+ BLI_assert(old_id->library_weak_reference != NULL);
+
+ LibWeakRefKey key;
+ lib_weak_key_create(&key, library_filepath, library_id_name);
+
+ BLI_assert(BLI_ghash_lookup(library_weak_reference_mapping, &key) == old_id);
+ BLI_ghash_remove(library_weak_reference_mapping, &key, MEM_freeN, NULL);
+
+ MEM_SAFE_FREE(old_id->library_weak_reference);
+}
+
/**
* Generates a raw .blend file thumbnail data from given image.
*
diff --git a/source/blender/blenkernel/intern/mask.c b/source/blender/blenkernel/intern/mask.c
index a93fcb6e8e0..1d3ebaac303 100644
--- a/source/blender/blenkernel/intern/mask.c
+++ b/source/blender/blenkernel/intern/mask.c
@@ -254,7 +254,7 @@ IDTypeInfo IDType_ID_MSK = {
.name = "Mask",
.name_plural = "masks",
.translation_context = BLT_I18NCONTEXT_ID_MASK,
- .flags = 0,
+ .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE,
.init_data = NULL,
.copy_data = mask_copy_data,
diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c
index 13b5bca5638..5f53d5e1ae8 100644
--- a/source/blender/blenkernel/intern/material.c
+++ b/source/blender/blenkernel/intern/material.c
@@ -259,7 +259,7 @@ IDTypeInfo IDType_ID_MA = {
.name = "Material",
.name_plural = "materials",
.translation_context = BLT_I18NCONTEXT_ID_MATERIAL,
- .flags = 0,
+ .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE,
.init_data = material_init_data,
.copy_data = material_copy_data,
diff --git a/source/blender/blenkernel/intern/mball.c b/source/blender/blenkernel/intern/mball.c
index 45cf0f17840..6c8664aefed 100644
--- a/source/blender/blenkernel/intern/mball.c
+++ b/source/blender/blenkernel/intern/mball.c
@@ -188,7 +188,7 @@ IDTypeInfo IDType_ID_MB = {
.name = "Metaball",
.name_plural = "metaballs",
.translation_context = BLT_I18NCONTEXT_ID_METABALL,
- .flags = 0,
+ .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE,
.init_data = metaball_init_data,
.copy_data = metaball_copy_data,
diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c
index d631993c4e8..ed3766ad6a3 100644
--- a/source/blender/blenkernel/intern/mesh.c
+++ b/source/blender/blenkernel/intern/mesh.c
@@ -360,7 +360,7 @@ IDTypeInfo IDType_ID_ME = {
.name = "Mesh",
.name_plural = "meshes",
.translation_context = BLT_I18NCONTEXT_ID_MESH,
- .flags = 0,
+ .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE,
.init_data = mesh_init_data,
.copy_data = mesh_copy_data,
diff --git a/source/blender/blenkernel/intern/movieclip.c b/source/blender/blenkernel/intern/movieclip.c
index e507252307b..0c2ac841b87 100644
--- a/source/blender/blenkernel/intern/movieclip.c
+++ b/source/blender/blenkernel/intern/movieclip.c
@@ -346,7 +346,7 @@ IDTypeInfo IDType_ID_MC = {
.name = "MovieClip",
.name_plural = "movieclips",
.translation_context = BLT_I18NCONTEXT_ID_MOVIECLIP,
- .flags = 0,
+ .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE,
.init_data = movie_clip_init_data,
.copy_data = movie_clip_copy_data,
diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc
index 3c54d88c93a..d56a7bf8fb4 100644
--- a/source/blender/blenkernel/intern/node.cc
+++ b/source/blender/blenkernel/intern/node.cc
@@ -992,7 +992,7 @@ IDTypeInfo IDType_ID_NT = {
/* name */ "NodeTree",
/* name_plural */ "node_groups",
/* translation_context */ BLT_I18NCONTEXT_ID_NODETREE,
- /* flags */ 0,
+ /* flags */ IDTYPE_FLAGS_APPEND_IS_REUSABLE,
/* init_data */ ntree_init_data,
/* copy_data */ ntree_copy_data,
diff --git a/source/blender/blenkernel/intern/pointcloud.cc b/source/blender/blenkernel/intern/pointcloud.cc
index 837a772607f..1db14dc3dc8 100644
--- a/source/blender/blenkernel/intern/pointcloud.cc
+++ b/source/blender/blenkernel/intern/pointcloud.cc
@@ -174,7 +174,7 @@ IDTypeInfo IDType_ID_PT = {
/* name */ "PointCloud",
/* name_plural */ "pointclouds",
/* translation_context */ BLT_I18NCONTEXT_ID_POINTCLOUD,
- /* flags */ 0,
+ /* flags */ IDTYPE_FLAGS_APPEND_IS_REUSABLE,
/* init_data */ pointcloud_init_data,
/* copy_data */ pointcloud_copy_data,
diff --git a/source/blender/blenkernel/intern/simulation.cc b/source/blender/blenkernel/intern/simulation.cc
index 4c97ccdf8b1..1d297b3ced9 100644
--- a/source/blender/blenkernel/intern/simulation.cc
+++ b/source/blender/blenkernel/intern/simulation.cc
@@ -152,7 +152,7 @@ IDTypeInfo IDType_ID_SIM = {
/* name */ "Simulation",
/* name_plural */ "simulations",
/* translation_context */ BLT_I18NCONTEXT_ID_SIMULATION,
- /* flags */ 0,
+ /* flags */ IDTYPE_FLAGS_APPEND_IS_REUSABLE,
/* init_data */ simulation_init_data,
/* copy_data */ simulation_copy_data,
diff --git a/source/blender/blenkernel/intern/sound.c b/source/blender/blenkernel/intern/sound.c
index 093e4f42d4a..8feda76cc5b 100644
--- a/source/blender/blenkernel/intern/sound.c
+++ b/source/blender/blenkernel/intern/sound.c
@@ -204,7 +204,7 @@ IDTypeInfo IDType_ID_SO = {
.name = "Sound",
.name_plural = "sounds",
.translation_context = BLT_I18NCONTEXT_ID_SOUND,
- .flags = IDTYPE_FLAGS_NO_ANIMDATA,
+ .flags = IDTYPE_FLAGS_NO_ANIMDATA | IDTYPE_FLAGS_APPEND_IS_REUSABLE,
/* A fuzzy case, think NULLified content is OK here... */
.init_data = NULL,
diff --git a/source/blender/blenkernel/intern/speaker.c b/source/blender/blenkernel/intern/speaker.c
index 4b10522c375..b361f31cc30 100644
--- a/source/blender/blenkernel/intern/speaker.c
+++ b/source/blender/blenkernel/intern/speaker.c
@@ -98,7 +98,7 @@ IDTypeInfo IDType_ID_SPK = {
.name = "Speaker",
.name_plural = "speakers",
.translation_context = BLT_I18NCONTEXT_ID_SPEAKER,
- .flags = 0,
+ .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE,
.init_data = speaker_init_data,
.copy_data = NULL,
diff --git a/source/blender/blenkernel/intern/text.c b/source/blender/blenkernel/intern/text.c
index f67bf68010d..5eb40b6624a 100644
--- a/source/blender/blenkernel/intern/text.c
+++ b/source/blender/blenkernel/intern/text.c
@@ -241,7 +241,7 @@ IDTypeInfo IDType_ID_TXT = {
.name = "Text",
.name_plural = "texts",
.translation_context = BLT_I18NCONTEXT_ID_TEXT,
- .flags = IDTYPE_FLAGS_NO_ANIMDATA,
+ .flags = IDTYPE_FLAGS_NO_ANIMDATA | IDTYPE_FLAGS_APPEND_IS_REUSABLE,
.init_data = text_init_data,
.copy_data = text_copy_data,
diff --git a/source/blender/blenkernel/intern/texture.c b/source/blender/blenkernel/intern/texture.c
index 228e6fffdf7..d5f7647f07a 100644
--- a/source/blender/blenkernel/intern/texture.c
+++ b/source/blender/blenkernel/intern/texture.c
@@ -210,7 +210,7 @@ IDTypeInfo IDType_ID_TE = {
.name = "Texture",
.name_plural = "textures",
.translation_context = BLT_I18NCONTEXT_ID_TEXTURE,
- .flags = 0,
+ .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE,
.init_data = texture_init_data,
.copy_data = texture_copy_data,
diff --git a/source/blender/blenkernel/intern/volume.cc b/source/blender/blenkernel/intern/volume.cc
index 69452d6896f..0b9ef5c537d 100644
--- a/source/blender/blenkernel/intern/volume.cc
+++ b/source/blender/blenkernel/intern/volume.cc
@@ -642,7 +642,7 @@ IDTypeInfo IDType_ID_VO = {
/* name */ "Volume",
/* name_plural */ "volumes",
/* translation_context */ BLT_I18NCONTEXT_ID_VOLUME,
- /* flags */ 0,
+ /* flags */ IDTYPE_FLAGS_APPEND_IS_REUSABLE,
/* init_data */ volume_init_data,
/* copy_data */ volume_copy_data,
diff --git a/source/blender/blenkernel/intern/world.c b/source/blender/blenkernel/intern/world.c
index 4abe1ff0f20..fe03c5b817a 100644
--- a/source/blender/blenkernel/intern/world.c
+++ b/source/blender/blenkernel/intern/world.c
@@ -190,7 +190,7 @@ IDTypeInfo IDType_ID_WO = {
.name = "World",
.name_plural = "worlds",
.translation_context = BLT_I18NCONTEXT_ID_WORLD,
- .flags = 0,
+ .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE,
.init_data = world_init_data,
.copy_data = world_copy_data,
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index 15653264211..3cda1e613f8 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -2195,6 +2195,13 @@ static void direct_link_id_common(
/* Initialize with provided tag. */
id->tag = tag;
+ if (ID_IS_LINKED(id)) {
+ id->library_weak_reference = NULL;
+ }
+ else {
+ BLO_read_data_address(reader, &id->library_weak_reference);
+ }
+
if (tag & LIB_TAG_ID_LINK_PLACEHOLDER) {
/* For placeholder we only need to set the tag and properly initialize generic ID fields above,
* no further data to read. */
diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h
index 5b7c99f2545..d829d707a71 100644
--- a/source/blender/makesdna/DNA_ID.h
+++ b/source/blender/makesdna/DNA_ID.h
@@ -392,7 +392,14 @@ typedef struct ID {
* that references this ID (the bones of an armature or the modifiers of an object for e.g.).
*/
void *py_instance;
- void *_pad1;
+
+ /**
+ * Weak reference to an ID in a given library file, used to allow re-using already appended data
+ * in some cases, instead of appending it again.
+ *
+ * May be NULL.
+ */
+ struct LibraryWeakReference *library_weak_reference;
} ID;
/**
@@ -426,6 +433,26 @@ typedef struct Library {
short versionfile, subversionfile;
} Library;
+/**
+ * A weak library/ID reference for local data that has been appended, to allow re-using that local
+ * data instead of creating a new copy of it in future appends.
+ *
+ * NOTE: This is by design a week reference, in other words code should be totally fine and perform
+ * a regular append if it cannot find a valid matching local ID.
+ *
+ * NOTE: There should always be only one single ID in current Main matching a given linked
+ * reference.
+ */
+typedef struct LibraryWeakReference {
+ /** Expected to match a `Library.filepath`. */
+ char library_filepath[1024];
+
+ /** MAX_ID_NAME. May be different from the current local ID name. */
+ char library_id_name[66];
+
+ char _pad[2];
+} LibraryWeakReference;
+
/* for PreviewImage->flag */
enum ePreviewImage_Flag {
PRV_CHANGED = (1 << 0),
diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c
index eb887e1881b..1113ac0eb45 100644
--- a/source/blender/makesrna/intern/rna_ID.c
+++ b/source/blender/makesrna/intern/rna_ID.c
@@ -1936,6 +1936,15 @@ static void rna_def_ID(BlenderRNA *brna)
RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON);
RNA_def_property_ui_text(prop, "Library", "Library file the data-block is linked from");
+ prop = RNA_def_pointer(srna,
+ "library_weak_reference",
+ "LibraryWeakReference",
+ "Library Weak Reference",
+ "Weak reference to a data-block in another library .blend file (used to "
+ "re-use already appended data instead of appending new copies)");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON);
+
prop = RNA_def_property(srna, "asset_data", PROP_POINTER, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON);
@@ -2143,6 +2152,31 @@ static void rna_def_library(BlenderRNA *brna)
RNA_def_function_ui_description(func, "Reload this library and all its linked data-blocks");
}
+static void rna_def_library_weak_reference(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "LibraryWeakReference", NULL);
+ RNA_def_struct_ui_text(
+ srna,
+ "LibraryWeakReference",
+ "Read-only external reference to a linked data-block and its library file");
+
+ prop = RNA_def_property(srna, "filepath", PROP_STRING, PROP_FILEPATH);
+ RNA_def_property_string_sdna(prop, NULL, "library_filepath");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "File Path", "Path to the library .blend file");
+
+ prop = RNA_def_property(srna, "id_name", PROP_STRING, PROP_FILEPATH);
+ RNA_def_property_string_sdna(prop, NULL, "library_id_name");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(
+ prop,
+ "ID name",
+ "Full ID name in the library .blend file (including the two leading 'id type' chars)");
+}
+
/**
* \attention This is separate from the above. It allows for RNA functions to
* return an IDProperty *. See MovieClip.metadata for a usage example.
@@ -2175,6 +2209,7 @@ void RNA_def_ID(BlenderRNA *brna)
rna_def_ID_properties(brna);
rna_def_ID_materials(brna);
rna_def_library(brna);
+ rna_def_library_weak_reference(brna);
rna_def_idproperty_wrap_ptr(brna);
}
diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c
index d0117d9d57a..09567eca17f 100644
--- a/source/blender/windowmanager/intern/wm_files_link.c
+++ b/source/blender/windowmanager/intern/wm_files_link.c
@@ -190,6 +190,9 @@ typedef struct WMLinkAppendData {
/** Allows to easily find an existing items from an ID pointer. Used by append code. */
GHash *new_id_to_item;
+ /** Runtime info used by append code to manage re-use of already appended matching IDs. */
+ GHash *library_weak_reference_mapping;
+
/* Internal 'private' data */
MemArena *memarena;
} WMLinkAppendData;
@@ -230,6 +233,8 @@ static void wm_link_append_data_free(WMLinkAppendData *lapp_data)
BLI_ghash_free(lapp_data->new_id_to_item, NULL, NULL);
}
+ BLI_assert(lapp_data->library_weak_reference_mapping == NULL);
+
BLI_memarena_free(lapp_data->memarena);
}
@@ -637,6 +642,9 @@ static void wm_append_do(WMLinkAppendData *lapp_data,
BLI_ghash_insert(lapp_data->new_id_to_item, id, item);
}
+ const bool do_reuse_existing_id = false;
+ lapp_data->library_weak_reference_mapping = BKE_main_library_weak_reference_create(bmain);
+
/* NOTE: Since we append items for IDs not already listed (i.e. implicitly linked indirect
* dependencies), this list will grow and we will process those IDs later, leading to a flatten
* recursive processing of all the linked dependencies. */
@@ -650,7 +658,14 @@ static void wm_append_do(WMLinkAppendData *lapp_data,
/* Clear tag previously used to mark IDs needing post-processing (instantiation of loose
* objects etc.). */
- id->tag &= ~LIB_TAG_DOIT;
+ BLI_assert((id->tag & LIB_TAG_DOIT) == 0);
+
+ ID *existing_local_id = BKE_idtype_idcode_append_is_reusable(GS(id->name)) ?
+ BKE_main_library_weak_reference_search_item(
+ lapp_data->library_weak_reference_mapping,
+ id->lib->filepath,
+ id->name) :
+ NULL;
if (item->append_action != WM_APPEND_ACT_UNSET) {
/* Already set, pass. */
@@ -659,6 +674,14 @@ static void wm_append_do(WMLinkAppendData *lapp_data,
CLOG_INFO(&LOG, 3, "Appended ID '%s' is proxified, keeping it linked...", id->name);
item->append_action = WM_APPEND_ACT_KEEP_LINKED;
}
+ /* Only re-use existing local ID for indirectly linked data, the ID explicitely selected by the
+ * user we always fully append. */
+ else if (do_reuse_existing_id && existing_local_id != NULL &&
+ (item->append_tag & WM_APPEND_TAG_INDIRECT) != 0) {
+ CLOG_INFO(&LOG, 3, "Appended ID '%s' as a matching local one, re-using it...", id->name);
+ item->append_action = WM_APPEND_ACT_REUSE_LOCAL;
+ item->customdata = existing_local_id;
+ }
else if (id->tag & LIB_TAG_PRE_EXISTING) {
CLOG_INFO(&LOG, 3, "Appended ID '%s' was already linked, need to copy it...", id->name);
item->append_action = WM_APPEND_ACT_COPY_LOCAL;
@@ -678,6 +701,16 @@ static void wm_append_do(WMLinkAppendData *lapp_data,
BKE_library_foreach_ID_link(
bmain, id, foreach_libblock_append_callback, &cb_data, IDWALK_NOP);
}
+
+ /* If we found a matching existing local id but are not re-using it, we need to properly clear
+ * its weak reference to linked data. */
+ if (existing_local_id != NULL &&
+ !ELEM(item->append_action, WM_APPEND_ACT_KEEP_LINKED, WM_APPEND_ACT_REUSE_LOCAL)) {
+ BKE_main_library_weak_reference_remove_item(lapp_data->library_weak_reference_mapping,
+ id->lib->filepath,
+ id->name,
+ existing_local_id);
+ }
}
/* Effectively perform required operation on every linked ID. */
@@ -689,6 +722,11 @@ static void wm_append_do(WMLinkAppendData *lapp_data,
}
ID *local_appended_new_id = NULL;
+ char lib_filepath[FILE_MAX];
+ BLI_strncpy(lib_filepath, id->lib->filepath, sizeof(lib_filepath));
+ char lib_id_name[MAX_ID_NAME];
+ BLI_strncpy(lib_id_name, id->name, sizeof(lib_id_name));
+
switch (item->append_action) {
case WM_APPEND_ACT_COPY_LOCAL: {
BKE_lib_id_make_local(
@@ -721,6 +759,13 @@ static void wm_append_do(WMLinkAppendData *lapp_data,
}
if (local_appended_new_id != NULL) {
+ if (BKE_idtype_idcode_append_is_reusable(GS(local_appended_new_id->name))) {
+ BKE_main_library_weak_reference_add_item(lapp_data->library_weak_reference_mapping,
+ lib_filepath,
+ lib_id_name,
+ local_appended_new_id);
+ }
+
if (GS(local_appended_new_id->name) == ID_OB) {
BKE_rigidbody_ensure_local_object(bmain, (Object *)local_appended_new_id);
}
@@ -733,6 +778,9 @@ static void wm_append_do(WMLinkAppendData *lapp_data,
}
}
+ BKE_main_library_weak_reference_destroy(lapp_data->library_weak_reference_mapping);
+ lapp_data->library_weak_reference_mapping = NULL;
+
/* Remap IDs as needed. */
for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) {
WMLinkAppendDataItem *item = itemlink->link;
@@ -745,7 +793,7 @@ static void wm_append_do(WMLinkAppendData *lapp_data,
if (id == NULL) {
continue;
}
- if (item->append_action == WM_APPEND_ACT_COPY_LOCAL) {
+ if (ELEM(item->append_action, WM_APPEND_ACT_COPY_LOCAL, WM_APPEND_ACT_REUSE_LOCAL)) {
BLI_assert(ID_IS_LINKED(id));
id = id->newid;
if (id == NULL) {