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/blenkernel/intern/lib_override.c')
-rw-r--r--source/blender/blenkernel/intern/lib_override.c759
1 files changed, 568 insertions, 191 deletions
diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c
index 8341c5b6e78..c93971e7b11 100644
--- a/source/blender/blenkernel/intern/lib_override.c
+++ b/source/blender/blenkernel/intern/lib_override.c
@@ -48,6 +48,7 @@
#include "BKE_lib_query.h"
#include "BKE_lib_remap.h"
#include "BKE_main.h"
+#include "BKE_node.h"
#include "BKE_report.h"
#include "BKE_scene.h"
@@ -80,6 +81,19 @@ static void lib_override_library_property_clear(IDOverrideLibraryProperty *op);
static void lib_override_library_property_operation_clear(
IDOverrideLibraryPropertyOperation *opop);
+/** Get override data for a given ID. Needed because of our beloved shape keys snowflake. */
+BLI_INLINE IDOverrideLibrary *lib_override_get(Main *bmain, ID *id)
+{
+ if (id->flag & LIB_EMBEDDED_DATA_LIB_OVERRIDE) {
+ const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id);
+ if (id_type->owner_get != NULL) {
+ return id_type->owner_get(bmain, id)->override_library;
+ }
+ BLI_assert(!"IDTypeInfo of liboverride-embedded ID with no owner getter");
+ }
+ return id->override_library;
+}
+
/** Initialize empty overriding of \a reference_id by \a local_id. */
IDOverrideLibrary *BKE_lib_override_library_init(ID *local_id, ID *reference_id)
{
@@ -194,12 +208,17 @@ void BKE_lib_override_library_free(struct IDOverrideLibrary **override, const bo
*override = NULL;
}
-static ID *lib_override_library_create_from(Main *bmain, ID *reference_id)
+static ID *lib_override_library_create_from(Main *bmain,
+ ID *reference_id,
+ const int lib_id_copy_flags)
{
/* Note: We do not want to copy possible override data from reference here (whether it is an
* override template, or already an override of some other ref data). */
- ID *local_id = BKE_id_copy_ex(
- bmain, reference_id, NULL, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_NO_LIB_OVERRIDE);
+ ID *local_id = BKE_id_copy_ex(bmain,
+ reference_id,
+ NULL,
+ LIB_ID_COPY_DEFAULT | LIB_ID_COPY_NO_LIB_OVERRIDE |
+ lib_id_copy_flags);
if (local_id == NULL) {
return NULL;
@@ -233,6 +252,13 @@ bool BKE_lib_override_library_is_user_edited(struct ID *id)
return false;
}
+ /* A bit weird, but those embedded IDs are handled by their owner ID anyway, so we can just
+ * assume they are never user-edited, actual proper detection will happen from their owner check.
+ */
+ if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
+ return false;
+ }
+
LISTBASE_FOREACH (IDOverrideLibraryProperty *, op, &id->override_library->properties) {
LISTBASE_FOREACH (IDOverrideLibraryPropertyOperation *, opop, &op->operations) {
if ((opop->flag & IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE) != 0) {
@@ -257,7 +283,7 @@ ID *BKE_lib_override_library_create_from_id(Main *bmain,
BLI_assert(reference_id != NULL);
BLI_assert(reference_id->lib != NULL);
- ID *local_id = lib_override_library_create_from(bmain, reference_id);
+ ID *local_id = lib_override_library_create_from(bmain, reference_id, 0);
if (do_tagged_remap) {
Key *reference_key, *local_key = NULL;
@@ -302,9 +328,17 @@ ID *BKE_lib_override_library_create_from_id(Main *bmain,
* main. You can add more local IDs to be remapped to use new overriding ones by setting their
* LIB_TAG_DOIT tag.
*
+ * \param reference_library the library from which the linked data being overridden come from
+ * (i.e. the library of the linked reference ID).
+ *
+ * \param do_no_main Create the new override data outside of Main database. Used for resyncing of
+ * linked overrides.
+ *
* \return \a true on success, \a false otherwise.
*/
-bool BKE_lib_override_library_create_from_tag(Main *bmain)
+bool BKE_lib_override_library_create_from_tag(Main *bmain,
+ const Library *reference_library,
+ const bool do_no_main)
{
ID *reference_id;
bool success = true;
@@ -314,7 +348,7 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain)
/* Get all IDs we want to override. */
FOREACH_MAIN_ID_BEGIN (bmain, reference_id) {
- if ((reference_id->tag & LIB_TAG_DOIT) != 0 && reference_id->lib != NULL &&
+ if ((reference_id->tag & LIB_TAG_DOIT) != 0 && reference_id->lib == reference_library &&
BKE_idtype_idcode_is_linkable(GS(reference_id->name))) {
todo_id_iter = MEM_callocN(sizeof(*todo_id_iter), __func__);
todo_id_iter->data = reference_id;
@@ -326,10 +360,16 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain)
/* Override the IDs. */
for (todo_id_iter = todo_ids.first; todo_id_iter != NULL; todo_id_iter = todo_id_iter->next) {
reference_id = todo_id_iter->data;
+
+ /* If `newid` is already set, assume it has been handled by calling code.
+ * Only current use case: re-using proxy ID when converting to liboverride. */
if (reference_id->newid == NULL) {
- /* If `newid` is already set, assume it has been handled by calling code.
- * Only current use case: re-using proxy ID when converting to liboverride. */
- if ((reference_id->newid = lib_override_library_create_from(bmain, reference_id)) == NULL) {
+ /* Note: `no main` case is used during resync procedure, to support recursive resync.
+ * This requires extra care further down the resync process,
+ * see: #BKE_lib_override_library_resync. */
+ reference_id->newid = lib_override_library_create_from(
+ bmain, reference_id, do_no_main ? LIB_ID_CREATE_NO_MAIN : 0);
+ if (reference_id->newid == NULL) {
success = false;
break;
}
@@ -369,23 +409,43 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain)
/* Still checking the whole Main, that way we can tag other local IDs as needing to be
* remapped to use newly created overriding IDs, if needed. */
- FOREACH_MAIN_ID_BEGIN (bmain, other_id) {
- if ((other_id->tag & LIB_TAG_DOIT) != 0 && other_id->lib == NULL) {
- /* Note that using ID_REMAP_SKIP_INDIRECT_USAGE below is superfluous, as we only remap
- * local IDs usages anyway. */
+ ID *id;
+ FOREACH_MAIN_ID_BEGIN (bmain, id) {
+ /* In case we created new overrides as 'no main', they are not accessible directly in this
+ * loop, but we can get to them through their reference's `newid` pointer. */
+ if (do_no_main && id->lib == reference_id->lib && id->newid != NULL) {
+ other_id = id->newid;
+ /* Otherwise we cannot properly distinguish between IDs that are actually from the
+ * linked library (and should not be remapped), and IDs that are overrides re-generated
+ * from the reference from the linked library, and must therefore be remapped.
+ *
+ * This is reset afterwards at the end of this loop. */
+ other_id->lib = NULL;
+ }
+ else {
+ other_id = id;
+ }
+
+ /* If other ID is a linked one, but not from the same library as our reference, then we
+ * consider we should also remap it, as part of recursive resync. */
+ if ((other_id->tag & LIB_TAG_DOIT) != 0 && other_id->lib != reference_id->lib &&
+ other_id != local_id) {
BKE_libblock_relink_ex(bmain,
other_id,
reference_id,
local_id,
- ID_REMAP_SKIP_INDIRECT_USAGE | ID_REMAP_SKIP_OVERRIDE_LIBRARY);
+ ID_REMAP_SKIP_OVERRIDE_LIBRARY | ID_REMAP_FORCE_USER_REFCOUNT);
if (reference_key != NULL) {
BKE_libblock_relink_ex(bmain,
other_id,
&reference_key->id,
&local_key->id,
- ID_REMAP_SKIP_INDIRECT_USAGE | ID_REMAP_SKIP_OVERRIDE_LIBRARY);
+ ID_REMAP_SKIP_OVERRIDE_LIBRARY | ID_REMAP_FORCE_USER_REFCOUNT);
}
}
+ if (other_id != id) {
+ other_id->lib = reference_id->lib;
+ }
}
FOREACH_MAIN_ID_END;
}
@@ -409,6 +469,8 @@ typedef struct LibOverrideGroupTagData {
ID *id_root;
uint tag;
uint missing_tag;
+ /* Whether we are looping on override data, or their references (linked) one. */
+ bool is_override;
} LibOverrideGroupTagData;
/* Tag all IDs in dependency relationships within an override hierarchy/group.
@@ -421,6 +483,7 @@ static bool lib_override_hierarchy_dependencies_recursive_tag(LibOverrideGroupTa
{
Main *bmain = data->bmain;
ID *id = data->id_root;
+ const bool is_override = data->is_override;
MainIDRelationsEntry *entry = BLI_ghash_lookup(bmain->relations->relations_from_pointers, id);
BLI_assert(entry != NULL);
@@ -442,16 +505,16 @@ static bool lib_override_hierarchy_dependencies_recursive_tag(LibOverrideGroupTa
}
/* We only consider IDs from the same library. */
ID *to_id = *to_id_entry->id_pointer.to;
- if (!ID_IS_LINKED(to_id) && !ID_IS_OVERRIDE_LIBRARY(to_id)) {
- /* Pure local data is a barrier of dependency in override cases. */
+ if (to_id == NULL || to_id->lib != id->lib ||
+ (is_override && !ID_IS_OVERRIDE_LIBRARY(to_id))) {
+ /* IDs from different libraries, or non-override IDs in case we are processing overrides, are
+ * both barriers of dependency. */
continue;
}
- if (to_id != NULL && to_id->lib == id->lib) {
- LibOverrideGroupTagData sub_data = *data;
- sub_data.id_root = to_id;
- if (lib_override_hierarchy_dependencies_recursive_tag(&sub_data)) {
- id->tag |= data->tag;
- }
+ LibOverrideGroupTagData sub_data = *data;
+ sub_data.id_root = to_id;
+ if (lib_override_hierarchy_dependencies_recursive_tag(&sub_data)) {
+ id->tag |= data->tag;
}
}
@@ -463,6 +526,7 @@ static void lib_override_linked_group_tag_recursive(LibOverrideGroupTagData *dat
Main *bmain = data->bmain;
ID *id_owner = data->id_root;
BLI_assert(ID_IS_LINKED(id_owner));
+ BLI_assert(!data->is_override);
const uint tag = data->tag;
const uint missing_tag = data->missing_tag;
@@ -528,6 +592,7 @@ static void lib_override_linked_group_tag(LibOverrideGroupTagData *data)
{
Main *bmain = data->bmain;
ID *id_root = data->id_root;
+ BLI_assert(!data->is_override);
if ((id_root->tag & LIB_TAG_MISSING)) {
id_root->tag |= data->missing_tag;
@@ -554,11 +619,12 @@ static void lib_override_linked_group_tag(LibOverrideGroupTagData *data)
}
}
-static void lib_override_local_group_tag_recursive(LibOverrideGroupTagData *data)
+static void lib_override_overrides_group_tag_recursive(LibOverrideGroupTagData *data)
{
Main *bmain = data->bmain;
ID *id_owner = data->id_root;
BLI_assert(ID_IS_OVERRIDE_LIBRARY(id_owner));
+ BLI_assert(data->is_override);
const uint tag = data->tag;
const uint missing_tag = data->missing_tag;
@@ -585,45 +651,37 @@ static void lib_override_local_group_tag_recursive(LibOverrideGroupTagData *data
if (ELEM(to_id, NULL, id_owner)) {
continue;
}
- if (!ID_IS_OVERRIDE_LIBRARY(to_id) || ID_IS_LINKED(to_id)) {
+ if (!ID_IS_OVERRIDE_LIBRARY(to_id) || (to_id->lib != id_owner->lib)) {
continue;
}
- /* Do not tag 'virtual' overrides (shape keys here, as we already rejected embedded case
- * above). */
- if (ID_IS_OVERRIDE_LIBRARY_REAL(to_id)) {
- Library *reference_lib = NULL;
- if (GS(id_owner->name) == ID_KE) {
- reference_lib = ((Key *)id_owner)->from->override_library->reference->lib;
- }
- else {
- reference_lib = id_owner->override_library->reference->lib;
- }
- if (to_id->override_library->reference->lib != reference_lib) {
- /* We do not override data-blocks from other libraries, nor do we process them. */
- continue;
- }
+ Library *reference_lib = lib_override_get(bmain, id_owner)->reference->lib;
+ ID *to_id_reference = lib_override_get(bmain, to_id)->reference;
+ if (to_id_reference->lib != reference_lib) {
+ /* We do not override data-blocks from other libraries, nor do we process them. */
+ continue;
+ }
- if (to_id->override_library->reference->tag & LIB_TAG_MISSING) {
- to_id->tag |= missing_tag;
- }
- else {
- to_id->tag |= tag;
- }
+ if (to_id_reference->tag & LIB_TAG_MISSING) {
+ to_id->tag |= missing_tag;
+ }
+ else {
+ to_id->tag |= tag;
}
/* Recursively process the dependencies. */
LibOverrideGroupTagData sub_data = *data;
sub_data.id_root = to_id;
- lib_override_local_group_tag_recursive(&sub_data);
+ lib_override_overrides_group_tag_recursive(&sub_data);
}
}
/* This will tag all override IDs of an override group defined by the given `id_root`. */
-static void lib_override_local_group_tag(LibOverrideGroupTagData *data)
+static void lib_override_overrides_group_tag(LibOverrideGroupTagData *data)
{
ID *id_root = data->id_root;
- BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root) && !ID_IS_LINKED(id_root));
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root));
+ BLI_assert(data->is_override);
if ((id_root->override_library->reference->tag & LIB_TAG_MISSING)) {
id_root->tag |= data->missing_tag;
@@ -633,14 +691,17 @@ static void lib_override_local_group_tag(LibOverrideGroupTagData *data)
}
/* Tag all local overrides in id_root's group. */
- lib_override_local_group_tag_recursive(data);
+ lib_override_overrides_group_tag_recursive(data);
}
static bool lib_override_library_create_do(Main *bmain, ID *id_root)
{
BKE_main_relations_create(bmain, 0);
- LibOverrideGroupTagData data = {
- .bmain = bmain, .id_root = id_root, .tag = LIB_TAG_DOIT, .missing_tag = LIB_TAG_MISSING};
+ LibOverrideGroupTagData data = {.bmain = bmain,
+ .id_root = id_root,
+ .tag = LIB_TAG_DOIT,
+ .missing_tag = LIB_TAG_MISSING,
+ .is_override = false};
lib_override_linked_group_tag(&data);
BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
@@ -648,7 +709,7 @@ static bool lib_override_library_create_do(Main *bmain, ID *id_root)
BKE_main_relations_free(bmain);
- return BKE_lib_override_library_create_from_tag(bmain);
+ return BKE_lib_override_library_create_from_tag(bmain, id_root->lib, false);
}
static void lib_override_library_create_post_process(Main *bmain,
@@ -659,6 +720,9 @@ static void lib_override_library_create_post_process(Main *bmain,
Collection *residual_storage,
const bool is_resync)
{
+ /* NOTE: We only care about local IDs here, if a linked object is not instantiated in any way we
+ * do not do anything about it. */
+
BKE_main_collection_sync(bmain);
/* We create a set of all objects referenced into the scene by its hierarchy of collections.
@@ -668,7 +732,7 @@ static void lib_override_library_create_post_process(Main *bmain,
/* Instantiating the root collection or object should never be needed in resync case, since the
* old override would be remapped to the new one. */
- if (!is_resync && id_root != NULL && id_root->newid != NULL) {
+ if (!is_resync && id_root != NULL && id_root->newid != NULL && !ID_IS_LINKED(id_root->newid)) {
switch (GS(id_root->name)) {
case ID_GR: {
Object *ob_reference = id_reference != NULL && GS(id_reference->name) == ID_OB ?
@@ -713,56 +777,58 @@ static void lib_override_library_create_post_process(Main *bmain,
Collection *default_instantiating_collection = residual_storage;
LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
Object *ob_new = (Object *)ob->id.newid;
- if (ob_new != NULL) {
- BLI_assert(ob_new->id.override_library != NULL &&
- ob_new->id.override_library->reference == &ob->id);
-
- if (BLI_gset_lookup(all_objects_in_scene, ob_new) == NULL) {
- if (id_root != NULL && default_instantiating_collection == NULL) {
- ID *id_ref = id_root->newid != NULL ? id_root->newid : id_root;
- switch (GS(id_ref->name)) {
- case ID_GR: {
- /* Adding the object to a specific collection outside of the root overridden one is a
- * fairly bad idea (it breaks the override hierarchy concept). But there is no other
- * way to do this currently (we cannot add new collections to overridden root one,
- * this is not currently supported).
- * Since that will be fairly annoying and noisy, only do that in case the override
- * object is not part of any existing collection (i.e. its user count is 0). In
- * practice this should never happen I think. */
- if (ID_REAL_USERS(ob_new) != 0) {
- continue;
- }
- default_instantiating_collection = BKE_collection_add(
- bmain, (Collection *)id_root, "OVERRIDE_HIDDEN");
- /* Hide the collection from viewport and render. */
- default_instantiating_collection->flag |= COLLECTION_RESTRICT_VIEWPORT |
- COLLECTION_RESTRICT_RENDER;
- break;
+ if (ob_new == NULL || ob_new->id.lib != NULL) {
+ continue;
+ }
+
+ BLI_assert(ob_new->id.override_library != NULL &&
+ ob_new->id.override_library->reference == &ob->id);
+
+ if (BLI_gset_lookup(all_objects_in_scene, ob_new) == NULL) {
+ if (id_root != NULL && default_instantiating_collection == NULL) {
+ ID *id_ref = id_root->newid != NULL ? id_root->newid : id_root;
+ switch (GS(id_ref->name)) {
+ case ID_GR: {
+ /* Adding the object to a specific collection outside of the root overridden one is a
+ * fairly bad idea (it breaks the override hierarchy concept). But there is no other
+ * way to do this currently (we cannot add new collections to overridden root one,
+ * this is not currently supported).
+ * Since that will be fairly annoying and noisy, only do that in case the override
+ * object is not part of any existing collection (i.e. its user count is 0). In
+ * practice this should never happen I think. */
+ if (ID_REAL_USERS(ob_new) != 0) {
+ continue;
}
- case ID_OB: {
- /* Add the other objects to one of the collections instantiating the
- * root object, or scene's master collection if none found. */
- Object *ob_ref = (Object *)id_ref;
- LISTBASE_FOREACH (Collection *, collection, &bmain->collections) {
- if (BKE_collection_has_object(collection, ob_ref) &&
- BKE_view_layer_has_collection(view_layer, collection) &&
- !ID_IS_LINKED(collection) && !ID_IS_OVERRIDE_LIBRARY(collection)) {
- default_instantiating_collection = collection;
- }
+ default_instantiating_collection = BKE_collection_add(
+ bmain, (Collection *)id_root, "OVERRIDE_HIDDEN");
+ /* Hide the collection from viewport and render. */
+ default_instantiating_collection->flag |= COLLECTION_RESTRICT_VIEWPORT |
+ COLLECTION_RESTRICT_RENDER;
+ break;
+ }
+ case ID_OB: {
+ /* Add the other objects to one of the collections instantiating the
+ * root object, or scene's master collection if none found. */
+ Object *ob_ref = (Object *)id_ref;
+ LISTBASE_FOREACH (Collection *, collection, &bmain->collections) {
+ if (BKE_collection_has_object(collection, ob_ref) &&
+ BKE_view_layer_has_collection(view_layer, collection) &&
+ !ID_IS_LINKED(collection) && !ID_IS_OVERRIDE_LIBRARY(collection)) {
+ default_instantiating_collection = collection;
}
- break;
}
- default:
- BLI_assert(0);
+ break;
}
+ default:
+ BLI_assert(0);
}
- if (default_instantiating_collection == NULL) {
- default_instantiating_collection = scene->master_collection;
- }
-
- BKE_collection_object_add(bmain, default_instantiating_collection, ob_new);
- DEG_id_tag_update_ex(bmain, &ob_new->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS);
}
+ if (default_instantiating_collection == NULL) {
+ default_instantiating_collection = scene->master_collection;
+ }
+
+ BKE_collection_object_add(bmain, default_instantiating_collection, ob_new);
+ DEG_id_tag_update_ex(bmain, &ob_new->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS);
}
}
@@ -772,7 +838,7 @@ static void lib_override_library_create_post_process(Main *bmain,
/**
* Advanced 'smart' function to create fully functional overrides.
*
- * \note Currently it only does special things if given \a id_root is an object of collection, more
+ * \note Currently it only does special things if given \a id_root is an object or collection, more
* specific behaviors may be added in the future for other ID types.
*
* \note It will override all IDs tagged with \a LIB_TAG_DOIT, and it does not clear that tag at
@@ -782,22 +848,35 @@ static void lib_override_library_create_post_process(Main *bmain,
* \param id_reference: Some reference ID used to do some post-processing after overrides have been
* created, may be NULL. Typically, the Empty object instantiating the linked collection we
* override, currently.
+ * \param r_id_root_override if not NULL, the override generated for the given \a id_root.
* \return true if override was successfully created.
*/
-bool BKE_lib_override_library_create(
- Main *bmain, Scene *scene, ViewLayer *view_layer, ID *id_root, ID *id_reference)
+bool BKE_lib_override_library_create(Main *bmain,
+ Scene *scene,
+ ViewLayer *view_layer,
+ ID *id_root,
+ ID *id_reference,
+ ID **r_id_root_override)
{
+ if (r_id_root_override != NULL) {
+ *r_id_root_override = NULL;
+ }
+
const bool success = lib_override_library_create_do(bmain, id_root);
if (!success) {
return success;
}
+ if (r_id_root_override != NULL) {
+ *r_id_root_override = id_root->newid;
+ }
+
lib_override_library_create_post_process(
bmain, scene, view_layer, id_root, id_reference, NULL, false);
/* Cleanup. */
- BKE_main_id_clear_newpoins(bmain);
+ BKE_main_id_newptr_and_tag_clear(bmain);
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
/* We need to rebuild some of the deleted override rules (for UI feedback purpose). */
@@ -862,7 +941,7 @@ bool BKE_lib_override_library_proxy_convert(Main *bmain,
DEG_id_tag_update(&ob_proxy->id, ID_RECALC_COPY_ON_WRITE);
- return BKE_lib_override_library_create(bmain, scene, view_layer, id_root, id_reference);
+ return BKE_lib_override_library_create(bmain, scene, view_layer, id_root, id_reference, NULL);
}
/**
@@ -882,17 +961,20 @@ bool BKE_lib_override_library_resync(Main *bmain,
ReportList *reports)
{
BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root));
- BLI_assert(!ID_IS_LINKED(id_root));
ID *id_root_reference = id_root->override_library->reference;
BKE_main_relations_create(bmain, 0);
- LibOverrideGroupTagData data = {
- .bmain = bmain, .id_root = id_root, .tag = LIB_TAG_DOIT, .missing_tag = LIB_TAG_MISSING};
- lib_override_local_group_tag(&data);
+ LibOverrideGroupTagData data = {.bmain = bmain,
+ .id_root = id_root,
+ .tag = LIB_TAG_DOIT,
+ .missing_tag = LIB_TAG_MISSING,
+ .is_override = true};
+ lib_override_overrides_group_tag(&data);
BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
data.id_root = id_root_reference;
+ data.is_override = false;
lib_override_linked_group_tag(&data);
BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
@@ -916,12 +998,35 @@ bool BKE_lib_override_library_resync(Main *bmain,
id->tag |= LIB_TAG_MISSING;
}
- if (id->tag & LIB_TAG_DOIT && !ID_IS_LINKED(id) && ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
+ if (id->tag & LIB_TAG_DOIT && (id->lib == id_root->lib) && ID_IS_OVERRIDE_LIBRARY(id)) {
/* While this should not happen in typical cases (and won't be properly supported here), user
* is free to do all kind of very bad things, including having different local overrides of a
* same linked ID in a same hierarchy. */
- if (!BLI_ghash_haskey(linkedref_to_old_override, id->override_library->reference)) {
- BLI_ghash_insert(linkedref_to_old_override, id->override_library->reference, id);
+ IDOverrideLibrary *id_override_library = lib_override_get(bmain, id);
+ ID *reference_id = id_override_library->reference;
+ if (GS(reference_id->name) != GS(id->name)) {
+ switch (GS(id->name)) {
+ case ID_KE:
+ reference_id = (ID *)BKE_key_from_id(reference_id);
+ break;
+ case ID_GR:
+ BLI_assert(GS(reference_id->name) == ID_SCE);
+ reference_id = (ID *)((Scene *)reference_id)->master_collection;
+ break;
+ case ID_NT:
+ reference_id = (ID *)ntreeFromID(id);
+ break;
+ default:
+ break;
+ }
+ }
+ BLI_assert(GS(reference_id->name) == GS(id->name));
+
+ if (!BLI_ghash_haskey(linkedref_to_old_override, reference_id)) {
+ BLI_ghash_insert(linkedref_to_old_override, reference_id, id);
+ if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
+ continue;
+ }
if ((id->override_library->reference->tag & LIB_TAG_DOIT) == 0) {
/* We have an override, but now it does not seem to be necessary to override that ID
* anymore. Check if there are some actual overrides from the user, otherwise assume
@@ -960,7 +1065,8 @@ bool BKE_lib_override_library_resync(Main *bmain,
/* Note that this call also remaps all pointers of tagged IDs from old override IDs to new
* override IDs (including within the old overrides themselves, since those are tagged too
* above). */
- const bool success = BKE_lib_override_library_create_from_tag(bmain);
+ const bool success = BKE_lib_override_library_create_from_tag(
+ bmain, id_root_reference->lib, true);
if (!success) {
return success;
@@ -969,55 +1075,104 @@ bool BKE_lib_override_library_resync(Main *bmain,
ListBase *lb;
FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) {
FOREACH_MAIN_LISTBASE_ID_BEGIN (lb, id) {
- if (id->tag & LIB_TAG_DOIT && id->newid != NULL && ID_IS_LINKED(id)) {
+ if (id->tag & LIB_TAG_DOIT && id->newid != NULL && id->lib == id_root_reference->lib) {
ID *id_override_new = id->newid;
ID *id_override_old = BLI_ghash_lookup(linkedref_to_old_override, id);
BLI_assert((id_override_new->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) == 0);
+ /* We need to 'move back' newly created override into its proper library (since it was
+ * duplicated from the reference ID with 'no main' option, it should currently be the same
+ * as the reference ID one). */
+ BLI_assert(/*id_override_new->lib == NULL || */ id_override_new->lib == id->lib);
+ BLI_assert(id_override_old == NULL || id_override_old->lib == id_root->lib);
+ id_override_new->lib = id_root->lib;
+ /* Remap step below will tag directly linked ones properly as needed. */
+ if (ID_IS_LINKED(id_override_new)) {
+ id_override_new->tag |= LIB_TAG_INDIRECT;
+ }
+
if (id_override_old != NULL) {
/* Swap the names between old override ID and new one. */
char id_name_buf[MAX_ID_NAME];
memcpy(id_name_buf, id_override_old->name, sizeof(id_name_buf));
memcpy(id_override_old->name, id_override_new->name, sizeof(id_override_old->name));
memcpy(id_override_new->name, id_name_buf, sizeof(id_override_new->name));
- /* Note that this is a very efficient way to keep BMain IDs ordered as expected after
- * swapping their names.
- * However, one has to be very careful with this when iterating over the listbase at the
- * same time. Here it works because we only execute this code when we are in the linked
- * IDs, which are always *after* all local ones, and we only affect local IDs. */
- BLI_listbase_swaplinks(lb, id_override_old, id_override_new);
-
- /* Remap the whole local IDs to use the new override. */
- BKE_libblock_remap(
- bmain, id_override_old, id_override_new, ID_REMAP_SKIP_INDIRECT_USAGE);
-
- /* Copy over overrides rules from old override ID to new one. */
- BLI_duplicatelist(&id_override_new->override_library->properties,
- &id_override_old->override_library->properties);
- for (IDOverrideLibraryProperty *
- op_new = id_override_new->override_library->properties.first,
- *op_old = id_override_old->override_library->properties.first;
- op_new;
- op_new = op_new->next, op_old = op_old->next) {
- lib_override_library_property_copy(op_new, op_old);
+
+ BLI_insertlinkreplace(lb, id_override_old, id_override_new);
+ id_override_old->tag |= LIB_TAG_NO_MAIN;
+ id_override_new->tag &= ~LIB_TAG_NO_MAIN;
+
+ if (ID_IS_OVERRIDE_LIBRARY_REAL(id_override_new)) {
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_override_old));
+
+ /* Copy over overrides rules from old override ID to new one. */
+ BLI_duplicatelist(&id_override_new->override_library->properties,
+ &id_override_old->override_library->properties);
+ IDOverrideLibraryProperty *op_new =
+ id_override_new->override_library->properties.first;
+ IDOverrideLibraryProperty *op_old =
+ id_override_old->override_library->properties.first;
+ for (; op_new; op_new = op_new->next, op_old = op_old->next) {
+ lib_override_library_property_copy(op_new, op_old);
+ }
}
}
+ else {
+ /* Add to proper main list, ensure unique name for local ID, sort, and clear relevant
+ * tags. */
+ BKE_libblock_management_main_add(bmain, id_override_new);
+ }
}
}
FOREACH_MAIN_LISTBASE_ID_END;
}
FOREACH_MAIN_LISTBASE_END;
+ /* We need to remap old to new override usages in a separate loop, after all new overrides have
+ * been added to Main. */
+ FOREACH_MAIN_ID_BEGIN (bmain, id) {
+ if (id->tag & LIB_TAG_DOIT && id->newid != NULL && id->lib == id_root_reference->lib) {
+ ID *id_override_new = id->newid;
+ ID *id_override_old = BLI_ghash_lookup(linkedref_to_old_override, id);
+
+ if (id_override_old != NULL) {
+ /* Remap all IDs to use the new override. */
+ BKE_libblock_remap(bmain, id_override_old, id_override_new, 0);
+ /* Remap no-main override IDs we just created too. */
+ GHashIterator linkedref_to_old_override_iter;
+ GHASH_ITER (linkedref_to_old_override_iter, linkedref_to_old_override) {
+ ID *id_override_old_iter = BLI_ghashIterator_getValue(&linkedref_to_old_override_iter);
+ if (id_override_old_iter->tag & LIB_TAG_NO_MAIN) {
+ BKE_libblock_relink_ex(bmain,
+ id_override_old_iter,
+ id_override_old,
+ id_override_new,
+ ID_REMAP_FORCE_USER_REFCOUNT | ID_REMAP_FORCE_NEVER_NULL_USAGE);
+ }
+ }
+ }
+ }
+ }
+ FOREACH_MAIN_ID_END;
+
+ BKE_main_collection_sync(bmain);
+
/* We need to apply override rules in a separate loop, after all ID pointers have been properly
* remapped, and all new local override IDs have gotten their proper original names, otherwise
* override operations based on those ID names would fail. */
FOREACH_MAIN_ID_BEGIN (bmain, id) {
- if (id->tag & LIB_TAG_DOIT && id->newid != NULL && ID_IS_LINKED(id)) {
+ if (id->tag & LIB_TAG_DOIT && id->newid != NULL && id->lib == id_root_reference->lib) {
ID *id_override_new = id->newid;
+ if (!ID_IS_OVERRIDE_LIBRARY_REAL(id_override_new)) {
+ continue;
+ }
ID *id_override_old = BLI_ghash_lookup(linkedref_to_old_override, id);
- if (id_override_old != NULL) {
+ if (id_override_old == NULL) {
+ continue;
+ }
+ if (ID_IS_OVERRIDE_LIBRARY_REAL(id_override_old)) {
/* Apply rules on new override ID using old one as 'source' data. */
/* Note that since we already remapped ID pointers in old override IDs to new ones, we
* can also apply ID pointer override rules safely here. */
@@ -1051,30 +1206,44 @@ bool BKE_lib_override_library_resync(Main *bmain,
RNA_OVERRIDE_APPLY_FLAG_IGNORE_ID_POINTERS :
RNA_OVERRIDE_APPLY_FLAG_NOP);
}
+
+ /* Once overrides have been properly 'transferred' from old to new ID, we can clear ID usages
+ * of the old one.
+ * This is necessary in case said old ID is not in Main anymore. */
+ BKE_libblock_relink_ex(bmain,
+ id_override_old,
+ NULL,
+ NULL,
+ ID_REMAP_FORCE_USER_REFCOUNT | ID_REMAP_FORCE_NEVER_NULL_USAGE);
+ id_override_old->tag |= LIB_TAG_NO_USER_REFCOUNT;
}
}
FOREACH_MAIN_ID_END;
/* Delete old override IDs.
- * Note that we have to use tagged group deletion here, since ID deletion also uses LIB_TAG_DOIT.
- * This improves performances anyway, so everything is fine. */
+ * Note that we have to use tagged group deletion here, since ID deletion also uses
+ * LIB_TAG_DOIT. This improves performances anyway, so everything is fine. */
int user_edited_overrides_deletion_count = 0;
FOREACH_MAIN_ID_BEGIN (bmain, id) {
if (id->tag & LIB_TAG_DOIT) {
- /* Note that this works because linked IDs are always after local ones (including overrides),
- * so we will only ever tag an old override ID after we have already checked it in this loop,
- * hence we cannot untag it later. */
- if (id->newid != NULL && ID_IS_LINKED(id)) {
+ /* Note that this works because linked IDs are always after local ones (including
+ * overrides), so we will only ever tag an old override ID after we have already checked it
+ * in this loop, hence we cannot untag it later. */
+ if (id->newid != NULL && id->lib == id_root_reference->lib) {
ID *id_override_old = BLI_ghash_lookup(linkedref_to_old_override, id);
if (id_override_old != NULL) {
id->newid->tag &= ~LIB_TAG_DOIT;
id_override_old->tag |= LIB_TAG_DOIT;
+ if (id_override_old->tag & LIB_TAG_NO_MAIN) {
+ BKE_id_free(bmain, id_override_old);
+ }
}
}
id->tag &= ~LIB_TAG_DOIT;
}
- /* Also deal with old overrides that went missing in new linked data. */
+ /* Also deal with old overrides that went missing in new linked data - only for real local
+ * overrides for now, not those who are linked. */
else if (id->tag & LIB_TAG_MISSING && !ID_IS_LINKED(id)) {
BLI_assert(ID_IS_OVERRIDE_LIBRARY(id));
if (!BKE_lib_override_library_is_user_edited(id)) {
@@ -1105,6 +1274,10 @@ bool BKE_lib_override_library_resync(Main *bmain,
}
}
FOREACH_MAIN_ID_END;
+
+ /* Cleanup, many pointers in this GHash are already invalid now. */
+ BLI_ghash_free(linkedref_to_old_override, NULL, NULL);
+
BKE_id_multi_tagged_delete(bmain);
/* At this point, `id_root` has very likely been deleted, we need to update it to its new
@@ -1137,50 +1310,136 @@ bool BKE_lib_override_library_resync(Main *bmain,
}
/* Cleanup. */
- BLI_ghash_free(linkedref_to_old_override, NULL, NULL);
-
- BKE_main_id_clear_newpoins(bmain);
+ BKE_main_id_newptr_and_tag_clear(bmain);
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); /* That one should not be needed in fact. */
return success;
}
-/**
- * Detect and handle required resync of overrides data, when relations between reference linked IDs
- * have changed.
+/* Also tag ancestors overrides for resync.
*
- * This is a fairly complex and costly operation, typically it should be called after
- * #BKE_lib_override_library_main_update, which would already detect and tag a lot of cases.
+ * WARNING: Expects `bmain` to have valid relation data.
*
- * This function will first detect the remaining cases requiring a resync (namely, either when an
- * existing linked ID that did not require to be overridden before now would be, or when new IDs
- * are added to the hierarchy).
+ * NOTE: Related to `lib_override_library_main_resync_find_root_recurse` below.
*
- * Then it will handle the resync of necessary IDs (through calls to
- * #BKE_lib_override_library_resync).
+ * TODO: This is a sub-optimal, simple solution. At some point, we should rather find a way to
+ * resync a set of 'sub-roots' overrides, instead of having to 'go back' to the real root and
+ * resync the whole hierarchy.
*/
-void BKE_lib_override_library_main_resync(Main *bmain,
- Scene *scene,
- ViewLayer *view_layer,
- ReportList *reports)
+static void lib_override_resync_tagging_finalize_recurse(Main *bmain,
+ ID *id,
+ const int library_indirect_level)
{
- /* We use a specific collection to gather/store all 'orphaned' override collections and objects
- * generated by re-sync-process. This avoids putting them in scene's master collection. */
-#define OVERRIDE_RESYNC_RESIDUAL_STORAGE_NAME "OVERRIDE_RESYNC_LEFTOVERS"
- Collection *override_resync_residual_storage = BLI_findstring(
- &bmain->collections, OVERRIDE_RESYNC_RESIDUAL_STORAGE_NAME, offsetof(ID, name) + 2);
- if (override_resync_residual_storage != NULL &&
- override_resync_residual_storage->id.lib != NULL) {
- override_resync_residual_storage = NULL;
+ if (id->lib != NULL && id->lib->temp_index > library_indirect_level) {
+ CLOG_ERROR(
+ &LOG,
+ "While processing indirect level %d, ID %s from lib %s of indirect level %d detected "
+ "as needing resync.",
+ library_indirect_level,
+ id->name,
+ id->lib->filepath,
+ id->lib->temp_index);
}
- if (override_resync_residual_storage == NULL) {
- override_resync_residual_storage = BKE_collection_add(
- bmain, scene->master_collection, OVERRIDE_RESYNC_RESIDUAL_STORAGE_NAME);
- /* Hide the collection from viewport and render. */
- override_resync_residual_storage->flag |= COLLECTION_RESTRICT_VIEWPORT |
- COLLECTION_RESTRICT_RENDER;
+
+ MainIDRelationsEntry *entry = BLI_ghash_lookup(bmain->relations->relations_from_pointers, id);
+ BLI_assert(entry != NULL);
+
+ if (entry->tags & MAINIDRELATIONS_ENTRY_TAGS_PROCESSED) {
+ /* This ID has already been processed. */
+ return;
+ }
+ /* This way we won't process again that ID, should we encounter it again through another
+ * relationship hierarchy. */
+ entry->tags |= MAINIDRELATIONS_ENTRY_TAGS_PROCESSED;
+
+ for (MainIDRelationsEntryItem *entry_item = entry->from_ids; entry_item != NULL;
+ entry_item = entry_item->next) {
+ if (entry_item->usage_flag &
+ (IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE | IDWALK_CB_LOOPBACK)) {
+ continue;
+ }
+ ID *id_from = entry_item->id_pointer.from;
+
+ /* Case where this ID pointer was to a linked ID, that now needs to be overridden. */
+ if (id_from != id && ID_IS_OVERRIDE_LIBRARY_REAL(id_from) && id_from->lib == id->lib) {
+ id_from->tag |= LIB_TAG_LIB_OVERRIDE_NEED_RESYNC;
+ CLOG_INFO(&LOG,
+ 4,
+ "ID %s (%p) now tagged as needing resync because they use %s (%p) that needs to "
+ "be overridden",
+ id_from->name,
+ id_from->lib,
+ id->name,
+ id->lib);
+ lib_override_resync_tagging_finalize_recurse(bmain, id_from, library_indirect_level);
+ }
+ }
+}
+
+/* Ensures parent collection (or objects) in the same override group are also tagged for resync.
+ *
+ * This is needed since otherwise, some (new) ID added in one sub-collection might be used in
+ * another unrelated sub-collection, if 'root' collection is not resynced separated resync of those
+ * sub-collections would be unaware that this is the same ID, and would re-generate several
+ * overrides for it.
+ *
+ * NOTE: Related to `lib_override_resync_tagging_finalize` above.
+ */
+static ID *lib_override_library_main_resync_find_root_recurse(ID *id, int *level)
+{
+ (*level)++;
+ ID *return_id = id;
+
+ switch (GS(id->name)) {
+ case ID_GR: {
+ /* Find the highest valid collection in the parenting hierarchy.
+ * Note that in practice, in any decent common case there is only one well defined root
+ * collection anyway. */
+ int max_level = *level;
+ Collection *collection = (Collection *)id;
+ LISTBASE_FOREACH (CollectionParent *, collection_parent_iter, &collection->parents) {
+ Collection *collection_parent = collection_parent_iter->collection;
+ if (ID_IS_OVERRIDE_LIBRARY_REAL(collection_parent) &&
+ collection_parent->id.lib == id->lib) {
+ int tmp_level = *level;
+ ID *tmp_id = lib_override_library_main_resync_find_root_recurse(&collection_parent->id,
+ &tmp_level);
+ if (tmp_level > max_level) {
+ max_level = tmp_level;
+ return_id = tmp_id;
+ }
+ }
+ }
+ break;
+ }
+ case ID_OB: {
+ Object *object = (Object *)id;
+ if (object->parent != NULL && ID_IS_OVERRIDE_LIBRARY_REAL(object->parent) &&
+ object->parent->id.lib == id->lib) {
+ return_id = lib_override_library_main_resync_find_root_recurse(&object->parent->id, level);
+ }
+ break;
+ }
+ default:
+ break;
}
+ return return_id;
+}
+
+/* Ensure resync of all overrides at one level of indirect usage.
+ *
+ * We need to handle each level independently, since an override at level n may be affected by
+ * other overrides from level n + 1 etc. (i.e. from linked overrides it may use).
+ */
+static void lib_override_library_main_resync_on_library_indirect_level(
+ Main *bmain,
+ Scene *scene,
+ ViewLayer *view_layer,
+ Collection *override_resync_residual_storage,
+ const int library_indirect_level,
+ ReportList *reports)
+{
BKE_main_relations_create(bmain, 0);
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
@@ -1192,7 +1451,7 @@ void BKE_lib_override_library_main_resync(Main *bmain,
* those used by current existing overrides. */
ID *id;
FOREACH_MAIN_ID_BEGIN (bmain, id) {
- if (!ID_IS_OVERRIDE_LIBRARY_REAL(id) || ID_IS_LINKED(id)) {
+ if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
continue;
}
if (id->tag & (LIB_TAG_DOIT | LIB_TAG_MISSING)) {
@@ -1203,7 +1462,8 @@ void BKE_lib_override_library_main_resync(Main *bmain,
LibOverrideGroupTagData data = {.bmain = bmain,
.id_root = id->override_library->reference,
.tag = LIB_TAG_DOIT,
- .missing_tag = LIB_TAG_MISSING};
+ .missing_tag = LIB_TAG_MISSING,
+ .is_override = false};
lib_override_linked_group_tag(&data);
BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
lib_override_hierarchy_dependencies_recursive_tag(&data);
@@ -1213,13 +1473,15 @@ void BKE_lib_override_library_main_resync(Main *bmain,
/* Now check existing overrides, those needing resync will be the one either already tagged as
* such, or the one using linked data that is now tagged as needing override. */
+ BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
FOREACH_MAIN_ID_BEGIN (bmain, id) {
- if (!ID_IS_OVERRIDE_LIBRARY_REAL(id) || ID_IS_LINKED(id)) {
+ if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
continue;
}
if (id->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) {
- CLOG_INFO(&LOG, 4, "ID %s was already tagged as needing resync", id->name);
+ CLOG_INFO(&LOG, 4, "ID %s (%p) was already tagged as needing resync", id->name, id->lib);
+ lib_override_resync_tagging_finalize_recurse(bmain, id, library_indirect_level);
continue;
}
@@ -1234,14 +1496,17 @@ void BKE_lib_override_library_main_resync(Main *bmain,
ID *id_to = *entry_item->id_pointer.to;
/* Case where this ID pointer was to a linked ID, that now needs to be overridden. */
- if (ID_IS_LINKED(id_to) && (id_to->tag & LIB_TAG_DOIT) != 0) {
+ if (ID_IS_LINKED(id_to) && (id_to->lib != id->lib) && (id_to->tag & LIB_TAG_DOIT) != 0) {
id->tag |= LIB_TAG_LIB_OVERRIDE_NEED_RESYNC;
CLOG_INFO(&LOG,
3,
- "ID %s now tagged as needing resync because they use linked %s that now needs "
- "to be overridden",
+ "ID %s (%p) now tagged as needing resync because they use linked %s (%p) that "
+ "now needs to be overridden",
id->name,
- id_to->name);
+ id->lib,
+ id_to->name,
+ id_to->lib);
+ lib_override_resync_tagging_finalize_recurse(bmain, id, library_indirect_level);
break;
}
}
@@ -1256,27 +1521,29 @@ void BKE_lib_override_library_main_resync(Main *bmain,
* `FOREACH_MAIN_ID_BEGIN/END` here, and need special multi-loop processing. */
bool do_continue = true;
while (do_continue) {
- ListBase *lb;
do_continue = false;
+ ListBase *lb;
FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) {
FOREACH_MAIN_LISTBASE_ID_BEGIN (lb, id) {
- if ((id->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) == 0) {
- continue;
- }
- BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id));
- if (ID_IS_LINKED(id)) {
+ if ((id->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) == 0 ||
+ (ID_IS_LINKED(id) && id->lib->temp_index < library_indirect_level) ||
+ (!ID_IS_LINKED(id) && library_indirect_level != 0)) {
continue;
}
- do_continue = true;
+ int level = 0;
/* In complex non-supported cases, with several different override hierarchies sharing
* relations between each-other, we may end up not actually updating/replacing the given
* root id (see e.g. pro/shots/110_rextoria/110_0150_A/110_0150_A.anim.blend of sprites
* project repository, r2687).
* This can lead to infinite loop here, at least avoid this. */
id->tag &= ~LIB_TAG_LIB_OVERRIDE_NEED_RESYNC;
+ id = lib_override_library_main_resync_find_root_recurse(id, &level);
+ id->tag &= ~LIB_TAG_LIB_OVERRIDE_NEED_RESYNC;
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id));
+ do_continue = true;
- CLOG_INFO(&LOG, 2, "Resyncing %s...", id->name);
+ CLOG_INFO(&LOG, 2, "Resyncing %s (%p)...", id->name, id->lib);
const bool success = BKE_lib_override_library_resync(
bmain, scene, view_layer, id, override_resync_residual_storage, false, false, reports);
CLOG_INFO(&LOG, 2, "\tSuccess: %d", success);
@@ -1289,6 +1556,113 @@ void BKE_lib_override_library_main_resync(Main *bmain,
}
FOREACH_MAIN_LISTBASE_END;
}
+}
+
+static int lib_override_sort_libraries_func(LibraryIDLinkCallbackData *cb_data)
+{
+ if (cb_data->cb_flag & IDWALK_CB_LOOPBACK) {
+ return IDWALK_RET_NOP;
+ }
+ ID *id_owner = cb_data->id_owner;
+ ID *id = *cb_data->id_pointer;
+ if (id != NULL && ID_IS_LINKED(id) && id->lib != id_owner->lib) {
+ const int owner_library_indirect_level = id_owner->lib != NULL ? id_owner->lib->temp_index : 0;
+ if (owner_library_indirect_level > 10000) {
+ CLOG_ERROR(
+ &LOG,
+ "Levels of indirect usages of libraries is way too high, skipping further building "
+ "loops (Involves at least '%s' and '%s')",
+ id_owner->lib->filepath,
+ id->lib->filepath);
+ BLI_assert(0);
+ return IDWALK_RET_NOP;
+ }
+
+ if (owner_library_indirect_level >= id->lib->temp_index) {
+ id->lib->temp_index = owner_library_indirect_level + 1;
+ *(bool *)cb_data->user_data = true;
+ }
+ }
+ return IDWALK_RET_NOP;
+}
+
+/** Define the `temp_index` of libraries from their highest level of indirect usage.
+ *
+ * E.g. if lib_a uses lib_b, lib_c and lib_d, and lib_b also uses lib_d, then lib_a has an index of
+ * 1, lib_b and lib_c an index of 2, and lib_d an index of 3. */
+static int lib_override_libraries_index_define(Main *bmain)
+{
+ LISTBASE_FOREACH (Library *, library, &bmain->libraries) {
+ /* index 0 is reserved for local data. */
+ library->temp_index = 1;
+ }
+ bool do_continue = true;
+ while (do_continue) {
+ do_continue = false;
+ ID *id;
+ FOREACH_MAIN_ID_BEGIN (bmain, id) {
+ BKE_library_foreach_ID_link(
+ bmain, id, lib_override_sort_libraries_func, &do_continue, IDWALK_READONLY);
+ }
+ FOREACH_MAIN_ID_END;
+ }
+
+ int library_indirect_level_max = 0;
+ LISTBASE_FOREACH (Library *, library, &bmain->libraries) {
+ if (library->temp_index > library_indirect_level_max) {
+ library_indirect_level_max = library->temp_index;
+ }
+ }
+ return library_indirect_level_max;
+}
+
+/**
+ * Detect and handle required resync of overrides data, when relations between reference linked IDs
+ * have changed.
+ *
+ * This is a fairly complex and costly operation, typically it should be called after
+ * #BKE_lib_override_library_main_update, which would already detect and tag a lot of cases.
+ *
+ * This function will first detect the remaining cases requiring a resync (namely, either when an
+ * existing linked ID that did not require to be overridden before now would be, or when new IDs
+ * are added to the hierarchy).
+ *
+ * Then it will handle the resync of necessary IDs (through calls to
+ * #BKE_lib_override_library_resync).
+ */
+void BKE_lib_override_library_main_resync(Main *bmain,
+ Scene *scene,
+ ViewLayer *view_layer,
+ ReportList *reports)
+{
+ /* We use a specific collection to gather/store all 'orphaned' override collections and objects
+ * generated by re-sync-process. This avoids putting them in scene's master collection. */
+#define OVERRIDE_RESYNC_RESIDUAL_STORAGE_NAME "OVERRIDE_RESYNC_LEFTOVERS"
+ Collection *override_resync_residual_storage = BLI_findstring(
+ &bmain->collections, OVERRIDE_RESYNC_RESIDUAL_STORAGE_NAME, offsetof(ID, name) + 2);
+ if (override_resync_residual_storage != NULL &&
+ override_resync_residual_storage->id.lib != NULL) {
+ override_resync_residual_storage = NULL;
+ }
+ if (override_resync_residual_storage == NULL) {
+ override_resync_residual_storage = BKE_collection_add(
+ bmain, scene->master_collection, OVERRIDE_RESYNC_RESIDUAL_STORAGE_NAME);
+ /* Hide the collection from viewport and render. */
+ override_resync_residual_storage->flag |= COLLECTION_RESTRICT_VIEWPORT |
+ COLLECTION_RESTRICT_RENDER;
+ }
+
+ int library_indirect_level = lib_override_libraries_index_define(bmain);
+ while (library_indirect_level >= 0) {
+ /* Update overrides from each indirect level separately. */
+ lib_override_library_main_resync_on_library_indirect_level(bmain,
+ scene,
+ view_layer,
+ override_resync_residual_storage,
+ library_indirect_level,
+ reports);
+ library_indirect_level--;
+ }
/* Essentially ensures that potentially new overrides of new objects will be instantiated. */
lib_override_library_create_post_process(
@@ -1313,9 +1687,12 @@ void BKE_lib_override_library_delete(Main *bmain, ID *id_root)
/* Tag all library overrides in the chains of dependencies from the given root one. */
BKE_main_relations_create(bmain, 0);
- LibOverrideGroupTagData data = {
- .bmain = bmain, .id_root = id_root, .tag = LIB_TAG_DOIT, .missing_tag = LIB_TAG_MISSING};
- lib_override_local_group_tag(&data);
+ LibOverrideGroupTagData data = {.bmain = bmain,
+ .id_root = id_root,
+ .tag = LIB_TAG_DOIT,
+ .missing_tag = LIB_TAG_MISSING,
+ .is_override = true};
+ lib_override_overrides_group_tag(&data);
BKE_main_relations_free(bmain);
@@ -1961,7 +2338,7 @@ bool BKE_lib_override_library_main_operations_create(Main *bmain, const bool for
TaskPool *task_pool = BLI_task_pool_create(&create_pool_data, TASK_PRIORITY_HIGH);
FOREACH_MAIN_ID_BEGIN (bmain, id) {
- if (ID_IS_OVERRIDE_LIBRARY_REAL(id) &&
+ if (!ID_IS_LINKED(id) && ID_IS_OVERRIDE_LIBRARY_REAL(id) &&
(force_auto || (id->tag & LIB_TAG_OVERRIDE_LIBRARY_AUTOREFRESH))) {
/* Usual issue with pose, it's quiet rare but sometimes they may not be up to date when this
* function is called. */