Fix #106801: loading file with overriden NLA in tweak mode loses action

The root issue was that the `tmpact` field of an object's animation
data wasn't exposed to RNA, and thus wasn't getting overridden.

Pull Request: https://projects.blender.org/blender/blender/pulls/108548
This commit is contained in:
Nathan Vegdahl
2023-06-06 11:10:58 +02:00
committed by Nathan Vegdahl
parent 8a0a9ec0c3
commit 997ad50b49
3 changed files with 119 additions and 34 deletions

View File

@@ -57,6 +57,11 @@ struct AnimData *BKE_animdata_ensure_id(struct ID *id);
*/
bool BKE_animdata_set_action(struct ReportList *reports, struct ID *id, struct bAction *act);
/**
* Same as BKE_animdata_set_action(), except sets `tmpact` instead of `action`.
*/
bool BKE_animdata_set_tmpact(struct ReportList *reports, struct ID *id, struct bAction *act);
bool BKE_animdata_action_editable(const struct AnimData *adt);
/**

View File

@@ -113,39 +113,23 @@ AnimData *BKE_animdata_ensure_id(ID *id)
return NULL;
}
/* Action Setter --------------------------------------- */
bool BKE_animdata_set_action(ReportList *reports, ID *id, bAction *act)
/* Action / Tmpact Setter shared code -------------------------
*
* Both the action and tmpact setter functions have essentially
* identical semantics, because tmpact is just a place to temporarily
* store the main action during tweaking. This function contains the
* shared code between those two setter functions, setting the action
* of the passed `act_slot` to `act`.
*
* Preconditions:
* - `id` and `act_slot` must be non-null (but the pointer `act_slot`
* points to can be null).
* - `id` must have animation data.
* - `act_slot` must be a pointer to either the `action` or `tmpact`
* field of `id`'s animation data.
*/
static bool animdata_set_action(ReportList *reports, ID *id, bAction **act_slot, bAction *act)
{
AnimData *adt = BKE_animdata_from_id(id);
/* Animdata validity check. */
if (adt == NULL) {
BKE_report(reports, RPT_WARNING, "No AnimData to set action on");
return false;
}
if (adt->action == act) {
/* Don't bother reducing and increasing the user count when there is nothing changing. */
return true;
}
if (!BKE_animdata_action_editable(adt)) {
/* Cannot remove, otherwise things turn to custard. */
BKE_report(reports, RPT_ERROR, "Cannot change action, as it is still being edited in NLA");
return false;
}
/* Unassign current action. */
if (adt->action) {
id_us_min((ID *)adt->action);
adt->action = NULL;
}
if (act == NULL) {
return true;
}
/* Action must have same type as owner. */
if (!BKE_animdata_action_ensure_idroot(id, act)) {
/* Cannot set to this type. */
@@ -159,12 +143,59 @@ bool BKE_animdata_set_action(ReportList *reports, ID *id, bAction *act)
return false;
}
adt->action = act;
id_us_plus((ID *)adt->action);
if (*act_slot == act) {
/* Don't bother reducing and increasing the user count when there is nothing changing. */
return true;
}
/* Unassign current action. */
if (*act_slot) {
id_us_min((ID *)*act_slot);
*act_slot = NULL;
}
if (act == NULL) {
return true;
}
*act_slot = act;
id_us_plus((ID *)*act_slot);
return true;
}
/* Tmpact Setter --------------------------------------- */
bool BKE_animdata_set_tmpact(ReportList *reports, ID *id, bAction *act)
{
AnimData *adt = BKE_animdata_from_id(id);
if (adt == NULL) {
BKE_report(reports, RPT_WARNING, "No AnimData to set tmpact on");
return false;
}
return animdata_set_action(reports, id, &adt->tmpact, act);
}
/* Action Setter --------------------------------------- */
bool BKE_animdata_set_action(ReportList *reports, ID *id, bAction *act)
{
AnimData *adt = BKE_animdata_from_id(id);
if (adt == NULL) {
BKE_report(reports, RPT_WARNING, "No AnimData to set action on");
return false;
}
if (!BKE_animdata_action_editable(adt)) {
/* Cannot remove, otherwise things turn to custard. */
BKE_report(reports, RPT_ERROR, "Cannot change action, as it is still being edited in NLA");
return false;
}
return animdata_set_action(reports, id, &adt->action, act);
}
bool BKE_animdata_action_editable(const AnimData *adt)
{
/* Active action is only editable when it is not a tweaking strip. */

View File

@@ -143,6 +143,16 @@ static void rna_AnimData_action_set(PointerRNA *ptr,
BKE_animdata_set_action(NULL, ownerId, value.data);
}
static void rna_AnimData_tmpact_set(PointerRNA *ptr,
PointerRNA value,
struct ReportList *UNUSED(reports))
{
ID *ownerId = ptr->owner_id;
/* set action */
BKE_animdata_set_tmpact(NULL, ownerId, value.data);
}
static void rna_AnimData_tweakmode_set(PointerRNA *ptr, const bool value)
{
AnimData *adt = (AnimData *)ptr->data;
@@ -160,6 +170,34 @@ static void rna_AnimData_tweakmode_set(PointerRNA *ptr, const bool value)
}
}
/* This is used to avoid the check for NLA tracks when enabling tweak
* mode while loading overrides. This is necessary because the normal
* RNA tweak-mode setter refuses to enable tweak mode if there are no
* NLA tracks since that's normally an invalid state... but the
* overriden NLA tracks are only added *after* setting the tweak mode
* override. */
bool rna_AnimData_tweakmode_override_apply(Main *UNUSED(bmain),
PointerRNA *ptr_dst,
PointerRNA *ptr_src,
PointerRNA *UNUSED(ptr_storage),
PropertyRNA *UNUSED(prop_dst),
PropertyRNA *UNUSED(prop_src),
PropertyRNA *UNUSED(prop_storage),
const int UNUSED(len_dst),
const int UNUSED(len_src),
const int UNUSED(len_storage),
PointerRNA *UNUSED(ptr_item_dst),
PointerRNA *UNUSED(ptr_item_src),
PointerRNA *UNUSED(ptr_item_storage),
IDOverrideLibraryPropertyOperation *UNUSED(opop))
{
AnimData *anim_data_dst = (AnimData *)ptr_dst->data;
AnimData *anim_data_src = (AnimData *)ptr_src->data;
anim_data_dst->flag =(anim_data_dst->flag & ~ADT_NLA_EDIT_ON) | (anim_data_src->flag & ADT_NLA_EDIT_ON);
return true;
}
/* ****************************** */
/* wrapper for poll callback */
@@ -1370,6 +1408,16 @@ static void rna_def_animdata(BlenderRNA *brna)
"Amount the Active Action contributes to the result of the NLA stack");
RNA_def_property_update(prop, NC_ANIMATION | ND_NLA, "rna_AnimData_update"); /* this will do? */
/* Temporary action slot for tweak mode. */
prop = RNA_def_property(srna, "action_tweak_storage", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "tmpact");
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_EDITABLE | PROP_ID_REFCOUNT);
RNA_def_property_pointer_funcs(
prop, NULL, "rna_AnimData_tmpact_set", NULL, "rna_Action_id_poll");
RNA_def_property_ui_text(
prop, "Tweak Mode Action Storage", "Slot to temporarily hold the main action while in tweak mode");
RNA_def_property_update(prop, NC_ANIMATION | ND_NLA_ACTCHANGE, "rna_AnimData_dependency_update");
/* Drivers */
prop = RNA_def_property(srna, "drivers", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, NULL, "drivers", NULL);
@@ -1395,6 +1443,7 @@ static void rna_def_animdata(BlenderRNA *brna)
RNA_def_property_ui_text(
prop, "Use NLA Tweak Mode", "Whether to enable or disable tweak mode in NLA");
RNA_def_property_update(prop, NC_ANIMATION | ND_NLA, "rna_AnimData_update");
RNA_def_property_override_funcs(prop, NULL, NULL, "rna_AnimData_tweakmode_override_apply");
prop = RNA_def_property(srna, "use_pin", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE);