From 74557ca4f7cfe14090d6ca245e03e651b4b2524f Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Fri, 12 Mar 2021 12:31:25 +0100 Subject: [PATCH] LibOverride: Add a new operation to Outliner to enforce resync of hierarchies. This is basically done by ignoring override operations from old override affecting ID pointer properties, when the new (destination) one is not NULL. Fix T86501: New object added to overridden collection doesn't show up in linking file on Resync. This is more of a work-around actually, since there is no real way to fix the issue in a fully automated and consistent way, it is caused by older blender files being saved with 'broken' overrides. WARNING: This cannot ensure that some purposedly edited/overridden ID pointer properties won't be lost in the process. --- source/blender/blenkernel/BKE_lib_override.h | 3 +- .../blender/blenkernel/intern/lib_override.c | 23 ++++++++---- .../editors/space_outliner/outliner_tools.c | 33 +++++++++++++++-- source/blender/makesrna/RNA_access.h | 12 ++++++- .../intern/rna_access_compare_override.c | 36 ++++++++++++++++++- 5 files changed, 96 insertions(+), 11 deletions(-) diff --git a/source/blender/blenkernel/BKE_lib_override.h b/source/blender/blenkernel/BKE_lib_override.h index e548f778c71..ccb313f0e2a 100644 --- a/source/blender/blenkernel/BKE_lib_override.h +++ b/source/blender/blenkernel/BKE_lib_override.h @@ -79,7 +79,8 @@ bool BKE_lib_override_library_proxy_convert(struct Main *bmain, bool BKE_lib_override_library_resync(struct Main *bmain, struct Scene *scene, struct ViewLayer *view_layer, - struct ID *id_root); + struct ID *id_root, + const bool do_hierarchy_enforce); void BKE_lib_override_library_main_resync(struct Main *bmain, struct Scene *scene, struct ViewLayer *view_layer); diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index 5100fbf08ad..223ae13cba9 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -829,7 +829,8 @@ bool BKE_lib_override_library_proxy_convert(Main *bmain, * \param id_root: The root liboverride ID to resync from. * \return true if override was successfully resynced. */ -bool BKE_lib_override_library_resync(Main *bmain, Scene *scene, ViewLayer *view_layer, ID *id_root) +bool BKE_lib_override_library_resync( + Main *bmain, Scene *scene, ViewLayer *view_layer, ID *id_root, const bool do_hierarchy_enforce) { BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root)); @@ -978,8 +979,14 @@ bool BKE_lib_override_library_resync(Main *bmain, Scene *scene, ViewLayer *view_ } } - RNA_struct_override_apply( - bmain, &rnaptr_dst, &rnaptr_src, NULL, id_override_new->override_library); + RNA_struct_override_apply(bmain, + &rnaptr_dst, + &rnaptr_src, + NULL, + id_override_new->override_library, + do_hierarchy_enforce ? + RNA_OVERRIDE_APPLY_FLAG_IGNORE_ID_POINTERS : + RNA_OVERRIDE_APPLY_FLAG_NOP); } } } @@ -1144,7 +1151,7 @@ void BKE_lib_override_library_main_resync(Main *bmain, Scene *scene, ViewLayer * continue; } do_continue = true; - const bool success = BKE_lib_override_library_resync(bmain, scene, view_layer, id); + const bool success = BKE_lib_override_library_resync(bmain, scene, view_layer, id, false); CLOG_INFO(&LOG, 2, "Resynced %s, success: %d", id->name, success); break; } @@ -2104,8 +2111,12 @@ void BKE_lib_override_library_update(Main *bmain, ID *local) 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_struct_override_apply(bmain, + &rnaptr_dst, + &rnaptr_src, + rnaptr_storage, + local->override_library, + RNA_OVERRIDE_APPLY_FLAG_NOP); /* 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. */ diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c index 9af2ba6a82b..d9641930134 100644 --- a/source/blender/editors/space_outliner/outliner_tools.c +++ b/source/blender/editors/space_outliner/outliner_tools.c @@ -776,6 +776,11 @@ static void object_proxy_to_override_convert_fn(bContext *C, typedef struct OutlinerLibOverrideData { bool do_hierarchy; + /** + * For resync operation, force keeping newly created override IDs (or original linked IDs) + * instead of re-applying relevant existing ID pointer property override operations. Helps + * solving broken overrides while not losing *all* of your overrides. */ + bool do_resync_hierarchy_enforce; } OutlinerLibOverrideData; static void id_override_library_create_fn(bContext *C, @@ -872,10 +877,12 @@ static void id_override_library_resync_fn(bContext *C, TreeElement *te, TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem, - void *UNUSED(user_data)) + void *user_data) { BLI_assert(TSE_IS_REAL_ID(tselem)); ID *id_root = tselem->id; + OutlinerLibOverrideData *data = user_data; + const bool do_hierarchy_enforce = data->do_resync_hierarchy_enforce; if (ID_IS_OVERRIDE_LIBRARY_REAL(id_root)) { Main *bmain = CTX_data_main(C); @@ -893,7 +900,8 @@ static void id_override_library_resync_fn(bContext *C, te->store_elem->id->tag |= LIB_TAG_DOIT; } - BKE_lib_override_library_resync(bmain, scene, CTX_data_view_layer(C), id_root); + BKE_lib_override_library_resync( + bmain, scene, CTX_data_view_layer(C), id_root, do_hierarchy_enforce); WM_event_add_notifier(C, NC_WINDOW, NULL); } @@ -1710,6 +1718,7 @@ typedef enum eOutlinerIdOpTypes { OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET, OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY, OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY, + OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY_ENFORCE, OUTLINER_IDOP_OVERRIDE_LIBRARY_DELETE_HIERARCHY, OUTLINER_IDOP_SINGLE, OUTLINER_IDOP_DELETE, @@ -1770,6 +1779,13 @@ static const EnumPropertyItem prop_id_op_types[] = { "Resync Library Override Hierarchy", "Rebuild this local override from its linked reference, as well as its hierarchy of " "dependencies"}, + {OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY_ENFORCE, + "OVERRIDE_LIBRARY_RESYNC_HIERARCHY_ENFORCE", + 0, + "Resync Library Override Hierarchy Enforce", + "Rebuild this local override from its linked reference, as well as its hierarchy of " + "dependencies, enforcing that hierarchy to match the linked data (i.e. ignoring exiting " + "overrides on data-blocks pointer properties)"}, {OUTLINER_IDOP_OVERRIDE_LIBRARY_DELETE_HIERARCHY, "OVERRIDE_LIBRARY_DELETE_HIERARCHY", 0, @@ -1831,6 +1847,7 @@ static bool outliner_id_operation_item_poll(bContext *C, case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET: case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY: case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY: + case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY_ENFORCE: case OUTLINER_IDOP_OVERRIDE_LIBRARY_DELETE_HIERARCHY: if (ID_IS_OVERRIDE_LIBRARY_REAL(tselem->id)) { return true; @@ -2041,6 +2058,18 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) ED_undo_push(C, "Resync Overridden Data Hierarchy"); break; } + case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY_ENFORCE: { + outliner_do_libdata_operation( + C, + op->reports, + scene, + space_outliner, + &space_outliner->tree, + id_override_library_resync_fn, + &(OutlinerLibOverrideData){.do_hierarchy = true, .do_resync_hierarchy_enforce = true}); + ED_undo_push(C, "Resync Overridden Data Hierarchy"); + break; + } case OUTLINER_IDOP_OVERRIDE_LIBRARY_DELETE_HIERARCHY: { outliner_do_libdata_operation(C, op->reports, diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index 74b8517f538..4c60ffe4f16 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -1512,11 +1512,21 @@ bool RNA_struct_override_store(struct Main *bmain, PointerRNA *ptr_storage, struct IDOverrideLibrary *override); +typedef enum eRNAOverrideApplyFlag { + RNA_OVERRIDE_APPLY_FLAG_NOP = 0, + /** + * Hack to work around/fix older broken overrides: Do not apply override operations affecting ID + * pointers properties, unless the destination original value (the one being overridden) is NULL. + */ + RNA_OVERRIDE_APPLY_FLAG_IGNORE_ID_POINTERS = 1 << 0, +} eRNAOverrideApplyFlag; + void RNA_struct_override_apply(struct Main *bmain, struct PointerRNA *ptr_dst, struct PointerRNA *ptr_src, struct PointerRNA *ptr_storage, - struct IDOverrideLibrary *override); + struct IDOverrideLibrary *override, + const eRNAOverrideApplyFlag flag); struct IDOverrideLibraryProperty *RNA_property_override_property_find(struct Main *bmain, PointerRNA *ptr, diff --git a/source/blender/makesrna/intern/rna_access_compare_override.c b/source/blender/makesrna/intern/rna_access_compare_override.c index 6a8152d1139..28d4cc4d075 100644 --- a/source/blender/makesrna/intern/rna_access_compare_override.c +++ b/source/blender/makesrna/intern/rna_access_compare_override.c @@ -1123,7 +1123,8 @@ void RNA_struct_override_apply(Main *bmain, PointerRNA *ptr_dst, PointerRNA *ptr_src, PointerRNA *ptr_storage, - IDOverrideLibrary *override) + IDOverrideLibrary *override, + const eRNAOverrideApplyFlag flag) { #ifdef DEBUG_OVERRIDE_TIMEIT TIMEIT_START_AVERAGED(RNA_struct_override_apply); @@ -1187,6 +1188,39 @@ void RNA_struct_override_apply(Main *bmain, } } + /* Workaround for older broken overrides, we then assume that non-matching ID pointers + * override operations that replace a non-NULL value are 'mistakes', and ignore (do not + * apply) them. */ + if ((flag & RNA_OVERRIDE_APPLY_FLAG_IGNORE_ID_POINTERS) != 0 && + op->rna_prop_type == PROP_POINTER && + (((IDOverrideLibraryPropertyOperation *)op->operations.first)->flag & + IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE) == 0) { + BLI_assert(ptr_src->owner_id == + rna_property_override_property_real_id_owner(bmain, &data_src, NULL, NULL)); + BLI_assert(ptr_dst->owner_id == + rna_property_override_property_real_id_owner(bmain, &data_dst, NULL, NULL)); + + PointerRNA prop_ptr_dst = RNA_property_pointer_get(&data_dst, prop_dst); + if (prop_ptr_dst.type != NULL && RNA_struct_is_ID(prop_ptr_dst.type)) { +#ifndef NDEBUG + PointerRNA prop_ptr_src = RNA_property_pointer_get(&data_src, prop_src); + BLI_assert(prop_ptr_src.type == NULL || RNA_struct_is_ID(prop_ptr_src.type)); +#endif + ID *id_dst = rna_property_override_property_real_id_owner( + bmain, &prop_ptr_dst, NULL, NULL); + + if (id_dst != NULL) { + CLOG_INFO(&LOG, + 3, + "%s: Ignoring local override on ID pointer property '%s', as requested by " + "RNA_OVERRIDE_APPLY_FLAG_IGNORE_ID_POINTERS flag", + ptr_dst->owner_id->name, + op->rna_path); + continue; + } + } + } + rna_property_override_apply_ex(bmain, &data_dst, &data_src,