diff options
Diffstat (limited to 'rigify/utils/animation.py')
-rw-r--r-- | rigify/utils/animation.py | 93 |
1 files changed, 74 insertions, 19 deletions
diff --git a/rigify/utils/animation.py b/rigify/utils/animation.py index b8f3725a..21aa2a89 100644 --- a/rigify/utils/animation.py +++ b/rigify/utils/animation.py @@ -1,18 +1,23 @@ # SPDX-License-Identifier: GPL-2.0-or-later +# noinspection PyUnresolvedReferences import bpy - +# noinspection PyUnresolvedReferences import math -import json - +# noinspection PyUnresolvedReferences from mathutils import Matrix, Vector +from typing import Callable, Any, Collection, Iterator +from bpy.types import Action, bpy_struct, FCurve + +import json + rig_id = None -#============================================= -# Keyframing functions -#============================================= +############################################## +# Keyframing functions +############################################## def get_keyed_frames_in_range(context, rig): action = find_action(rig) @@ -34,11 +39,11 @@ def bones_in_frame(f, rig, *args): """ if rig.animation_data and rig.animation_data.action: - fcus = rig.animation_data.action.fcurves + fcurves = rig.animation_data.action.fcurves else: return False - for fc in fcus: + for fc in fcurves: animated_frames = [kp.co[0] for kp in fc.keyframe_points] for bone in args: if bone in fc.data_path.split('"') and f in animated_frames: @@ -68,10 +73,12 @@ def overwrite_prop_animation(rig, bone, prop_name, value, frames): if kp.co[0] in frames: kp.co[1] = value + ################################################################ # Utilities for inserting keyframes and/or setting transforms ## ################################################################ +# noinspection SpellCheckingInspection SCRIPT_UTILITIES_KEYING = [''' ###################### ## Keyframing tools ## @@ -118,7 +125,8 @@ def get_4d_rotlock(bone): else: return [all(bone.lock_rotation)] * 4 -def keyframe_transform_properties(obj, bone_name, keyflags, *, ignore_locks=False, no_loc=False, no_rot=False, no_scale=False): +def keyframe_transform_properties(obj, bone_name, keyflags, *, + ignore_locks=False, no_loc=False, no_rot=False, no_scale=False): "Keyframe transformation properties, taking flags and mode into account, and avoiding keying locked channels." bone = obj.pose.bones[bone_name] @@ -155,7 +163,8 @@ def get_constraint_target_matrix(con): if target.type == 'ARMATURE' and con.subtarget: if con.subtarget in target.pose.bones: bone = target.pose.bones[con.subtarget] - return target.convert_space(pose_bone=bone, matrix=bone.matrix, from_space='POSE', to_space=con.target_space) + return target.convert_space( + pose_bone=bone, matrix=bone.matrix, from_space='POSE', to_space=con.target_space) else: return target.convert_space(matrix=target.matrix_world, from_space='WORLD', to_space=con.target_space) return Matrix.Identity(4) @@ -224,8 +233,10 @@ def get_transform_matrix(obj, bone_name, *, space='POSE', with_constraints=True) def get_chain_transform_matrices(obj, bone_names, **options): return [get_transform_matrix(obj, name, **options) for name in bone_names] -def set_transform_from_matrix(obj, bone_name, matrix, *, space='POSE', undo_copy_scale=False, ignore_locks=False, no_loc=False, no_rot=False, no_scale=False, keyflags=None): - "Apply the matrix to the transformation of the bone, taking locked channels, mode and certain constraints into account, and optionally keyframe it." +def set_transform_from_matrix(obj, bone_name, matrix, *, space='POSE', undo_copy_scale=False, + ignore_locks=False, no_loc=False, no_rot=False, no_scale=False, keyflags=None): + """Apply the matrix to the transformation of the bone, taking locked channels, mode and certain + constraints into account, and optionally keyframe it.""" bone = obj.pose.bones[bone_name] def restore_channels(prop, old_vec, locks, extra_lock): @@ -294,6 +305,7 @@ exec(SCRIPT_UTILITIES_KEYING[-1]) # Utilities for managing animation curves ## ############################################ +# noinspection SpellCheckingInspection SCRIPT_UTILITIES_CURVES = [''' ########################### ## Animation curve tools ## @@ -433,6 +445,24 @@ class DriverCurveTable(FCurveTable): self.index_curves(self.anim_data.drivers) '''] +AnyCurveSet = None | FCurve | dict | Collection +flatten_curve_set: Callable[[AnyCurveSet], Iterator[FCurve]] +flatten_curve_key_set: Callable[..., set[float]] +get_curve_frame_set: Callable[..., set[float]] +set_curve_key_interpolation: Callable[..., None] +delete_curve_keys_in_range: Callable[..., None] +nla_tweak_to_scene: Callable +find_action: Callable[[bpy_struct], Action] +clean_action_empty_curves: Callable[[bpy_struct], None] +TRANSFORM_PROPS_LOCATION: frozenset[str] +TRANSFORM_PROPS_ROTATION = frozenset[str] +TRANSFORM_PROPS_SCALE = frozenset[str] +TRANSFORM_PROPS_ALL = frozenset[str] +transform_props_with_locks: Callable[[bool, bool, bool], set[str]] +FCurveTable: Any +ActionCurveTable: Any +DriverCurveTable: Any + exec(SCRIPT_UTILITIES_CURVES[-1]) ################################################ @@ -441,7 +471,9 @@ exec(SCRIPT_UTILITIES_CURVES[-1]) _SCRIPT_REGISTER_WM_PROPS = ''' bpy.types.WindowManager.rigify_transfer_use_all_keys = bpy.props.BoolProperty( - name="Bake All Keyed Frames", description="Bake on every frame that has a key for any of the bones, as opposed to just the relevant ones", default=False + name="Bake All Keyed Frames", + description="Bake on every frame that has a key for any of the bones, as opposed to just the relevant ones", + default=False ) bpy.types.WindowManager.rigify_transfer_use_frame_range = bpy.props.BoolProperty( name="Limit Frame Range", description="Only bake keyframes in a certain frame range", default=False @@ -461,6 +493,7 @@ del bpy.types.WindowManager.rigify_transfer_start_frame del bpy.types.WindowManager.rigify_transfer_end_frame ''' +# noinspection SpellCheckingInspection _SCRIPT_UTILITIES_BAKE_OPS = ''' class RIGIFY_OT_get_frame_range(bpy.types.Operator): bl_idname = "rigify.get_frame_range" + ('_'+rig_id if rig_id else '') @@ -497,6 +530,8 @@ class RIGIFY_OT_get_frame_range(bpy.types.Operator): row.operator(self.bl_idname, icon='TIME', text='') ''' +RIGIFY_OT_get_frame_range: Any + exec(_SCRIPT_UTILITIES_BAKE_OPS) ################################################ @@ -505,6 +540,7 @@ exec(_SCRIPT_UTILITIES_BAKE_OPS) SCRIPT_REGISTER_BAKE = ['RIGIFY_OT_get_frame_range'] +# noinspection SpellCheckingInspection SCRIPT_UTILITIES_BAKE = SCRIPT_UTILITIES_KEYING + SCRIPT_UTILITIES_CURVES + [''' ################################## # Common bake operator settings ## @@ -756,6 +792,10 @@ class RigifySingleUpdateMixin(RigifyOperatorMixinBase): return self.execute(context) '''] +RigifyOperatorMixinBase: Any +RigifyBakeKeyframesMixin: Any +RigifySingleUpdateMixin: Any + exec(SCRIPT_UTILITIES_BAKE[-1]) ##################################### @@ -764,6 +804,7 @@ exec(SCRIPT_UTILITIES_BAKE[-1]) SCRIPT_REGISTER_OP_CLEAR_KEYS = ['POSE_OT_rigify_clear_keyframes'] +# noinspection SpellCheckingInspection SCRIPT_UTILITIES_OP_CLEAR_KEYS = [''' ############################# ## Generic Clear Keyframes ## @@ -806,14 +847,17 @@ class POSE_OT_rigify_clear_keyframes(bpy.types.Operator): return {'FINISHED'} '''] + +# noinspection PyDefaultArgument,PyUnusedLocal def add_clear_keyframes_button(panel, *, bones=[], label='', text=''): panel.use_bake_settings() panel.script.add_utilities(SCRIPT_UTILITIES_OP_CLEAR_KEYS) panel.script.register_classes(SCRIPT_REGISTER_OP_CLEAR_KEYS) - op_props = { 'bones': json.dumps(bones) } + op_props = {'bones': json.dumps(bones)} - panel.operator('pose.rigify_clear_keyframes_{rig_id}', text=text, icon='CANCEL', properties=op_props) + panel.operator('pose.rigify_clear_keyframes_{rig_id}', text=text, icon='CANCEL', + properties=op_props) ################################### @@ -822,6 +866,7 @@ def add_clear_keyframes_button(panel, *, bones=[], label='', text=''): SCRIPT_REGISTER_OP_SNAP = ['POSE_OT_rigify_generic_snap', 'POSE_OT_rigify_generic_snap_bake'] +# noinspection SpellCheckingInspection SCRIPT_UTILITIES_OP_SNAP = [''' ############################# ## Generic Snap (FK to IK) ## @@ -875,11 +920,13 @@ class POSE_OT_rigify_generic_snap_bake(RigifyGenericSnapBase, RigifyBakeKeyframe return self.bake_get_all_bone_curves(self.output_bone_list, props) '''] -def add_fk_ik_snap_buttons(panel, op_single, op_bake, *, label=None, rig_name='', properties=None, clear_bones=None, compact=None): + +def add_fk_ik_snap_buttons(panel, op_single, op_bake, *, label=None, rig_name='', properties=None, + clear_bones=None, compact=None): assert label and properties if rig_name: - label += ' (%s)' % (rig_name) + label += ' (%s)' % rig_name if compact or not clear_bones: row = panel.row(align=True) @@ -895,7 +942,10 @@ def add_fk_ik_snap_buttons(panel, op_single, op_bake, *, label=None, rig_name='' row.operator(op_bake, text='Action', icon='ACTION_TWEAK', properties=properties) add_clear_keyframes_button(row, bones=clear_bones, text='Clear') -def add_generic_snap(panel, *, output_bones=[], input_bones=[], input_ctrl_bones=[], label='Snap', rig_name='', undo_copy_scale=False, compact=None, clear=True, locks=None, tooltip=None): + +# noinspection PyDefaultArgument +def add_generic_snap(panel, *, output_bones=[], input_bones=[], input_ctrl_bones=[], label='Snap', + rig_name='', undo_copy_scale=False, compact=None, clear=True, locks=None, tooltip=None): panel.use_bake_settings() panel.script.add_utilities(SCRIPT_UTILITIES_OP_SNAP) panel.script.register_classes(SCRIPT_REGISTER_OP_SNAP) @@ -920,12 +970,16 @@ def add_generic_snap(panel, *, output_bones=[], input_bones=[], input_ctrl_bones label=label, rig_name=rig_name, properties=op_props, clear_bones=clear_bones, compact=compact, ) -def add_generic_snap_fk_to_ik(panel, *, fk_bones=[], ik_bones=[], ik_ctrl_bones=[], label='FK->IK', rig_name='', undo_copy_scale=False, compact=None, clear=True): + +# noinspection PyDefaultArgument +def add_generic_snap_fk_to_ik(panel, *, fk_bones=[], ik_bones=[], ik_ctrl_bones=[], label='FK->IK', + rig_name='', undo_copy_scale=False, compact=None, clear=True): add_generic_snap( panel, output_bones=fk_bones, input_bones=ik_bones, input_ctrl_bones=ik_ctrl_bones, label=label, rig_name=rig_name, undo_copy_scale=undo_copy_scale, compact=compact, clear=clear ) + ############################### # Module register/unregister ## ############################### @@ -937,6 +991,7 @@ def register(): register_class(RIGIFY_OT_get_frame_range) + def unregister(): from bpy.utils import unregister_class |