Anim: Insert keyframes without keying sets #113504
|
@ -165,6 +165,7 @@ const UserDef U_default = {
|
||||||
.glalphaclip = 0.004,
|
.glalphaclip = 0.004,
|
||||||
.autokey_mode = (AUTOKEY_MODE_NORMAL & ~AUTOKEY_ON),
|
.autokey_mode = (AUTOKEY_MODE_NORMAL & ~AUTOKEY_ON),
|
||||||
.autokey_flag = AUTOKEY_FLAG_XYZ2RGB,
|
.autokey_flag = AUTOKEY_FLAG_XYZ2RGB,
|
||||||
|
.key_insert_channels = USER_ANIM_KEY_CHANNEL_LOCATION,
|
||||||
.animation_flag = USER_ANIM_HIGH_QUALITY_DRAWING,
|
.animation_flag = USER_ANIM_HIGH_QUALITY_DRAWING,
|
||||||
.text_render = 0,
|
.text_render = 0,
|
||||||
.navigation_mode = VIEW_NAVIGATION_WALK,
|
.navigation_mode = VIEW_NAVIGATION_WALK,
|
||||||
|
|
|
@ -4684,7 +4684,7 @@ def km_object_mode(params):
|
||||||
("object.join", {"type": 'J', "value": 'PRESS', "ctrl": True}, None),
|
("object.join", {"type": 'J', "value": 'PRESS', "ctrl": True}, None),
|
||||||
("wm.context_toggle", {"type": 'PERIOD', "value": 'PRESS', "ctrl": True},
|
("wm.context_toggle", {"type": 'PERIOD', "value": 'PRESS', "ctrl": True},
|
||||||
{"properties": [("data_path", 'tool_settings.use_transform_data_origin')]}),
|
{"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.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),
|
("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),
|
("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}),
|
op_menu("VIEW3D_MT_bone_collections", {"type": 'M', "value": 'PRESS', "shift": True}),
|
||||||
("armature.move_to_collection", {"type": 'M', "value": 'PRESS'}, None),
|
("armature.move_to_collection", {"type": 'M', "value": 'PRESS'}, None),
|
||||||
("transform.bbone_resize", {"type": 'S', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, 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.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),
|
("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),
|
("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},
|
("pose.select_hierarchy", {"type": 'DOWN_ARROW', "value": 'PRESS', "shift": True, "repeat": True},
|
||||||
{"properties": [("direction", 'CHILD'), ("extend", True)]}),
|
{"properties": [("direction", 'CHILD'), ("extend", True)]}),
|
||||||
("pose.select_linked", {"type": 'L', "value": 'PRESS', "ctrl": True}, None),
|
("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'},
|
("anim.keyframe_insert_by_name", {"type": 'S', "value": 'PRESS'},
|
||||||
{"properties": [("type", 'LocRotScale')]}),
|
{"properties": [("type", 'LocRotScale')]}),
|
||||||
("anim.keyframe_insert_by_name", {"type": 'W', "value": 'PRESS', "shift": True},
|
("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)]}),
|
{"properties": [("use_global", True), ("confirm", False)]}),
|
||||||
("object.duplicate_move", {"type": 'D', "value": 'PRESS', "ctrl": True}, None),
|
("object.duplicate_move", {"type": 'D', "value": 'PRESS', "ctrl": True}, None),
|
||||||
# Keyframing
|
# 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'},
|
("anim.keyframe_insert_by_name", {"type": 'S', "value": 'PRESS'},
|
||||||
{"properties": [("type", 'LocRotScale')]}),
|
{"properties": [("type", 'LocRotScale')]}),
|
||||||
("anim.keyframe_insert_by_name", {"type": 'W', "value": 'PRESS', "shift": True},
|
("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
|
prefs = context.preferences
|
||||||
edit = prefs.edit
|
edit = prefs.edit
|
||||||
|
|
||||||
|
layout.prop(edit, "key_insert_channels", expand=True)
|
||||||
|
|
||||||
col = layout.column()
|
col = layout.column()
|
||||||
col.prop(edit, "use_visual_keying")
|
col.prop(edit, "use_visual_keying")
|
||||||
col.prop(edit, "use_keyframe_insert_needed", text="Only Insert Needed")
|
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):
|
def draw(self, _context):
|
||||||
layout = self.layout
|
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_delete_v3d", text="Delete Keyframes...")
|
||||||
layout.operator("anim.keyframe_clear_v3d", text="Clear Keyframes...")
|
layout.operator("anim.keyframe_clear_v3d", text="Clear Keyframes...")
|
||||||
layout.operator("anim.keying_set_active_set", text="Change Keying Set...")
|
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.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()
|
layout.separator()
|
||||||
|
|
||||||
|
@ -4177,7 +4179,8 @@ class VIEW3D_MT_pose_context_menu(Menu):
|
||||||
|
|
||||||
layout.operator_context = 'INVOKE_REGION_WIN'
|
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()
|
layout.separator()
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,9 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "BLI_vector.hh"
|
||||||
#include "DNA_anim_types.h"
|
#include "DNA_anim_types.h"
|
||||||
#include "ED_transform.hh"
|
#include "ED_transform.hh"
|
||||||
#include "RNA_types.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
|
} // namespace blender::animrig
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
#include <cfloat>
|
#include <cfloat>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include "ANIM_action.hh"
|
#include "ANIM_action.hh"
|
||||||
#include "ANIM_animdata.hh"
|
#include "ANIM_animdata.hh"
|
||||||
|
@ -38,6 +39,7 @@
|
||||||
#include "RNA_access.hh"
|
#include "RNA_access.hh"
|
||||||
#include "RNA_define.hh"
|
#include "RNA_define.hh"
|
||||||
#include "RNA_path.hh"
|
#include "RNA_path.hh"
|
||||||
|
#include "RNA_prototypes.h"
|
||||||
#include "RNA_types.hh"
|
#include "RNA_types.hh"
|
||||||
|
|
||||||
#include "WM_api.hh"
|
#include "WM_api.hh"
|
||||||
|
@ -965,4 +967,40 @@ int clear_keyframe(Main *bmain,
|
||||||
return key_count;
|
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
|
} // namespace blender::animrig
|
||||||
|
|
|
@ -916,6 +916,9 @@ void blo_do_versions_userdef(UserDef *userdef)
|
||||||
*/
|
*/
|
||||||
{
|
{
|
||||||
/* Keep this block, even when empty. */
|
/* 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) {
|
LISTBASE_FOREACH (bTheme *, btheme, &userdef->themes) {
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
|
|
||||||
#include "BLT_translation.h"
|
#include "BLT_translation.h"
|
||||||
|
|
||||||
|
#include "DNA_ID.h"
|
||||||
#include "DNA_action_types.h"
|
#include "DNA_action_types.h"
|
||||||
#include "DNA_anim_types.h"
|
#include "DNA_anim_types.h"
|
||||||
#include "DNA_armature_types.h"
|
#include "DNA_armature_types.h"
|
||||||
|
@ -32,6 +33,7 @@
|
||||||
#include "BKE_fcurve.h"
|
#include "BKE_fcurve.h"
|
||||||
#include "BKE_global.h"
|
#include "BKE_global.h"
|
||||||
#include "BKE_idtype.h"
|
#include "BKE_idtype.h"
|
||||||
|
#include "BKE_lib_id.h"
|
||||||
#include "BKE_nla.h"
|
#include "BKE_nla.h"
|
||||||
#include "BKE_report.h"
|
#include "BKE_report.h"
|
||||||
#include "BKE_scene.h"
|
#include "BKE_scene.h"
|
||||||
|
@ -48,6 +50,7 @@
|
||||||
#include "ANIM_fcurve.hh"
|
#include "ANIM_fcurve.hh"
|
||||||
#include "ANIM_keyframing.hh"
|
#include "ANIM_keyframing.hh"
|
||||||
#include "ANIM_rna.hh"
|
#include "ANIM_rna.hh"
|
||||||
|
#include "ANIM_visualkey.hh"
|
||||||
|
|
||||||
#include "UI_interface.hh"
|
#include "UI_interface.hh"
|
||||||
#include "UI_resources.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... */
|
/* 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) {
|
if (use_autokey_mode) {
|
||||||
/* keyframing mode - only replace existing keyframes */
|
/* keyframing mode - only replace existing keyframes */
|
||||||
if (is_autokey_mode(scene, AUTOKEY_MODE_EDITKEYS)) {
|
if (is_autokey_mode(scene, AUTOKEY_MODE_EDITKEYS)) {
|
||||||
|
@ -268,21 +274,14 @@ static bool modify_key_op_poll(bContext *C)
|
||||||
|
|
||||||
/* Insert Key Operator ------------------------ */
|
/* 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);
|
Scene *scene = CTX_data_scene(C);
|
||||||
Object *obedit = CTX_data_edit_object(C);
|
Object *obedit = CTX_data_edit_object(C);
|
||||||
bool ob_edit_mode = false;
|
bool ob_edit_mode = false;
|
||||||
|
|
||||||
const float cfra = BKE_scene_frame_get(scene);
|
const float cfra = BKE_scene_frame_get(scene);
|
||||||
int num_channels;
|
|
||||||
const bool confirm = op->flag & OP_IS_INVOKE;
|
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
|
/* 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
|
* 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 */
|
/* 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) {
|
if (G.debug & G_DEBUG) {
|
||||||
BKE_reportf(op->reports,
|
BKE_reportf(op->reports,
|
||||||
RPT_INFO,
|
RPT_INFO,
|
||||||
|
@ -334,15 +333,225 @@ static int insert_key_exec(bContext *C, wmOperator *op)
|
||||||
return OPERATOR_FINISHED;
|
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)
|
void ANIM_OT_keyframe_insert(wmOperatorType *ot)
|
||||||
{
|
{
|
||||||
PropertyRNA *prop;
|
|
||||||
|
|
||||||
/* identifiers */
|
/* identifiers */
|
||||||
ot->name = "Insert Keyframe";
|
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->idname = "ANIM_OT_keyframe_insert";
|
||||||
ot->description =
|
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 */
|
/* callbacks */
|
||||||
ot->exec = insert_key_exec;
|
ot->exec = insert_key_exec;
|
||||||
|
@ -350,13 +559,16 @@ void ANIM_OT_keyframe_insert(wmOperatorType *ot)
|
||||||
|
|
||||||
/* flags */
|
/* flags */
|
||||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||||
|
}
|
||||||
|
|
||||||
/* keyingset to use (dynamic enum) */
|
static int keyframe_insert_with_keyingset_exec(bContext *C, wmOperator *op)
|
||||||
prop = RNA_def_enum(
|
{
|
||||||
ot->srna, "type", rna_enum_dummy_DEFAULT_items, 0, "Keying Set", "The Keying Set to use");
|
Scene *scene = CTX_data_scene(C);
|
||||||
RNA_def_enum_funcs(prop, ANIM_keying_sets_enum_itemf);
|
KeyingSet *ks = keyingset_get_from_op_with_error(op, op->type->prop, scene);
|
||||||
RNA_def_property_flag(prop, PROP_HIDDEN);
|
if (ks == nullptr) {
|
||||||
ot->prop = prop;
|
return OPERATOR_CANCELLED;
|
||||||
|
}
|
||||||
|
return insert_key_with_keyingset(C, op, ks);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ANIM_OT_keyframe_insert_by_name(wmOperatorType *ot)
|
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";
|
ot->description = "Alternate access to 'Insert Keyframe' for keymaps to use";
|
||||||
|
|
||||||
/* callbacks */
|
/* callbacks */
|
||||||
ot->exec = insert_key_exec;
|
ot->exec = keyframe_insert_with_keyingset_exec;
|
||||||
ot->poll = modify_key_op_poll;
|
ot->poll = modify_key_op_poll;
|
||||||
|
|
||||||
/* flags */
|
/* flags */
|
||||||
|
@ -459,7 +671,7 @@ void ANIM_OT_keyframe_insert_menu(wmOperatorType *ot)
|
||||||
|
|
||||||
/* callbacks */
|
/* callbacks */
|
||||||
ot->invoke = insert_key_menu_invoke;
|
ot->invoke = insert_key_menu_invoke;
|
||||||
ot->exec = insert_key_exec;
|
ot->exec = keyframe_insert_with_keyingset_exec;
|
||||||
ot->poll = ED_operator_areaactive;
|
ot->poll = ED_operator_areaactive;
|
||||||
|
|
||||||
/* flags */
|
/* flags */
|
||||||
|
|
|
@ -955,6 +955,9 @@ typedef struct UserDef {
|
||||||
short autokey_mode;
|
short autokey_mode;
|
||||||
/** Flags for autokeying. */
|
/** Flags for autokeying. */
|
||||||
short autokey_flag;
|
short autokey_flag;
|
||||||
|
/** Flags for which channels to insert keys at. */
|
||||||
|
short key_insert_channels; // eKeyInsertChannels
|
||||||
|
char _pad15[6];
|
||||||
/** Flags for animation. */
|
/** Flags for animation. */
|
||||||
short animation_flag;
|
short animation_flag;
|
||||||
|
|
||||||
|
@ -1295,6 +1298,14 @@ typedef enum eAutokey_Flag {
|
||||||
ANIMRECORD_FLAG_WITHNLA = (1 << 10),
|
ANIMRECORD_FLAG_WITHNLA = (1 << 10),
|
||||||
} eAutokey_Flag;
|
} 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
|
* Animation flags
|
||||||
* #UserDef.animation_flag, used for animation flags that aren't covered by more specific 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},
|
{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[] = {
|
static const EnumPropertyItem rna_enum_preference_gpu_backend_items[] = {
|
||||||
{GPU_BACKEND_OPENGL, "OPENGL", 0, "OpenGL", "Use OpenGL backend"},
|
{GPU_BACKEND_OPENGL, "OPENGL", 0, "OpenGL", "Use OpenGL backend"},
|
||||||
{GPU_BACKEND_METAL, "METAL", 0, "Metal", "Use Metal 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");
|
"Show warning indicators when transforming objects and bones if auto keying is enabled");
|
||||||
|
|
||||||
/* keyframing settings */
|
/* 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);
|
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_boolean_sdna(prop, nullptr, "autokey_flag", AUTOKEY_FLAG_INSERTNEEDED);
|
||||||
RNA_def_property_ui_text(
|
RNA_def_property_ui_text(
|
||||||
|
|
|
@ -51,6 +51,16 @@ def _insert_by_name_test(insert_key: str, expected_paths: list):
|
||||||
return match
|
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):
|
def _get_keying_set(scene, name: str):
|
||||||
return scene.keying_sets_all[scene.keying_sets_all.find(name)]
|
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"])
|
_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):
|
class VisualKeyingTest(AbstractKeyframingTest, unittest.TestCase):
|
||||||
""" Check if visual keying produces the correct keyframe values. """
|
""" 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(target, do_unlink=True)
|
||||||
bpy.data.objects.remove(constrained, 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,
|
# When enabling the user preference setting,
|
||||||
# the normal keying sets behave like their visual keying set counterpart.
|
# the normal keying sets behave like their visual keying set counterpart.
|
||||||
bpy.context.preferences.edit.use_visual_keying = True
|
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.data.objects.remove(constrained, do_unlink=True)
|
||||||
bpy.context.preferences.edit.use_visual_keying = False
|
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):
|
class CycleAwareKeyingTest(AbstractKeyframingTest, unittest.TestCase):
|
||||||
""" Check if cycle aware keying remaps the keyframes correctly and adds fcurve modifiers. """
|
""" 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")
|
bpy.ops.anim.keyframe_insert_by_name(type="Location")
|
||||||
|
|
||||||
# Will be mapped to frame 3.
|
# 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.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.
|
# Will be mapped to frame 9.
|
||||||
bpy.context.scene.frame_set(-10)
|
bpy.context.scene.frame_set(-10)
|
||||||
|
|
Loading…
Reference in New Issue
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.