Merge branch 'blender-v4.1-release'
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "BLI_bitmap.h"
|
||||
#include "BLI_vector.hh"
|
||||
#include "DNA_anim_types.h"
|
||||
#include "ED_transform.hh"
|
||||
@@ -192,6 +193,8 @@ bool autokeyframe_property(bContext *C,
|
||||
* expected to be the size of the property array.
|
||||
* \param frame: is expected to be in the local time of the action, meaning it has to be NLA mapped
|
||||
* already.
|
||||
* \param keying_mask is expected to have the same size as `rna_path`. A false bit means that index
|
||||
* will be skipped.
|
||||
* \returns The number of keys inserted.
|
||||
*/
|
||||
int insert_key_action(Main *bmain,
|
||||
@@ -202,7 +205,8 @@ int insert_key_action(Main *bmain,
|
||||
float frame,
|
||||
Span<float> values,
|
||||
eInsertKeyFlags insert_key_flag,
|
||||
eBezTriple_KeyframeType key_type);
|
||||
eBezTriple_KeyframeType key_type,
|
||||
const BLI_bitmap *keying_mask);
|
||||
|
||||
/**
|
||||
* Insert keys to the ID of the given PointerRNA for the given RNA paths. Tries to create an
|
||||
@@ -215,6 +219,7 @@ void insert_key_rna(PointerRNA *rna_pointer,
|
||||
eInsertKeyFlags insert_key_flags,
|
||||
eBezTriple_KeyframeType key_type,
|
||||
Main *bmain,
|
||||
ReportList *reports);
|
||||
ReportList *reports,
|
||||
const AnimationEvalContext &anim_eval_context);
|
||||
|
||||
} // namespace blender::animrig
|
||||
|
||||
@@ -862,7 +862,8 @@ int insert_key_action(Main *bmain,
|
||||
const float frame,
|
||||
const Span<float> values,
|
||||
eInsertKeyFlags insert_key_flag,
|
||||
eBezTriple_KeyframeType key_type)
|
||||
eBezTriple_KeyframeType key_type,
|
||||
const BLI_bitmap *keying_mask)
|
||||
{
|
||||
BLI_assert(bmain != nullptr);
|
||||
BLI_assert(action != nullptr);
|
||||
@@ -879,6 +880,10 @@ int insert_key_action(Main *bmain,
|
||||
int property_array_index = 0;
|
||||
int inserted_keys = 0;
|
||||
for (float value : values) {
|
||||
if (!BLI_BITMAP_TEST_BOOL(keying_mask, property_array_index)) {
|
||||
property_array_index++;
|
||||
continue;
|
||||
}
|
||||
const bool inserted_key = insert_keyframe_fcurve_value(bmain,
|
||||
nullptr,
|
||||
ptr,
|
||||
@@ -924,7 +929,8 @@ void insert_key_rna(PointerRNA *rna_pointer,
|
||||
const eInsertKeyFlags insert_key_flags,
|
||||
const eBezTriple_KeyframeType key_type,
|
||||
Main *bmain,
|
||||
ReportList *reports)
|
||||
ReportList *reports,
|
||||
const AnimationEvalContext &anim_eval_context)
|
||||
{
|
||||
ID *id = rna_pointer->owner_id;
|
||||
bAction *action = id_action_ensure(bmain, id);
|
||||
@@ -938,6 +944,15 @@ void insert_key_rna(PointerRNA *rna_pointer,
|
||||
}
|
||||
|
||||
AnimData *adt = BKE_animdata_from_id(id);
|
||||
|
||||
/* Keyframing functions can deal with the nla_context being a nullptr. */
|
||||
ListBase nla_cache = {nullptr, nullptr};
|
||||
NlaKeyframingContext *nla_context = nullptr;
|
||||
if (adt && adt->action == action) {
|
||||
nla_context = BKE_animsys_get_nla_keyframing_context(
|
||||
&nla_cache, rna_pointer, adt, &anim_eval_context);
|
||||
}
|
||||
|
||||
const float nla_frame = BKE_nla_tweakedit_remap(adt, scene_frame, NLATIME_CONVERT_UNMAP);
|
||||
const bool visual_keyframing = insert_key_flags & INSERTKEY_MATRIX;
|
||||
|
||||
@@ -960,6 +975,16 @@ void insert_key_rna(PointerRNA *rna_pointer,
|
||||
prop);
|
||||
Vector<float> rna_values = get_keyframe_values(&ptr, prop, visual_keyframing);
|
||||
|
||||
BLI_bitmap *successful_remaps = BLI_BITMAP_NEW(rna_values.size(), __func__);
|
||||
BKE_animsys_nla_remap_keyframe_values(nla_context,
|
||||
rna_pointer,
|
||||
prop,
|
||||
rna_values.as_mutable_span(),
|
||||
-1,
|
||||
&anim_eval_context,
|
||||
nullptr,
|
||||
successful_remaps);
|
||||
|
||||
insert_key_count += insert_key_action(bmain,
|
||||
action,
|
||||
rna_pointer,
|
||||
@@ -968,7 +993,9 @@ void insert_key_rna(PointerRNA *rna_pointer,
|
||||
nla_frame,
|
||||
rna_values.as_span(),
|
||||
insert_key_flags,
|
||||
key_type);
|
||||
key_type,
|
||||
successful_remaps);
|
||||
MEM_freeN(successful_remaps);
|
||||
}
|
||||
|
||||
if (insert_key_count == 0) {
|
||||
|
||||
@@ -160,7 +160,8 @@ void autokeyframe_object(bContext *C, Scene *scene, Object *ob, Span<std::string
|
||||
flag,
|
||||
eBezTriple_KeyframeType(scene->toolsettings->keyframe_type),
|
||||
bmain,
|
||||
reports);
|
||||
reports,
|
||||
anim_eval_context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -285,7 +286,8 @@ void autokeyframe_pose_channel(bContext *C,
|
||||
flag,
|
||||
eBezTriple_KeyframeType(scene->toolsettings->keyframe_type),
|
||||
bmain,
|
||||
reports);
|
||||
reports,
|
||||
anim_eval_context);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2071,8 +2071,15 @@ static bool lib_override_library_resync(Main *bmain,
|
||||
|
||||
ID *id_root_reference = id_root->override_library->reference;
|
||||
ID *id;
|
||||
BKE_view_layer_synced_ensure(scene, view_layer);
|
||||
const Object *old_active_object = BKE_view_layer_active_object_get(view_layer);
|
||||
|
||||
const Object *old_active_object = nullptr;
|
||||
if (view_layer) {
|
||||
BKE_view_layer_synced_ensure(scene, view_layer);
|
||||
old_active_object = BKE_view_layer_active_object_get(view_layer);
|
||||
}
|
||||
else {
|
||||
BKE_scene_view_layers_synced_ensure(scene);
|
||||
}
|
||||
|
||||
if (id_root_reference->tag & LIB_TAG_MISSING) {
|
||||
BKE_reportf(reports != nullptr ? reports->reports : nullptr,
|
||||
|
||||
@@ -442,9 +442,9 @@ uint64_t BKE_library_id_can_use_filter_id(const ID *owner_id, const bool include
|
||||
/* Could be more specific, but simpler to just always say 'yes' here. */
|
||||
return FILTER_ID_ALL;
|
||||
case ID_ME:
|
||||
return FILTER_ID_ME | FILTER_ID_MA | FILTER_ID_IM;
|
||||
return FILTER_ID_ME | FILTER_ID_MA | FILTER_ID_IM | FILTER_ID_KE;
|
||||
case ID_CU_LEGACY:
|
||||
return FILTER_ID_OB | FILTER_ID_MA | FILTER_ID_VF;
|
||||
return FILTER_ID_OB | FILTER_ID_MA | FILTER_ID_VF | FILTER_ID_KE;
|
||||
case ID_MB:
|
||||
return FILTER_ID_MA;
|
||||
case ID_MA:
|
||||
@@ -454,7 +454,7 @@ uint64_t BKE_library_id_can_use_filter_id(const ID *owner_id, const bool include
|
||||
case ID_LT:
|
||||
return 0;
|
||||
case ID_LA:
|
||||
return FILTER_ID_TE;
|
||||
return FILTER_ID_TE | FILTER_ID_KE;
|
||||
case ID_CA:
|
||||
return FILTER_ID_OB | FILTER_ID_IM;
|
||||
case ID_KE:
|
||||
|
||||
@@ -345,6 +345,9 @@ static int insert_key(bContext *C, wmOperator *op)
|
||||
const eInsertKeyFlags insert_key_flags = ANIM_get_keyframing_flags(scene);
|
||||
const eBezTriple_KeyframeType key_type = eBezTriple_KeyframeType(
|
||||
scene->toolsettings->keyframe_type);
|
||||
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
|
||||
const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(
|
||||
depsgraph, BKE_scene_frame_get(scene));
|
||||
|
||||
LISTBASE_FOREACH (CollectionPointerLink *, collection_ptr_link, &selection) {
|
||||
ID *selected_id = collection_ptr_link->ptr.owner_id;
|
||||
@@ -355,8 +358,14 @@ static int insert_key(bContext *C, wmOperator *op)
|
||||
PointerRNA id_ptr = collection_ptr_link->ptr;
|
||||
Vector<std::string> rna_paths = construct_rna_paths(&collection_ptr_link->ptr);
|
||||
|
||||
animrig::insert_key_rna(
|
||||
&id_ptr, rna_paths.as_span(), scene_frame, insert_key_flags, key_type, bmain, op->reports);
|
||||
animrig::insert_key_rna(&id_ptr,
|
||||
rna_paths.as_span(),
|
||||
scene_frame,
|
||||
insert_key_flags,
|
||||
key_type,
|
||||
bmain,
|
||||
op->reports,
|
||||
anim_eval_context);
|
||||
}
|
||||
|
||||
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_ADDED, nullptr);
|
||||
|
||||
@@ -36,6 +36,19 @@ def _get_view3d_context():
|
||||
return ctx
|
||||
|
||||
|
||||
def _get_nla_context():
|
||||
ctx = bpy.context.copy()
|
||||
|
||||
for area in bpy.context.window.screen.areas:
|
||||
if area.type != 'NLA_EDITOR':
|
||||
continue
|
||||
ctx['area'] = area
|
||||
ctx['space'] = area.spaces.active
|
||||
break
|
||||
|
||||
return ctx
|
||||
|
||||
|
||||
def _create_animation_object():
|
||||
anim_object = bpy.data.objects.new("anim_object", None)
|
||||
# Ensure that the rotation mode is correct so we can check against rotation_euler
|
||||
@@ -487,6 +500,125 @@ class InsertNeededTest(AbstractKeyframingTest, unittest.TestCase):
|
||||
self.assertEqual(expected_keys[fcurve.data_path][fcurve.array_index], len(fcurve.keyframe_points))
|
||||
|
||||
|
||||
def _create_nla_anim_object():
|
||||
"""
|
||||
Creates an object with 3 NLA tracks each with a strip that has its own action.
|
||||
The middle layer is additive.
|
||||
Creates a key on frame 0 and frame 10 for each of them.
|
||||
The values are:
|
||||
top: 0, 0
|
||||
add: 0, 1
|
||||
base: 0, 1
|
||||
"""
|
||||
anim_object = bpy.data.objects.new("anim_object", None)
|
||||
bpy.context.scene.collection.objects.link(anim_object)
|
||||
bpy.context.view_layer.objects.active = anim_object
|
||||
anim_object.select_set(True)
|
||||
anim_object.animation_data_create()
|
||||
|
||||
track = anim_object.animation_data.nla_tracks.new()
|
||||
track.name = "base"
|
||||
action_base = bpy.data.actions.new(name="action_base")
|
||||
fcu = action_base.fcurves.new(data_path="location", index=0)
|
||||
fcu.keyframe_points.insert(0, value=0).interpolation = 'LINEAR'
|
||||
fcu.keyframe_points.insert(10, value=1).interpolation = 'LINEAR'
|
||||
track.strips.new("base_strip", 0, action_base)
|
||||
|
||||
track = anim_object.animation_data.nla_tracks.new()
|
||||
track.name = "add"
|
||||
action_add = bpy.data.actions.new(name="action_add")
|
||||
fcu = action_add.fcurves.new(data_path="location", index=0)
|
||||
fcu.keyframe_points.insert(0, value=0).interpolation = 'LINEAR'
|
||||
fcu.keyframe_points.insert(10, value=1).interpolation = 'LINEAR'
|
||||
strip = track.strips.new("add_strip", 0, action_add)
|
||||
strip.blend_type = "ADD"
|
||||
|
||||
track = anim_object.animation_data.nla_tracks.new()
|
||||
track.name = "top"
|
||||
action_top = bpy.data.actions.new(name="action_top")
|
||||
fcu = action_top.fcurves.new(data_path="location", index=0)
|
||||
fcu.keyframe_points.insert(0, value=0).interpolation = 'LINEAR'
|
||||
fcu.keyframe_points.insert(10, value=0).interpolation = 'LINEAR'
|
||||
track.strips.new("top_strip", 0, action_top)
|
||||
|
||||
return anim_object
|
||||
|
||||
|
||||
class NlaInsertTest(AbstractKeyframingTest, unittest.TestCase):
|
||||
"""
|
||||
Testing inserting keys into an NLA stack.
|
||||
The system is expected to remap the inserted values based on the strips blend_type.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
bpy.context.preferences.edit.key_insert_channels = {'LOCATION'}
|
||||
# Change one area to the NLA so we can call operators in it.
|
||||
# Assumes there is at least one editor in the blender default startup file that is not the 3D viewport.
|
||||
for area in bpy.context.window.screen.areas:
|
||||
if area.type == 'VIEW_3D':
|
||||
continue
|
||||
area.type = "NLA_EDITOR"
|
||||
break
|
||||
|
||||
def test_insert_failure(self):
|
||||
# If the topmost track is set to "REPLACE" the system will fail
|
||||
# when trying to insert keys into a layer beneath.
|
||||
nla_anim_object = _create_nla_anim_object()
|
||||
tracks = nla_anim_object.animation_data.nla_tracks
|
||||
|
||||
with bpy.context.temp_override(**_get_nla_context()):
|
||||
bpy.ops.nla.select_all(action="DESELECT")
|
||||
tracks.active = tracks["base"]
|
||||
tracks["base"].strips[0].select = True
|
||||
bpy.ops.nla.tweakmode_enter(use_upper_stack_evaluation=True)
|
||||
|
||||
with bpy.context.temp_override(**_get_view3d_context()):
|
||||
bpy.context.scene.frame_set(5)
|
||||
bpy.ops.anim.keyframe_insert()
|
||||
|
||||
base_action = bpy.data.actions["action_base"]
|
||||
# Location X should not have been able to insert a keyframe because the top strip is overriding the result completely,
|
||||
# making it impossible to calculate which value should be inserted.
|
||||
self.assertEqual(len(base_action.fcurves.find("location", index=0).keyframe_points), 2)
|
||||
# Location Y and Z will go through since they have not been defined in the action of the top strip.
|
||||
self.assertEqual(len(base_action.fcurves.find("location", index=1).keyframe_points), 1)
|
||||
self.assertEqual(len(base_action.fcurves.find("location", index=2).keyframe_points), 1)
|
||||
|
||||
def test_insert_additive(self):
|
||||
nla_anim_object = _create_nla_anim_object()
|
||||
tracks = nla_anim_object.animation_data.nla_tracks
|
||||
|
||||
# This leaves the additive track as the topmost track with influence
|
||||
tracks["top"].mute = True
|
||||
|
||||
with bpy.context.temp_override(**_get_nla_context()):
|
||||
bpy.ops.nla.select_all(action="DESELECT")
|
||||
tracks.active = tracks["base"]
|
||||
tracks["base"].strips[0].select = True
|
||||
bpy.ops.nla.tweakmode_enter(use_upper_stack_evaluation=True)
|
||||
|
||||
# Inserting over the existing keyframe.
|
||||
bpy.context.scene.frame_set(10)
|
||||
with bpy.context.temp_override(**_get_view3d_context()):
|
||||
bpy.ops.anim.keyframe_insert()
|
||||
|
||||
base_action = bpy.data.actions["action_base"]
|
||||
# This should have added keys to Y and Z but not X.
|
||||
# X already had two keys from the file setup.
|
||||
self.assertEqual(len(base_action.fcurves.find("location", index=0).keyframe_points), 2)
|
||||
self.assertEqual(len(base_action.fcurves.find("location", index=1).keyframe_points), 1)
|
||||
self.assertEqual(len(base_action.fcurves.find("location", index=2).keyframe_points), 1)
|
||||
|
||||
# The keyframe value should not be changed even though the position of the
|
||||
# object is modified by the additive layer.
|
||||
self.assertAlmostEqual(nla_anim_object.location.x, 2.0, 8)
|
||||
fcurve_loc_x = base_action.fcurves.find("location", index=0)
|
||||
self.assertAlmostEqual(fcurve_loc_x.keyframe_points[-1].co[1], 1.0, 8)
|
||||
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
global args
|
||||
import argparse
|
||||
|
||||
Reference in New Issue
Block a user