Anim: Insert keyframes without keying sets #113504
@ -165,6 +165,7 @@ const UserDef U_default = {
|
||||
.glalphaclip = 0.004,
|
||||
.autokey_mode = (AUTOKEY_MODE_NORMAL & ~AUTOKEY_ON),
|
||||
.autokey_flag = AUTOKEY_FLAG_XYZ2RGB,
|
||||
.key_insert_channels = USER_ANIM_KEY_CHANNEL_LOCATION,
|
||||
.animation_flag = USER_ANIM_HIGH_QUALITY_DRAWING,
|
||||
.text_render = 0,
|
||||
.navigation_mode = VIEW_NAVIGATION_WALK,
|
||||
|
@ -4684,7 +4684,7 @@ def km_object_mode(params):
|
||||
("object.join", {"type": 'J', "value": 'PRESS', "ctrl": True}, None),
|
||||
("wm.context_toggle", {"type": 'PERIOD', "value": 'PRESS', "ctrl": True},
|
||||
{"properties": [("data_path", 'tool_settings.use_transform_data_origin')]}),
|
||||
("anim.keyframe_insert_menu", {"type": 'I', "value": 'PRESS'}, None),
|
||||
("anim.keyframe_insert", {"type": 'I', "value": 'PRESS'}, None),
|
||||
("anim.keyframe_delete_v3d", {"type": 'I', "value": 'PRESS', "alt": True}, None),
|
||||
("anim.keying_set_active_set", {"type": 'I', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, None),
|
||||
("collection.create", {"type": 'G', "value": 'PRESS', "ctrl": True}, None),
|
||||
@ -4823,7 +4823,7 @@ def km_pose(params):
|
||||
op_menu("VIEW3D_MT_bone_collections", {"type": 'M', "value": 'PRESS', "shift": True}),
|
||||
("armature.move_to_collection", {"type": 'M', "value": 'PRESS'}, None),
|
||||
("transform.bbone_resize", {"type": 'S', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, None),
|
||||
("anim.keyframe_insert_menu", {"type": 'I', "value": 'PRESS'}, None),
|
||||
("anim.keyframe_insert", {"type": 'I', "value": 'PRESS'}, None),
|
||||
("anim.keyframe_delete_v3d", {"type": 'I', "value": 'PRESS', "alt": True}, None),
|
||||
("anim.keying_set_active_set", {"type": 'I', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, None),
|
||||
("pose.push", {"type": 'E', "value": 'PRESS', "ctrl": True}, None),
|
||||
|
@ -3225,7 +3225,7 @@ def km_pose(params):
|
||||
("pose.select_hierarchy", {"type": 'DOWN_ARROW', "value": 'PRESS', "shift": True, "repeat": True},
|
||||
{"properties": [("direction", 'CHILD'), ("extend", True)]}),
|
||||
("pose.select_linked", {"type": 'L', "value": 'PRESS', "ctrl": True}, None),
|
||||
("anim.keyframe_insert_menu", {"type": 'S', "value": 'PRESS', "shift": True}, None),
|
||||
("anim.keyframe_insert", {"type": 'S', "value": 'PRESS', "shift": True}, None),
|
||||
("anim.keyframe_insert_by_name", {"type": 'S', "value": 'PRESS'},
|
||||
{"properties": [("type", 'LocRotScale')]}),
|
||||
("anim.keyframe_insert_by_name", {"type": 'W', "value": 'PRESS', "shift": True},
|
||||
@ -3297,7 +3297,7 @@ def km_object_mode(params):
|
||||
{"properties": [("use_global", True), ("confirm", False)]}),
|
||||
("object.duplicate_move", {"type": 'D', "value": 'PRESS', "ctrl": True}, None),
|
||||
# Keyframing
|
||||
("anim.keyframe_insert_menu", {"type": 'S', "value": 'PRESS', "shift": True}, None),
|
||||
("anim.keyframe_insert", {"type": 'S', "value": 'PRESS', "shift": True}, None),
|
||||
("anim.keyframe_insert_by_name", {"type": 'S', "value": 'PRESS'},
|
||||
{"properties": [("type", 'LocRotScale')]}),
|
||||
("anim.keyframe_insert_by_name", {"type": 'W', "value": 'PRESS', "shift": True},
|
||||
|
@ -576,6 +576,8 @@ class USERPREF_PT_animation_keyframes(AnimationPanel, CenterAlignMixIn, Panel):
|
||||
prefs = context.preferences
|
||||
edit = prefs.edit
|
||||
|
||||
layout.prop(edit, "key_insert_channels", expand=True)
|
||||
|
||||
col = layout.column()
|
||||
col.prop(edit, "use_visual_keying")
|
||||
col.prop(edit, "use_keyframe_insert_needed", text="Only Insert Needed")
|
||||
|
@ -2755,7 +2755,8 @@ class VIEW3D_MT_object_animation(Menu):
|
||||
def draw(self, _context):
|
||||
layout = self.layout
|
||||
|
||||
layout.operator("anim.keyframe_insert_menu", text="Insert Keyframe...")
|
||||
layout.operator("anim.keyframe_insert", text="Insert Keyframe")
|
||||
layout.operator("anim.keyframe_insert_menu", text="Insert Keyframe with Keying Set")
|
||||
layout.operator("anim.keyframe_delete_v3d", text="Delete Keyframes...")
|
||||
layout.operator("anim.keyframe_clear_v3d", text="Clear Keyframes...")
|
||||
layout.operator("anim.keying_set_active_set", text="Change Keying Set...")
|
||||
@ -3019,7 +3020,8 @@ class VIEW3D_MT_object_context_menu(Menu):
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.operator("anim.keyframe_insert_menu", text="Insert Keyframe...")
|
||||
layout.operator("anim.keyframe_insert", text="Insert Keyframe")
|
||||
layout.operator("anim.keyframe_insert_menu", text="Insert Keyframe with Keying Set")
|
||||
|
||||
layout.separator()
|
||||
|
||||
@ -4177,7 +4179,8 @@ class VIEW3D_MT_pose_context_menu(Menu):
|
||||
|
||||
layout.operator_context = 'INVOKE_REGION_WIN'
|
||||
|
||||
layout.operator("anim.keyframe_insert_menu", text="Insert Keyframe...")
|
||||
layout.operator("anim.keyframe_insert", text="Insert Keyframe")
|
||||
layout.operator("anim.keyframe_insert_menu", text="Insert Keyframe with Keying Set")
|
||||
|
||||
layout.separator()
|
||||
|
||||
|
@ -10,6 +10,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "BLI_vector.hh"
|
||||
#include "DNA_anim_types.h"
|
||||
#include "ED_transform.hh"
|
||||
#include "RNA_types.hh"
|
||||
@ -175,4 +178,20 @@ bool autokeyframe_property(bContext *C,
|
||||
|
||||
/** \} */
|
||||
|
||||
/**
|
||||
* Insert keys for the given rna_path in the given action. The length of the values Span is
|
||||
* 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.
|
||||
* \returns The number of keys inserted.
|
||||
*/
|
||||
int insert_key_action(Main *bmain,
|
||||
bAction *action,
|
||||
PointerRNA *ptr,
|
||||
const std::string &rna_path,
|
||||
float frame,
|
||||
const Span<float> values,
|
||||
eInsertKeyFlags insert_key_flag,
|
||||
eBezTriple_KeyframeType key_type);
|
||||
|
||||
} // namespace blender::animrig
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include <cfloat>
|
||||
#include <cmath>
|
||||
#include <string>
|
||||
|
||||
#include "ANIM_action.hh"
|
||||
#include "ANIM_animdata.hh"
|
||||
@ -38,6 +39,7 @@
|
||||
#include "RNA_access.hh"
|
||||
#include "RNA_define.hh"
|
||||
#include "RNA_path.hh"
|
||||
#include "RNA_prototypes.h"
|
||||
#include "RNA_types.hh"
|
||||
|
||||
#include "WM_api.hh"
|
||||
@ -965,4 +967,40 @@ int clear_keyframe(Main *bmain,
|
||||
return key_count;
|
||||
}
|
||||
|
||||
int insert_key_action(Main *bmain,
|
||||
bAction *action,
|
||||
PointerRNA *ptr,
|
||||
const std::string &rna_path,
|
||||
const float frame,
|
||||
const Span<float> values,
|
||||
eInsertKeyFlags insert_key_flag,
|
||||
eBezTriple_KeyframeType key_type)
|
||||
{
|
||||
BLI_assert(bmain != nullptr);
|
||||
BLI_assert(action != nullptr);
|
||||
|
||||
std::string group;
|
||||
if (ptr->type == &RNA_PoseBone) {
|
||||
bPoseChannel *pose_channel = static_cast<bPoseChannel *>(ptr->data);
|
||||
group = pose_channel->name;
|
||||
}
|
||||
else {
|
||||
group = "Object Transforms";
|
||||
}
|
||||
|
||||
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);
|
||||
if (inserted_key) {
|
||||
inserted_keys++;
|
||||
}
|
||||
property_array_index++;
|
||||
}
|
||||
return inserted_keys;
|
||||
}
|
||||
|
||||
} // namespace blender::animrig
|
||||
|
@ -916,6 +916,9 @@ void blo_do_versions_userdef(UserDef *userdef)
|
||||
*/
|
||||
{
|
||||
/* Keep this block, even when empty. */
|
||||
userdef->key_insert_channels = (USER_ANIM_KEY_CHANNEL_LOCATION |
|
||||
USER_ANIM_KEY_CHANNEL_ROTATION | USER_ANIM_KEY_CHANNEL_SCALE |
|
||||
USER_ANIM_KEY_CHANNEL_CUSTOM_PROPERTIES);
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (bTheme *, btheme, &userdef->themes) {
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
#include "DNA_ID.h"
|
||||
#include "DNA_action_types.h"
|
||||
#include "DNA_anim_types.h"
|
||||
#include "DNA_armature_types.h"
|
||||
@ -32,6 +33,7 @@
|
||||
#include "BKE_fcurve.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_idtype.h"
|
||||
#include "BKE_lib_id.h"
|
||||
#include "BKE_nla.h"
|
||||
#include "BKE_report.h"
|
||||
#include "BKE_scene.h"
|
||||
@ -48,6 +50,7 @@
|
||||
#include "ANIM_fcurve.hh"
|
||||
#include "ANIM_keyframing.hh"
|
||||
#include "ANIM_rna.hh"
|
||||
#include "ANIM_visualkey.hh"
|
||||
|
||||
#include "UI_interface.hh"
|
||||
#include "UI_resources.hh"
|
||||
@ -96,6 +99,9 @@ eInsertKeyFlags ANIM_get_keyframing_flags(Scene *scene, const bool use_autokey_m
|
||||
}
|
||||
|
||||
/* only if including settings from the autokeying mode... */
|
||||
/* TODO: The fact that this flag needs to be passed as true is confusing because it is not clear
|
||||
* why those two flags would be exclusive to autokeying. Refactor flags so they are separate
|
||||
* between normal keying and autokeying. */
|
||||
if (use_autokey_mode) {
|
||||
/* keyframing mode - only replace existing keyframes */
|
||||
if (is_autokey_mode(scene, AUTOKEY_MODE_EDITKEYS)) {
|
||||
@ -268,21 +274,14 @@ static bool modify_key_op_poll(bContext *C)
|
||||
|
||||
/* Insert Key Operator ------------------------ */
|
||||
|
||||
static int insert_key_exec(bContext *C, wmOperator *op)
|
||||
static int insert_key_with_keyingset(bContext *C, wmOperator *op, KeyingSet *ks)
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
Object *obedit = CTX_data_edit_object(C);
|
||||
bool ob_edit_mode = false;
|
||||
|
||||
const float cfra = BKE_scene_frame_get(scene);
|
||||
int num_channels;
|
||||
const bool confirm = op->flag & OP_IS_INVOKE;
|
||||
|
||||
KeyingSet *ks = keyingset_get_from_op_with_error(op, op->type->prop, scene);
|
||||
if (ks == nullptr) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
/* exit the edit mode to make sure that those object data properties that have been
|
||||
* updated since the last switching to the edit mode will be keyframed correctly
|
||||
*/
|
||||
@ -292,7 +291,7 @@ static int insert_key_exec(bContext *C, wmOperator *op)
|
||||
}
|
||||
|
||||
/* try to insert keyframes for the channels specified by KeyingSet */
|
||||
num_channels = ANIM_apply_keyingset(C, nullptr, ks, MODIFYKEY_MODE_INSERT, cfra);
|
||||
const int num_channels = ANIM_apply_keyingset(C, nullptr, ks, MODIFYKEY_MODE_INSERT, cfra);
|
||||
if (G.debug & G_DEBUG) {
|
||||
BKE_reportf(op->reports,
|
||||
RPT_INFO,
|
||||
@ -334,15 +333,225 @@ static int insert_key_exec(bContext *C, wmOperator *op)
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static blender::Vector<std::string> construct_rna_paths(PointerRNA *ptr)
|
||||
{
|
||||
eRotationModes rotation_mode;
|
||||
IDProperty *properties;
|
||||
blender::Vector<std::string> paths;
|
||||
|
||||
if (ptr->type == &RNA_PoseBone) {
|
||||
bPoseChannel *pchan = static_cast<bPoseChannel *>(ptr->data);
|
||||
rotation_mode = eRotationModes(pchan->rotmode);
|
||||
properties = pchan->prop;
|
||||
}
|
||||
else if (ptr->type == &RNA_Object) {
|
||||
Object *ob = static_cast<Object *>(ptr->data);
|
||||
rotation_mode = eRotationModes(ob->rotmode);
|
||||
properties = ob->id.properties;
|
||||
}
|
||||
else {
|
||||
/* Pointer type not supported. */
|
||||
return paths;
|
||||
}
|
||||
|
||||
eKeyInsertChannels insert_channel_flags = eKeyInsertChannels(U.key_insert_channels);
|
||||
if (insert_channel_flags & USER_ANIM_KEY_CHANNEL_LOCATION) {
|
||||
paths.append("location");
|
||||
}
|
||||
if (insert_channel_flags & USER_ANIM_KEY_CHANNEL_ROTATION) {
|
||||
switch (rotation_mode) {
|
||||
case ROT_MODE_QUAT:
|
||||
paths.append("rotation_quaternion");
|
||||
break;
|
||||
case ROT_MODE_AXISANGLE:
|
||||
paths.append("rotation_axis_angle");
|
||||
break;
|
||||
case ROT_MODE_XYZ:
|
||||
case ROT_MODE_XZY:
|
||||
case ROT_MODE_YXZ:
|
||||
case ROT_MODE_YZX:
|
||||
case ROT_MODE_ZXY:
|
||||
case ROT_MODE_ZYX:
|
||||
paths.append("rotation_euler");
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (insert_channel_flags & USER_ANIM_KEY_CHANNEL_SCALE) {
|
||||
paths.append("scale");
|
||||
}
|
||||
if (insert_channel_flags & USER_ANIM_KEY_CHANNEL_ROTATION_MODE) {
|
||||
paths.append("rotation_mode");
|
||||
}
|
||||
if (insert_channel_flags & USER_ANIM_KEY_CHANNEL_CUSTOM_PROPERTIES) {
|
||||
if (properties) {
|
||||
LISTBASE_FOREACH (IDProperty *, prop, &properties->data.group) {
|
||||
std::string name = prop->name;
|
||||
std::string rna_path = "[\"" + name + "\"]";
|
||||
paths.append(rna_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
return paths;
|
||||
}
|
||||
|
||||
static blender::Vector<float> get_keyframe_values(PointerRNA *ptr,
|
||||
PropertyRNA *prop,
|
||||
const bool visual_key)
|
||||
{
|
||||
using namespace blender;
|
||||
Vector<float> values;
|
||||
|
||||
if (visual_key && animrig::visualkey_can_use(ptr, prop)) {
|
||||
/* Visual-keying is only available for object and pchan datablocks, as
|
||||
* it works by keyframing using a value extracted from the final matrix
|
||||
* instead of using the kt system to extract a value.
|
||||
*/
|
||||
values = animrig::visualkey_get_values(ptr, prop);
|
||||
}
|
||||
else {
|
||||
values = animrig::get_rna_values(ptr, prop);
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
static void insert_key_rna(PointerRNA *rna_pointer,
|
||||
const blender::Span<std::string> rna_paths,
|
||||
const float scene_frame,
|
||||
const eInsertKeyFlags insert_key_flags,
|
||||
const eBezTriple_KeyframeType key_type,
|
||||
Main *bmain,
|
||||
ReportList *reports)
|
||||
{
|
||||
using namespace blender;
|
||||
|
||||
ID *id = rna_pointer->owner_id;
|
||||
bAction *action = ED_id_action_ensure(bmain, id);
|
||||
if (action == nullptr) {
|
||||
BKE_reportf(reports,
|
||||
RPT_ERROR,
|
||||
"Could not insert keyframe, as this type does not support animation data (ID = "
|
||||
"%s)",
|
||||
id->name);
|
||||
return;
|
||||
}
|
||||
|
||||
AnimData *adt = BKE_animdata_from_id(id);
|
||||
const float nla_frame = BKE_nla_tweakedit_remap(adt, scene_frame, NLATIME_CONVERT_UNMAP);
|
||||
const bool visual_keyframing = insert_key_flags & INSERTKEY_MATRIX;
|
||||
|
||||
int insert_key_count = 0;
|
||||
for (const std::string &rna_path : rna_paths) {
|
||||
PointerRNA ptr;
|
||||
PropertyRNA *prop = nullptr;
|
||||
const bool path_resolved = RNA_path_resolve_property(
|
||||
rna_pointer, rna_path.c_str(), &ptr, &prop);
|
||||
if (!path_resolved) {
|
||||
BKE_reportf(reports,
|
||||
RPT_ERROR,
|
||||
"Could not insert keyframe, as this property does not exist (ID = "
|
||||
"%s, path = %s)",
|
||||
id->name,
|
||||
rna_path.c_str());
|
||||
continue;
|
||||
}
|
||||
std::string rna_path_id_to_prop = RNA_path_from_ID_to_property(&ptr, prop);
|
||||
Vector<float> rna_values = get_keyframe_values(&ptr, prop, visual_keyframing);
|
||||
|
||||
insert_key_count += animrig::insert_key_action(bmain,
|
||||
action,
|
||||
rna_pointer,
|
||||
rna_path_id_to_prop,
|
||||
nla_frame,
|
||||
rna_values.as_span(),
|
||||
insert_key_flags,
|
||||
key_type);
|
||||
}
|
||||
|
||||
if (insert_key_count == 0) {
|
||||
BKE_reportf(reports, RPT_ERROR, "Failed to insert any keys");
|
||||
}
|
||||
}
|
||||
|
||||
/* Fill the list with CollectionPointerLink depending on the mode of the context. */
|
||||
static bool get_selection(bContext *C, ListBase *r_selection)
|
||||
{
|
||||
const eContextObjectMode context_mode = CTX_data_mode_enum(C);
|
||||
|
||||
switch (context_mode) {
|
||||
case CTX_MODE_OBJECT: {
|
||||
CTX_data_selected_objects(C, r_selection);
|
||||
break;
|
||||
}
|
||||
case CTX_MODE_POSE: {
|
||||
CTX_data_selected_pose_bones(C, r_selection);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int insert_key(bContext *C, wmOperator *op)
|
||||
{
|
||||
using namespace blender;
|
||||
|
||||
ListBase selection = {nullptr, nullptr};
|
||||
const bool found_selection = get_selection(C, &selection);
|
||||
if (!found_selection) {
|
||||
BKE_reportf(op->reports, RPT_ERROR, "Unsupported context mode");
|
||||
}
|
||||
|
||||
Main *bmain = CTX_data_main(C);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
nathanvegdahl marked this conversation as resolved
Outdated
|
||||
const float scene_frame = BKE_scene_frame_get(scene);
|
||||
|
||||
/* Passing autokey mode as true because that is needed to get the cycle aware keying flag. */
|
||||
const bool use_autokey_mode = true;
|
||||
const eInsertKeyFlags insert_key_flags = ANIM_get_keyframing_flags(scene, use_autokey_mode);
|
||||
const eBezTriple_KeyframeType key_type = eBezTriple_KeyframeType(
|
||||
scene->toolsettings->keyframe_type);
|
||||
|
||||
LISTBASE_FOREACH (CollectionPointerLink *, collection_ptr_link, &selection) {
|
||||
ID *selected_id = collection_ptr_link->ptr.owner_id;
|
||||
if (!BKE_id_is_editable(bmain, selected_id)) {
|
||||
BKE_reportf(op->reports, RPT_ERROR, "'%s' is not editable", selected_id->name + 2);
|
||||
continue;
|
||||
}
|
||||
PointerRNA id_ptr = collection_ptr_link->ptr;
|
||||
Vector<std::string> rna_paths = construct_rna_paths(&collection_ptr_link->ptr);
|
||||
|
||||
insert_key_rna(
|
||||
&id_ptr, rna_paths.as_span(), scene_frame, insert_key_flags, key_type, bmain, op->reports);
|
||||
}
|
||||
|
||||
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_ADDED, nullptr);
|
||||
BLI_freelistN(&selection);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static int insert_key_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
/* Use the active keying set if there is one. */
|
||||
KeyingSet *ks = ANIM_keyingset_get_from_enum_type(scene, scene->active_keyingset);
|
||||
if (ks) {
|
||||
return insert_key_with_keyingset(C, op, ks);
|
||||
}
|
||||
return insert_key(C, op);
|
||||
}
|
||||
|
||||
void ANIM_OT_keyframe_insert(wmOperatorType *ot)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
|
||||
/* identifiers */
|
||||
ot->name = "Insert Keyframe";
|
||||
nathanvegdahl marked this conversation as resolved
Outdated
Nathan Vegdahl
commented
Typo: "using the either the" -> "using either the" Typo: "using the either the" -> "using either the"
|
||||
ot->idname = "ANIM_OT_keyframe_insert";
|
||||
ot->description =
|
||||
"Insert keyframes on the current frame for all properties in the specified Keying Set";
|
||||
"Insert keyframes on the current frame using either the active keying set, or the user "
|
||||
"preferences if no keying set is active";
|
||||
|
||||
/* callbacks */
|
||||
ot->exec = insert_key_exec;
|
||||
@ -350,13 +559,16 @@ void ANIM_OT_keyframe_insert(wmOperatorType *ot)
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
/* keyingset to use (dynamic enum) */
|
||||
prop = RNA_def_enum(
|
||||
ot->srna, "type", rna_enum_dummy_DEFAULT_items, 0, "Keying Set", "The Keying Set to use");
|
||||
RNA_def_enum_funcs(prop, ANIM_keying_sets_enum_itemf);
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN);
|
||||
ot->prop = prop;
|
||||
static int keyframe_insert_with_keyingset_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
KeyingSet *ks = keyingset_get_from_op_with_error(op, op->type->prop, scene);
|
||||
if (ks == nullptr) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
return insert_key_with_keyingset(C, op, ks);
|
||||
}
|
||||
|
||||
void ANIM_OT_keyframe_insert_by_name(wmOperatorType *ot)
|
||||
@ -369,7 +581,7 @@ void ANIM_OT_keyframe_insert_by_name(wmOperatorType *ot)
|
||||
ot->description = "Alternate access to 'Insert Keyframe' for keymaps to use";
|
||||
|
||||
/* callbacks */
|
||||
ot->exec = insert_key_exec;
|
||||
ot->exec = keyframe_insert_with_keyingset_exec;
|
||||
ot->poll = modify_key_op_poll;
|
||||
|
||||
/* flags */
|
||||
@ -459,7 +671,7 @@ void ANIM_OT_keyframe_insert_menu(wmOperatorType *ot)
|
||||
|
||||
/* callbacks */
|
||||
ot->invoke = insert_key_menu_invoke;
|
||||
ot->exec = insert_key_exec;
|
||||
ot->exec = keyframe_insert_with_keyingset_exec;
|
||||
ot->poll = ED_operator_areaactive;
|
||||
|
||||
/* flags */
|
||||
|
@ -955,6 +955,9 @@ typedef struct UserDef {
|
||||
short autokey_mode;
|
||||
/** Flags for autokeying. */
|
||||
short autokey_flag;
|
||||
/** Flags for which channels to insert keys at. */
|
||||
short key_insert_channels; // eKeyInsertChannels
|
||||
char _pad15[6];
|
||||
/** Flags for animation. */
|
||||
short animation_flag;
|
||||
|
||||
@ -1295,6 +1298,14 @@ typedef enum eAutokey_Flag {
|
||||
ANIMRECORD_FLAG_WITHNLA = (1 << 10),
|
||||
} eAutokey_Flag;
|
||||
|
||||
typedef enum eKeyInsertChannels {
|
||||
USER_ANIM_KEY_CHANNEL_LOCATION = (1 << 0),
|
||||
USER_ANIM_KEY_CHANNEL_ROTATION = (1 << 1),
|
||||
USER_ANIM_KEY_CHANNEL_SCALE = (1 << 2),
|
||||
USER_ANIM_KEY_CHANNEL_ROTATION_MODE = (1 << 3),
|
||||
USER_ANIM_KEY_CHANNEL_CUSTOM_PROPERTIES = (1 << 4),
|
||||
} eKeyInsertChannels;
|
||||
|
||||
/**
|
||||
* Animation flags
|
||||
* #UserDef.animation_flag, used for animation flags that aren't covered by more specific flags
|
||||
|
@ -149,6 +149,15 @@ static const EnumPropertyItem rna_enum_userdef_viewport_aa_items[] = {
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
|
||||
static const EnumPropertyItem rna_enum_key_insert_channels[] = {
|
||||
{USER_ANIM_KEY_CHANNEL_LOCATION, "LOCATION", 0, "Location", ""},
|
||||
{USER_ANIM_KEY_CHANNEL_ROTATION, "ROTATION", 0, "Rotation", ""},
|
||||
{USER_ANIM_KEY_CHANNEL_SCALE, "SCALE", 0, "Scale", ""},
|
||||
{USER_ANIM_KEY_CHANNEL_ROTATION_MODE, "ROTATE_MODE", 0, "Rotation Mode", ""},
|
||||
{USER_ANIM_KEY_CHANNEL_CUSTOM_PROPERTIES, "CUSTOM_PROPS", 0, "Custom Properties", ""},
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
|
||||
static const EnumPropertyItem rna_enum_preference_gpu_backend_items[] = {
|
||||
{GPU_BACKEND_OPENGL, "OPENGL", 0, "OpenGL", "Use OpenGL backend"},
|
||||
{GPU_BACKEND_METAL, "METAL", 0, "Metal", "Use Metal backend"},
|
||||
@ -5426,6 +5435,14 @@ static void rna_def_userdef_edit(BlenderRNA *brna)
|
||||
"Show warning indicators when transforming objects and bones if auto keying is enabled");
|
||||
|
||||
/* keyframing settings */
|
||||
prop = RNA_def_property(srna, "key_insert_channels", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_bitflag_sdna(prop, nullptr, "key_insert_channels");
|
||||
RNA_def_property_enum_items(prop, rna_enum_key_insert_channels);
|
||||
RNA_def_property_flag(prop, PROP_ENUM_FLAG);
|
||||
RNA_def_property_ui_text(prop,
|
||||
"Default Key Channels",
|
||||
"Which channels to insert keys at when no keying set is active");
|
||||
|
||||
prop = RNA_def_property(srna, "use_keyframe_insert_needed", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, nullptr, "autokey_flag", AUTOKEY_FLAG_INSERTNEEDED);
|
||||
RNA_def_property_ui_text(
|
||||
|
@ -51,6 +51,16 @@ def _insert_by_name_test(insert_key: str, expected_paths: list):
|
||||
return match
|
||||
|
||||
|
||||
def _insert_from_user_preference_test(enabled_user_pref_fields: set, expected_paths: list):
|
||||
keyed_object = _create_animation_object()
|
||||
bpy.context.preferences.edit.key_insert_channels = enabled_user_pref_fields
|
||||
with bpy.context.temp_override(**_get_view3d_context()):
|
||||
bpy.ops.anim.keyframe_insert()
|
||||
match = _fcurve_paths_match(keyed_object.animation_data.action.fcurves, expected_paths)
|
||||
bpy.data.objects.remove(keyed_object, do_unlink=True)
|
||||
return match
|
||||
|
||||
|
||||
def _get_keying_set(scene, name: str):
|
||||
return scene.keying_sets_all[scene.keying_sets_all.find(name)]
|
||||
|
||||
@ -90,6 +100,13 @@ class InsertKeyTest(AbstractKeyframingTest, unittest.TestCase):
|
||||
_insert_with_keying_set_test("Location, Rotation & Scale", ["location", "rotation_euler", "scale"])
|
||||
)
|
||||
|
||||
def test_insert_from_user_preferences(self):
|
||||
self.assertTrue(_insert_from_user_preference_test({"LOCATION"}, ["location"]))
|
||||
self.assertTrue(_insert_from_user_preference_test({"ROTATION"}, ["rotation_euler"]))
|
||||
self.assertTrue(_insert_from_user_preference_test({"SCALE"}, ["scale"]))
|
||||
self.assertTrue(_insert_from_user_preference_test(
|
||||
{"LOCATION", "ROTATION", "SCALE"}, ["location", "rotation_euler", "scale"]))
|
||||
|
||||
|
||||
class VisualKeyingTest(AbstractKeyframingTest, unittest.TestCase):
|
||||
""" Check if visual keying produces the correct keyframe values. """
|
||||
@ -130,7 +147,7 @@ class VisualKeyingTest(AbstractKeyframingTest, unittest.TestCase):
|
||||
bpy.data.objects.remove(target, do_unlink=True)
|
||||
bpy.data.objects.remove(constrained, do_unlink=True)
|
||||
|
||||
def test_visual_location_user_pref(self):
|
||||
def test_visual_location_user_pref_override(self):
|
||||
# When enabling the user preference setting,
|
||||
# the normal keying sets behave like their visual keying set counterpart.
|
||||
bpy.context.preferences.edit.use_visual_keying = True
|
||||
@ -151,6 +168,27 @@ class VisualKeyingTest(AbstractKeyframingTest, unittest.TestCase):
|
||||
bpy.data.objects.remove(constrained, do_unlink=True)
|
||||
bpy.context.preferences.edit.use_visual_keying = False
|
||||
|
||||
def test_visual_location_user_pref(self):
|
||||
target = _create_animation_object()
|
||||
t_value = 1
|
||||
target.location = (t_value, t_value, t_value)
|
||||
constrained = _create_animation_object()
|
||||
constraint = constrained.constraints.new("COPY_LOCATION")
|
||||
constraint.target = target
|
||||
|
||||
bpy.context.preferences.edit.use_visual_keying = True
|
||||
bpy.context.preferences.edit.key_insert_channels = {"LOCATION"}
|
||||
|
||||
with bpy.context.temp_override(**_get_view3d_context()):
|
||||
bpy.ops.anim.keyframe_insert()
|
||||
|
||||
for fcurve in constrained.animation_data.action.fcurves:
|
||||
self.assertEqual(fcurve.keyframe_points[0].co.y, t_value)
|
||||
|
||||
bpy.data.objects.remove(target, do_unlink=True)
|
||||
bpy.data.objects.remove(constrained, do_unlink=True)
|
||||
bpy.context.preferences.edit.use_visual_keying = False
|
||||
|
||||
|
||||
class CycleAwareKeyingTest(AbstractKeyframingTest, unittest.TestCase):
|
||||
""" Check if cycle aware keying remaps the keyframes correctly and adds fcurve modifiers. """
|
||||
@ -175,8 +213,10 @@ class CycleAwareKeyingTest(AbstractKeyframingTest, unittest.TestCase):
|
||||
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_by_name(type="Location")
|
||||
bpy.ops.anim.keyframe_insert()
|
||||
|
||||
# Will be mapped to frame 9.
|
||||
bpy.context.scene.frame_set(-10)
|
||||
|
Loading…
Reference in New Issue
Block a user
Maybe add a TODO here to investigate why that's the case? Because that seems pretty odd, and is perhaps a sign of something else that also needs some spring cleaning.
would the TODO make more sense if I added it in the
ANIM_get_keyframing_flags
function itself?by adding it below the current comment its a bit far away from where the actual issue is, and might get missed when the function is refactored/
Oh! Yeah, that would make a lot more sense. Good call.