diff --git a/source/blender/blenkernel/BKE_lib_override.h b/source/blender/blenkernel/BKE_lib_override.h index 990517f128e604b8f6baedd627b39a0c38312694..70b717004c090d5e4f3ee1581a34e1891353fb94 100644 --- a/source/blender/blenkernel/BKE_lib_override.h +++ b/source/blender/blenkernel/BKE_lib_override.h @@ -303,6 +303,27 @@ struct IDOverrideLibraryProperty *BKE_lib_override_library_property_get( */ void BKE_lib_override_library_property_delete(struct IDOverrideLibrary *override, struct IDOverrideLibraryProperty *override_property); +/** + * Delete a property override from the given ID \a override, if it exists. + * + * \return True when the property was found (and thus deleted), false if it wasn't found. + */ +bool BKE_lib_override_library_property_search_and_delete(struct IDOverrideLibrary *override, + const char *rna_path); + +/** Change the RNA path of a library override on a property. + * + * No-op if the property override cannot be found. + * + * \param from_rna_path The RNA path of the property to change. + * \param to_rna_path The new RNA path. The library override system will copy the string to its own + * memory; the caller will retain ownership of the passed pointer. + * \return True if the property was found (and thus changed), false if it wasn't found. + */ +bool BKE_lib_override_library_property_rna_path_change(IDOverrideLibrary *liboverride, + const char *old_rna_path, + const char *new_rna_path); + /** * Get the RNA-property matching the \a library_prop override property. Used for UI to query * additional data about the overridden property (e.g. UI name). diff --git a/source/blender/blenkernel/intern/lib_override.cc b/source/blender/blenkernel/intern/lib_override.cc index 69933617f5f0731b1d3ca20cb44a9615b5483d4d..859c7b44106ee186cf67e2717eab3a18d86576ae 100644 --- a/source/blender/blenkernel/intern/lib_override.cc +++ b/source/blender/blenkernel/intern/lib_override.cc @@ -3530,6 +3530,41 @@ void lib_override_library_property_clear(IDOverrideLibraryProperty *op) BLI_freelistN(&op->operations); } +bool BKE_lib_override_library_property_search_and_delete(IDOverrideLibrary *override, + const char *rna_path) +{ + IDOverrideLibraryProperty *override_property = BKE_lib_override_library_property_find(override, + rna_path); + if (override_property == nullptr) { + return false; + } + BKE_lib_override_library_property_delete(override, override_property); + return true; +} + +bool BKE_lib_override_library_property_rna_path_change(IDOverrideLibrary *override, + const char *old_rna_path, + const char *new_rna_path) +{ + /* Find the override property by its old RNA path. */ + GHash *override_runtime = override_library_rna_path_mapping_ensure(override); + IDOverrideLibraryProperty *override_property = static_cast<IDOverrideLibraryProperty *>( + BLI_ghash_popkey(override_runtime, old_rna_path, nullptr)); + + if (override_property == nullptr) { + return false; + } + + /* Switch over the RNA path. */ + MEM_SAFE_FREE(override_property->rna_path); + override_property->rna_path = BLI_strdup(new_rna_path); + + /* Put property back into the lookup mapping, using the new RNA path. */ + BLI_ghash_insert(override_runtime, override_property->rna_path, override_property); + + return true; +} + void BKE_lib_override_library_property_delete(IDOverrideLibrary *override, IDOverrideLibraryProperty *override_property) { diff --git a/source/blender/blenloader/intern/versioning_300.cc b/source/blender/blenloader/intern/versioning_300.cc index 841ba4a53393cb070aaa8205e01933dc59cfe1b6..0ebf1ed9a0688aa7b7740b5e451ee084a3ad7c9e 100644 --- a/source/blender/blenloader/intern/versioning_300.cc +++ b/source/blender/blenloader/intern/versioning_300.cc @@ -2321,6 +2321,73 @@ static void version_ensure_missing_regions(ScrArea *area, SpaceLink *sl) } } +/** + * Change override RNA path from `frame_{start,end}` to `frame_{start,end}_raw`. + * See #102662. + */ +static void version_liboverride_nla_strip_frame_start_end(IDOverrideLibrary *liboverride, + const char *parent_rna_path, + NlaStrip *strip) +{ + if (!strip) { + return; + } + + /* Escape the strip name for inclusion in the RNA path. */ + char name_esc_strip[sizeof(strip->name) * 2]; + BLI_str_escape(name_esc_strip, strip->name, sizeof(name_esc_strip)); + + const std::string rna_path_strip = std::string(parent_rna_path) + ".strips[\"" + name_esc_strip + + "\"]"; + + { /* Rename .frame_start -> .frame_start_raw: */ + const std::string rna_path_prop = rna_path_strip + ".frame_start"; + BKE_lib_override_library_property_rna_path_change( + liboverride, rna_path_prop.c_str(), (rna_path_prop + "_raw").c_str()); + } + + { /* Rename .frame_end -> .frame_end_raw: */ + const std::string rna_path_prop = rna_path_strip + ".frame_end"; + BKE_lib_override_library_property_rna_path_change( + liboverride, rna_path_prop.c_str(), (rna_path_prop + "_raw").c_str()); + } + + { /* Remove .frame_start_ui: */ + const std::string rna_path_prop = rna_path_strip + ".frame_start_ui"; + BKE_lib_override_library_property_search_and_delete(liboverride, rna_path_prop.c_str()); + } + + { /* Remove .frame_end_ui: */ + const std::string rna_path_prop = rna_path_strip + ".frame_end_ui"; + BKE_lib_override_library_property_search_and_delete(liboverride, rna_path_prop.c_str()); + } + + /* Handle meta-strip contents. */ + LISTBASE_FOREACH (NlaStrip *, substrip, &strip->strips) { + version_liboverride_nla_strip_frame_start_end(liboverride, rna_path_strip.c_str(), substrip); + } +} + +/** Fix the `frame_start` and `frame_end` overrides on NLA strips. See #102662. */ +static void version_liboverride_nla_frame_start_end(ID *id, AnimData *adt, void * /*user_data*/) +{ + IDOverrideLibrary *liboverride = id->override_library; + if (!liboverride) { + return; + } + + int track_index; + LISTBASE_FOREACH_INDEX (NlaTrack *, track, &adt->nla_tracks, track_index) { + char *rna_path_track = BLI_sprintfN("animation_data.nla_tracks[%d]", track_index); + + LISTBASE_FOREACH (NlaStrip *, strip, &track->strips) { + version_liboverride_nla_strip_frame_start_end(liboverride, rna_path_track, strip); + } + + MEM_freeN(rna_path_track); + } +} + /* NOLINTNEXTLINE: readability-function-size */ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain) { @@ -4417,5 +4484,7 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain) */ { /* Keep this block, even when empty. */ + + BKE_animdata_main_cb(bmain, version_liboverride_nla_frame_start_end, NULL); } } diff --git a/source/blender/makesrna/intern/rna_nla.c b/source/blender/makesrna/intern/rna_nla.c index 6ba6973de26278833666cff11e902431be62fbb2..0e9658e46f68faafbd94212bf8e312cf65760ee9 100644 --- a/source/blender/makesrna/intern/rna_nla.c +++ b/source/blender/makesrna/intern/rna_nla.c @@ -739,6 +739,10 @@ static void rna_def_nlastrip(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Start Frame", ""); RNA_def_property_update( prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_transform_update"); + /* The `frame_start` and `frame_end` properties should NOT be considered for library overrides, + * as their setters always enforce a valid state. While library overrides are applied, the + * intermediate state may be invalid, even when the end state is valid. */ + RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON); prop = RNA_def_property(srna, "frame_end", PROP_FLOAT, PROP_TIME); RNA_def_property_float_sdna(prop, NULL, "end"); @@ -746,6 +750,29 @@ static void rna_def_nlastrip(BlenderRNA *brna) RNA_def_property_ui_text(prop, "End Frame", ""); RNA_def_property_update( prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_transform_update"); + /* The `frame_start` and `frame_end` properties should NOT be considered for library overrides, + * as their setters always enforce a valid state. While library overrides are applied, the + * intermediate state may be invalid, even when the end state is valid. */ + RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON); + + /* Strip extents without enforcing a valid state. */ + prop = RNA_def_property(srna, "frame_start_raw", PROP_FLOAT, PROP_TIME); + RNA_def_property_float_sdna(prop, NULL, "start"); + RNA_def_property_ui_text(prop, + "Start Frame (raw value)", + "Same as frame_start, except that any value can be set, including ones " + "that create an invalid state"); + RNA_def_property_update( + prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_transform_update"); + + prop = RNA_def_property(srna, "frame_end_raw", PROP_FLOAT, PROP_TIME); + RNA_def_property_float_sdna(prop, NULL, "end"); + RNA_def_property_ui_text(prop, + "End Frame (raw value)", + "Same as frame_end, except that any value can be set, including ones " + "that create an invalid state"); + RNA_def_property_update( + prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_transform_update"); /* Strip extents, when called from UI elements : */ prop = RNA_def_property(srna, "frame_start_ui", PROP_FLOAT, PROP_TIME); @@ -759,6 +786,9 @@ static void rna_def_nlastrip(BlenderRNA *brna) "property instead"); RNA_def_property_update( prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_transform_update"); + /* The `..._ui` properties should NOT be considered for library overrides, as they are meant to + * have different behavior than when setting their non-`..._ui` counterparts. */ + RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON); prop = RNA_def_property(srna, "frame_end_ui", PROP_FLOAT, PROP_TIME); RNA_def_property_float_sdna(prop, NULL, "end"); @@ -771,6 +801,9 @@ static void rna_def_nlastrip(BlenderRNA *brna) "changed, see the \"frame_end\" property instead"); RNA_def_property_update( prop, NC_ANIMATION | ND_NLA | NA_EDITED, "rna_NlaStrip_transform_update"); + /* The `..._ui` properties should NOT be considered for library overrides, as they are meant to + * have different behavior than when setting their non-`..._ui` counterparts. */ + RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON); /* Blending */ prop = RNA_def_property(srna, "blend_in", PROP_FLOAT, PROP_NONE);