Fix: Issue with Cycle aware keying when inserting second key
Caused by #113504 While basic cycle aware keying works, there is a special case when inserting the first key. In that case, after the key has been inserted, it is duplicated and moved around so the FCurve range from first to last key is exactly the range of the action. It also auto-creates the Cycle modifier on the FCurve . Fix the issue by calling the function that does the key duplication and cover with unit tests. Pull Request: https://projects.blender.org/blender/blender/pulls/116943
This commit is contained in:

committed by
Christoph Lendenfeld

parent
bc5a8f33d8
commit
afa4391eeb
@@ -197,6 +197,7 @@ bool autokeyframe_property(bContext *C,
|
||||
int insert_key_action(Main *bmain,
|
||||
bAction *action,
|
||||
PointerRNA *ptr,
|
||||
PropertyRNA *prop,
|
||||
const std::string &rna_path,
|
||||
float frame,
|
||||
Span<float> values,
|
||||
|
@@ -466,7 +466,7 @@ static bool insert_keyframe_fcurve_value(Main *bmain,
|
||||
|
||||
const bool success = insert_keyframe_value(fcu, fcurve_frame, curval, keytype, flag);
|
||||
|
||||
if (!success) {
|
||||
if (!success && reports != nullptr) {
|
||||
BKE_reportf(reports,
|
||||
RPT_ERROR,
|
||||
"Failed to insert keys on F-Curve with path '%s[%d]', ensure that it is not "
|
||||
@@ -858,6 +858,7 @@ int clear_keyframe(Main *bmain,
|
||||
int insert_key_action(Main *bmain,
|
||||
bAction *action,
|
||||
PointerRNA *ptr,
|
||||
PropertyRNA *prop,
|
||||
const std::string &rna_path,
|
||||
const float frame,
|
||||
const Span<float> values,
|
||||
@@ -879,10 +880,18 @@ int insert_key_action(Main *bmain,
|
||||
int property_array_index = 0;
|
||||
int inserted_keys = 0;
|
||||
for (float value : values) {
|
||||
FCurve *fcurve = action_fcurve_ensure(
|
||||
bmain, action, group.c_str(), ptr, rna_path.c_str(), property_array_index);
|
||||
const bool inserted_key = insert_keyframe_value(
|
||||
fcurve, frame, value, key_type, insert_key_flag);
|
||||
const bool inserted_key = insert_keyframe_fcurve_value(bmain,
|
||||
nullptr,
|
||||
ptr,
|
||||
prop,
|
||||
action,
|
||||
group.c_str(),
|
||||
rna_path.c_str(),
|
||||
property_array_index,
|
||||
frame,
|
||||
value,
|
||||
key_type,
|
||||
insert_key_flag);
|
||||
if (inserted_key) {
|
||||
inserted_keys++;
|
||||
}
|
||||
@@ -955,6 +964,7 @@ void insert_key_rna(PointerRNA *rna_pointer,
|
||||
insert_key_count += insert_key_action(bmain,
|
||||
action,
|
||||
rna_pointer,
|
||||
prop,
|
||||
rna_path_id_to_prop->c_str(),
|
||||
nla_frame,
|
||||
rna_values.as_span(),
|
||||
|
@@ -204,11 +204,14 @@ class VisualKeyingTest(AbstractKeyframingTest, unittest.TestCase):
|
||||
class CycleAwareKeyingTest(AbstractKeyframingTest, unittest.TestCase):
|
||||
""" Check if cycle aware keying remaps the keyframes correctly and adds fcurve modifiers. """
|
||||
|
||||
def test_insert_location_cycle_aware(self):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
bpy.context.scene.tool_settings.use_keyframe_cycle_aware = True
|
||||
|
||||
def test_insert_by_name(self):
|
||||
# In order to make cycle aware keying work, the action needs to be created and have the
|
||||
# frame_range set plus the use_frame_range flag set to True.
|
||||
keyed_object = _create_animation_object()
|
||||
bpy.context.scene.tool_settings.use_keyframe_cycle_aware = True
|
||||
|
||||
with bpy.context.temp_override(**_get_view3d_context()):
|
||||
bpy.ops.anim.keyframe_insert_by_name(type="Location")
|
||||
@@ -216,18 +219,16 @@ class CycleAwareKeyingTest(AbstractKeyframingTest, unittest.TestCase):
|
||||
action = keyed_object.animation_data.action
|
||||
action.use_cyclic = True
|
||||
action.use_frame_range = True
|
||||
cyclic_range_end = 20
|
||||
action.frame_range = [1, cyclic_range_end]
|
||||
action.frame_range = [1, 20]
|
||||
|
||||
with bpy.context.temp_override(**_get_view3d_context()):
|
||||
bpy.context.scene.frame_set(5)
|
||||
bpy.ops.anim.keyframe_insert_by_name(type="Location")
|
||||
|
||||
# Will be mapped to frame 3.
|
||||
# This will insert the key based on the user preference settings.
|
||||
bpy.context.preferences.edit.key_insert_channels = {"LOCATION"}
|
||||
bpy.context.scene.frame_set(22)
|
||||
bpy.ops.anim.keyframe_insert()
|
||||
bpy.ops.anim.keyframe_insert_by_name(type="Location")
|
||||
|
||||
# Will be mapped to frame 9.
|
||||
bpy.context.scene.frame_set(-10)
|
||||
@@ -248,6 +249,39 @@ class CycleAwareKeyingTest(AbstractKeyframingTest, unittest.TestCase):
|
||||
# All fcurves should have a cycles modifier.
|
||||
self.assertTrue(fcurve.modifiers[0].type == "CYCLES")
|
||||
|
||||
def test_insert_key(self):
|
||||
keyed_object = _create_animation_object()
|
||||
|
||||
bpy.context.preferences.edit.key_insert_channels = {'LOCATION'}
|
||||
|
||||
with bpy.context.temp_override(**_get_view3d_context()):
|
||||
bpy.ops.anim.keyframe_insert()
|
||||
|
||||
action = keyed_object.animation_data.action
|
||||
action.use_cyclic = True
|
||||
action.use_frame_range = True
|
||||
action.frame_range = [1, 20]
|
||||
|
||||
with bpy.context.temp_override(**_get_view3d_context()):
|
||||
bpy.context.scene.frame_set(5)
|
||||
bpy.ops.anim.keyframe_insert()
|
||||
|
||||
# Will be mapped to frame 3.
|
||||
bpy.context.scene.frame_set(22)
|
||||
bpy.ops.anim.keyframe_insert()
|
||||
|
||||
expected_keys = [1, 3, 5, 20]
|
||||
|
||||
for fcurve in action.fcurves:
|
||||
self.assertEqual(len(fcurve.keyframe_points), len(expected_keys))
|
||||
key_index = 0
|
||||
for key in fcurve.keyframe_points:
|
||||
self.assertEqual(key.co.x, expected_keys[key_index])
|
||||
key_index += 1
|
||||
|
||||
# All fcurves should have a cycles modifier.
|
||||
self.assertTrue(fcurve.modifiers[0].type == "CYCLES")
|
||||
|
||||
|
||||
class AutoKeyframingTest(AbstractKeyframingTest, unittest.TestCase):
|
||||
|
||||
|
Reference in New Issue
Block a user