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.cc')
-rw-r--r--source/blender/blenkernel/intern/lib_override.cc3873
1 files changed, 3873 insertions, 0 deletions
diff --git a/source/blender/blenkernel/intern/lib_override.cc b/source/blender/blenkernel/intern/lib_override.cc
new file mode 100644
index 00000000000..0c5f59be768
--- /dev/null
+++ b/source/blender/blenkernel/intern/lib_override.cc
@@ -0,0 +1,3873 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2016 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup bke
+ */
+
+#include <cstdlib>
+#include <cstring>
+
+#include "CLG_log.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_ID.h"
+#include "DNA_collection_types.h"
+#include "DNA_key_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+
+#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_build.h"
+
+#include "BKE_anim_data.h"
+#include "BKE_armature.h"
+#include "BKE_collection.h"
+#include "BKE_fcurve.h"
+#include "BKE_global.h"
+#include "BKE_idtype.h"
+#include "BKE_key.h"
+#include "BKE_layer.h"
+#include "BKE_lib_id.h"
+#include "BKE_lib_override.h"
+#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"
+
+#include "BLO_readfile.h"
+
+#include "BLI_ghash.h"
+#include "BLI_linklist.h"
+#include "BLI_listbase.h"
+#include "BLI_memarena.h"
+#include "BLI_string.h"
+#include "BLI_task.h"
+#include "BLI_utildefines.h"
+
+#include "PIL_time.h"
+
+#include "RNA_access.h"
+#include "RNA_prototypes.h"
+#include "RNA_types.h"
+
+#include "atomic_ops.h"
+
+#define OVERRIDE_AUTO_CHECK_DELAY 0.2 /* 200ms between auto-override checks. */
+//#define DEBUG_OVERRIDE_TIMEIT
+
+#ifdef DEBUG_OVERRIDE_TIMEIT
+# include "PIL_time_utildefines.h"
+#endif
+
+static CLG_LogRef LOG = {"bke.liboverride"};
+
+static void lib_override_library_property_copy(IDOverrideLibraryProperty *op_dst,
+ IDOverrideLibraryProperty *op_src);
+static void lib_override_library_property_operation_copy(
+ IDOverrideLibraryPropertyOperation *opop_dst, IDOverrideLibraryPropertyOperation *opop_src);
+
+static void lib_override_library_property_clear(IDOverrideLibraryProperty *op);
+static void lib_override_library_property_operation_clear(
+ IDOverrideLibraryPropertyOperation *opop);
+
+/** Helper to preserve Pose mode on override objects.
+ * A bit annoying to have this special case, but not much to be done here currently, since the
+ * matching RNA property is read-only. */
+BLI_INLINE void lib_override_object_posemode_transfer(ID *id_dst, ID *id_src)
+{
+ if (GS(id_src->name) == ID_OB && GS(id_dst->name) == ID_OB) {
+ Object *ob_src = (Object *)id_src;
+ Object *ob_dst = (Object *)id_dst;
+ if (ob_src->type == OB_ARMATURE && (ob_src->mode & OB_MODE_POSE) != 0) {
+ ob_dst->restore_mode = ob_dst->mode;
+ ob_dst->mode |= OB_MODE_POSE;
+ }
+ }
+}
+
+/** Get override data for a given ID. Needed because of our beloved shape keys snowflake. */
+BLI_INLINE const IDOverrideLibrary *lib_override_get(const Main *bmain,
+ const ID *id,
+ const ID **r_owner_id)
+{
+ if (r_owner_id != nullptr) {
+ *r_owner_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 != nullptr) {
+ /* The #IDTypeInfo::owner_get callback should not modify the arguments, so casting away const
+ * is okay. */
+ const ID *owner_id = id_type->owner_get(const_cast<Main *>(bmain), const_cast<ID *>(id));
+ if (r_owner_id != nullptr) {
+ *r_owner_id = owner_id;
+ }
+ return owner_id->override_library;
+ }
+ BLI_assert_msg(0, "IDTypeInfo of liboverride-embedded ID with no owner getter");
+ }
+ return id->override_library;
+}
+
+BLI_INLINE IDOverrideLibrary *lib_override_get(Main *bmain, ID *id, ID **r_owner_id)
+{
+ /* Reuse the implementation of the const access function, which does not change the arguments.
+ * Add const explicitly to make it clear to the compiler to avoid just calling this function. */
+ return const_cast<IDOverrideLibrary *>(lib_override_get(const_cast<const Main *>(bmain),
+ const_cast<const ID *>(id),
+ const_cast<const ID **>(r_owner_id)));
+}
+
+IDOverrideLibrary *BKE_lib_override_library_init(ID *local_id, ID *reference_id)
+{
+ /* If reference_id is nullptr, we are creating an override template for purely local data.
+ * Else, reference *must* be linked data. */
+ BLI_assert(reference_id == nullptr || ID_IS_LINKED(reference_id));
+ BLI_assert(local_id->override_library == nullptr);
+
+ ID *ancestor_id;
+ for (ancestor_id = reference_id;
+ ancestor_id != nullptr && ancestor_id->override_library != nullptr &&
+ ancestor_id->override_library->reference != nullptr;
+ ancestor_id = ancestor_id->override_library->reference) {
+ /* pass */
+ }
+
+ if (ancestor_id != nullptr && ancestor_id->override_library != nullptr) {
+ /* Original ID has a template, use it! */
+ BKE_lib_override_library_copy(local_id, ancestor_id, true);
+ if (local_id->override_library->reference != reference_id) {
+ id_us_min(local_id->override_library->reference);
+ local_id->override_library->reference = reference_id;
+ id_us_plus(local_id->override_library->reference);
+ }
+ return local_id->override_library;
+ }
+
+ /* Else, generate new empty override. */
+ local_id->override_library = MEM_cnew<IDOverrideLibrary>(__func__);
+ local_id->override_library->reference = reference_id;
+ id_us_plus(local_id->override_library->reference);
+ local_id->tag &= ~LIB_TAG_OVERRIDE_LIBRARY_REFOK;
+ /* By default initialized liboverrides are 'system overrides', higher-level code is responsible
+ * to unset this flag for specific IDs. */
+ local_id->override_library->flag |= IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED;
+ /* TODO: do we want to add tag or flag to referee to mark it as such? */
+ return local_id->override_library;
+}
+
+void BKE_lib_override_library_copy(ID *dst_id, const ID *src_id, const bool do_full_copy)
+{
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY(src_id) || ID_IS_OVERRIDE_LIBRARY_TEMPLATE(src_id));
+
+ if (dst_id->override_library != nullptr) {
+ if (src_id->override_library == nullptr) {
+ BKE_lib_override_library_free(&dst_id->override_library, true);
+ return;
+ }
+
+ BKE_lib_override_library_clear(dst_id->override_library, true);
+ }
+ else if (src_id->override_library == nullptr) {
+ /* Virtual overrides of embedded data does not require any extra work. */
+ return;
+ }
+ else {
+ BKE_lib_override_library_init(dst_id, nullptr);
+ }
+
+ /* If source is already overriding data, we copy it but reuse its reference for dest ID.
+ * Otherwise, source is only an override template, it then becomes reference of dest ID. */
+ dst_id->override_library->reference = src_id->override_library->reference ?
+ src_id->override_library->reference :
+ (ID *)src_id;
+ id_us_plus(dst_id->override_library->reference);
+
+ dst_id->override_library->hierarchy_root = src_id->override_library->hierarchy_root;
+ dst_id->override_library->flag = src_id->override_library->flag;
+
+ if (do_full_copy) {
+ BLI_duplicatelist(&dst_id->override_library->properties,
+ &src_id->override_library->properties);
+ for (IDOverrideLibraryProperty *op_dst = static_cast<IDOverrideLibraryProperty *>(
+ dst_id->override_library->properties.first),
+ *op_src = static_cast<IDOverrideLibraryProperty *>(
+ src_id->override_library->properties.first);
+ op_dst;
+ op_dst = op_dst->next, op_src = op_src->next) {
+ lib_override_library_property_copy(op_dst, op_src);
+ }
+ }
+
+ dst_id->tag &= ~LIB_TAG_OVERRIDE_LIBRARY_REFOK;
+}
+
+void BKE_lib_override_library_clear(IDOverrideLibrary *override, const bool do_id_user)
+{
+ BLI_assert(override != nullptr);
+
+ if (!ELEM(nullptr, override->runtime, override->runtime->rna_path_to_override_properties)) {
+ BLI_ghash_clear(override->runtime->rna_path_to_override_properties, nullptr, nullptr);
+ }
+
+ LISTBASE_FOREACH (IDOverrideLibraryProperty *, op, &override->properties) {
+ lib_override_library_property_clear(op);
+ }
+ BLI_freelistN(&override->properties);
+
+ if (do_id_user) {
+ id_us_min(override->reference);
+ /* override->storage should never be refcounted... */
+ }
+}
+
+void BKE_lib_override_library_free(IDOverrideLibrary **override, const bool do_id_user)
+{
+ BLI_assert(*override != nullptr);
+
+ if ((*override)->runtime != nullptr) {
+ if ((*override)->runtime->rna_path_to_override_properties != nullptr) {
+ BLI_ghash_free((*override)->runtime->rna_path_to_override_properties, nullptr, nullptr);
+ }
+ MEM_SAFE_FREE((*override)->runtime);
+ }
+
+ BKE_lib_override_library_clear(*override, do_id_user);
+ MEM_freeN(*override);
+ *override = nullptr;
+}
+
+static ID *lib_override_library_create_from(Main *bmain,
+ Library *owner_library,
+ 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,
+ nullptr,
+ LIB_ID_COPY_DEFAULT | LIB_ID_COPY_NO_LIB_OVERRIDE |
+ lib_id_copy_flags);
+
+ if (local_id == nullptr) {
+ return nullptr;
+ }
+ id_us_min(local_id);
+
+ /* TODO: Handle this properly in LIB_NO_MAIN case as well (i.e. resync case). Or offload to
+ * generic ID copy code? */
+ if ((lib_id_copy_flags & LIB_ID_CREATE_NO_MAIN) == 0) {
+ local_id->lib = owner_library;
+ }
+
+ BKE_lib_override_library_init(local_id, reference_id);
+
+ /* NOTE: From liboverride perspective (and RNA one), shape keys are considered as local embedded
+ * data-blocks, just like root node trees or master collections. Therefore, we never need to
+ * create overrides for them. We need a way to mark them as overrides though. */
+ Key *reference_key;
+ if ((reference_key = BKE_key_from_id(reference_id)) != nullptr) {
+ Key *local_key = BKE_key_from_id(local_id);
+ BLI_assert(local_key != nullptr);
+ local_key->id.flag |= LIB_EMBEDDED_DATA_LIB_OVERRIDE;
+ }
+
+ return local_id;
+}
+
+/* TODO: This could be simplified by storing a flag in #IDOverrideLibrary
+ * during the diffing process? */
+bool BKE_lib_override_library_is_user_edited(const ID *id)
+{
+
+ if (!ID_IS_OVERRIDE_LIBRARY(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 (const IDOverrideLibraryProperty *, op, &id->override_library->properties) {
+ LISTBASE_FOREACH (const IDOverrideLibraryPropertyOperation *, opop, &op->operations) {
+ if ((opop->flag & IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE) != 0) {
+ continue;
+ }
+ if (opop->operation == IDOVERRIDE_LIBRARY_OP_NOOP) {
+ continue;
+ }
+ /* If an operation does not match the filters above, it is considered as a user-editing one,
+ * therefore this override is user-edited. */
+ return true;
+ }
+ }
+ return false;
+}
+
+bool BKE_lib_override_library_is_system_defined(const Main *bmain, const ID *id)
+{
+ if (ID_IS_OVERRIDE_LIBRARY(id)) {
+ const ID *override_owner_id;
+ lib_override_get(bmain, id, &override_owner_id);
+ return (override_owner_id->override_library->flag & IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED) !=
+ 0;
+ }
+ return false;
+}
+
+bool BKE_lib_override_library_property_is_animated(const ID *id,
+ const IDOverrideLibraryProperty *override_prop,
+ const PropertyRNA *override_rna_prop,
+ const int rnaprop_index)
+{
+ AnimData *anim_data = BKE_animdata_from_id(id);
+ if (anim_data != nullptr) {
+ struct FCurve *fcurve;
+ char *index_token_start = const_cast<char *>(
+ RNA_path_array_index_token_find(override_prop->rna_path, override_rna_prop));
+ if (index_token_start != nullptr) {
+ const char index_token_start_backup = *index_token_start;
+ *index_token_start = '\0';
+ fcurve = BKE_animadata_fcurve_find_by_rna_path(
+ anim_data, override_prop->rna_path, rnaprop_index, nullptr, nullptr);
+ *index_token_start = index_token_start_backup;
+ }
+ else {
+ fcurve = BKE_animadata_fcurve_find_by_rna_path(
+ anim_data, override_prop->rna_path, 0, nullptr, nullptr);
+ }
+ if (fcurve != nullptr) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static int foreachid_is_hierarchy_leaf_fn(LibraryIDLinkCallbackData *cb_data)
+{
+ ID *id_owner = cb_data->id_owner;
+ ID *id = *cb_data->id_pointer;
+ bool *is_leaf = static_cast<bool *>(cb_data->user_data);
+
+ if (id != nullptr && ID_IS_OVERRIDE_LIBRARY_REAL(id) &&
+ id->override_library->hierarchy_root == id_owner->override_library->hierarchy_root) {
+ *is_leaf = false;
+ return IDWALK_RET_STOP_ITER;
+ }
+ return IDWALK_RET_NOP;
+}
+
+bool BKE_lib_override_library_is_hierarchy_leaf(Main *bmain, ID *id)
+{
+ if (ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
+ bool is_leaf = true;
+ BKE_library_foreach_ID_link(
+ bmain, id, foreachid_is_hierarchy_leaf_fn, &is_leaf, IDWALK_READONLY);
+ return is_leaf;
+ }
+
+ return false;
+}
+
+ID *BKE_lib_override_library_create_from_id(Main *bmain,
+ ID *reference_id,
+ const bool do_tagged_remap)
+{
+ BLI_assert(reference_id != nullptr);
+ BLI_assert(ID_IS_LINKED(reference_id));
+
+ ID *local_id = lib_override_library_create_from(bmain, nullptr, reference_id, 0);
+ /* We cannot allow automatic hierarchy resync on this ID, it is highly likely to generate a giant
+ * mess in case there are a lot of hidden, non-instantiated, non-properly organized dependencies.
+ * Ref T94650. */
+ local_id->override_library->flag |= IDOVERRIDE_LIBRARY_FLAG_NO_HIERARCHY;
+ local_id->override_library->flag &= ~IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED;
+ local_id->override_library->hierarchy_root = local_id;
+
+ if (do_tagged_remap) {
+ Key *reference_key, *local_key = nullptr;
+ if ((reference_key = BKE_key_from_id(reference_id)) != nullptr) {
+ local_key = BKE_key_from_id(local_id);
+ BLI_assert(local_key != nullptr);
+ }
+
+ ID *other_id;
+ FOREACH_MAIN_ID_BEGIN (bmain, other_id) {
+ if ((other_id->tag & LIB_TAG_DOIT) != 0 && !ID_IS_LINKED(other_id)) {
+ /* Note that using ID_REMAP_SKIP_INDIRECT_USAGE below is superfluous, as we only remap
+ * local IDs usages anyway. */
+ BKE_libblock_relink_ex(bmain,
+ other_id,
+ reference_id,
+ local_id,
+ ID_REMAP_SKIP_INDIRECT_USAGE | ID_REMAP_SKIP_OVERRIDE_LIBRARY);
+ if (reference_key != nullptr) {
+ BKE_libblock_relink_ex(bmain,
+ other_id,
+ &reference_key->id,
+ &local_key->id,
+ ID_REMAP_SKIP_INDIRECT_USAGE | ID_REMAP_SKIP_OVERRIDE_LIBRARY);
+ }
+ }
+ }
+ FOREACH_MAIN_ID_END;
+ }
+
+ return local_id;
+}
+
+static void lib_override_prefill_newid_from_existing_overrides(Main *bmain, ID *id_hierarchy_root)
+{
+ ID *id_iter;
+ FOREACH_MAIN_ID_BEGIN (bmain, id_iter) {
+ ID *id = id_iter;
+ if (GS(id_iter->name) == ID_KE) {
+ id = reinterpret_cast<Key *>(id_iter)->from;
+ BLI_assert(id != nullptr);
+ }
+ if (ID_IS_OVERRIDE_LIBRARY_REAL(id) &&
+ id->override_library->hierarchy_root == id_hierarchy_root) {
+ id->override_library->reference->newid = id;
+ if (GS(id_iter->name) == ID_KE) {
+ Key *reference_key = BKE_key_from_id(id->override_library->reference);
+ if (reference_key != nullptr) {
+ reference_key->id.newid = id_iter;
+ }
+ }
+ }
+ }
+ FOREACH_MAIN_ID_END;
+}
+
+static void lib_override_remapper_overrides_add(IDRemapper *id_remapper,
+ ID *reference_id,
+ ID *local_id)
+{
+ BKE_id_remapper_add(id_remapper, reference_id, local_id);
+
+ Key *reference_key, *local_key = nullptr;
+ if ((reference_key = BKE_key_from_id(reference_id)) != nullptr) {
+ if (reference_id->newid != nullptr) {
+ local_key = BKE_key_from_id(reference_id->newid);
+ BLI_assert(local_key != nullptr);
+ }
+
+ BKE_id_remapper_add(id_remapper, &reference_key->id, &local_key->id);
+ }
+}
+
+/* TODO: Make this static local function instead? API is becoming complex, and it's not used
+ * outside of this file anyway. */
+bool BKE_lib_override_library_create_from_tag(Main *bmain,
+ Library *owner_library,
+ const ID *id_root_reference,
+ ID *id_hierarchy_root,
+ const ID *id_hierarchy_root_reference,
+ const bool do_no_main,
+ const bool do_fully_editable)
+{
+ BLI_assert(id_root_reference != nullptr && ID_IS_LINKED(id_root_reference));
+ /* If we do not have any hierarchy root given, then the root reference must be tagged for
+ * override. */
+ BLI_assert(id_hierarchy_root != nullptr || id_hierarchy_root_reference != nullptr ||
+ (id_root_reference->tag & LIB_TAG_DOIT) != 0);
+ /* At least one of the hierarchy root pointers must be nullptr, passing both is useless and can
+ * create confusion. */
+ BLI_assert(ELEM(nullptr, id_hierarchy_root, id_hierarchy_root_reference));
+
+ if (id_hierarchy_root != nullptr) {
+ /* If the hierarchy root is given, it must be a valid existing override (used during partial
+ * resync process mainly). */
+ BLI_assert((ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root) &&
+ id_hierarchy_root->override_library->reference->lib == id_root_reference->lib));
+
+ if (!do_no_main) {
+ /* When processing within Main, set existing overrides in given hierarchy as 'newid' of their
+ * linked reference. This allows to re-use existing overrides instead of creating new ones in
+ * partial override cases. */
+ lib_override_prefill_newid_from_existing_overrides(bmain, id_hierarchy_root);
+ }
+ }
+ if (!ELEM(id_hierarchy_root_reference, nullptr, id_root_reference)) {
+ /* If the reference hierarchy root is given, it must be from the same library as the reference
+ * root, and also tagged for override. */
+ BLI_assert((id_hierarchy_root_reference->lib == id_root_reference->lib &&
+ (id_hierarchy_root_reference->tag & LIB_TAG_DOIT) != 0));
+ }
+
+ const Library *reference_library = id_root_reference->lib;
+
+ ID *reference_id;
+ bool success = true;
+
+ ListBase todo_ids = {nullptr};
+ LinkData *todo_id_iter;
+
+ /* 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 == reference_library &&
+ BKE_idtype_idcode_is_linkable(GS(reference_id->name))) {
+ todo_id_iter = MEM_cnew<LinkData>(__func__);
+ todo_id_iter->data = reference_id;
+ BLI_addtail(&todo_ids, todo_id_iter);
+ }
+ }
+ FOREACH_MAIN_ID_END;
+
+ /* Override the IDs. */
+ for (todo_id_iter = static_cast<LinkData *>(todo_ids.first); todo_id_iter != nullptr;
+ todo_id_iter = todo_id_iter->next) {
+ reference_id = static_cast<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 == nullptr) {
+ /* 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, owner_library, reference_id, do_no_main ? LIB_ID_CREATE_NO_MAIN : 0);
+ if (reference_id->newid == nullptr) {
+ success = false;
+ break;
+ }
+ if (do_fully_editable) {
+ reference_id->newid->override_library->flag &= ~IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED;
+ }
+ }
+ /* We also tag the new IDs so that in next step we can remap their pointers too. */
+ reference_id->newid->tag |= LIB_TAG_DOIT;
+
+ Key *reference_key;
+ if ((reference_key = BKE_key_from_id(reference_id)) != nullptr) {
+ reference_key->id.tag |= LIB_TAG_DOIT;
+
+ Key *local_key = BKE_key_from_id(reference_id->newid);
+ BLI_assert(local_key != nullptr);
+ reference_key->id.newid = &local_key->id;
+ /* We also tag the new IDs so that in next step we can remap their pointers too. */
+ local_key->id.tag |= LIB_TAG_DOIT;
+ }
+ }
+
+ /* Only remap new local ID's pointers, we don't want to force our new overrides onto our whole
+ * existing linked IDs usages. */
+ if (success) {
+ if (id_hierarchy_root_reference != nullptr) {
+ id_hierarchy_root = id_hierarchy_root_reference->newid;
+ }
+ else if (id_root_reference->newid != nullptr &&
+ (id_hierarchy_root == nullptr ||
+ id_hierarchy_root->override_library->reference == id_root_reference)) {
+ id_hierarchy_root = id_root_reference->newid;
+ }
+ BLI_assert(id_hierarchy_root != nullptr);
+
+ LinkNode *relinked_ids = nullptr;
+ IDRemapper *id_remapper = BKE_id_remapper_create();
+ /* 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. */
+ ID *id;
+ FOREACH_MAIN_ID_BEGIN (bmain, id) {
+ ID *other_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 == id_root_reference->lib && id->newid != nullptr) {
+ 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 = nullptr;
+ }
+ 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 relink it, as part of recursive resync. */
+ if ((other_id->tag & LIB_TAG_DOIT) != 0 && other_id->lib != id_root_reference->lib) {
+ BLI_linklist_prepend(&relinked_ids, other_id);
+ if (ID_IS_OVERRIDE_LIBRARY_REAL(other_id) &&
+ other_id->override_library->hierarchy_root == id_hierarchy_root) {
+ reference_id = other_id->override_library->reference;
+ ID *local_id = reference_id->newid;
+ if (other_id == local_id) {
+ lib_override_remapper_overrides_add(id_remapper, reference_id, local_id);
+ }
+ }
+ }
+ if (other_id != id) {
+ other_id->lib = id_root_reference->lib;
+ }
+ }
+ FOREACH_MAIN_ID_END;
+
+ for (todo_id_iter = static_cast<LinkData *>(todo_ids.first); todo_id_iter != nullptr;
+ todo_id_iter = todo_id_iter->next) {
+ reference_id = static_cast<ID *>(todo_id_iter->data);
+ ID *local_id = reference_id->newid;
+
+ if (local_id == nullptr) {
+ continue;
+ }
+
+ local_id->override_library->hierarchy_root = id_hierarchy_root;
+
+ lib_override_remapper_overrides_add(id_remapper, reference_id, local_id);
+ }
+
+ BKE_libblock_relink_multiple(bmain,
+ relinked_ids,
+ ID_REMAP_TYPE_REMAP,
+ id_remapper,
+ ID_REMAP_SKIP_OVERRIDE_LIBRARY | ID_REMAP_FORCE_USER_REFCOUNT);
+
+ BKE_id_remapper_free(id_remapper);
+ BLI_linklist_free(relinked_ids, nullptr);
+ }
+ else {
+ /* We need to cleanup potentially already created data. */
+ for (todo_id_iter = static_cast<LinkData *>(todo_ids.first); todo_id_iter != nullptr;
+ todo_id_iter = todo_id_iter->next) {
+ reference_id = static_cast<ID *>(todo_id_iter->data);
+ BKE_id_delete(bmain, reference_id->newid);
+ reference_id->newid = nullptr;
+ }
+ }
+
+ BLI_freelistN(&todo_ids);
+
+ return success;
+}
+
+struct LibOverrideGroupTagData {
+ Main *bmain;
+ Scene *scene;
+ ID *id_root;
+ ID *hierarchy_root_id;
+ uint tag;
+ uint missing_tag;
+ /* Whether we are looping on override data, or their references (linked) one. */
+ bool is_override;
+ /* Whether we are creating new override, or resyncing existing one. */
+ bool is_resync;
+
+ /* Mapping linked objects to all their instantiating collections (as a linked list).
+ * Avoids calling #BKE_collection_object_find over and over, this function is very expansive. */
+ GHash *linked_object_to_instantiating_collections;
+ MemArena *mem_arena;
+};
+
+static void lib_override_group_tag_data_object_to_collection_init_collection_process(
+ LibOverrideGroupTagData *data, Collection *collection)
+{
+ LISTBASE_FOREACH (CollectionObject *, collection_object, &collection->gobject) {
+ Object *ob = collection_object->ob;
+ if (!ID_IS_LINKED(ob)) {
+ continue;
+ }
+
+ LinkNodePair **collections_linkedlist_p;
+ if (!BLI_ghash_ensure_p(data->linked_object_to_instantiating_collections,
+ ob,
+ (void ***)&collections_linkedlist_p)) {
+ *collections_linkedlist_p = static_cast<LinkNodePair *>(
+ BLI_memarena_calloc(data->mem_arena, sizeof(**collections_linkedlist_p)));
+ }
+ BLI_linklist_append_arena(*collections_linkedlist_p, collection, data->mem_arena);
+ }
+}
+
+/* Initialize complex data, `data` is expected to be already initialized with basic pointers and
+ * other simple data.
+ *
+ * NOTE: Currently creates a mapping from linked object to all of their instantiating collections
+ * (as returned by #BKE_collection_object_find). */
+static void lib_override_group_tag_data_object_to_collection_init(LibOverrideGroupTagData *data)
+{
+ data->mem_arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
+
+ data->linked_object_to_instantiating_collections = BLI_ghash_new(
+ BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__);
+ if (data->scene != nullptr) {
+ lib_override_group_tag_data_object_to_collection_init_collection_process(
+ data, data->scene->master_collection);
+ }
+ LISTBASE_FOREACH (Collection *, collection, &data->bmain->collections) {
+ lib_override_group_tag_data_object_to_collection_init_collection_process(data, collection);
+ }
+}
+
+static void lib_override_group_tag_data_clear(LibOverrideGroupTagData *data)
+{
+ BLI_ghash_free(data->linked_object_to_instantiating_collections, nullptr, nullptr);
+ BLI_memarena_free(data->mem_arena);
+ memset(data, 0, sizeof(*data));
+}
+
+static void lib_override_hierarchy_dependencies_recursive_tag_from(LibOverrideGroupTagData *data)
+{
+ Main *bmain = data->bmain;
+ ID *id = data->id_root;
+ const bool is_override = data->is_override;
+
+ if ((*(uint *)&id->tag & data->tag) == 0) {
+ /* This ID is not tagged, no reason to proceed further to its parents. */
+ return;
+ }
+
+ MainIDRelationsEntry *entry = static_cast<MainIDRelationsEntry *>(
+ BLI_ghash_lookup(bmain->relations->relations_from_pointers, id));
+ BLI_assert(entry != nullptr);
+
+ if (entry->tags & MAINIDRELATIONS_ENTRY_TAGS_PROCESSED_FROM) {
+ /* 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_FROM;
+
+ for (MainIDRelationsEntryItem *from_id_entry = entry->from_ids; from_id_entry != nullptr;
+ from_id_entry = from_id_entry->next) {
+ if ((from_id_entry->usage_flag & IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE) != 0) {
+ /* Never consider non-overridable relationships ('from', 'parents', 'owner' etc. pointers)
+ * as actual dependencies. */
+ continue;
+ }
+ /* We only consider IDs from the same library. */
+ ID *from_id = from_id_entry->id_pointer.from;
+ if (from_id == nullptr || from_id->lib != id->lib ||
+ (is_override && !ID_IS_OVERRIDE_LIBRARY(from_id))) {
+ /* IDs from different libraries, or non-override IDs in case we are processing overrides,
+ * are both barriers of dependency. */
+ continue;
+ }
+ from_id->tag |= data->tag;
+ LibOverrideGroupTagData sub_data = *data;
+ sub_data.id_root = from_id;
+ lib_override_hierarchy_dependencies_recursive_tag_from(&sub_data);
+ }
+}
+
+/* Tag all IDs in dependency relationships within an override hierarchy/group.
+ *
+ * Requires existing `Main.relations`.
+ *
+ * NOTE: This is typically called to complete `lib_override_linked_group_tag()`.
+ */
+static bool lib_override_hierarchy_dependencies_recursive_tag(LibOverrideGroupTagData *data)
+{
+ Main *bmain = data->bmain;
+ ID *id = data->id_root;
+ const bool is_override = data->is_override;
+ const bool is_resync = data->is_resync;
+
+ MainIDRelationsEntry *entry = static_cast<MainIDRelationsEntry *>(
+ BLI_ghash_lookup(bmain->relations->relations_from_pointers, id));
+ BLI_assert(entry != nullptr);
+
+ if (entry->tags & MAINIDRELATIONS_ENTRY_TAGS_PROCESSED_TO) {
+ /* This ID has already been processed. */
+ return (*(uint *)&id->tag & data->tag) != 0;
+ }
+ /* This way we won't process again that ID, should we encounter it again through another
+ * relationship hierarchy. */
+ entry->tags |= MAINIDRELATIONS_ENTRY_TAGS_PROCESSED_TO;
+
+ for (MainIDRelationsEntryItem *to_id_entry = entry->to_ids; to_id_entry != nullptr;
+ to_id_entry = to_id_entry->next) {
+ if ((to_id_entry->usage_flag & IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE) != 0) {
+ /* Never consider non-overridable relationships ('from', 'parents', 'owner' etc. pointers) as
+ * actual dependencies. */
+ continue;
+ }
+ /* We only consider IDs from the same library. */
+ ID *to_id = *to_id_entry->id_pointer.to;
+ if (to_id == nullptr || 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;
+ }
+ LibOverrideGroupTagData sub_data = *data;
+ sub_data.id_root = to_id;
+ if (lib_override_hierarchy_dependencies_recursive_tag(&sub_data)) {
+ id->tag |= data->tag;
+ }
+ }
+
+ /* If the current ID is/has been tagged for override above, then check its reversed dependencies
+ * (i.e. IDs that depend on the current one).
+ *
+ * This will cover e.g. the case where user override an armature, and would expect the mesh
+ * object deformed by that armature to also be overridden. */
+ if ((*(uint *)&id->tag & data->tag) != 0 && !is_resync) {
+ lib_override_hierarchy_dependencies_recursive_tag_from(data);
+ }
+
+ return (*(uint *)&id->tag & data->tag) != 0;
+}
+
+static void lib_override_linked_group_tag_recursive(LibOverrideGroupTagData *data)
+{
+ 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;
+
+ MainIDRelationsEntry *entry = static_cast<MainIDRelationsEntry *>(
+ BLI_ghash_lookup(bmain->relations->relations_from_pointers, id_owner));
+ BLI_assert(entry != nullptr);
+
+ 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 *to_id_entry = entry->to_ids; to_id_entry != nullptr;
+ to_id_entry = to_id_entry->next) {
+ if ((to_id_entry->usage_flag & IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE) != 0) {
+ /* Never consider non-overridable relationships as actual dependencies. */
+ continue;
+ }
+
+ ID *to_id = *to_id_entry->id_pointer.to;
+ if (ELEM(to_id, nullptr, id_owner)) {
+ continue;
+ }
+ /* We only consider IDs from the same library. */
+ if (to_id->lib != id_owner->lib) {
+ continue;
+ }
+ BLI_assert(ID_IS_LINKED(to_id));
+
+ /* We tag all collections and objects for override. And we also tag all other data-blocks which
+ * would use one of those.
+ * NOTE: missing IDs (aka placeholders) are never overridden. */
+ if (ELEM(GS(to_id->name), ID_OB, ID_GR)) {
+ if (to_id->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_linked_group_tag_recursive(&sub_data);
+ }
+}
+
+static bool lib_override_linked_group_tag_collections_keep_tagged_check_recursive(
+ LibOverrideGroupTagData *data, Collection *collection)
+{
+ /* NOTE: Collection's object cache (using bases, as returned by #BKE_collection_object_cache_get)
+ * is not usable here, as it may have become invalid from some previous operation and it should
+ * not be updated here. So instead only use collections' reliable 'raw' data to check if some
+ * object in the hierarchy of the given collection is still tagged for override. */
+ for (CollectionObject *collection_object =
+ static_cast<CollectionObject *>(collection->gobject.first);
+ collection_object != nullptr;
+ collection_object = collection_object->next) {
+ Object *object = collection_object->ob;
+ if (object == nullptr) {
+ continue;
+ }
+ if ((object->id.tag & data->tag) != 0) {
+ return true;
+ }
+ }
+
+ for (CollectionChild *collection_child =
+ static_cast<CollectionChild *>(collection->children.first);
+ collection_child != nullptr;
+ collection_child = collection_child->next) {
+ if (lib_override_linked_group_tag_collections_keep_tagged_check_recursive(
+ data, collection_child->collection)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static void lib_override_linked_group_tag_clear_boneshapes_objects(LibOverrideGroupTagData *data)
+{
+ Main *bmain = data->bmain;
+ ID *id_root = data->id_root;
+
+ /* Remove (untag) bone shape objects, they shall never need to be to directly/explicitly
+ * overridden. */
+ LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
+ if (ob->type == OB_ARMATURE && ob->pose != nullptr && (ob->id.tag & data->tag)) {
+ for (bPoseChannel *pchan = static_cast<bPoseChannel *>(ob->pose->chanbase.first);
+ pchan != nullptr;
+ pchan = pchan->next) {
+ if (pchan->custom != nullptr && &pchan->custom->id != id_root) {
+ pchan->custom->id.tag &= ~data->tag;
+ }
+ }
+ }
+ }
+
+ /* Remove (untag) collections if they do not own any tagged object (either themselves, or in
+ * their children collections). */
+ LISTBASE_FOREACH (Collection *, collection, &bmain->collections) {
+ if ((collection->id.tag & data->tag) == 0 || &collection->id == id_root) {
+ continue;
+ }
+
+ if (!lib_override_linked_group_tag_collections_keep_tagged_check_recursive(data, collection)) {
+ collection->id.tag &= ~data->tag;
+ }
+ }
+}
+
+/* This will tag at least all 'boundary' linked IDs for a potential override group.
+ *
+ * Requires existing `Main.relations`.
+ *
+ * Note that you will then need to call #lib_override_hierarchy_dependencies_recursive_tag to
+ * complete tagging of all dependencies within the override group.
+ *
+ * We currently only consider Collections and Objects (that are not used as bone shapes) as valid
+ * boundary IDs to define an override group.
+ */
+static void lib_override_linked_group_tag(LibOverrideGroupTagData *data)
+{
+ Main *bmain = data->bmain;
+ ID *id_root = data->id_root;
+ const bool is_resync = data->is_resync;
+ BLI_assert(!data->is_override);
+
+ if (id_root->tag & LIB_TAG_MISSING) {
+ id_root->tag |= data->missing_tag;
+ }
+ else {
+ id_root->tag |= data->tag;
+ }
+
+ /* Tag all collections and objects recursively. */
+ lib_override_linked_group_tag_recursive(data);
+
+ /* Do not override objects used as bone shapes, nor their collections if possible. */
+ lib_override_linked_group_tag_clear_boneshapes_objects(data);
+
+ /* For each object tagged for override, ensure we get at least one local or liboverride
+ * collection to host it. Avoids getting a bunch of random object in the scene's master
+ * collection when all objects' dependencies are not properly 'packed' into a single root
+ * collection.
+ *
+ * NOTE: In resync case, we do not handle this at all, since:
+ * - In normal, valid cases nothing would be needed anyway (resync process takes care
+ * of tagging needed 'owner' collection then).
+ * - Partial resync makes it extremely difficult to properly handle such extra
+ * collection 'tagging for override' (since one would need to know if the new object
+ * is actually going to replace an already existing override [most common case], or
+ * if it is actually a real new 'orphan' one).
+ * - While not ideal, having objects dangling around is less critical than both points
+ * above.
+ * So if users add new objects to their library override hierarchy in an invalid way, so
+ * be it. Trying to find a collection to override and host this new object would most
+ * likely make existing override very unclean anyway. */
+ if (is_resync) {
+ return;
+ }
+ LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
+ if (ID_IS_LINKED(ob) && (ob->id.tag & data->tag) != 0) {
+ Collection *instantiating_collection = nullptr;
+ Collection *instantiating_collection_override_candidate = nullptr;
+ /* Loop over all collections instantiating the object, if we already have a 'locale' one we
+ * have nothing to do, otherwise try to find a 'linked' one that we can override too. */
+ LinkNodePair *instantiating_collection_linklist = static_cast<LinkNodePair *>(
+ BLI_ghash_lookup(data->linked_object_to_instantiating_collections, ob));
+ if (instantiating_collection_linklist != nullptr) {
+ for (LinkNode *instantiating_collection_linknode = instantiating_collection_linklist->list;
+ instantiating_collection_linknode != nullptr;
+ instantiating_collection_linknode = instantiating_collection_linknode->next) {
+ instantiating_collection = static_cast<Collection *>(
+ instantiating_collection_linknode->link);
+ if (!ID_IS_LINKED(instantiating_collection)) {
+ /* There is a local collection instantiating the linked object to override, nothing
+ * else to be done here. */
+ break;
+ }
+ if (instantiating_collection->id.tag & data->tag) {
+ /* There is a linked collection instantiating the linked object to override,
+ * already tagged to be overridden, nothing else to be done here. */
+ break;
+ }
+ instantiating_collection_override_candidate = instantiating_collection;
+ instantiating_collection = nullptr;
+ }
+ }
+
+ if (instantiating_collection == nullptr &&
+ instantiating_collection_override_candidate != nullptr) {
+ if (instantiating_collection_override_candidate->id.tag & LIB_TAG_MISSING) {
+ instantiating_collection_override_candidate->id.tag |= data->missing_tag;
+ }
+ else {
+ instantiating_collection_override_candidate->id.tag |= data->tag;
+ }
+ }
+ }
+ }
+}
+
+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);
+
+ ID *id_hierarchy_root = data->hierarchy_root_id;
+
+ if (ID_IS_OVERRIDE_LIBRARY_REAL(id_owner) &&
+ (id_owner->override_library->flag & IDOVERRIDE_LIBRARY_FLAG_NO_HIERARCHY) != 0) {
+ return;
+ }
+
+ const uint tag = data->tag;
+ const uint missing_tag = data->missing_tag;
+
+ MainIDRelationsEntry *entry = static_cast<MainIDRelationsEntry *>(
+ BLI_ghash_lookup(bmain->relations->relations_from_pointers, id_owner));
+ BLI_assert(entry != nullptr);
+
+ 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 *to_id_entry = entry->to_ids; to_id_entry != nullptr;
+ to_id_entry = to_id_entry->next) {
+ if ((to_id_entry->usage_flag & IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE) != 0) {
+ /* Never consider non-overridable relationships as actual dependencies. */
+ continue;
+ }
+
+ ID *to_id = *to_id_entry->id_pointer.to;
+ if (ELEM(to_id, nullptr, id_owner)) {
+ continue;
+ }
+ /* Different libraries or different hierarchy roots are break points in override hierarchies.
+ */
+ if (!ID_IS_OVERRIDE_LIBRARY(to_id) || (to_id->lib != id_owner->lib)) {
+ continue;
+ }
+ if (ID_IS_OVERRIDE_LIBRARY_REAL(to_id) &&
+ to_id->override_library->hierarchy_root != id_hierarchy_root) {
+ continue;
+ }
+
+ const Library *reference_lib = lib_override_get(bmain, id_owner, nullptr)->reference->lib;
+ const ID *to_id_reference = lib_override_get(bmain, to_id, nullptr)->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_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_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_overrides_group_tag(LibOverrideGroupTagData *data)
+{
+ ID *id_root = data->id_root;
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root));
+ BLI_assert(data->is_override);
+
+ ID *id_hierarchy_root = data->hierarchy_root_id;
+ BLI_assert(id_hierarchy_root != nullptr);
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root));
+ UNUSED_VARS_NDEBUG(id_hierarchy_root);
+
+ if (id_root->override_library->reference->tag & LIB_TAG_MISSING) {
+ id_root->tag |= data->missing_tag;
+ }
+ else {
+ id_root->tag |= data->tag;
+ }
+
+ /* Tag all local overrides in id_root's group. */
+ lib_override_overrides_group_tag_recursive(data);
+}
+
+static bool lib_override_library_create_do(Main *bmain,
+ Scene *scene,
+ Library *owner_library,
+ ID *id_root_reference,
+ ID *id_hierarchy_root_reference,
+ const bool do_fully_editable)
+{
+ BKE_main_relations_create(bmain, 0);
+ LibOverrideGroupTagData data{};
+ data.bmain = bmain;
+ data.scene = scene;
+ data.id_root = id_root_reference;
+ data.tag = LIB_TAG_DOIT;
+ data.missing_tag = LIB_TAG_MISSING;
+ data.is_override = false;
+ data.is_resync = false;
+ lib_override_group_tag_data_object_to_collection_init(&data);
+ lib_override_linked_group_tag(&data);
+
+ BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
+ lib_override_hierarchy_dependencies_recursive_tag(&data);
+
+ /* In case the operation is on an already partially overridden hierarchy, all existing overrides
+ * in that hierarchy need to be tagged for remapping from linked reference ID usages to newly
+ * created overrides ones. */
+ if (id_hierarchy_root_reference->lib != id_root_reference->lib) {
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference));
+ BLI_assert(id_hierarchy_root_reference->override_library->reference->lib ==
+ id_root_reference->lib);
+
+ BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
+ data.hierarchy_root_id = id_hierarchy_root_reference;
+ data.id_root = id_hierarchy_root_reference;
+ data.is_override = true;
+ lib_override_overrides_group_tag(&data);
+ }
+
+ BKE_main_relations_free(bmain);
+ lib_override_group_tag_data_clear(&data);
+
+ bool success = false;
+ if (id_hierarchy_root_reference->lib != id_root_reference->lib) {
+ success = BKE_lib_override_library_create_from_tag(bmain,
+ owner_library,
+ id_root_reference,
+ id_hierarchy_root_reference,
+ nullptr,
+ false,
+ do_fully_editable);
+ }
+ else {
+ success = BKE_lib_override_library_create_from_tag(bmain,
+ owner_library,
+ id_root_reference,
+ nullptr,
+ id_hierarchy_root_reference,
+ false,
+ do_fully_editable);
+ }
+
+ return success;
+}
+
+static void lib_override_library_create_post_process(Main *bmain,
+ Scene *scene,
+ ViewLayer *view_layer,
+ const Library *owner_library,
+ ID *id_root,
+ ID *id_instance_hint,
+ 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. */
+
+ /* We need to use the `_remap` version here as we prevented any LayerCollection resync during the
+ * whole liboverride resyncing, which involves a lot of ID remapping.
+ *
+ * Otherwise, cached Base GHash e.g. can contain invalid stale data. */
+ BKE_main_collection_sync_remap(bmain);
+
+ /* We create a set of all objects referenced into the scene by its hierarchy of collections.
+ * NOTE: This is different that the list of bases, since objects in excluded collections etc.
+ * won't have a base, but are still considered as instanced from our point of view. */
+ GSet *all_objects_in_scene = BKE_scene_objects_as_gset(scene, nullptr);
+
+ /* 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 != nullptr && id_root->newid != nullptr &&
+ (!ID_IS_LINKED(id_root->newid) || id_root->newid->lib == owner_library)) {
+ switch (GS(id_root->name)) {
+ case ID_GR: {
+ Object *ob_reference = id_instance_hint != nullptr && GS(id_instance_hint->name) == ID_OB ?
+ (Object *)id_instance_hint :
+ nullptr;
+ Collection *collection_new = ((Collection *)id_root->newid);
+ if (is_resync && BKE_collection_is_in_scene(collection_new)) {
+ break;
+ }
+ if (ob_reference != nullptr) {
+ BKE_collection_add_from_object(bmain, scene, ob_reference, collection_new);
+ }
+ else if (id_instance_hint != nullptr) {
+ BLI_assert(GS(id_instance_hint->name) == ID_GR);
+ BKE_collection_add_from_collection(
+ bmain, scene, ((Collection *)id_instance_hint), collection_new);
+ }
+ else {
+ BKE_collection_add_from_collection(
+ bmain, scene, ((Collection *)id_root), collection_new);
+ }
+
+ BLI_assert(BKE_collection_is_in_scene(collection_new));
+
+ all_objects_in_scene = BKE_scene_objects_as_gset(scene, all_objects_in_scene);
+ break;
+ }
+ case ID_OB: {
+ Object *ob_new = (Object *)id_root->newid;
+ if (BLI_gset_lookup(all_objects_in_scene, ob_new) == nullptr) {
+ BKE_collection_object_add_from(bmain, scene, (Object *)id_root, ob_new);
+ all_objects_in_scene = BKE_scene_objects_as_gset(scene, all_objects_in_scene);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ /* We need to ensure all new overrides of objects are properly instantiated. */
+ Collection *default_instantiating_collection = residual_storage;
+ LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
+ Object *ob_new = (Object *)ob->id.newid;
+ if (ob_new == nullptr || (ID_IS_LINKED(ob_new) && ob_new->id.lib != owner_library)) {
+ continue;
+ }
+
+ BLI_assert(ob_new->id.override_library != nullptr &&
+ ob_new->id.override_library->reference == &ob->id);
+
+ if (BLI_gset_lookup(all_objects_in_scene, ob_new) == nullptr) {
+ if (id_root != nullptr && default_instantiating_collection == nullptr) {
+ ID *id_ref = id_root->newid != nullptr ? 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 = static_cast<Collection *>(
+ BKE_id_new(bmain, ID_GR, "OVERRIDE_HIDDEN"));
+ id_us_min(&default_instantiating_collection->id);
+ /* Hide the collection from viewport and render. */
+ default_instantiating_collection->flag |= COLLECTION_HIDE_VIEWPORT |
+ COLLECTION_HIDE_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) &&
+ (view_layer != nullptr ?
+ BKE_view_layer_has_collection(view_layer, collection) :
+ BKE_collection_has_collection(scene->master_collection, collection)) &&
+ !ID_IS_LINKED(collection) && !ID_IS_OVERRIDE_LIBRARY(collection)) {
+ default_instantiating_collection = collection;
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ if (default_instantiating_collection == nullptr) {
+ 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 (id_root != nullptr &&
+ !ELEM(default_instantiating_collection, nullptr, scene->master_collection)) {
+ ID *id_ref = id_root->newid != nullptr ? id_root->newid : id_root;
+ switch (GS(id_ref->name)) {
+ case ID_GR:
+ BKE_collection_add_from_collection(
+ bmain, scene, (Collection *)id_ref, default_instantiating_collection);
+ break;
+ default:
+ /* Add to master collection. */
+ BKE_collection_add_from_collection(
+ bmain, scene, nullptr, default_instantiating_collection);
+ break;
+ }
+ }
+
+ BLI_gset_free(all_objects_in_scene, nullptr);
+}
+
+bool BKE_lib_override_library_create(Main *bmain,
+ Scene *scene,
+ ViewLayer *view_layer,
+ Library *owner_library,
+ ID *id_root_reference,
+ ID *id_hierarchy_root_reference,
+ ID *id_instance_hint,
+ ID **r_id_root_override,
+ const bool do_fully_editable)
+{
+ if (r_id_root_override != nullptr) {
+ *r_id_root_override = nullptr;
+ }
+
+ if (id_hierarchy_root_reference == nullptr) {
+ id_hierarchy_root_reference = id_root_reference;
+ }
+
+ const bool success = lib_override_library_create_do(bmain,
+ scene,
+ owner_library,
+ id_root_reference,
+ id_hierarchy_root_reference,
+ do_fully_editable);
+
+ if (!success) {
+ return success;
+ }
+
+ if (r_id_root_override != nullptr) {
+ *r_id_root_override = id_root_reference->newid;
+ }
+
+ lib_override_library_create_post_process(bmain,
+ scene,
+ view_layer,
+ owner_library,
+ id_root_reference,
+ id_instance_hint,
+ nullptr,
+ false);
+
+ /* Cleanup. */
+ 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). */
+ BKE_lib_override_library_main_operations_create(bmain, true);
+
+ return success;
+}
+
+bool BKE_lib_override_library_template_create(ID *id)
+{
+ if (ID_IS_LINKED(id)) {
+ return false;
+ }
+ if (ID_IS_OVERRIDE_LIBRARY(id)) {
+ return false;
+ }
+
+ BKE_lib_override_library_init(id, nullptr);
+ return true;
+}
+
+static ID *lib_override_root_find(Main *bmain, ID *id, const int curr_level, int *r_best_level)
+{
+ if (curr_level > 1000) {
+ CLOG_ERROR(&LOG,
+ "Levels of dependency relationships between library overrides IDs is way too high, "
+ "skipping further processing loops (involves at least '%s')",
+ id->name);
+ return nullptr;
+ }
+
+ if (!ID_IS_OVERRIDE_LIBRARY(id)) {
+ BLI_assert_unreachable();
+ return nullptr;
+ }
+
+ MainIDRelationsEntry *entry = static_cast<MainIDRelationsEntry *>(
+ BLI_ghash_lookup(bmain->relations->relations_from_pointers, id));
+ BLI_assert(entry != nullptr);
+
+ if (entry->tags & MAINIDRELATIONS_ENTRY_TAGS_PROCESSED) {
+ if (ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
+ /* This ID has already been processed. */
+ *r_best_level = curr_level;
+ return id->override_library->hierarchy_root;
+ }
+
+ BLI_assert(id->flag & LIB_EMBEDDED_DATA_LIB_OVERRIDE);
+ ID *id_owner;
+ int best_level_placeholder = 0;
+ lib_override_get(bmain, id, &id_owner);
+ return lib_override_root_find(bmain, id_owner, curr_level + 1, &best_level_placeholder);
+ }
+ /* This way we won't process again that ID, should we encounter it again through another
+ * relationship hierarchy. */
+ entry->tags |= MAINIDRELATIONS_ENTRY_TAGS_PROCESSED;
+
+ int best_level_candidate = curr_level;
+ ID *best_root_id_candidate = id;
+
+ for (MainIDRelationsEntryItem *from_id_entry = entry->from_ids; from_id_entry != nullptr;
+ from_id_entry = from_id_entry->next) {
+ if ((from_id_entry->usage_flag & IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE) != 0) {
+ /* Never consider non-overridable relationships as actual dependencies. */
+ continue;
+ }
+
+ ID *from_id = from_id_entry->id_pointer.from;
+ if (ELEM(from_id, nullptr, id)) {
+ continue;
+ }
+ if (!ID_IS_OVERRIDE_LIBRARY(from_id) || (from_id->lib != id->lib)) {
+ continue;
+ }
+
+ int level_candidate = curr_level + 1;
+ /* Recursively process the parent. */
+ ID *root_id_candidate = lib_override_root_find(
+ bmain, from_id, curr_level + 1, &level_candidate);
+ if (level_candidate > best_level_candidate && root_id_candidate != nullptr) {
+ best_root_id_candidate = root_id_candidate;
+ best_level_candidate = level_candidate;
+ }
+ }
+
+ if (!ID_IS_OVERRIDE_LIBRARY_REAL(best_root_id_candidate)) {
+ BLI_assert(id->flag & LIB_EMBEDDED_DATA_LIB_OVERRIDE);
+ ID *id_owner;
+ int best_level_placeholder = 0;
+ lib_override_get(bmain, best_root_id_candidate, &id_owner);
+ best_root_id_candidate = lib_override_root_find(
+ bmain, id_owner, curr_level + 1, &best_level_placeholder);
+ }
+
+ BLI_assert(best_root_id_candidate != nullptr);
+ BLI_assert((best_root_id_candidate->flag & LIB_EMBEDDED_DATA_LIB_OVERRIDE) == 0);
+
+ *r_best_level = best_level_candidate;
+ return best_root_id_candidate;
+}
+
+static void lib_override_root_hierarchy_set(Main *bmain, ID *id_root, ID *id, ID *id_from)
+{
+ if (ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
+ if (id->override_library->hierarchy_root == id_root) {
+ /* Already set, nothing else to do here, sub-hierarchy is also assumed to be properly set
+ * then. */
+ return;
+ }
+
+ /* Hierarchy root already set, and not matching currently proposed one, try to find which is
+ * best. */
+ if (id->override_library->hierarchy_root != nullptr) {
+ /* Check if given `id_from` matches with the hierarchy of the linked reference ID, in which
+ * case we assume that the given hierarchy root is the 'real' one.
+ *
+ * NOTE: This can fail if user mixed dependencies between several overrides of a same
+ * reference linked hierarchy. Not much to be done in that case, it's virtually impossible to
+ * fix this automatically in a reliable way. */
+ if (id_from == nullptr || !ID_IS_OVERRIDE_LIBRARY_REAL(id_from)) {
+ /* Too complicated to deal with for now. */
+ CLOG_WARN(&LOG,
+ "Inconsistency in library override hierarchy of ID '%s'.\n"
+ "\tNot enough data to verify validity of current proposed root '%s', assuming "
+ "already set one '%s' is valid.",
+ id->name,
+ id_root->name,
+ id->override_library->hierarchy_root->name);
+ return;
+ }
+
+ ID *id_from_ref = id_from->override_library->reference;
+ MainIDRelationsEntry *entry = static_cast<MainIDRelationsEntry *>(BLI_ghash_lookup(
+ bmain->relations->relations_from_pointers, id->override_library->reference));
+ BLI_assert(entry != nullptr);
+
+ bool do_replace_root = false;
+ for (MainIDRelationsEntryItem *from_id_entry = entry->from_ids; from_id_entry != nullptr;
+ from_id_entry = from_id_entry->next) {
+ if ((from_id_entry->usage_flag & IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE) != 0) {
+ /* Never consider non-overridable relationships as actual dependencies. */
+ continue;
+ }
+
+ if (id_from_ref == from_id_entry->id_pointer.from) {
+ /* A matching parent was found in reference linked data, assume given hierarchy root is
+ * the valid one. */
+ do_replace_root = true;
+ CLOG_WARN(
+ &LOG,
+ "Inconsistency in library override hierarchy of ID '%s'.\n"
+ "\tCurrent proposed root '%s' detected as valid, will replace already set one '%s'.",
+ id->name,
+ id_root->name,
+ id->override_library->hierarchy_root->name);
+ break;
+ }
+ }
+
+ if (!do_replace_root) {
+ CLOG_WARN(
+ &LOG,
+ "Inconsistency in library override hierarchy of ID '%s'.\n"
+ "\tCurrent proposed root '%s' not detected as valid, keeping already set one '%s'.",
+ id->name,
+ id_root->name,
+ id->override_library->hierarchy_root->name);
+ return;
+ }
+ }
+
+ id->override_library->hierarchy_root = id_root;
+ }
+
+ MainIDRelationsEntry *entry = static_cast<MainIDRelationsEntry *>(
+ BLI_ghash_lookup(bmain->relations->relations_from_pointers, id));
+ BLI_assert(entry != nullptr);
+
+ for (MainIDRelationsEntryItem *to_id_entry = entry->to_ids; to_id_entry != nullptr;
+ to_id_entry = to_id_entry->next) {
+ if ((to_id_entry->usage_flag & IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE) != 0) {
+ /* Never consider non-overridable relationships as actual dependencies. */
+ continue;
+ }
+
+ ID *to_id = *to_id_entry->id_pointer.to;
+ if (ELEM(to_id, nullptr, id)) {
+ continue;
+ }
+ if (!ID_IS_OVERRIDE_LIBRARY(to_id) || (to_id->lib != id->lib)) {
+ continue;
+ }
+
+ /* Recursively process the sub-hierarchy. */
+ lib_override_root_hierarchy_set(bmain, id_root, to_id, id);
+ }
+}
+
+void BKE_lib_override_library_main_hierarchy_root_ensure(Main *bmain)
+{
+ ID *id;
+
+ BKE_main_relations_create(bmain, 0);
+
+ FOREACH_MAIN_ID_BEGIN (bmain, id) {
+ if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
+ continue;
+ }
+ if (id->override_library->hierarchy_root != nullptr) {
+ if (!ID_IS_OVERRIDE_LIBRARY_REAL(id->override_library->hierarchy_root) ||
+ id->override_library->hierarchy_root->lib != id->lib) {
+ CLOG_ERROR(
+ &LOG,
+ "Existing override hierarchy root ('%s') for ID '%s' is invalid, will try to find a "
+ "new valid one",
+ id->override_library->hierarchy_root != nullptr ?
+ id->override_library->hierarchy_root->name :
+ "<NONE>",
+ id->name);
+ id->override_library->hierarchy_root = nullptr;
+ }
+ else {
+ continue;
+ }
+ }
+
+ BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
+
+ int best_level = 0;
+ ID *id_root = lib_override_root_find(bmain, id, best_level, &best_level);
+
+ if (!ELEM(id_root->override_library->hierarchy_root, id_root, nullptr)) {
+ CLOG_WARN(&LOG,
+ "Potential inconsistency in library override hierarchy of ID '%s', detected as "
+ "part of the hierarchy of '%s', which has a different root '%s'",
+ id->name,
+ id_root->name,
+ id_root->override_library->hierarchy_root->name);
+ continue;
+ }
+
+ lib_override_root_hierarchy_set(bmain, id_root, id, nullptr);
+
+ BLI_assert(id->override_library->hierarchy_root != nullptr);
+ }
+ FOREACH_MAIN_ID_END;
+
+ BKE_main_relations_free(bmain);
+}
+
+static void lib_override_library_remap(Main *bmain,
+ const ID *id_root_reference,
+ GHash *linkedref_to_old_override)
+{
+ ID *id;
+ IDRemapper *remapper = BKE_id_remapper_create();
+ LinkNode *nomain_ids = nullptr;
+
+ FOREACH_MAIN_ID_BEGIN (bmain, id) {
+ if (id->tag & LIB_TAG_DOIT && id->newid != nullptr && id->lib == id_root_reference->lib) {
+ ID *id_override_new = id->newid;
+ ID *id_override_old = static_cast<ID *>(BLI_ghash_lookup(linkedref_to_old_override, id));
+ if (id_override_old == nullptr) {
+ continue;
+ }
+
+ BKE_id_remapper_add(remapper, id_override_old, id_override_new);
+ }
+ }
+ FOREACH_MAIN_ID_END;
+
+ /* 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 = static_cast<ID *>(
+ BLI_ghashIterator_getValue(&linkedref_to_old_override_iter));
+ if ((id_override_old_iter->tag & LIB_TAG_NO_MAIN) == 0) {
+ continue;
+ }
+
+ BLI_linklist_prepend(&nomain_ids, id_override_old_iter);
+ }
+
+ /* Remap all IDs to use the new override. */
+ BKE_libblock_remap_multiple(bmain, remapper, 0);
+ BKE_libblock_relink_multiple(bmain,
+ nomain_ids,
+ ID_REMAP_TYPE_REMAP,
+ remapper,
+ ID_REMAP_FORCE_USER_REFCOUNT | ID_REMAP_FORCE_NEVER_NULL_USAGE);
+ BKE_id_remapper_free(remapper);
+ BLI_linklist_free(nomain_ids, nullptr);
+}
+
+static bool lib_override_library_resync(Main *bmain,
+ Scene *scene,
+ ViewLayer *view_layer,
+ ID *id_root,
+ LinkNode *id_resync_roots,
+ ListBase *no_main_ids_list,
+ Collection *override_resync_residual_storage,
+ const bool do_hierarchy_enforce,
+ const bool do_post_process,
+ BlendFileReadReport *reports)
+{
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root));
+
+ ID *id_root_reference = id_root->override_library->reference;
+ ID *id;
+
+ if (id_root_reference->tag & LIB_TAG_MISSING) {
+ BKE_reportf(reports != nullptr ? reports->reports : nullptr,
+ RPT_ERROR,
+ "Impossible to resync data-block %s and its dependencies, as its linked reference "
+ "is missing",
+ id_root->name + 2);
+ return false;
+ }
+
+ BKE_main_relations_create(bmain, 0);
+ LibOverrideGroupTagData data{};
+ data.bmain = bmain;
+ data.scene = scene;
+ data.id_root = id_root;
+ data.hierarchy_root_id = id_root->override_library->hierarchy_root;
+ data.tag = LIB_TAG_DOIT;
+ data.missing_tag = LIB_TAG_MISSING;
+ data.is_override = true;
+ data.is_resync = true;
+ lib_override_group_tag_data_object_to_collection_init(&data);
+
+ /* Mapping 'linked reference IDs' -> 'Local override IDs' of existing overrides, populated from
+ * each sub-tree that actually needs to be resynced. */
+ GHash *linkedref_to_old_override = BLI_ghash_new(
+ BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__);
+
+ /* Only tag linked IDs from related linked reference hierarchy that are actually part of
+ * the sub-trees of each detected sub-roots needing resync. */
+ for (LinkNode *resync_root_link = id_resync_roots; resync_root_link != nullptr;
+ resync_root_link = resync_root_link->next) {
+ ID *id_resync_root = static_cast<ID *>(resync_root_link->link);
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_resync_root));
+
+ if ((id_resync_root->tag & LIB_TAG_NO_MAIN) != 0) {
+ CLOG_ERROR(&LOG,
+ "While dealing with root '%s', resync root ID '%s' (%p) found to be alreaady "
+ "resynced.\n",
+ id_root->name,
+ id_resync_root->name,
+ id_resync_root);
+ }
+ // if (no_main_ids_list && BLI_findindex(no_main_ids_list, id_resync_root) != -1) {
+ // CLOG_ERROR(
+ // &LOG,
+ // "While dealing with root '%s', resync root ID '%s' found to be alreaady
+ // resynced.\n", id_root->name, id_resync_root->name);
+ // }
+
+ ID *id_resync_root_reference = id_resync_root->override_library->reference;
+
+ if (id_resync_root_reference->tag & LIB_TAG_MISSING) {
+ BKE_reportf(
+ reports != nullptr ? reports->reports : nullptr,
+ RPT_ERROR,
+ "Impossible to resync data-block %s and its dependencies, as its linked reference "
+ "is missing",
+ id_root->name + 2);
+ BLI_ghash_free(linkedref_to_old_override, nullptr, nullptr);
+ BKE_main_relations_free(bmain);
+ lib_override_group_tag_data_clear(&data);
+ return false;
+ }
+
+ /* Tag local overrides of the current resync sub-hierarchy. */
+ BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
+ data.id_root = id_resync_root;
+ data.is_override = true;
+ lib_override_overrides_group_tag(&data);
+
+ /* Tag reference data matching the current resync sub-hierarchy. */
+ BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
+ data.id_root = id_resync_root->override_library->reference;
+ data.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);
+
+ FOREACH_MAIN_ID_BEGIN (bmain, id) {
+ /* IDs that get fully removed from linked data remain as local overrides (using place-holder
+ * linked IDs as reference), but they are often not reachable from any current valid local
+ * override hierarchy anymore. This will ensure they get properly deleted at the end of this
+ * function. */
+ if (!ID_IS_LINKED(id) && ID_IS_OVERRIDE_LIBRARY_REAL(id) &&
+ (id->override_library->reference->tag & LIB_TAG_MISSING) != 0 &&
+ /* Unfortunately deleting obdata means deleting their objects too. Since there is no
+ * guarantee that a valid override object using an obsolete override obdata gets properly
+ * updated, we ignore those here for now. In practice this should not be a big issue. */
+ !OB_DATA_SUPPORT_ID(GS(id->name))) {
+ id->tag |= LIB_TAG_MISSING;
+ }
+
+ if ((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. */
+ IDOverrideLibrary *id_override_library = lib_override_get(bmain, id, nullptr);
+
+ if (id_override_library->hierarchy_root != id_root->override_library->hierarchy_root) {
+ continue;
+ }
+
+ 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;
+ }
+ }
+ if (reference_id == nullptr) {
+ /* Can happen e.g. when there is a local override of a shapekey, but the matching linked
+ * obdata (mesh etc.) does not have any shapekey anymore. */
+ continue;
+ }
+ 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) || (id->tag & LIB_TAG_DOIT) == 0) {
+ 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
+ * that we can get rid of this local override. */
+ LISTBASE_FOREACH (IDOverrideLibraryProperty *, op, &id->override_library->properties) {
+ if (!ELEM(op->rna_prop_type, PROP_POINTER, PROP_COLLECTION)) {
+ id->override_library->reference->tag |= LIB_TAG_DOIT;
+ break;
+ }
+
+ bool do_break = false;
+ LISTBASE_FOREACH (IDOverrideLibraryPropertyOperation *, opop, &op->operations) {
+ if ((opop->flag & IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE) == 0) {
+ id->override_library->reference->tag |= LIB_TAG_DOIT;
+ do_break = true;
+ break;
+ }
+ }
+ if (do_break) {
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ FOREACH_MAIN_ID_END;
+
+ /* Code above may have added some tags, we need to update this too. */
+ BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
+ lib_override_hierarchy_dependencies_recursive_tag(&data);
+ }
+
+ /* Tag all local overrides of the current hierarchy. */
+ BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
+ data.id_root = id_root;
+ data.is_override = true;
+ lib_override_overrides_group_tag(&data);
+
+ BKE_main_relations_free(bmain);
+ lib_override_group_tag_data_clear(&data);
+
+ /* Make new override from linked data. */
+ /* 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,
+ nullptr,
+ id_root_reference,
+ id_root->override_library->hierarchy_root,
+ nullptr,
+ true,
+ false);
+
+ if (!success) {
+ BLI_ghash_free(linkedref_to_old_override, nullptr, nullptr);
+ return success;
+ }
+
+ ListBase *lb;
+ FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) {
+ FOREACH_MAIN_LISTBASE_ID_BEGIN (lb, id) {
+ if (id->tag & LIB_TAG_DOIT && id->newid != nullptr && id->lib == id_root_reference->lib) {
+ ID *id_override_new = id->newid;
+ ID *id_override_old = static_cast<ID *>(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_IS_LINKED(id_override_new) || */ id_override_new->lib == id->lib);
+ BLI_assert(id_override_old == nullptr || 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 != nullptr) {
+ /* 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));
+
+ 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;
+
+ lib_override_object_posemode_transfer(id_override_new, id_override_old);
+
+ if (ID_IS_OVERRIDE_LIBRARY_REAL(id_override_new)) {
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_override_old));
+
+ id_override_new->override_library->flag = id_override_old->override_library->flag;
+
+ /* 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 = static_cast<IDOverrideLibraryProperty *>(
+ id_override_new->override_library->properties.first);
+ IDOverrideLibraryProperty *op_old = static_cast<IDOverrideLibraryProperty *>(
+ 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);
+ }
+ }
+
+ BLI_addtail(no_main_ids_list, id_override_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 remap old to new override usages in a separate loop, after all new overrides have
+ * been added to Main. */
+ lib_override_library_remap(bmain, id_root_reference, linkedref_to_old_override);
+
+ BKE_main_collection_sync(bmain);
+
+ LinkNode *id_override_old_list = nullptr;
+
+ /* 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 != nullptr && 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 = static_cast<ID *>(BLI_ghash_lookup(linkedref_to_old_override, id));
+
+ if (id_override_old == nullptr) {
+ 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. */
+ PointerRNA rnaptr_src, rnaptr_dst;
+ RNA_id_pointer_create(id_override_old, &rnaptr_src);
+ RNA_id_pointer_create(id_override_new, &rnaptr_dst);
+
+ /* We remove any operation tagged with `IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE`,
+ * that way the potentially new pointer will be properly kept, when old one is still valid
+ * too (typical case: assigning new ID to some usage, while old one remains used elsewhere
+ * in the override hierarchy). */
+ LISTBASE_FOREACH_MUTABLE (
+ IDOverrideLibraryProperty *, op, &id_override_new->override_library->properties) {
+ LISTBASE_FOREACH_MUTABLE (IDOverrideLibraryPropertyOperation *, opop, &op->operations) {
+ if (opop->flag & IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE) {
+ lib_override_library_property_operation_clear(opop);
+ BLI_freelinkN(&op->operations, opop);
+ }
+ }
+ if (BLI_listbase_is_empty(&op->operations)) {
+ BKE_lib_override_library_property_delete(id_override_new->override_library, op);
+ }
+ }
+
+ RNA_struct_override_apply(bmain,
+ &rnaptr_dst,
+ &rnaptr_src,
+ nullptr,
+ id_override_new->override_library,
+ do_hierarchy_enforce ?
+ RNA_OVERRIDE_APPLY_FLAG_IGNORE_ID_POINTERS :
+ RNA_OVERRIDE_APPLY_FLAG_NOP);
+ }
+
+ BLI_linklist_prepend(&id_override_old_list, id_override_old);
+ }
+ }
+ FOREACH_MAIN_ID_END;
+
+ /* 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. */
+ IDRemapper *id_remapper = BKE_id_remapper_create();
+ BKE_libblock_relink_multiple(bmain,
+ id_override_old_list,
+ ID_REMAP_TYPE_CLEANUP,
+ id_remapper,
+ ID_REMAP_FORCE_USER_REFCOUNT | ID_REMAP_FORCE_NEVER_NULL_USAGE);
+ for (LinkNode *ln_iter = id_override_old_list; ln_iter != nullptr; ln_iter = ln_iter->next) {
+ ID *id_override_old = static_cast<ID *>(ln_iter->link);
+ id_override_old->tag |= LIB_TAG_NO_USER_REFCOUNT;
+ }
+ BKE_id_remapper_free(id_remapper);
+ BLI_linklist_free(id_override_old_list, nullptr);
+
+ /* 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. */
+ 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 != nullptr && id->lib == id_root_reference->lib) {
+ ID *id_override_old = static_cast<ID *>(BLI_ghash_lookup(linkedref_to_old_override, id));
+
+ if (id_override_old != nullptr) {
+ id->newid->tag &= ~LIB_TAG_DOIT;
+ id_override_old->tag |= LIB_TAG_DOIT;
+ if (id_override_old->tag & LIB_TAG_NO_MAIN) {
+ BLI_assert(BLI_findindex(no_main_ids_list, id_override_old) != -1);
+ }
+ }
+ }
+ id->tag &= ~LIB_TAG_DOIT;
+ }
+ /* 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)) {
+ /* If user never edited them, we can delete them. */
+ id->tag |= LIB_TAG_DOIT;
+ id->tag &= ~LIB_TAG_MISSING;
+ CLOG_INFO(&LOG, 2, "Old override %s is being deleted", id->name);
+ }
+#if 0
+ else {
+ /* Otherwise, keep them, user needs to decide whether what to do with them. */
+ BLI_assert((id->tag & LIB_TAG_DOIT) == 0);
+ id_fake_user_set(id);
+ id->flag |= LIB_LIB_OVERRIDE_RESYNC_LEFTOVER;
+ CLOG_INFO(&LOG, 2, "Old override %s is being kept around as it was user-edited", id->name);
+ }
+#else
+ else {
+ /* Delete them nevertheless, with fat warning, user needs to decide whether they want to
+ * save that version of the file (and accept the loss), or not. */
+ id->tag |= LIB_TAG_DOIT;
+ id->tag &= ~LIB_TAG_MISSING;
+ CLOG_WARN(
+ &LOG, "Old override %s is being deleted even though it was user-edited", id->name);
+ user_edited_overrides_deletion_count++;
+ }
+#endif
+ }
+ }
+ FOREACH_MAIN_ID_END;
+
+ /* Cleanup, many pointers in this GHash are already invalid now. */
+ BLI_ghash_free(linkedref_to_old_override, nullptr, nullptr);
+
+ BKE_id_multi_tagged_delete(bmain);
+
+ /* At this point, `id_root` may have been resynced, therefore deleted. In that case we need to
+ * update it to its new version.
+ */
+ if (id_root_reference->newid != nullptr) {
+ id_root = id_root_reference->newid;
+ }
+
+ if (user_edited_overrides_deletion_count > 0) {
+ BKE_reportf(reports != nullptr ? reports->reports : nullptr,
+ RPT_WARNING,
+ "During resync of data-block %s, %d obsolete overrides were deleted, that had "
+ "local changes defined by user",
+ id_root->name + 2,
+ user_edited_overrides_deletion_count);
+ }
+
+ if (do_post_process) {
+ /* Essentially ensures that potentially new overrides of new objects will be instantiated. */
+ /* NOTE: Here 'reference' collection and 'newly added' collection are the same, which is fine
+ * since we already relinked old root override collection to new resync'ed one above. So this
+ * call is not expected to instantiate this new resync'ed collection anywhere, just to ensure
+ * that we do not have any stray objects. */
+ lib_override_library_create_post_process(bmain,
+ scene,
+ view_layer,
+ nullptr,
+ id_root_reference,
+ id_root,
+ override_resync_residual_storage,
+ true);
+ }
+
+ /* Cleanup. */
+ 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;
+}
+
+bool BKE_lib_override_library_resync(Main *bmain,
+ Scene *scene,
+ ViewLayer *view_layer,
+ ID *id_root,
+ Collection *override_resync_residual_storage,
+ const bool do_hierarchy_enforce,
+ BlendFileReadReport *reports)
+{
+ ListBase no_main_ids_list = {nullptr};
+ LinkNode id_resync_roots{};
+ id_resync_roots.link = id_root;
+ id_resync_roots.next = nullptr;
+
+ const bool success = lib_override_library_resync(bmain,
+ scene,
+ view_layer,
+ id_root,
+ &id_resync_roots,
+ &no_main_ids_list,
+ override_resync_residual_storage,
+ do_hierarchy_enforce,
+ true,
+ reports);
+
+ LISTBASE_FOREACH_MUTABLE (ID *, id_iter, &no_main_ids_list) {
+ BKE_id_free(bmain, id_iter);
+ }
+
+ return success;
+}
+
+static bool lib_override_resync_id_lib_level_is_valid(ID *id,
+ const int library_indirect_level,
+ const bool do_strict_equal)
+{
+ const int id_lib_level = (ID_IS_LINKED(id) ? id->lib->temp_index : 0);
+ return do_strict_equal ? id_lib_level == library_indirect_level :
+ id_lib_level <= library_indirect_level;
+}
+
+/* Find the root of the override hierarchy the given `id` belongs to. */
+static ID *lib_override_library_main_resync_root_get(Main *bmain, ID *id)
+{
+ if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
+ const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id);
+ if (id_type->owner_get != nullptr) {
+ id = id_type->owner_get(bmain, id);
+ }
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id));
+ }
+
+ ID *hierarchy_root_id = id->override_library->hierarchy_root;
+ BLI_assert(hierarchy_root_id != nullptr);
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(hierarchy_root_id));
+ return hierarchy_root_id;
+}
+
+/* Check ancestors overrides for resync, to ensure all IDs in-between two tagged-for-resync ones
+ * are also properly tagged, and to identify the resync root of this subset of the whole hierarchy.
+ *
+ * WARNING: Expects `bmain` to have valid relation data.
+ *
+ * Returns `true` if it finds an ancestor within the current liboverride hierarchy also tagged as
+ * needing resync, `false` otherwise.
+ *
+ * NOTE: If `check_only` is true, it only does the check and returns, without any modification to
+ * the data. Used for debug/data validation purposes.
+ */
+static bool lib_override_resync_tagging_finalize_recurse(
+ Main *bmain, ID *id, GHash *id_roots, const int library_indirect_level, const bool check_only)
+{
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id));
+
+ if (!lib_override_resync_id_lib_level_is_valid(id, library_indirect_level, false) &&
+ !check_only) {
+ CLOG_ERROR(
+ &LOG,
+ "While processing indirect level %d, ID %s from lib %s of indirect level %d detected "
+ "as needing resync, skipping.",
+ library_indirect_level,
+ id->name,
+ id->lib->filepath,
+ id->lib->temp_index);
+ id->tag &= ~LIB_TAG_LIB_OVERRIDE_NEED_RESYNC;
+ return false;
+ }
+
+ MainIDRelationsEntry *entry = static_cast<MainIDRelationsEntry *>(
+ BLI_ghash_lookup(bmain->relations->relations_from_pointers, id));
+ BLI_assert(entry != nullptr);
+
+ if (entry->tags & MAINIDRELATIONS_ENTRY_TAGS_PROCESSED) {
+ /* This ID has already been processed. */
+ return (ID_IS_OVERRIDE_LIBRARY(id) && (id->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) != 0);
+ }
+
+ /* This way we won't process again that ID, should we encounter it again through another
+ * relationship hierarchy. */
+ entry->tags |= MAINIDRELATIONS_ENTRY_TAGS_PROCESSED;
+
+ /* There can be dependency loops in relationships, e.g.:
+ * - Rig object uses armature ID.
+ * - Armature ID has some drivers using rig object as variable.
+ *
+ * In that case, if this function is initially called on the rig object (therefore tagged as
+ * needing resync), it will:
+ * - Process the rig object
+ * -- Recurse over the armature ID
+ * --- Recurse over the rig object again
+ * --- The rig object is detected as already processed and returns true.
+ * -- The armature has a tagged ancestor (the rig object), so it is not a resync root.
+ * - The rig object has a tagged ancestor (the armature), so it is not a resync root.
+ * ...and this ends up with no resync root.
+ *
+ * Removing the 'need resync' tag before doing the recursive calls allow to break this loop and
+ * results in actually getting a resync root, even though it may not be the 'logical' one.
+ *
+ * In the example above, the armature will become the resync root.
+ */
+ const int id_tag_need_resync = (id->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC);
+ id->tag &= ~LIB_TAG_LIB_OVERRIDE_NEED_RESYNC;
+
+ bool is_ancestor_tagged_for_resync = false;
+ for (MainIDRelationsEntryItem *entry_item = entry->from_ids; entry_item != nullptr;
+ 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;
+
+ /* Check if this ID has an override hierarchy ancestor already tagged for resync. */
+ if (id_from != id && ID_IS_OVERRIDE_LIBRARY_REAL(id_from) && id_from->lib == id->lib &&
+ id_from->override_library->hierarchy_root == id->override_library->hierarchy_root) {
+ const bool is_ancestor_tagged_for_resync_prev = is_ancestor_tagged_for_resync;
+ is_ancestor_tagged_for_resync |= lib_override_resync_tagging_finalize_recurse(
+ bmain, id_from, id_roots, library_indirect_level, check_only);
+
+ if (!check_only && is_ancestor_tagged_for_resync && !is_ancestor_tagged_for_resync_prev) {
+ CLOG_INFO(&LOG,
+ 4,
+ "ID %s (%p) now tagged as needing resync because they are used by %s (%p) "
+ "that needs to be resynced",
+ id->name,
+ id->lib,
+ id_from->name,
+ id_from->lib);
+ }
+ }
+ }
+
+ /* Re-enable 'need resync' tag if needed. */
+ id->tag |= id_tag_need_resync;
+
+ if (check_only) {
+ return is_ancestor_tagged_for_resync;
+ }
+
+ if (is_ancestor_tagged_for_resync) {
+ /* If a tagged-for-resync ancestor was found, this id is not a resync sub-tree root, but it is
+ * part of one, and therefore needs to be tagged for resync too. */
+ id->tag |= LIB_TAG_LIB_OVERRIDE_NEED_RESYNC;
+ }
+ else if (id->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) {
+ CLOG_INFO(&LOG,
+ 4,
+ "ID %s (%p) is tagged as needing resync, but none of its override hierarchy "
+ "ancestors are tagged for resync, so it is a partial resync root",
+ id->name,
+ id->lib);
+
+ /* If no tagged-for-resync ancestor was found, but this id is tagged for resync, then it is a
+ * root of a resync sub-tree. Find the root of the whole override hierarchy and add this ID as
+ * one of its resync sub-tree roots. */
+ ID *id_root = lib_override_library_main_resync_root_get(bmain, id);
+ BLI_assert(id_root->lib == id->lib);
+
+ CLOG_INFO(&LOG, 4, "Found root ID '%s' for resync root ID '%s'", id_root->name, id->name);
+
+ BLI_assert(id_root->override_library != nullptr);
+
+ LinkNodePair **id_resync_roots_p;
+ if (!BLI_ghash_ensure_p(id_roots, id_root, (void ***)&id_resync_roots_p)) {
+ *id_resync_roots_p = MEM_cnew<LinkNodePair>(__func__);
+ }
+
+ BLI_linklist_append(*id_resync_roots_p, id);
+ is_ancestor_tagged_for_resync = true;
+ }
+
+ return is_ancestor_tagged_for_resync;
+}
+
+/* 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,
+ BlendFileReadReport *reports)
+{
+ const bool do_reports_recursive_resync_timing = (library_indirect_level != 0);
+ const double init_time = do_reports_recursive_resync_timing ? PIL_check_seconds_timer() : 0.0;
+
+ BKE_main_relations_create(bmain, 0);
+ BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
+
+ /* NOTE: in code below, the order in which `FOREACH_MAIN_ID_BEGIN` processes ID types ensures
+ * that we always process 'higher-level' overrides first (i.e. scenes, then collections, then
+ * objects, then other types). */
+
+ /* Detect all linked data that would need to be overridden if we had to create an override from
+ * those used by current existing overrides. */
+ LibOverrideGroupTagData data = {};
+ data.bmain = bmain;
+ data.scene = scene;
+ data.id_root = nullptr;
+ data.tag = LIB_TAG_DOIT;
+ data.missing_tag = LIB_TAG_MISSING;
+ data.is_override = false;
+ data.is_resync = true;
+ lib_override_group_tag_data_object_to_collection_init(&data);
+ ID *id;
+ FOREACH_MAIN_ID_BEGIN (bmain, id) {
+ if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
+ continue;
+ }
+ if (id->tag & (LIB_TAG_DOIT | LIB_TAG_MISSING)) {
+ /* We already processed that ID as part of another ID's hierarchy. */
+ continue;
+ }
+
+ if (id->override_library->flag & IDOVERRIDE_LIBRARY_FLAG_NO_HIERARCHY) {
+ /* This ID is not part of an override hierarchy. */
+ continue;
+ }
+
+ data.id_root = id->override_library->reference;
+ lib_override_linked_group_tag(&data);
+ BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
+ lib_override_hierarchy_dependencies_recursive_tag(&data);
+ BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
+ }
+ FOREACH_MAIN_ID_END;
+ lib_override_group_tag_data_clear(&data);
+
+ GHash *id_roots = BLI_ghash_ptr_new(__func__);
+
+ /* 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)) {
+ continue;
+ }
+
+ if (!lib_override_resync_id_lib_level_is_valid(id, library_indirect_level, true)) {
+ continue;
+ }
+
+ if (id->override_library->flag & IDOVERRIDE_LIBRARY_FLAG_NO_HIERARCHY) {
+ /* This ID is not part of an override hierarchy. */
+ BLI_assert((id->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) == 0);
+ continue;
+ }
+
+ if (id->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) {
+ 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, id_roots, library_indirect_level, false);
+ continue;
+ }
+
+ MainIDRelationsEntry *entry = static_cast<MainIDRelationsEntry *>(
+ BLI_ghash_lookup(bmain->relations->relations_from_pointers, id));
+ BLI_assert(entry != nullptr);
+
+ for (MainIDRelationsEntryItem *entry_item = entry->to_ids; entry_item != nullptr;
+ entry_item = entry_item->next) {
+ if (entry_item->usage_flag & IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE) {
+ continue;
+ }
+ 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->lib != id->lib) && (id_to->tag & LIB_TAG_DOIT) != 0) {
+ id->tag |= LIB_TAG_LIB_OVERRIDE_NEED_RESYNC;
+ CLOG_INFO(&LOG,
+ 3,
+ "ID %s (%p) now tagged as needing resync because they use linked %s (%p) that "
+ "now needs to be overridden",
+ id->name,
+ id->lib,
+ id_to->name,
+ id_to->lib);
+ lib_override_resync_tagging_finalize_recurse(
+ bmain, id, id_roots, library_indirect_level, false);
+ break;
+ }
+ }
+ }
+ FOREACH_MAIN_ID_END;
+
+#ifndef NDEBUG
+ /* Check for validity/integrity of the computed set of root IDs, and their sub-branches defined
+ * by their resync root IDs. */
+ {
+ BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
+ GHashIterator *id_roots_iter = BLI_ghashIterator_new(id_roots);
+ while (!BLI_ghashIterator_done(id_roots_iter)) {
+ ID *id_root = static_cast<ID *>(BLI_ghashIterator_getKey(id_roots_iter));
+ LinkNodePair *id_resync_roots = static_cast<LinkNodePair *>(
+ BLI_ghashIterator_getValue(id_roots_iter));
+ CLOG_INFO(
+ &LOG, 2, "Checking validity of computed TODO data for root '%s'... \n", id_root->name);
+ for (LinkNode *id_resync_root_iter = id_resync_roots->list; id_resync_root_iter != nullptr;
+ id_resync_root_iter = id_resync_root_iter->next) {
+ ID *id_resync_root = static_cast<ID *>(id_resync_root_iter->link);
+ BLI_assert(id_resync_root == id_root || !BLI_ghash_haskey(id_roots, id_resync_root));
+ if (id_resync_root == id_root) {
+ BLI_assert(id_resync_root_iter == id_resync_roots->list &&
+ id_resync_root_iter == id_resync_roots->last_node);
+ }
+ BLI_assert(!lib_override_resync_tagging_finalize_recurse(
+ bmain, id_resync_root, id_roots, library_indirect_level, true));
+ }
+ BLI_ghashIterator_step(id_roots_iter);
+ }
+ BLI_ghashIterator_free(id_roots_iter);
+ }
+#endif
+
+ BKE_main_relations_free(bmain);
+ BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
+
+ ListBase no_main_ids_list = {nullptr};
+
+ GHashIterator *id_roots_iter = BLI_ghashIterator_new(id_roots);
+ while (!BLI_ghashIterator_done(id_roots_iter)) {
+ ID *id_root = static_cast<ID *>(BLI_ghashIterator_getKey(id_roots_iter));
+ Library *library = id_root->lib;
+ LinkNodePair *id_resync_roots = static_cast<LinkNodePair *>(
+ BLI_ghashIterator_getValue(id_roots_iter));
+
+ if (ID_IS_LINKED(id_root)) {
+ id_root->lib->tag |= LIBRARY_TAG_RESYNC_REQUIRED;
+ }
+
+ CLOG_INFO(&LOG,
+ 2,
+ "Resyncing all dependencies under root %s (%p), first one being '%s'...",
+ id_root->name,
+ library,
+ ((ID *)id_resync_roots->list->link)->name);
+ const bool success = lib_override_library_resync(bmain,
+ scene,
+ view_layer,
+ id_root,
+ id_resync_roots->list,
+ &no_main_ids_list,
+ override_resync_residual_storage,
+ false,
+ false,
+ reports);
+ CLOG_INFO(&LOG, 2, "\tSuccess: %d", success);
+ if (success) {
+ reports->count.resynced_lib_overrides++;
+ if (library_indirect_level > 0 && reports->do_resynced_lib_overrides_libraries_list &&
+ BLI_linklist_index(reports->resynced_lib_overrides_libraries, library) < 0) {
+ BLI_linklist_prepend(&reports->resynced_lib_overrides_libraries, library);
+ reports->resynced_lib_overrides_libraries_count++;
+ }
+ }
+
+ BLI_linklist_free(id_resync_roots->list, nullptr);
+ BLI_ghashIterator_step(id_roots_iter);
+ }
+ BLI_ghashIterator_free(id_roots_iter);
+
+ LISTBASE_FOREACH_MUTABLE (ID *, id_iter, &no_main_ids_list) {
+ BKE_id_free(bmain, id_iter);
+ }
+ BLI_listbase_clear(&no_main_ids_list);
+
+ /* Check there are no left-over IDs needing resync from the current (or higher) level of indirect
+ * library level. */
+ FOREACH_MAIN_ID_BEGIN (bmain, id) {
+ const bool is_valid_tagged_need_resync = ((id->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) == 0 ||
+ lib_override_resync_id_lib_level_is_valid(
+ id, library_indirect_level - 1, false));
+ if (!is_valid_tagged_need_resync) {
+ CLOG_ERROR(&LOG,
+ "ID override %s from library level %d still found as needing resync, when all "
+ "IDs from that level should have been processed after tackling library level %d",
+ id->name,
+ ID_IS_LINKED(id) ? id->lib->temp_index : 0,
+ library_indirect_level);
+ id->tag &= ~LIB_TAG_LIB_OVERRIDE_NEED_RESYNC;
+ }
+ }
+ FOREACH_MAIN_ID_END;
+
+ BLI_ghash_free(id_roots, nullptr, MEM_freeN);
+
+ /* In some fairly rare (and degenerate) cases, some root ID from other liboverrides may have been
+ * freed, and therefore set to nullptr. Attempt to fix this as best as possible. */
+ BKE_lib_override_library_main_hierarchy_root_ensure(bmain);
+
+ if (do_reports_recursive_resync_timing) {
+ reports->duration.lib_overrides_recursive_resync += PIL_check_seconds_timer() - init_time;
+ }
+}
+
+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 != nullptr && ID_IS_LINKED(id) && id->lib != id_owner->lib) {
+ const int owner_library_indirect_level = ID_IS_LINKED(id_owner) ? id_owner->lib->temp_index :
+ 0;
+ if (owner_library_indirect_level > 100) {
+ CLOG_ERROR(&LOG,
+ "Levels of indirect usages of libraries is way too high, there are most likely "
+ "dependency loops, skipping further building loops (involves at least '%s' from "
+ "'%s' and '%s' from '%s')",
+ id_owner->name,
+ id_owner->lib->filepath,
+ id->name,
+ id->lib->filepath);
+ return IDWALK_RET_NOP;
+ }
+ if (owner_library_indirect_level > 90) {
+ CLOG_WARN(
+ &LOG,
+ "Levels of indirect usages of libraries is suspiciously too high, there are most likely "
+ "dependency loops (involves at least '%s' from '%s' and '%s' from '%s')",
+ id_owner->name,
+ id_owner->lib->filepath,
+ id->name,
+ id->lib->filepath);
+ }
+
+ 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;
+}
+
+void BKE_lib_override_library_main_resync(Main *bmain,
+ Scene *scene,
+ ViewLayer *view_layer,
+ BlendFileReadReport *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 = static_cast<Collection *>(BLI_findstring(
+ &bmain->collections, OVERRIDE_RESYNC_RESIDUAL_STORAGE_NAME, offsetof(ID, name) + 2));
+ if (override_resync_residual_storage != nullptr &&
+ ID_IS_LINKED(override_resync_residual_storage)) {
+ override_resync_residual_storage = nullptr;
+ }
+ if (override_resync_residual_storage == nullptr) {
+ 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_HIDE_VIEWPORT | COLLECTION_HIDE_RENDER;
+ }
+
+ /* Necessary to improve performances, and prevent layers matching override sub-collections to be
+ * lost when re-syncing the parent override collection.
+ * Ref. T73411. */
+ BKE_layer_collection_resync_forbid();
+
+ 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--;
+ }
+
+ BKE_layer_collection_resync_allow();
+
+ /* Essentially ensures that potentially new overrides of new objects will be instantiated. */
+ lib_override_library_create_post_process(
+ bmain, scene, view_layer, nullptr, nullptr, nullptr, override_resync_residual_storage, true);
+
+ if (BKE_collection_is_empty(override_resync_residual_storage)) {
+ BKE_collection_delete(bmain, override_resync_residual_storage, true);
+ }
+
+ LISTBASE_FOREACH (Library *, library, &bmain->libraries) {
+ if (library->tag & LIBRARY_TAG_RESYNC_REQUIRED) {
+ CLOG_INFO(&LOG,
+ 2,
+ "library '%s' contains some linked overrides that required recursive resync, "
+ "consider updating it",
+ library->filepath);
+ }
+ }
+}
+
+void BKE_lib_override_library_delete(Main *bmain, ID *id_root)
+{
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root));
+
+ /* Tag all library overrides in the chains of dependencies from the given root one. */
+ BKE_main_relations_create(bmain, 0);
+ LibOverrideGroupTagData data{};
+ data.bmain = bmain;
+ data.scene = nullptr;
+ data.id_root = id_root;
+ data.hierarchy_root_id = id_root->override_library->hierarchy_root;
+ data.tag = LIB_TAG_DOIT;
+ data.missing_tag = LIB_TAG_MISSING;
+ data.is_override = true;
+ data.is_resync = false;
+ lib_override_group_tag_data_object_to_collection_init(&data);
+ lib_override_overrides_group_tag(&data);
+
+ BKE_main_relations_free(bmain);
+ lib_override_group_tag_data_clear(&data);
+
+ ID *id;
+ FOREACH_MAIN_ID_BEGIN (bmain, id) {
+ if (id->tag & LIB_TAG_DOIT) {
+ if (ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
+ ID *id_override_reference = id->override_library->reference;
+
+ /* Remap the whole local IDs to use the linked data. */
+ BKE_libblock_remap(bmain, id, id_override_reference, ID_REMAP_SKIP_INDIRECT_USAGE);
+ }
+ }
+ }
+ FOREACH_MAIN_ID_END;
+
+ /* Delete the override IDs. */
+ BKE_id_multi_tagged_delete(bmain);
+
+ /* Should not actually be needed here. */
+ BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
+}
+
+void BKE_lib_override_library_make_local(ID *id)
+{
+ if (!ID_IS_OVERRIDE_LIBRARY(id)) {
+ return;
+ }
+ if (ID_IS_OVERRIDE_LIBRARY_VIRTUAL(id)) {
+ /* We should never directly 'make local' virtual overrides (aka shape keys). */
+ BLI_assert_unreachable();
+ id->flag &= ~LIB_EMBEDDED_DATA_LIB_OVERRIDE;
+ return;
+ }
+
+ BKE_lib_override_library_free(&id->override_library, true);
+
+ Key *shape_key = BKE_key_from_id(id);
+ if (shape_key != nullptr) {
+ shape_key->id.flag &= ~LIB_EMBEDDED_DATA_LIB_OVERRIDE;
+ }
+
+ if (GS(id->name) == ID_SCE) {
+ Collection *master_collection = ((Scene *)id)->master_collection;
+ if (master_collection != nullptr) {
+ master_collection->id.flag &= ~LIB_EMBEDDED_DATA_LIB_OVERRIDE;
+ }
+ }
+
+ bNodeTree *node_tree = ntreeFromID(id);
+ if (node_tree != nullptr) {
+ node_tree->id.flag &= ~LIB_EMBEDDED_DATA_LIB_OVERRIDE;
+ }
+}
+
+BLI_INLINE IDOverrideLibraryRuntime *override_library_rna_path_runtime_ensure(
+ IDOverrideLibrary *override)
+{
+ if (override->runtime == nullptr) {
+ override->runtime = MEM_cnew<IDOverrideLibraryRuntime>(__func__);
+ }
+ return override->runtime;
+}
+
+/* We only build override GHash on request. */
+BLI_INLINE GHash *override_library_rna_path_mapping_ensure(IDOverrideLibrary *override)
+{
+ IDOverrideLibraryRuntime *override_runtime = override_library_rna_path_runtime_ensure(override);
+ if (override_runtime->rna_path_to_override_properties == nullptr) {
+ override_runtime->rna_path_to_override_properties = BLI_ghash_new(
+ BLI_ghashutil_strhash_p_murmur, BLI_ghashutil_strcmp, __func__);
+ for (IDOverrideLibraryProperty *op =
+ static_cast<IDOverrideLibraryProperty *>(override->properties.first);
+ op != nullptr;
+ op = op->next) {
+ BLI_ghash_insert(override_runtime->rna_path_to_override_properties, op->rna_path, op);
+ }
+ }
+
+ return override_runtime->rna_path_to_override_properties;
+}
+
+IDOverrideLibraryProperty *BKE_lib_override_library_property_find(IDOverrideLibrary *override,
+ const char *rna_path)
+{
+ GHash *override_runtime = override_library_rna_path_mapping_ensure(override);
+ return static_cast<IDOverrideLibraryProperty *>(BLI_ghash_lookup(override_runtime, rna_path));
+}
+
+IDOverrideLibraryProperty *BKE_lib_override_library_property_get(IDOverrideLibrary *override,
+ const char *rna_path,
+ bool *r_created)
+{
+ IDOverrideLibraryProperty *op = BKE_lib_override_library_property_find(override, rna_path);
+
+ if (op == nullptr) {
+ op = MEM_cnew<IDOverrideLibraryProperty>(__func__);
+ op->rna_path = BLI_strdup(rna_path);
+ BLI_addtail(&override->properties, op);
+
+ GHash *override_runtime = override_library_rna_path_mapping_ensure(override);
+ BLI_ghash_insert(override_runtime, op->rna_path, op);
+
+ if (r_created) {
+ *r_created = true;
+ }
+ }
+ else if (r_created) {
+ *r_created = false;
+ }
+
+ return op;
+}
+
+bool BKE_lib_override_rna_property_find(PointerRNA *idpoin,
+ const IDOverrideLibraryProperty *library_prop,
+ PointerRNA *r_override_poin,
+ PropertyRNA **r_override_prop,
+ int *r_index)
+{
+ BLI_assert(RNA_struct_is_ID(idpoin->type) && ID_IS_OVERRIDE_LIBRARY(idpoin->data));
+ return RNA_path_resolve_property_full(
+ idpoin, library_prop->rna_path, r_override_poin, r_override_prop, r_index);
+}
+
+void lib_override_library_property_copy(IDOverrideLibraryProperty *op_dst,
+ IDOverrideLibraryProperty *op_src)
+{
+ op_dst->rna_path = BLI_strdup(op_src->rna_path);
+ BLI_duplicatelist(&op_dst->operations, &op_src->operations);
+
+ for (IDOverrideLibraryPropertyOperation *
+ opop_dst = static_cast<IDOverrideLibraryPropertyOperation *>(op_dst->operations.first),
+ *opop_src = static_cast<IDOverrideLibraryPropertyOperation *>(op_src->operations.first);
+ opop_dst;
+ opop_dst = opop_dst->next, opop_src = opop_src->next) {
+ lib_override_library_property_operation_copy(opop_dst, opop_src);
+ }
+}
+
+void lib_override_library_property_clear(IDOverrideLibraryProperty *op)
+{
+ BLI_assert(op->rna_path != nullptr);
+
+ MEM_freeN(op->rna_path);
+
+ LISTBASE_FOREACH (IDOverrideLibraryPropertyOperation *, opop, &op->operations) {
+ lib_override_library_property_operation_clear(opop);
+ }
+ BLI_freelistN(&op->operations);
+}
+
+void BKE_lib_override_library_property_delete(IDOverrideLibrary *override,
+ IDOverrideLibraryProperty *override_property)
+{
+ if (!ELEM(nullptr, override->runtime, override->runtime->rna_path_to_override_properties)) {
+ BLI_ghash_remove(override->runtime->rna_path_to_override_properties,
+ override_property->rna_path,
+ nullptr,
+ nullptr);
+ }
+ lib_override_library_property_clear(override_property);
+ BLI_freelinkN(&override->properties, override_property);
+}
+
+IDOverrideLibraryPropertyOperation *BKE_lib_override_library_property_operation_find(
+ IDOverrideLibraryProperty *override_property,
+ const char *subitem_refname,
+ const char *subitem_locname,
+ const int subitem_refindex,
+ const int subitem_locindex,
+ const bool strict,
+ bool *r_strict)
+{
+ IDOverrideLibraryPropertyOperation *opop;
+ const int subitem_defindex = -1;
+
+ if (r_strict) {
+ *r_strict = true;
+ }
+
+ if (subitem_locname != nullptr) {
+ opop = static_cast<IDOverrideLibraryPropertyOperation *>(
+ BLI_findstring_ptr(&override_property->operations,
+ subitem_locname,
+ offsetof(IDOverrideLibraryPropertyOperation, subitem_local_name)));
+
+ if (opop == nullptr) {
+ return nullptr;
+ }
+
+ if (subitem_refname == nullptr || opop->subitem_reference_name == nullptr) {
+ return subitem_refname == opop->subitem_reference_name ? opop : nullptr;
+ }
+ return (subitem_refname != nullptr && opop->subitem_reference_name != nullptr &&
+ STREQ(subitem_refname, opop->subitem_reference_name)) ?
+ opop :
+ nullptr;
+ }
+
+ if (subitem_refname != nullptr) {
+ opop = static_cast<IDOverrideLibraryPropertyOperation *>(
+ BLI_findstring_ptr(&override_property->operations,
+ subitem_refname,
+ offsetof(IDOverrideLibraryPropertyOperation, subitem_reference_name)));
+
+ if (opop == nullptr) {
+ return nullptr;
+ }
+
+ if (subitem_locname == nullptr || opop->subitem_local_name == nullptr) {
+ return subitem_locname == opop->subitem_local_name ? opop : nullptr;
+ }
+ return (subitem_locname != nullptr && opop->subitem_local_name != nullptr &&
+ STREQ(subitem_locname, opop->subitem_local_name)) ?
+ opop :
+ nullptr;
+ }
+
+ if ((opop = static_cast<IDOverrideLibraryPropertyOperation *>(BLI_listbase_bytes_find(
+ &override_property->operations,
+ &subitem_locindex,
+ sizeof(subitem_locindex),
+ offsetof(IDOverrideLibraryPropertyOperation, subitem_local_index))))) {
+ return ELEM(subitem_refindex, -1, opop->subitem_reference_index) ? opop : nullptr;
+ }
+
+ if ((opop = static_cast<IDOverrideLibraryPropertyOperation *>(BLI_listbase_bytes_find(
+ &override_property->operations,
+ &subitem_refindex,
+ sizeof(subitem_refindex),
+ offsetof(IDOverrideLibraryPropertyOperation, subitem_reference_index))))) {
+ return ELEM(subitem_locindex, -1, opop->subitem_local_index) ? opop : nullptr;
+ }
+
+ /* `index == -1` means all indices, that is a valid fallback in case we requested specific index.
+ */
+ if (!strict && (subitem_locindex != subitem_defindex) &&
+ (opop = static_cast<IDOverrideLibraryPropertyOperation *>(BLI_listbase_bytes_find(
+ &override_property->operations,
+ &subitem_defindex,
+ sizeof(subitem_defindex),
+ offsetof(IDOverrideLibraryPropertyOperation, subitem_local_index))))) {
+ if (r_strict) {
+ *r_strict = false;
+ }
+ return opop;
+ }
+
+ return nullptr;
+}
+
+IDOverrideLibraryPropertyOperation *BKE_lib_override_library_property_operation_get(
+ IDOverrideLibraryProperty *override_property,
+ const short operation,
+ const char *subitem_refname,
+ const char *subitem_locname,
+ const int subitem_refindex,
+ const int subitem_locindex,
+ const bool strict,
+ bool *r_strict,
+ bool *r_created)
+{
+ IDOverrideLibraryPropertyOperation *opop = BKE_lib_override_library_property_operation_find(
+ override_property,
+ subitem_refname,
+ subitem_locname,
+ subitem_refindex,
+ subitem_locindex,
+ strict,
+ r_strict);
+
+ if (opop == nullptr) {
+ opop = MEM_cnew<IDOverrideLibraryPropertyOperation>(__func__);
+ opop->operation = operation;
+ if (subitem_locname) {
+ opop->subitem_local_name = BLI_strdup(subitem_locname);
+ }
+ if (subitem_refname) {
+ opop->subitem_reference_name = BLI_strdup(subitem_refname);
+ }
+ opop->subitem_local_index = subitem_locindex;
+ opop->subitem_reference_index = subitem_refindex;
+
+ BLI_addtail(&override_property->operations, opop);
+
+ if (r_created) {
+ *r_created = true;
+ }
+ }
+ else if (r_created) {
+ *r_created = false;
+ }
+
+ return opop;
+}
+
+void lib_override_library_property_operation_copy(IDOverrideLibraryPropertyOperation *opop_dst,
+ IDOverrideLibraryPropertyOperation *opop_src)
+{
+ if (opop_src->subitem_reference_name) {
+ opop_dst->subitem_reference_name = BLI_strdup(opop_src->subitem_reference_name);
+ }
+ if (opop_src->subitem_local_name) {
+ opop_dst->subitem_local_name = BLI_strdup(opop_src->subitem_local_name);
+ }
+}
+
+void lib_override_library_property_operation_clear(IDOverrideLibraryPropertyOperation *opop)
+{
+ if (opop->subitem_reference_name) {
+ MEM_freeN(opop->subitem_reference_name);
+ }
+ if (opop->subitem_local_name) {
+ MEM_freeN(opop->subitem_local_name);
+ }
+}
+
+void BKE_lib_override_library_property_operation_delete(
+ IDOverrideLibraryProperty *override_property,
+ IDOverrideLibraryPropertyOperation *override_property_operation)
+{
+ lib_override_library_property_operation_clear(override_property_operation);
+ BLI_freelinkN(&override_property->operations, override_property_operation);
+}
+
+bool BKE_lib_override_library_property_operation_operands_validate(
+ IDOverrideLibraryPropertyOperation *override_property_operation,
+ PointerRNA *ptr_dst,
+ PointerRNA *ptr_src,
+ PointerRNA *ptr_storage,
+ PropertyRNA *prop_dst,
+ PropertyRNA *prop_src,
+ PropertyRNA *prop_storage)
+{
+ switch (override_property_operation->operation) {
+ case IDOVERRIDE_LIBRARY_OP_NOOP:
+ return true;
+ case IDOVERRIDE_LIBRARY_OP_ADD:
+ ATTR_FALLTHROUGH;
+ case IDOVERRIDE_LIBRARY_OP_SUBTRACT:
+ ATTR_FALLTHROUGH;
+ case IDOVERRIDE_LIBRARY_OP_MULTIPLY:
+ if (ptr_storage == nullptr || ptr_storage->data == nullptr || prop_storage == nullptr) {
+ BLI_assert_msg(0, "Missing data to apply differential override operation.");
+ return false;
+ }
+ ATTR_FALLTHROUGH;
+ case IDOVERRIDE_LIBRARY_OP_INSERT_AFTER:
+ ATTR_FALLTHROUGH;
+ case IDOVERRIDE_LIBRARY_OP_INSERT_BEFORE:
+ ATTR_FALLTHROUGH;
+ case IDOVERRIDE_LIBRARY_OP_REPLACE:
+ if ((ptr_dst == nullptr || ptr_dst->data == nullptr || prop_dst == nullptr) ||
+ (ptr_src == nullptr || ptr_src->data == nullptr || prop_src == nullptr)) {
+ BLI_assert_msg(0, "Missing data to apply override operation.");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void BKE_lib_override_library_validate(Main *UNUSED(bmain), ID *id, ReportList *reports)
+{
+ if (id->override_library == nullptr) {
+ return;
+ }
+ if (id->override_library->reference == nullptr) {
+ /* This is a template ID, could be linked or local, not an override. */
+ return;
+ }
+ if (id->override_library->reference == id) {
+ /* Very serious data corruption, cannot do much about it besides removing the reference
+ * (therefore making the id a local override template one only). */
+ BKE_reportf(reports,
+ RPT_ERROR,
+ "Data corruption: data-block '%s' is using itself as library override reference",
+ id->name);
+ id->override_library->reference = nullptr;
+ return;
+ }
+ if (!ID_IS_LINKED(id->override_library->reference)) {
+ /* Very serious data corruption, cannot do much about it besides removing the reference
+ * (therefore making the id a local override template one only). */
+ BKE_reportf(reports,
+ RPT_ERROR,
+ "Data corruption: data-block '%s' is using another local data-block ('%s') as "
+ "library override reference",
+ id->name,
+ id->override_library->reference->name);
+ id->override_library->reference = nullptr;
+ return;
+ }
+}
+
+void BKE_lib_override_library_main_validate(Main *bmain, ReportList *reports)
+{
+ ID *id;
+
+ FOREACH_MAIN_ID_BEGIN (bmain, id) {
+ if (id->override_library != nullptr) {
+ BKE_lib_override_library_validate(bmain, id, reports);
+ }
+ }
+ FOREACH_MAIN_ID_END;
+}
+
+bool BKE_lib_override_library_status_check_local(Main *bmain, ID *local)
+{
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(local));
+
+ ID *reference = local->override_library->reference;
+
+ if (reference == nullptr) {
+ /* This is an override template, local status is always OK! */
+ return true;
+ }
+
+ BLI_assert(GS(local->name) == GS(reference->name));
+
+ if (GS(local->name) == ID_OB) {
+ /* Our beloved pose's bone cross-data pointers. Usually, depsgraph evaluation would
+ * ensure this is valid, but in some situations (like hidden collections etc.) this won't
+ * be the case, so we need to take care of this ourselves. */
+ Object *ob_local = (Object *)local;
+ if (ob_local->type == OB_ARMATURE) {
+ Object *ob_reference = (Object *)local->override_library->reference;
+ BLI_assert(ob_local->data != nullptr);
+ BLI_assert(ob_reference->data != nullptr);
+ BKE_pose_ensure(bmain, ob_local, static_cast<bArmature *>(ob_local->data), true);
+ BKE_pose_ensure(bmain, ob_reference, static_cast<bArmature *>(ob_reference->data), true);
+ }
+ }
+
+ /* Note that reference is assumed always valid, caller has to ensure that itself. */
+
+ PointerRNA rnaptr_local, rnaptr_reference;
+ RNA_id_pointer_create(local, &rnaptr_local);
+ RNA_id_pointer_create(reference, &rnaptr_reference);
+
+ if (!RNA_struct_override_matches(
+ bmain,
+ &rnaptr_local,
+ &rnaptr_reference,
+ nullptr,
+ 0,
+ local->override_library,
+ (eRNAOverrideMatch)(RNA_OVERRIDE_COMPARE_IGNORE_NON_OVERRIDABLE |
+ RNA_OVERRIDE_COMPARE_IGNORE_OVERRIDDEN),
+ nullptr)) {
+ local->tag &= ~LIB_TAG_OVERRIDE_LIBRARY_REFOK;
+ return false;
+ }
+
+ return true;
+}
+
+bool BKE_lib_override_library_status_check_reference(Main *bmain, ID *local)
+{
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(local));
+
+ ID *reference = local->override_library->reference;
+
+ if (reference == nullptr) {
+ /* This is an override template, reference is virtual, so its status is always OK! */
+ return true;
+ }
+
+ BLI_assert(GS(local->name) == GS(reference->name));
+
+ if (reference->override_library && (reference->tag & LIB_TAG_OVERRIDE_LIBRARY_REFOK) == 0) {
+ if (!BKE_lib_override_library_status_check_reference(bmain, reference)) {
+ /* If reference is also an override of another data-block, and its status is not OK,
+ * then this override is not OK either.
+ * Note that this should only happen when reloading libraries. */
+ local->tag &= ~LIB_TAG_OVERRIDE_LIBRARY_REFOK;
+ return false;
+ }
+ }
+
+ if (GS(local->name) == ID_OB) {
+ /* Our beloved pose's bone cross-data pointers. Usually, depsgraph evaluation would
+ * ensure this is valid, but in some situations (like hidden collections etc.) this won't
+ * be the case, so we need to take care of this ourselves. */
+ Object *ob_local = (Object *)local;
+ if (ob_local->type == OB_ARMATURE) {
+ Object *ob_reference = (Object *)local->override_library->reference;
+ BLI_assert(ob_local->data != nullptr);
+ BLI_assert(ob_reference->data != nullptr);
+ BKE_pose_ensure(bmain, ob_local, static_cast<bArmature *>(ob_local->data), true);
+ BKE_pose_ensure(bmain, ob_reference, static_cast<bArmature *>(ob_reference->data), true);
+ }
+ }
+
+ PointerRNA rnaptr_local, rnaptr_reference;
+ RNA_id_pointer_create(local, &rnaptr_local);
+ RNA_id_pointer_create(reference, &rnaptr_reference);
+
+ if (!RNA_struct_override_matches(bmain,
+ &rnaptr_local,
+ &rnaptr_reference,
+ nullptr,
+ 0,
+ local->override_library,
+ RNA_OVERRIDE_COMPARE_IGNORE_OVERRIDDEN,
+ nullptr)) {
+ local->tag &= ~LIB_TAG_OVERRIDE_LIBRARY_REFOK;
+ return false;
+ }
+
+ return true;
+}
+
+bool BKE_lib_override_library_operations_create(Main *bmain, ID *local)
+{
+ BLI_assert(!ID_IS_LINKED(local));
+ BLI_assert(local->override_library != nullptr);
+ const bool is_template = (local->override_library->reference == nullptr);
+ bool created = false;
+
+ if (!is_template) {
+ /* Do not attempt to generate overriding rules from an empty place-holder generated by link
+ * code when it cannot find the actual library/ID. Much better to keep the local data-block as
+ * is in the file in that case, until broken lib is fixed. */
+ if (ID_MISSING(local->override_library->reference)) {
+ return created;
+ }
+
+ if (GS(local->name) == ID_OB) {
+ /* Our beloved pose's bone cross-data pointers. Usually, depsgraph evaluation would
+ * ensure this is valid, but in some situations (like hidden collections etc.) this won't
+ * be the case, so we need to take care of this ourselves. */
+ Object *ob_local = (Object *)local;
+ if (ob_local->type == OB_ARMATURE) {
+ Object *ob_reference = (Object *)local->override_library->reference;
+ BLI_assert(ob_local->data != nullptr);
+ BLI_assert(ob_reference->data != nullptr);
+ BKE_pose_ensure(bmain, ob_local, static_cast<bArmature *>(ob_local->data), true);
+ BKE_pose_ensure(bmain, ob_reference, static_cast<bArmature *>(ob_reference->data), true);
+ }
+ }
+
+ PointerRNA rnaptr_local, rnaptr_reference;
+ RNA_id_pointer_create(local, &rnaptr_local);
+ RNA_id_pointer_create(local->override_library->reference, &rnaptr_reference);
+
+ eRNAOverrideMatchResult report_flags = (eRNAOverrideMatchResult)0;
+ RNA_struct_override_matches(
+ bmain,
+ &rnaptr_local,
+ &rnaptr_reference,
+ nullptr,
+ 0,
+ local->override_library,
+ (eRNAOverrideMatch)(RNA_OVERRIDE_COMPARE_CREATE | RNA_OVERRIDE_COMPARE_RESTORE),
+ &report_flags);
+
+ if (report_flags & RNA_OVERRIDE_MATCH_RESULT_CREATED) {
+ created = true;
+ }
+
+ if (report_flags & RNA_OVERRIDE_MATCH_RESULT_RESTORED) {
+ CLOG_INFO(&LOG, 2, "We did restore some properties of %s from its reference", local->name);
+ }
+ if (report_flags & RNA_OVERRIDE_MATCH_RESULT_CREATED) {
+ CLOG_INFO(&LOG, 2, "We did generate library override rules for %s", local->name);
+ }
+ else {
+ CLOG_INFO(&LOG, 2, "No new library override rules for %s", local->name);
+ }
+ }
+ return created;
+}
+
+struct LibOverrideOpCreateData {
+ Main *bmain;
+ bool changed;
+};
+
+static void lib_override_library_operations_create_cb(TaskPool *__restrict pool, void *taskdata)
+{
+ LibOverrideOpCreateData *create_data = static_cast<LibOverrideOpCreateData *>(
+ BLI_task_pool_user_data(pool));
+ ID *id = static_cast<ID *>(taskdata);
+
+ if (BKE_lib_override_library_operations_create(create_data->bmain, id)) {
+ /* Technically no need for atomic, all jobs write the same value and we only care if one did
+ * it. But play safe and avoid implicit assumptions. */
+ atomic_fetch_and_or_uint8((uint8_t *)&create_data->changed, true);
+ }
+}
+
+bool BKE_lib_override_library_main_operations_create(Main *bmain, const bool force_auto)
+{
+ ID *id;
+
+#ifdef DEBUG_OVERRIDE_TIMEIT
+ TIMEIT_START_AVERAGED(BKE_lib_override_library_main_operations_create);
+#endif
+
+ /* When force-auto is set, we also remove all unused existing override properties & operations.
+ */
+ if (force_auto) {
+ BKE_lib_override_library_main_tag(bmain, IDOVERRIDE_LIBRARY_TAG_UNUSED, true);
+ }
+
+ /* Usual pose bones issue, need to be done outside of the threaded process or we may run into
+ * concurrency issues here.
+ * Note that calling #BKE_pose_ensure again in thread in
+ * #BKE_lib_override_library_operations_create is not a problem then. */
+ LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
+ if (ob->type == OB_ARMATURE) {
+ BLI_assert(ob->data != nullptr);
+ BKE_pose_ensure(bmain, ob, static_cast<bArmature *>(ob->data), true);
+ }
+ }
+
+ LibOverrideOpCreateData create_pool_data{};
+ create_pool_data.bmain = bmain;
+ create_pool_data.changed = false;
+ TaskPool *task_pool = BLI_task_pool_create(&create_pool_data, TASK_PRIORITY_HIGH);
+
+ FOREACH_MAIN_ID_BEGIN (bmain, 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. */
+ if (GS(id->name) == ID_OB) {
+ Object *ob = (Object *)id;
+ if (ob->type == OB_ARMATURE) {
+ BLI_assert(ob->data != nullptr);
+ BKE_pose_ensure(bmain, ob, static_cast<bArmature *>(ob->data), true);
+ }
+ }
+ /* Only check overrides if we do have the real reference data available, and not some empty
+ * 'placeholder' for missing data (broken links). */
+ if ((id->override_library->reference->tag & LIB_TAG_MISSING) == 0) {
+ BLI_task_pool_push(
+ task_pool, lib_override_library_operations_create_cb, id, false, nullptr);
+ }
+ else {
+ BKE_lib_override_library_properties_tag(
+ id->override_library, IDOVERRIDE_LIBRARY_TAG_UNUSED, false);
+ }
+ }
+ else {
+ /* Clear 'unused' tag for un-processed IDs, otherwise e.g. linked overrides will loose their
+ * list of overridden properties. */
+ BKE_lib_override_library_properties_tag(
+ id->override_library, IDOVERRIDE_LIBRARY_TAG_UNUSED, false);
+ }
+ id->tag &= ~LIB_TAG_OVERRIDE_LIBRARY_AUTOREFRESH;
+ }
+ FOREACH_MAIN_ID_END;
+
+ BLI_task_pool_work_and_wait(task_pool);
+
+ BLI_task_pool_free(task_pool);
+
+ if (force_auto) {
+ BKE_lib_override_library_main_unused_cleanup(bmain);
+ }
+
+#ifdef DEBUG_OVERRIDE_TIMEIT
+ TIMEIT_END_AVERAGED(BKE_lib_override_library_main_operations_create);
+#endif
+
+ return create_pool_data.changed;
+}
+
+static bool lib_override_library_id_reset_do(Main *bmain,
+ ID *id_root,
+ const bool do_reset_system_override)
+{
+ bool was_op_deleted = false;
+
+ if (do_reset_system_override) {
+ id_root->override_library->flag |= IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED;
+ }
+
+ LISTBASE_FOREACH_MUTABLE (
+ IDOverrideLibraryProperty *, op, &id_root->override_library->properties) {
+ bool do_op_delete = true;
+ const bool is_collection = op->rna_prop_type == PROP_COLLECTION;
+ if (is_collection || op->rna_prop_type == PROP_POINTER) {
+ PointerRNA ptr_root, ptr_root_lib, ptr, ptr_lib;
+ PropertyRNA *prop, *prop_lib;
+
+ RNA_pointer_create(id_root, &RNA_ID, id_root, &ptr_root);
+ RNA_pointer_create(id_root->override_library->reference,
+ &RNA_ID,
+ id_root->override_library->reference,
+ &ptr_root_lib);
+
+ bool prop_exists = RNA_path_resolve_property(&ptr_root, op->rna_path, &ptr, &prop);
+ if (prop_exists) {
+ prop_exists = RNA_path_resolve_property(&ptr_root_lib, op->rna_path, &ptr_lib, &prop_lib);
+
+ if (prop_exists) {
+ BLI_assert(ELEM(RNA_property_type(prop), PROP_POINTER, PROP_COLLECTION));
+ BLI_assert(RNA_property_type(prop) == RNA_property_type(prop_lib));
+ if (is_collection) {
+ ptr.type = RNA_property_pointer_type(&ptr, prop);
+ ptr_lib.type = RNA_property_pointer_type(&ptr_lib, prop_lib);
+ }
+ else {
+ ptr = RNA_property_pointer_get(&ptr, prop);
+ ptr_lib = RNA_property_pointer_get(&ptr_lib, prop_lib);
+ }
+ if (ptr.owner_id != nullptr && ptr_lib.owner_id != nullptr) {
+ BLI_assert(ptr.type == ptr_lib.type);
+ do_op_delete = !(RNA_struct_is_ID(ptr.type) &&
+ ptr.owner_id->override_library != nullptr &&
+ ptr.owner_id->override_library->reference == ptr_lib.owner_id);
+ }
+ }
+ }
+ }
+
+ if (do_op_delete) {
+ BKE_lib_override_library_property_delete(id_root->override_library, op);
+ was_op_deleted = true;
+ }
+ }
+
+ if (was_op_deleted) {
+ DEG_id_tag_update_ex(bmain, id_root, ID_RECALC_COPY_ON_WRITE);
+ IDOverrideLibraryRuntime *override_runtime = override_library_rna_path_runtime_ensure(
+ id_root->override_library);
+ override_runtime->tag |= IDOVERRIDE_LIBRARY_RUNTIME_TAG_NEEDS_RELOAD;
+ }
+
+ return was_op_deleted;
+}
+
+void BKE_lib_override_library_id_reset(Main *bmain,
+ ID *id_root,
+ const bool do_reset_system_override)
+{
+ if (!ID_IS_OVERRIDE_LIBRARY_REAL(id_root)) {
+ return;
+ }
+
+ if (lib_override_library_id_reset_do(bmain, id_root, do_reset_system_override)) {
+ if (id_root->override_library->runtime != nullptr &&
+ (id_root->override_library->runtime->tag & IDOVERRIDE_LIBRARY_RUNTIME_TAG_NEEDS_RELOAD) !=
+ 0) {
+ BKE_lib_override_library_update(bmain, id_root);
+ id_root->override_library->runtime->tag &= ~IDOVERRIDE_LIBRARY_RUNTIME_TAG_NEEDS_RELOAD;
+ }
+ }
+}
+
+static void lib_override_library_id_hierarchy_recursive_reset(Main *bmain,
+ ID *id_root,
+ const bool do_reset_system_override)
+{
+ if (!ID_IS_OVERRIDE_LIBRARY_REAL(id_root)) {
+ return;
+ }
+
+ void **entry_vp = BLI_ghash_lookup_p(bmain->relations->relations_from_pointers, id_root);
+ if (entry_vp == nullptr) {
+ /* This ID is not used by nor using any other ID. */
+ lib_override_library_id_reset_do(bmain, id_root, do_reset_system_override);
+ return;
+ }
+
+ MainIDRelationsEntry *entry = static_cast<MainIDRelationsEntry *>(*entry_vp);
+ if (entry->tags & MAINIDRELATIONS_ENTRY_TAGS_PROCESSED) {
+ /* This ID has already been processed. */
+ return;
+ }
+
+ lib_override_library_id_reset_do(bmain, id_root, do_reset_system_override);
+
+ /* 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 *to_id_entry = entry->to_ids; to_id_entry != nullptr;
+ to_id_entry = to_id_entry->next) {
+ if ((to_id_entry->usage_flag & IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE) != 0) {
+ /* Never consider non-overridable relationships ('from', 'parents', 'owner' etc. pointers) as
+ * actual dependencies. */
+ continue;
+ }
+ /* We only consider IDs from the same library. */
+ if (*to_id_entry->id_pointer.to != nullptr) {
+ ID *to_id = *to_id_entry->id_pointer.to;
+ if (to_id->override_library != nullptr) {
+ lib_override_library_id_hierarchy_recursive_reset(bmain, to_id, do_reset_system_override);
+ }
+ }
+ }
+}
+
+void BKE_lib_override_library_id_hierarchy_reset(Main *bmain,
+ ID *id_root,
+ const bool do_reset_system_override)
+{
+ BKE_main_relations_create(bmain, 0);
+
+ lib_override_library_id_hierarchy_recursive_reset(bmain, id_root, do_reset_system_override);
+
+ BKE_main_relations_free(bmain);
+
+ ID *id;
+ FOREACH_MAIN_ID_BEGIN (bmain, id) {
+ if (!ID_IS_OVERRIDE_LIBRARY_REAL(id) || id->override_library->runtime == nullptr ||
+ (id->override_library->runtime->tag & IDOVERRIDE_LIBRARY_RUNTIME_TAG_NEEDS_RELOAD) == 0) {
+ continue;
+ }
+ BKE_lib_override_library_update(bmain, id);
+ id->override_library->runtime->tag &= ~IDOVERRIDE_LIBRARY_RUNTIME_TAG_NEEDS_RELOAD;
+ }
+ FOREACH_MAIN_ID_END;
+}
+
+void BKE_lib_override_library_operations_tag(IDOverrideLibraryProperty *override_property,
+ const short tag,
+ const bool do_set)
+{
+ if (override_property != nullptr) {
+ if (do_set) {
+ override_property->tag |= tag;
+ }
+ else {
+ override_property->tag &= ~tag;
+ }
+
+ LISTBASE_FOREACH (IDOverrideLibraryPropertyOperation *, opop, &override_property->operations) {
+ if (do_set) {
+ opop->tag |= tag;
+ }
+ else {
+ opop->tag &= ~tag;
+ }
+ }
+ }
+}
+
+void BKE_lib_override_library_properties_tag(IDOverrideLibrary *override,
+ const short tag,
+ const bool do_set)
+{
+ if (override != nullptr) {
+ LISTBASE_FOREACH (IDOverrideLibraryProperty *, op, &override->properties) {
+ BKE_lib_override_library_operations_tag(op, tag, do_set);
+ }
+ }
+}
+
+void BKE_lib_override_library_main_tag(Main *bmain, const short tag, const bool do_set)
+{
+ ID *id;
+
+ FOREACH_MAIN_ID_BEGIN (bmain, id) {
+ if (ID_IS_OVERRIDE_LIBRARY(id)) {
+ BKE_lib_override_library_properties_tag(id->override_library, tag, do_set);
+ }
+ }
+ FOREACH_MAIN_ID_END;
+}
+
+void BKE_lib_override_library_id_unused_cleanup(ID *local)
+{
+ if (ID_IS_OVERRIDE_LIBRARY_REAL(local)) {
+ LISTBASE_FOREACH_MUTABLE (
+ IDOverrideLibraryProperty *, op, &local->override_library->properties) {
+ if (op->tag & IDOVERRIDE_LIBRARY_TAG_UNUSED) {
+ BKE_lib_override_library_property_delete(local->override_library, op);
+ }
+ else {
+ LISTBASE_FOREACH_MUTABLE (IDOverrideLibraryPropertyOperation *, opop, &op->operations) {
+ if (opop->tag & IDOVERRIDE_LIBRARY_TAG_UNUSED) {
+ BKE_lib_override_library_property_operation_delete(op, opop);
+ }
+ }
+ }
+ }
+ }
+}
+
+void BKE_lib_override_library_main_unused_cleanup(Main *bmain)
+{
+ ID *id;
+
+ FOREACH_MAIN_ID_BEGIN (bmain, id) {
+ if (ID_IS_OVERRIDE_LIBRARY(id)) {
+ BKE_lib_override_library_id_unused_cleanup(id);
+ }
+ }
+ FOREACH_MAIN_ID_END;
+}
+
+static void lib_override_id_swap(Main *bmain, ID *id_local, ID *id_temp)
+{
+ BKE_lib_id_swap(bmain, id_local, id_temp);
+ /* We need to keep these tags from temp ID into orig one.
+ * ID swap does not swap most of ID data itself. */
+ id_local->tag |= (id_temp->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC);
+}
+
+void BKE_lib_override_library_update(Main *bmain, ID *local)
+{
+ if (!ID_IS_OVERRIDE_LIBRARY_REAL(local)) {
+ return;
+ }
+
+ /* Do not attempt to apply overriding rules over an empty place-holder generated by link code
+ * when it cannot find the actual library/ID. Much better to keep the local data-block as loaded
+ * from the file in that case, until broken lib is fixed. */
+ if (ID_MISSING(local->override_library->reference)) {
+ return;
+ }
+
+ /* Recursively do 'ancestor' overrides first, if any. */
+ if (local->override_library->reference->override_library &&
+ (local->override_library->reference->tag & LIB_TAG_OVERRIDE_LIBRARY_REFOK) == 0) {
+ BKE_lib_override_library_update(bmain, local->override_library->reference);
+ }
+
+ /* We want to avoid having to remap here, however creating up-to-date override is much simpler
+ * if based on reference than on current override.
+ * So we work on temp copy of reference, and 'swap' its content with local. */
+
+ /* XXX We need a way to get off-Main copies of IDs (similar to localized mats/texts/ etc.)!
+ * However, this is whole bunch of code work in itself, so for now plain stupid ID copy
+ * will do, as inefficient as it is. :/
+ * Actually, maybe not! Since we are swapping with original ID's local content, we want to
+ * keep user-count in correct state when freeing tmp_id
+ * (and that user-counts of IDs used by 'new' local data also remain correct). */
+ /* This would imply change in handling of user-count all over RNA
+ * (and possibly all over Blender code).
+ * Not impossible to do, but would rather see first if extra useless usual user handling
+ * is actually a (performances) issue here. */
+
+ ID *tmp_id = BKE_id_copy_ex(bmain,
+ local->override_library->reference,
+ nullptr,
+ LIB_ID_COPY_DEFAULT | LIB_ID_COPY_NO_LIB_OVERRIDE_LOCAL_DATA_FLAG);
+
+ if (tmp_id == nullptr) {
+ return;
+ }
+
+ tmp_id->lib = local->lib;
+
+ /* This ID name is problematic, since it is an 'rna name property' it should not be editable or
+ * different from reference linked ID. But local ID names need to be unique in a given type
+ * list of Main, so we cannot always keep it identical, which is why we need this special
+ * manual handling here. */
+ BLI_strncpy(tmp_id->name, local->name, sizeof(tmp_id->name));
+
+ /* Those ugly loop-back pointers again. Luckily we only need to deal with the shape keys here,
+ * collections' parents are fully runtime and reconstructed later. */
+ Key *local_key = BKE_key_from_id(local);
+ Key *tmp_key = BKE_key_from_id(tmp_id);
+ if (local_key != nullptr && tmp_key != nullptr) {
+ tmp_key->id.flag |= (local_key->id.flag & LIB_EMBEDDED_DATA_LIB_OVERRIDE);
+ tmp_key->id.lib = local_key->id.lib;
+ }
+
+ PointerRNA rnaptr_src, rnaptr_dst, rnaptr_storage_stack, *rnaptr_storage = nullptr;
+ RNA_id_pointer_create(local, &rnaptr_src);
+ RNA_id_pointer_create(tmp_id, &rnaptr_dst);
+ if (local->override_library->storage) {
+ rnaptr_storage = &rnaptr_storage_stack;
+ RNA_id_pointer_create(local->override_library->storage, rnaptr_storage);
+ }
+
+ RNA_struct_override_apply(bmain,
+ &rnaptr_dst,
+ &rnaptr_src,
+ rnaptr_storage,
+ local->override_library,
+ RNA_OVERRIDE_APPLY_FLAG_NOP);
+
+ lib_override_object_posemode_transfer(tmp_id, local);
+
+ /* This also transfers all pointers (memory) owned by local to tmp_id, and vice-versa.
+ * So when we'll free tmp_id, we'll actually free old, outdated data from local. */
+ lib_override_id_swap(bmain, local, tmp_id);
+
+ if (local_key != nullptr && tmp_key != nullptr) {
+ /* This is some kind of hard-coded 'always enforced override'. */
+ lib_override_id_swap(bmain, &local_key->id, &tmp_key->id);
+ tmp_key->id.flag |= (local_key->id.flag & LIB_EMBEDDED_DATA_LIB_OVERRIDE);
+ /* The swap of local and tmp_id inverted those pointers, we need to redefine proper
+ * relationships. */
+ *BKE_key_from_id_p(local) = local_key;
+ *BKE_key_from_id_p(tmp_id) = tmp_key;
+ local_key->from = local;
+ tmp_key->from = tmp_id;
+ }
+
+ /* Again, horribly inefficient in our case, we need something off-Main
+ * (aka more generic nolib copy/free stuff)! */
+ BKE_id_free_ex(bmain, tmp_id, LIB_ID_FREE_NO_UI_USER, true);
+
+ if (GS(local->name) == ID_AR) {
+ /* Fun times again, thanks to bone pointers in pose data of objects. We keep same ID addresses,
+ * but internal data has changed for sure, so we need to invalidate pose-bones caches. */
+ LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
+ if (ob->pose != nullptr && ob->data == local) {
+ BLI_assert(ob->type == OB_ARMATURE);
+ ob->pose->flag |= POSE_RECALC;
+ /* We need to clear pose bone pointers immediately, some code may access those before pose
+ * is actually recomputed, which can lead to segfault. */
+ BKE_pose_clear_pointers(ob->pose);
+ }
+ }
+ }
+
+ if (local->override_library->storage) {
+ /* We know this data-block is not used anywhere besides local->override->storage. */
+ /* XXX For until we get fully shadow copies, we still need to ensure storage releases
+ * its usage of any ID pointers it may have. */
+ BKE_id_free_ex(bmain, local->override_library->storage, LIB_ID_FREE_NO_UI_USER, true);
+ local->override_library->storage = nullptr;
+ }
+
+ local->tag |= LIB_TAG_OVERRIDE_LIBRARY_REFOK;
+
+ /* NOTE: Since we reload full content from linked ID here, potentially from edited local
+ * override, we do not really have a way to know *what* is changed, so we need to rely on the
+ * massive destruction weapon of `ID_RECALC_ALL` here. */
+ DEG_id_tag_update_ex(bmain, local, ID_RECALC_ALL);
+ /* For same reason as above, also assume that the relationships between IDs changed. */
+ DEG_relations_tag_update(bmain);
+}
+
+void BKE_lib_override_library_main_update(Main *bmain)
+{
+ ID *id;
+
+ /* This temporary swap of G_MAIN is rather ugly,
+ * but necessary to avoid asserts checks in some RNA assignment functions,
+ * since those always use on G_MAIN when they need access to a Main database. */
+ Main *orig_gmain = G_MAIN;
+ G_MAIN = bmain;
+
+ FOREACH_MAIN_ID_BEGIN (bmain, id) {
+ if (id->override_library != nullptr) {
+ BKE_lib_override_library_update(bmain, id);
+ }
+ }
+ FOREACH_MAIN_ID_END;
+
+ G_MAIN = orig_gmain;
+}
+
+bool BKE_lib_override_library_id_is_user_deletable(Main *bmain, ID *id)
+{
+ /* The only strong known case currently are objects used by override collections. */
+ /* TODO: There are most likely other cases... This may need to be addressed in a better way at
+ * some point. */
+ if (GS(id->name) != ID_OB) {
+ return true;
+ }
+ Object *ob = (Object *)id;
+ LISTBASE_FOREACH (Collection *, collection, &bmain->collections) {
+ if (!ID_IS_OVERRIDE_LIBRARY(collection)) {
+ continue;
+ }
+ if (BKE_collection_has_object(collection, ob)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+/**
+ * Storage (how to store overriding data into `.blend` files).
+ *
+ * Basically:
+ * 1) Only 'differential' overrides needs special handling here. All others (replacing values or
+ * inserting/removing items from a collection) can be handled with simply storing current
+ * content of local data-block.
+ * 2) We store the differential value into a second 'ghost' data-block, which is an empty ID of
+ * same type as the local one, where we only define values that need differential data.
+ *
+ * This avoids us having to modify 'real' data-block at write time (and restoring it afterwards),
+ * which is inefficient, and potentially dangerous (in case of concurrent access...), while not
+ * using much extra memory in typical cases. It also ensures stored data-block always contains
+ * exact same data as "desired" ones (kind of "baked" data-blocks).
+ */
+
+OverrideLibraryStorage *BKE_lib_override_library_operations_store_init(void)
+{
+ return BKE_main_new();
+}
+
+ID *BKE_lib_override_library_operations_store_start(Main *bmain,
+ OverrideLibraryStorage *override_storage,
+ ID *local)
+{
+ if (ID_IS_OVERRIDE_LIBRARY_TEMPLATE(local) || ID_IS_OVERRIDE_LIBRARY_VIRTUAL(local)) {
+ /* This is actually purely local data with an override template, or one of those embedded IDs
+ * (root node trees, master collections or shape-keys) that cannot have their own override.
+ * Nothing to do here! */
+ return nullptr;
+ }
+
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(local));
+ BLI_assert(override_storage != nullptr);
+ UNUSED_VARS_NDEBUG(override_storage);
+
+ /* Forcefully ensure we know about all needed override operations. */
+ BKE_lib_override_library_operations_create(bmain, local);
+
+ ID *storage_id;
+#ifdef DEBUG_OVERRIDE_TIMEIT
+ TIMEIT_START_AVERAGED(BKE_lib_override_library_operations_store_start);
+#endif
+
+ /* This is fully disabled for now, as it generated very hard to solve issues with Collections and
+ * how they reference each-other in their parents/children relations.
+ * Core of the issue is creating and storing those copies in a separate Main, while collection
+ * copy code re-assign blindly parents/children, even if they do not belong to the same Main.
+ * One solution could be to implement special flag as discussed below, and prevent any
+ * other-ID-reference creation/update in that case (since no differential operation is expected
+ * to involve those anyway). */
+#if 0
+ /* XXX TODO: We may also want a specialized handling of things here too, to avoid copying heavy
+ * never-overridable data (like Mesh geometry etc.)? And also maybe avoid lib
+ * reference-counting completely (shallow copy). */
+ /* This would imply change in handling of user-count all over RNA
+ * (and possibly all over Blender code).
+ * Not impossible to do, but would rather see first is extra useless usual user handling is
+ * actually a (performances) issue here, before doing it. */
+ storage_id = BKE_id_copy((Main *)override_storage, local);
+
+ if (storage_id != nullptr) {
+ PointerRNA rnaptr_reference, rnaptr_final, rnaptr_storage;
+ RNA_id_pointer_create(local->override_library->reference, &rnaptr_reference);
+ RNA_id_pointer_create(local, &rnaptr_final);
+ RNA_id_pointer_create(storage_id, &rnaptr_storage);
+
+ if (!RNA_struct_override_store(
+ bmain, &rnaptr_final, &rnaptr_reference, &rnaptr_storage, local->override_library)) {
+ BKE_id_free_ex(override_storage, storage_id, LIB_ID_FREE_NO_UI_USER, true);
+ storage_id = nullptr;
+ }
+ }
+#else
+ storage_id = nullptr;
+#endif
+
+ local->override_library->storage = storage_id;
+
+#ifdef DEBUG_OVERRIDE_TIMEIT
+ TIMEIT_END_AVERAGED(BKE_lib_override_library_operations_store_start);
+#endif
+ return storage_id;
+}
+
+void BKE_lib_override_library_operations_store_end(
+ OverrideLibraryStorage *UNUSED(override_storage), ID *local)
+{
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(local));
+
+ /* Nothing else to do here really, we need to keep all temp override storage data-blocks in
+ * memory until whole file is written anyway (otherwise we'd get mem pointers overlap). */
+ local->override_library->storage = nullptr;
+}
+
+void BKE_lib_override_library_operations_store_finalize(OverrideLibraryStorage *override_storage)
+{
+ /* We cannot just call BKE_main_free(override_storage), not until we have option to make
+ * 'ghost' copies of IDs without increasing usercount of used data-blocks. */
+ ID *id;
+
+ FOREACH_MAIN_ID_BEGIN (override_storage, id) {
+ BKE_id_free_ex(override_storage, id, LIB_ID_FREE_NO_UI_USER, true);
+ }
+ FOREACH_MAIN_ID_END;
+
+ BKE_main_free(override_storage);
+}