From 33c5e7bcd5e5b790ee95caaa0c4d917996341266 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Fri, 19 Nov 2021 09:33:52 +0100 Subject: LibOverrides: Refactor how diffing of RNA collections is handled. Original implementation was a quick prototype which should have never landed as-is in master. It had very limiting constraints and did not allow for any real further development. This commit fixes the internal implementation to make more sensible, maintainable and evolutive. NOTE: This commit introduces another forward-incompatibility in the Blender file format: Files saved after this commit won't open properly in older versions of blender regarding local inserted constraints or modifiers into overrides of linked data. NOTE: Technical details: The 'anchor' item name/index is now stored in `subitem_reference_` members, and the actual 'source' item name/index is stored in `subitem_local_` members of the override property operation data. Previously, only the `subitem_local_` members were used, storing the anchor item name/index, and assuming the 'source' item was always the next in the list. Milestone I of T82160. Maniphest Tasks: T82160 Differential Revision: https://developer.blender.org/D13282 --- source/blender/blenloader/intern/versioning_300.c | 176 ++++++++++++++++++++-- 1 file changed, 166 insertions(+), 10 deletions(-) (limited to 'source/blender/blenloader') diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 98264883507..18baebf57fb 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -22,6 +22,8 @@ #include +#include "CLG_log.h" + #include "MEM_guardedalloc.h" #include "BLI_listbase.h" @@ -46,6 +48,7 @@ #include "DNA_workspace_types.h" #include "BKE_action.h" +#include "BKE_anim_data.h" #include "BKE_animsys.h" #include "BKE_armature.h" #include "BKE_asset.h" @@ -55,6 +58,7 @@ #include "BKE_fcurve_driver.h" #include "BKE_idprop.h" #include "BKE_lib_id.h" +#include "BKE_lib_override.h" #include "BKE_main.h" #include "BKE_modifier.h" #include "BKE_node.h" @@ -74,6 +78,8 @@ #include "versioning_common.h" +static CLG_LogRef LOG = {"blo.readfile.doversion"}; + static IDProperty *idproperty_find_ui_container(IDProperty *idprop_group) { LISTBASE_FOREACH (IDProperty *, prop, &idprop_group->data.group) { @@ -1282,6 +1288,140 @@ static bool version_fix_seq_meta_range(Sequence *seq, void *user_data) return true; } +/* Those `version_liboverride_rnacollections_*` functions mimic the old, pre-3.0 code to find + * anchor and source items in the given list of modifiers, constraints etc., using only the + * `subitem_local` data of the override property operation. + * + * Then they convert it into the new, proper `subitem_reference` data for the anchor, and + * `subitem_local` for the source. + * + * NOTE: Here only the stored override ID is available, unlike in the `override_apply` functions. + */ + +static void version_liboverride_rnacollections_insertion_object_constraints( + ListBase *constraints, IDOverrideLibraryProperty *op) +{ + LISTBASE_FOREACH_MUTABLE (IDOverrideLibraryPropertyOperation *, opop, &op->operations) { + if (opop->operation != IDOVERRIDE_LIBRARY_OP_INSERT_AFTER) { + continue; + } + bConstraint *constraint_anchor = BLI_listbase_string_or_index_find(constraints, + opop->subitem_local_name, + offsetof(bConstraint, name), + opop->subitem_local_index); + if (constraint_anchor == NULL || constraint_anchor->next == NULL) { + /* Invalid case, just remove that override property operation. */ + CLOG_ERROR(&LOG, "Could not find anchor or source constraints in stored override data"); + BKE_lib_override_library_property_operation_delete(op, opop); + continue; + } + bConstraint *constraint_src = constraint_anchor->next; + opop->subitem_reference_name = opop->subitem_local_name; + opop->subitem_local_name = BLI_strdup(constraint_src->name); + opop->subitem_reference_index = opop->subitem_local_index; + opop->subitem_local_index++; + } +} + +static void version_liboverride_rnacollections_insertion_object(Object *object) +{ + IDOverrideLibrary *liboverride = object->id.override_library; + IDOverrideLibraryProperty *op; + + op = BKE_lib_override_library_property_find(liboverride, "modifiers"); + if (op != NULL) { + LISTBASE_FOREACH_MUTABLE (IDOverrideLibraryPropertyOperation *, opop, &op->operations) { + if (opop->operation != IDOVERRIDE_LIBRARY_OP_INSERT_AFTER) { + continue; + } + ModifierData *mod_anchor = BLI_listbase_string_or_index_find(&object->modifiers, + opop->subitem_local_name, + offsetof(ModifierData, name), + opop->subitem_local_index); + if (mod_anchor == NULL || mod_anchor->next == NULL) { + /* Invalid case, just remove that override property operation. */ + CLOG_ERROR(&LOG, "Could not find anchor or source modifiers in stored override data"); + BKE_lib_override_library_property_operation_delete(op, opop); + continue; + } + ModifierData *mod_src = mod_anchor->next; + opop->subitem_reference_name = opop->subitem_local_name; + opop->subitem_local_name = BLI_strdup(mod_src->name); + opop->subitem_reference_index = opop->subitem_local_index; + opop->subitem_local_index++; + } + } + + op = BKE_lib_override_library_property_find(liboverride, "grease_pencil_modifiers"); + if (op != NULL) { + LISTBASE_FOREACH_MUTABLE (IDOverrideLibraryPropertyOperation *, opop, &op->operations) { + if (opop->operation != IDOVERRIDE_LIBRARY_OP_INSERT_AFTER) { + continue; + } + GpencilModifierData *gp_mod_anchor = BLI_listbase_string_or_index_find( + &object->greasepencil_modifiers, + opop->subitem_local_name, + offsetof(GpencilModifierData, name), + opop->subitem_local_index); + if (gp_mod_anchor == NULL || gp_mod_anchor->next == NULL) { + /* Invalid case, just remove that override property operation. */ + CLOG_ERROR(&LOG, "Could not find anchor GP modifier in stored override data"); + BKE_lib_override_library_property_operation_delete(op, opop); + continue; + } + GpencilModifierData *gp_mod_src = gp_mod_anchor->next; + opop->subitem_reference_name = opop->subitem_local_name; + opop->subitem_local_name = BLI_strdup(gp_mod_src->name); + opop->subitem_reference_index = opop->subitem_local_index; + opop->subitem_local_index++; + } + } + + op = BKE_lib_override_library_property_find(liboverride, "constraints"); + if (op != NULL) { + version_liboverride_rnacollections_insertion_object_constraints(&object->constraints, op); + } + + if (object->pose != NULL) { + LISTBASE_FOREACH (bPoseChannel *, pchan, &object->pose->chanbase) { + char rna_path[FILE_MAXFILE]; + BLI_snprintf(rna_path, sizeof(rna_path), "pose.bones[\"%s\"].constraints", pchan->name); + op = BKE_lib_override_library_property_find(liboverride, rna_path); + if (op != NULL) { + version_liboverride_rnacollections_insertion_object_constraints(&pchan->constraints, op); + } + } + } +} + +static void version_liboverride_rnacollections_insertion_animdata(ID *id) +{ + AnimData *anim_data = BKE_animdata_from_id(id); + if (anim_data == NULL) { + return; + } + + IDOverrideLibrary *liboverride = id->override_library; + IDOverrideLibraryProperty *op; + + op = BKE_lib_override_library_property_find(liboverride, "animation_data.nla_tracks"); + if (op != NULL) { + LISTBASE_FOREACH (IDOverrideLibraryPropertyOperation *, opop, &op->operations) { + if (opop->operation != IDOVERRIDE_LIBRARY_OP_INSERT_AFTER) { + continue; + } + /* NLA tracks are only referenced by index, which limits possibilities, basically they are + * always added at the end of the list, see #rna_NLA_tracks_override_apply. + * + * This makes things simple here. */ + opop->subitem_reference_name = opop->subitem_local_name; + opop->subitem_local_name = NULL; + opop->subitem_reference_index = opop->subitem_local_index; + opop->subitem_local_index++; + } + } +} + /* NOLINTNEXTLINE: readability-function-size */ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) { @@ -2158,16 +2298,20 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } - /** - * Versioning code until next subversion bump goes here. - * - * \note Be sure to check when bumping the version: - * - "versioning_userdef.c", #blo_do_versions_userdef - * - "versioning_userdef.c", #do_versions_theme - * - * \note Keep this message at the bottom of the function. - */ - { + if (!MAIN_VERSION_ATLEAST(bmain, 300, 42)) { + /* Update LibOverride operations regarding insertions in RNA collections (i.e. modifiers, + * constraints and NLA tracks). */ + ID *id_iter; + FOREACH_MAIN_ID_BEGIN (bmain, id_iter) { + if (ID_IS_OVERRIDE_LIBRARY_REAL(id_iter)) { + version_liboverride_rnacollections_insertion_animdata(id_iter); + if (GS(id_iter->name) == ID_OB) { + version_liboverride_rnacollections_insertion_object((Object *)id_iter); + } + } + } + FOREACH_MAIN_ID_END; + /* Use consistent socket identifiers for the math node. * The code to make unique identifiers from the names was inconsistent. */ FOREACH_NODETREE_BEGIN (bmain, ntree, id) { @@ -2215,4 +2359,16 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } + + /** + * Versioning code until next subversion bump goes here. + * + * \note Be sure to check when bumping the version: + * - "versioning_userdef.c", #blo_do_versions_userdef + * - "versioning_userdef.c", #do_versions_theme + * + * \note Keep this message at the bottom of the function. + */ + { + } } -- cgit v1.2.3