Fix: Autokeyframe with Insert Needed with no keyframes

The issue occurs when auto-keyframe AND "Only Insert Needed" enabled.
And only when no keyframes have been added yet.

Before the commit that caused the issue, moving an object with `G` would
create only location keyframes. After it would key all.
That is because that commit removed the logic that checks the
`eTfmMode` (Transform mode). That only works as long as there are already
keyframes on the object/bone because the logic needs an
existing value to compare against. In the case where the first keyframe is set,
it would always key everything.

The fix is to bring back the logic that checks the Transform mode and pass
a `Span` of rna paths to the autokeyframe function. This restores the behavior.

This still has the issue that "Only Insert Needed" behaves differently if
keys exist vs inserting the first keys. While this isn't ideal, I don't see a way
to get values of an object/bone before and after the transformation.
We might be able to fix this in a future PR, but for now we restore the
old behavior.

Caused by #115522

Pull Request: https://projects.blender.org/blender/blender/pulls/116219
This commit is contained in:
Christoph Lendenfeld 2023-12-21 10:50:17 +01:00 committed by Christoph Lendenfeld
parent d37f9e6587
commit 614d7749df
6 changed files with 239 additions and 80 deletions

View File

@ -142,7 +142,12 @@ bool is_autokey_flag(const Scene *scene, eKeyInsert_Flag flag);
*/
bool autokeyframe_cfra_can_key(const Scene *scene, ID *id);
void autokeyframe_object(bContext *C, Scene *scene, Object *ob);
/**
* Insert keyframes on the given object `ob` based on the auto-keying settings.
*
* \param rna_paths: Only inserts keys on those RNA paths.
*/
void autokeyframe_object(bContext *C, Scene *scene, Object *ob, Span<std::string> rna_paths);
/**
* Auto-keyframing feature - for objects
*
@ -154,12 +159,18 @@ bool autokeyframe_pchan(bContext *C, Scene *scene, Object *ob, bPoseChannel *pch
/**
* Auto-keyframing feature - for poses/pose-channels
*
* targetless_ik: has targetless ik been done on any channels?
* \param targetless_ik: Has targetless ik been done on any channels?
* \param rna_paths: Only inserts keys on those RNA paths.
*
* \note Context may not always be available,
* so must check before using it as it's a luxury for a few cases.
*/
void autokeyframe_pose(bContext *C, Scene *scene, Object *ob, short targetless_ik);
void autokeyframe_pose_channel(bContext *C,
Scene *scene,
Object *ob,
bPoseChannel *pose_channel,
Span<std::string> rna_paths,
short targetless_ik);
/**
* Use for auto-key-framing.
* \param only_if_property_keyed: if true, auto-key-framing only creates keyframes on already keyed

View File

@ -9,6 +9,7 @@
*/
#include "BLI_vector.hh"
#include "DNA_action_types.h"
#include "RNA_types.hh"
namespace blender::animrig {
@ -16,4 +17,7 @@ namespace blender::animrig {
/** Get the values of the given property. Casts non-float properties to float. */
Vector<float> get_rna_values(PointerRNA *ptr, PropertyRNA *prop);
/** Get the rna path for the given rotation mode. */
std::string get_rotation_mode_path(eRotationModes rotation_mode);
} // namespace blender::animrig

View File

@ -8,6 +8,7 @@
#include "ANIM_rna.hh"
#include "BLI_vector.hh"
#include "DNA_action_types.h"
#include "RNA_access.hh"
namespace blender::animrig {
@ -68,4 +69,16 @@ Vector<float> get_rna_values(PointerRNA *ptr, PropertyRNA *prop)
return values;
}
std::string get_rotation_mode_path(const eRotationModes rotation_mode)
{
switch (rotation_mode) {
case ROT_MODE_QUAT:
return "rotation_quaternion";
case ROT_MODE_AXISANGLE:
return "rotation_axis_angle";
default:
return "rotation_euler";
}
}
} // namespace blender::animrig

View File

