Anim: add keying set support to Copy Global Transform add-on
When the scene settings indicate the active keying set should be used, this is taken into account for all auto-keying done by the add-on. This covers: - Pasting global transform - Pasting relative transform - Fix to Camera Support is limited to *relative* keying sets. Absolute keying sets, which thus determine what gets keyed regardless of the selection, are not supported, as the interaction between the "copy this object/bone's transform" is currently not known. Fix to Camera has its own settings for what to key; these will be combined with the keying set. Only those properties that are enabled in the Fix to Camera settings AND included in the keying set will be keyed. To give a concrete example: when the "Location & Rotation" keying set is active, and "Location" and "Scale" are checked in the Fix to Camera panel, only the location will be keyed. A message is shown in the Fix to Camera panel to notify users that the keying set will affect the keys as well. The keying set will only be considered when the auto-keying option "Only Active Keying Set" is enabled. Fixes: #136076 Pull Request: https://projects.blender.org/blender/blender/pulls/136187
This commit is contained in:
@@ -29,7 +29,10 @@ from typing import Iterable, Optional, Union, Any, TypeAlias, Iterator
|
||||
|
||||
import bpy
|
||||
from bpy.app.translations import contexts as i18n_contexts
|
||||
from bpy.types import Context, Object, Operator, Panel, PoseBone, UILayout, Camera, ID, ActionChannelbag
|
||||
from bpy.types import (
|
||||
Context, Object, Operator, Panel, PoseBone,
|
||||
UILayout, Camera, ID, ActionChannelbag, KeyingSet,
|
||||
)
|
||||
from mathutils import Matrix
|
||||
|
||||
|
||||
@@ -110,6 +113,21 @@ class AutoKeying:
|
||||
options.add('INSERTKEY_CYCLE_AWARE')
|
||||
return options
|
||||
|
||||
@classmethod
|
||||
def keying_options_from_keyingset(cls, context: Context, keyingset: KeyingSet) -> set[str]:
|
||||
"""Retrieve the general keyframing options from user preferences."""
|
||||
|
||||
ts = context.scene.tool_settings
|
||||
options = set()
|
||||
|
||||
if keyingset.use_insertkey_visual:
|
||||
options.add('INSERTKEY_VISUAL')
|
||||
if keyingset.use_insertkey_needed:
|
||||
options.add('INSERTKEY_NEEDED')
|
||||
if ts.use_keyframe_cycle_aware:
|
||||
options.add('INSERTKEY_CYCLE_AWARE')
|
||||
return options
|
||||
|
||||
@classmethod
|
||||
def autokeying_options(cls, context: Context) -> Optional[set[str]]:
|
||||
"""Retrieve the Auto Keyframe options, or None if disabled."""
|
||||
@@ -119,9 +137,10 @@ class AutoKeying:
|
||||
if not (cls._force_autokey or ts.use_keyframe_insert_auto):
|
||||
return None
|
||||
|
||||
if ts.use_keyframe_insert_keyingset:
|
||||
# No support for keying sets (yet).
|
||||
return None
|
||||
active_keyingset = context.scene.keying_sets_all.active
|
||||
if ts.use_keyframe_insert_keyingset and active_keyingset:
|
||||
# No support for keying sets in this function
|
||||
raise RuntimeError("This function should not be called when there is an active keying set")
|
||||
|
||||
prefs = context.preferences
|
||||
options = cls.keying_options(context)
|
||||
@@ -192,10 +211,87 @@ class AutoKeying:
|
||||
if cls._use_scale:
|
||||
keyframe("scale", target.lock_scale)
|
||||
|
||||
@classmethod
|
||||
def key_transformation_via_keyingset(cls,
|
||||
context: Context,
|
||||
target: Union[Object, PoseBone],
|
||||
keyingset: KeyingSet) -> None:
|
||||
"""Auto-key transformation properties with the given keying set."""
|
||||
|
||||
keyingset.refresh()
|
||||
|
||||
is_bone = isinstance(target, PoseBone)
|
||||
options = cls.keying_options_from_keyingset(context, keyingset)
|
||||
|
||||
paths_to_key = {keysetpath.data_path: keysetpath for keysetpath in keyingset.paths}
|
||||
|
||||
def keyframe(data_path: str, locks: Iterable[bool]) -> None:
|
||||
# Keying sets are relative to the ID.
|
||||
full_data_path = target.path_from_id(data_path)
|
||||
try:
|
||||
keysetpath = paths_to_key[full_data_path]
|
||||
except KeyError:
|
||||
# No biggie, just means this property shouldn't be keyed.
|
||||
return
|
||||
|
||||
match keysetpath.group_method:
|
||||
case 'NAMED':
|
||||
group = keysetpath.group
|
||||
case 'KEYINGSET':
|
||||
group = keyingset.name
|
||||
case 'NONE', _:
|
||||
group = ""
|
||||
|
||||
cls.keyframe_channels(target, options, data_path, group, locks)
|
||||
|
||||
if cls._use_loc and not (is_bone and target.bone.use_connect):
|
||||
keyframe("location", target.lock_location)
|
||||
|
||||
if cls._use_rot:
|
||||
if target.rotation_mode == 'QUATERNION':
|
||||
keyframe("rotation_quaternion", cls.get_4d_rotlock(target))
|
||||
elif target.rotation_mode == 'AXIS_ANGLE':
|
||||
keyframe("rotation_axis_angle", cls.get_4d_rotlock(target))
|
||||
else:
|
||||
keyframe("rotation_euler", target.lock_rotation)
|
||||
|
||||
if cls._use_scale:
|
||||
keyframe("scale", target.lock_scale)
|
||||
|
||||
@classmethod
|
||||
def active_keyingset(cls, context: Context) -> KeyingSet | None:
|
||||
"""Return the active keying set, if it should be used.
|
||||
|
||||
Only returns the active keying set when the auto-key settings indicate
|
||||
it should be used, and when it is not using absolute paths (because
|
||||
that's not supported by the Copy Global Transform add-on).
|
||||
"""
|
||||
ts = context.scene.tool_settings
|
||||
if not ts.use_keyframe_insert_keyingset:
|
||||
return None
|
||||
|
||||
active_keyingset = context.scene.keying_sets_all.active
|
||||
if not active_keyingset:
|
||||
return None
|
||||
|
||||
active_keyingset.refresh()
|
||||
if active_keyingset.is_path_absolute:
|
||||
# Absolute-path keying sets are not supported (yet?).
|
||||
return None
|
||||
|
||||
return active_keyingset
|
||||
|
||||
@classmethod
|
||||
def autokey_transformation(cls, context: Context, target: Union[Object, PoseBone]) -> None:
|
||||
"""Auto-key transformation properties."""
|
||||
|
||||
# See if the active keying set should be used.
|
||||
keyingset = cls.active_keyingset(context)
|
||||
if keyingset:
|
||||
cls.key_transformation_via_keyingset(context, target, keyingset)
|
||||
return
|
||||
|
||||
# Use regular autokeying options.
|
||||
options = cls.autokeying_options(context)
|
||||
if options is None:
|
||||
return
|
||||
@@ -617,7 +713,7 @@ class OBJECT_OT_paste_transform(Operator):
|
||||
context.scene.frame_set(int(current_frame), subframe=current_frame % 1.0)
|
||||
|
||||
|
||||
# Mapping from frame number to the dominant key type.
|
||||
# Mapping from frame number to the dominant (in terms of genetics) key type.
|
||||
# GENERATED is the only recessive key type, others are dominant.
|
||||
KeyInfo: TypeAlias = dict[float, str]
|
||||
|
||||
@@ -969,6 +1065,16 @@ class VIEW3D_PT_copy_global_transform_fix_to_camera(PanelMixin, Panel):
|
||||
props_box.prop(scene, "addon_copy_global_transform_fix_cam_use_rot", text="Rotation")
|
||||
props_box.prop(scene, "addon_copy_global_transform_fix_cam_use_scale", text="Scale")
|
||||
|
||||
keyingset = AutoKeying.active_keyingset(context)
|
||||
if keyingset:
|
||||
# Show an explicit message here, even though the keying set affects
|
||||
# the other operators as well. Fix to Camera is treated as a special
|
||||
# case because it also has options for selecting what to key. The
|
||||
# logical AND of the settings is used, so a property is only keyed
|
||||
# when the keying set AND the above checkboxes say it's ok.
|
||||
props_box.label(text="Keying set is active, which may")
|
||||
props_box.label(text="reduce the effect of the above options")
|
||||
|
||||
row = layout.row(align=True)
|
||||
props = row.operator("object.fix_to_camera")
|
||||
props.use_loc = scene.addon_copy_global_transform_fix_cam_use_loc
|
||||
|
||||
Reference in New Issue
Block a user