@ -26,6 +26,7 @@
#include "ED_transform.hh"
#include "ANIM_keyframing.hh"
#include "ANIM_rna.hh"
#include "WM_api.hh"
#include "WM_types.hh"
@ -82,19 +83,7 @@ bool autokeyframe_cfra_can_key(const Scene *scene, ID *id)
return true;
}
static std::string get_rotation_mode_path(const eRotationModes rotmode)
{
switch (rotmode) {
case ROT_MODE_QUAT:
return "rotation_quaternion";
case ROT_MODE_AXISANGLE:
return "rotation_axis_angle";
default:
return "rotation_euler";
}
}
void autokeyframe_object(bContext *C, Scene *scene, Object *ob)
void autokeyframe_object(bContext *C, Scene *scene, Object *ob, Span<std::string> rna_paths)
{
ID *id = &ob->id;
if (!autokeyframe_cfra_can_key(scene, id)) {
@ -148,13 +137,11 @@ void autokeyframe_object(bContext *C, Scene *scene, Object *ob)
}
const float scene_frame = BKE_scene_frame_get(scene);
std::string rotation_rna_path = get_rotation_mode_path(eRotationModes(ob->rotmode));
Vector<std::string> rna_paths = {"location", rotation_rna_path, "scale"};
Main *bmain = CTX_data_main(C);
for (PointerRNA ptr : sources) {
insert_key_rna(&ptr,
rna_paths.as_span(),
rna_paths,
scene_frame,
flag,
eBezTriple_KeyframeType(scene->toolsettings->keyframe_type),
@ -199,13 +186,17 @@ bool autokeyframe_pchan(bContext *C, Scene *scene, Object *ob, bPoseChannel *pch
return true;
}
void autokeyframe_pose(bContext *C, Scene *scene, Object *ob, short targetless_ik)
void autokeyframe_pose_channel(bContext *C,
Scene *scene,
Object *ob,
bPoseChannel *pose_channel,
Span<std::string> rna_paths,
short targetless_ik)
{
Main *bmain = CTX_data_main(C);
ID *id = &ob->id;
AnimData *adt = ob->adt;
bAction *act = (adt) ? adt->action : nullptr;
bPose *pose = ob->pose;
if (!blender::animrig::autokeyframe_cfra_can_key(scene, id)) {
return;
@ -230,68 +221,57 @@ void autokeyframe_pose(bContext *C, Scene *scene, Object *ob, short targetless_i
flag |= INSERTKEY_MATRIX;
}
LISTBASE_FOREACH (bPoseChannel *, pchan, &pose->chanbase) {
if ((pchan->bone->flag & BONE_TRANSFORM) == 0 &&
!((pose->flag & POSE_MIRROR_EDIT) && (pchan->bone->flag & BONE_TRANSFORM_MIRROR)))
{
continue;
blender::Vector<PointerRNA> sources;
/* Add data-source override for the camera object. */
ANIM_relative_keyingset_add_source(sources, id, &RNA_PoseBone, pose_channel);
/* only insert into active keyingset? */
if (blender::animrig::is_autokey_flag(scene, AUTOKEY_FLAG_ONLYKEYINGSET) && (active_ks)) {
/* Run the active Keying Set on the current data-source. */
ANIM_apply_keyingset(
C, &sources, active_ks, MODIFYKEY_MODE_INSERT, anim_eval_context.eval_time);
return;
}
/* only insert into available channels? */
if (blender::animrig::is_autokey_flag(scene, AUTOKEY_FLAG_INSERTAVAILABLE)) {
if (!act) {
return;
}
blender::Vector<PointerRNA> sources;
/* Add data-source override for the camera object. */
ANIM_relative_keyingset_add_source(sources, id, &RNA_PoseBone, pchan);
/* only insert into active keyingset? */
if (blender::animrig::is_autokey_flag(scene, AUTOKEY_FLAG_ONLYKEYINGSET) && (active_ks)) {
/* Run the active Keying Set on the current data-source. */
ANIM_apply_keyingset(
C, &sources, active_ks, MODIFYKEY_MODE_INSERT, anim_eval_context.eval_time);
continue;
}
/* only insert into available channels? */
if (blender::animrig::is_autokey_flag(scene, AUTOKEY_FLAG_INSERTAVAILABLE)) {
if (!act) {
LISTBASE_FOREACH (FCurve *, fcu, &act->curves) {
/* only insert keyframes for this F-Curve if it affects the current bone */
char pchan_name[sizeof(pose_channel->name)];
if (!BLI_str_quoted_substr(fcu->rna_path, "bones[", pchan_name, sizeof(pchan_name))) {
continue;
}
LISTBASE_FOREACH (FCurve *, fcu, &act->curves) {
/* only insert keyframes for this F-Curve if it affects the current bone */
char pchan_name[sizeof(pchan->name)];
if (!BLI_str_quoted_substr(fcu->rna_path, "bones[", pchan_name, sizeof(pchan_name))) {
continue;
}
/* only if bone name matches too...
* NOTE: this will do constraints too, but those are ok to do here too?
*/
if (STREQ(pchan_name, pchan->name)) {
blender::animrig::insert_keyframe(bmain,
reports,
id,
act,
((fcu->grp) ? (fcu->grp->name) : (nullptr)),
fcu->rna_path,
fcu->array_index,
&anim_eval_context,
eBezTriple_KeyframeType(ts->keyframe_type),
flag);
}
/* only if bone name matches too...
* NOTE: this will do constraints too, but those are ok to do here too?
*/
if (STREQ(pchan_name, pose_channel->name)) {
blender::animrig::insert_keyframe(bmain,
reports,
id,
act,
((fcu->grp) ? (fcu->grp->name) : (nullptr)),
fcu->rna_path,
fcu->array_index,
&anim_eval_context,
eBezTriple_KeyframeType(ts->keyframe_type),
flag);
}
continue;
}
return;
}
Main *bmain = CTX_data_main(C);
std::string rotation_rna_path = get_rotation_mode_path(eRotationModes(pchan->rotmode));
Vector<std::string> rna_paths = {"location", rotation_rna_path, "scale"};
for (PointerRNA &ptr : sources) {
insert_key_rna(&ptr,
rna_paths,
scene_frame,
flag,
eBezTriple_KeyframeType(scene->toolsettings->keyframe_type),
bmain,
reports);
}
for (PointerRNA &ptr : sources) {
insert_key_rna(&ptr,
rna_paths,
scene_frame,
flag,
eBezTriple_KeyframeType(scene->toolsettings->keyframe_type),
bmain,
reports);
}
}

View File

@ -39,6 +39,7 @@
#include "ANIM_bone_collections.hh"
#include "ANIM_keyframing.hh"
#include "ANIM_rna.hh"
#include "transform.hh"
#include "transform_orientations.hh"
@ -1266,6 +1267,81 @@ static void restoreMirrorPoseBones(TransDataContainer *tc)
}
}
/* Given the transform mode `tmode` return a Vector of RNA paths that were possibly modified during
* that transformation. */
static blender::Vector<std::string> get_affected_rna_paths_from_transform_mode(
const eTfmMode tmode,
ToolSettings *toolsettings,
const blender::StringRef rotation_path,
const bool targetless_ik)
{
blender::Vector<std::string> rna_paths;
switch (tmode) {
case TFM_TRANSLATION:
if (targetless_ik) {
rna_paths.append(rotation_path);
}
else {
rna_paths.append("location");
}
break;
case TFM_ROTATION:
case TFM_TRACKBALL:
if (ELEM(toolsettings->transform_pivot_point, V3D_AROUND_CURSOR, V3D_AROUND_ACTIVE)) {
rna_paths.append("location");
}
if ((toolsettings->transform_flag & SCE_XFORM_AXIS_ALIGN) == 0) {
rna_paths.append(rotation_path);
}
break;
case TFM_RESIZE:
if (ELEM(toolsettings->transform_pivot_point, V3D_AROUND_CURSOR, V3D_AROUND_ACTIVE)) {
rna_paths.append("location");
}
if ((toolsettings->transform_flag & SCE_XFORM_AXIS_ALIGN) == 0) {
rna_paths.append("scale");
}
break;
default:
break;
}
return rna_paths;
}
static void autokeyframe_pose(
bContext *C, Scene *scene, Object *ob, short targetless_ik, const eTfmMode tmode)
{
bPose *pose = ob->pose;
LISTBASE_FOREACH (bPoseChannel *, pchan, &pose->chanbase) {
if ((pchan->bone->flag & BONE_TRANSFORM) == 0 &&
!((pose->flag & POSE_MIRROR_EDIT) && (pchan->bone->flag & BONE_TRANSFORM_MIRROR)))
{
continue;
}
blender::Vector<std::string> rna_paths;
const blender::StringRef rotation_path = blender::animrig::get_rotation_mode_path(
eRotationModes(pchan->rotmode));
if (blender::animrig::is_autokey_flag(scene, AUTOKEY_FLAG_INSERTNEEDED)) {
rna_paths = get_affected_rna_paths_from_transform_mode(
tmode, scene->toolsettings, rotation_path, targetless_ik);
}
else {
rna_paths = {"location", rotation_path, "scale"};
}
blender::animrig::autokeyframe_pose_channel(
C, scene, ob, pchan, rna_paths.as_span(), targetless_ik);
}
}
static void recalcData_pose(TransInfo *t)
{
if (t->mode == TFM_BONESIZE) {
@ -1326,7 +1402,7 @@ static void recalcData_pose(TransInfo *t)
int targetless_ik = (t->flag & T_AUTOIK);
animrecord_check_state(t, &ob->id);
blender::animrig::autokeyframe_pose(t->context, t->scene, ob, targetless_ik);
autokeyframe_pose(t->context, t->scene, ob, targetless_ik, t->mode);
}
if (motionpath_need_update_pose(t->scene, ob)) {
@ -1589,7 +1665,7 @@ static void special_aftertrans_update__pose(bContext *C, TransInfo *t)
/* automatic inserting of keys and unkeyed tagging -
* only if transform wasn't canceled (or TFM_DUMMY) */
if (!canceled && (t->mode != TFM_DUMMY)) {
blender::animrig::autokeyframe_pose(C, t->scene, ob, targetless_ik);
autokeyframe_pose(C, t->scene, ob, targetless_ik, t->mode);
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
}
else {

View File

@ -25,6 +25,7 @@
#include "BKE_scene.h"
#include "ANIM_keyframing.hh"
#include "ANIM_rna.hh"
#include "ED_keyframing.hh"
#include "ED_object.hh"
@ -755,6 +756,80 @@ static bool motionpath_need_update_object(Scene *scene, Object *ob)
/** \name Recalc Data object
* \{ */
/* Given the transform mode `tmode` return a Vector of RNA paths that were possibly modified during
* that transformation. */
static blender::Vector<std::string> get_affected_rna_paths_from_transform_mode(
const eTfmMode tmode,
Scene *scene,
ViewLayer *view_layer,
Object *ob,
const blender::StringRef rotation_path)
{
blender::Vector<std::string> rna_paths;
switch (tmode) {
case TFM_TRANSLATION:
rna_paths.append("location");
break;
case TFM_ROTATION:
case TFM_TRACKBALL:
if (scene->toolsettings->transform_pivot_point == V3D_AROUND_ACTIVE) {
BKE_view_layer_synced_ensure(scene, view_layer);
if (ob != BKE_view_layer_active_object_get(view_layer)) {
rna_paths.append("location");
}
}
else if (scene->toolsettings->transform_pivot_point == V3D_AROUND_CURSOR) {
rna_paths.append("location");
}
if ((scene->toolsettings->transform_flag & SCE_XFORM_AXIS_ALIGN) == 0) {
rna_paths.append(rotation_path);
}
break;
case TFM_RESIZE:
if (scene->toolsettings->transform_pivot_point == V3D_AROUND_ACTIVE) {
BKE_view_layer_synced_ensure(scene, view_layer);
if (ob != BKE_view_layer_active_object_get(view_layer)) {
rna_paths.append("location");
}
}
else if (scene->toolsettings->transform_pivot_point == V3D_AROUND_CURSOR) {
rna_paths.append("location");
}
if ((scene->toolsettings->transform_flag & SCE_XFORM_AXIS_ALIGN) == 0) {
rna_paths.append("scale");
}
break;
default:
rna_paths.append("location");
rna_paths.append(rotation_path);
rna_paths.append("scale");
}
return rna_paths;
}
static void autokeyframe_object(bContext *C, Scene *scene, Object *ob, const eTfmMode tmode)
{
blender::Vector<std::string> rna_paths;
ViewLayer *view_layer = CTX_data_view_layer(C);
const blender::StringRef rotation_path = blender::animrig::get_rotation_mode_path(
eRotationModes(ob->rotmode));
if (blender::animrig::is_autokey_flag(scene, AUTOKEY_FLAG_INSERTNEEDED)) {
rna_paths = get_affected_rna_paths_from_transform_mode(
tmode, scene, view_layer, ob, rotation_path);
}
else {
rna_paths = {"location", rotation_path, "scale"};
}
blender::animrig::autokeyframe_object(C, scene, ob, rna_paths.as_span());
}
static void recalcData_objects(TransInfo *t)
{
bool motionpath_update = false;
@ -780,7 +855,7 @@ static void recalcData_objects(TransInfo *t)
* (FPoints) instead of keyframes? */
if ((t->animtimer) && blender::animrig::is_autokey_on(t->scene)) {
animrecord_check_state(t, &ob->id);
blender::animrig::autokeyframe_object(t->context, t->scene, ob);
autokeyframe_object(t->context, t->scene, ob, t->mode);
}
motionpath_update |= motionpath_need_update_object(t->scene, ob);
@ -855,7 +930,7 @@ static void special_aftertrans_update__object(bContext *C, TransInfo *t)
/* Set auto-key if necessary. */
if (!canceled) {
blender::animrig::autokeyframe_object(C, t->scene, ob);
autokeyframe_object(C, t->scene, ob, t->mode);
}
motionpath_update |= motionpath_need_update_object(t->scene, ob);