WIP: Animation: operators to update the pose library #104673

Draft
Sybren A. Stüvel wants to merge 10 commits from dr.sybren/blender-addons:pr/poselib-replace-pose into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
Showing only changes of commit a9057116a1 - Show all commits

View File

@ -139,6 +139,20 @@ class POSELIB_OT_create_pose_asset(PoseAssetCreator, Operator):
self.report({'WARNING'}, tip_("Action %s marked Fake User to prevent loss") % action.name) self.report({'WARNING'}, tip_("Action %s marked Fake User to prevent loss") % action.name)
def _get_action(context: Context) -> Action | None:
"""Get the current action from the asset browser or the asset view."""
if context.asset_library_ref and context.asset_file_handle and context.asset_file_handle.id_type == 'ACTION':
context_id = context.asset_file_handle.local_id
else:
context_id = getattr(context, "id", None)
if not isinstance(context_id, Action):
return None
return context_id
class POSELIB_OT_replace_pose_asset(Operator): class POSELIB_OT_replace_pose_asset(Operator):
bl_idname = "poselib.replace_pose_asset" bl_idname = "poselib.replace_pose_asset"
bl_label = "Replace Pose Asset" bl_label = "Replace Pose Asset"
@ -149,7 +163,7 @@ class POSELIB_OT_replace_pose_asset(Operator):
@classmethod @classmethod
def poll(cls, context: Context) -> bool: def poll(cls, context: Context) -> bool:
if not isinstance(getattr(context, "id", None), Action): if not _get_action(context):
return False return False
if context.object is None or context.object.mode != "POSE": if context.object is None or context.object.mode != "POSE":
@ -165,19 +179,21 @@ class POSELIB_OT_replace_pose_asset(Operator):
return True return True
def execute(self, context: Context) -> Set[str]: def execute(self, context: Context) -> Set[str]:
pose_name = context.id.name # This will not use this name entirely, but it'll be made unique. original_pose: Action = _get_action(context)
pose_name = original_pose.name # This will not use this name entirely, but it'll be made unique.
asset = pose_creation.create_pose_asset_from_context(context, pose_name) asset = pose_creation.create_pose_asset_from_context(context, pose_name)
if not asset: if not asset:
self.report({"WARNING"}, "No keyframes were found for this pose") self.report({"WARNING"}, "No keyframes were found for this pose")
return {"CANCELLED"} return {"CANCELLED"}
# Copy the asset metadata to the newly created asset. # Copy the asset metadata to the newly created asset.
asset.asset_data = context.id.asset_data asset.asset_data = original_pose.asset_data
self._activate_asset_in_browser(context, asset) self._activate_asset_in_browser(context, asset)
# Delete the old asset # Delete the old asset
bpy.data.actions.remove(context.id) bpy.data.actions.remove(original_pose)
# Now that the old name has become available, rename the asset to it. # Now that the old name has become available, rename the asset to it.
asset.name = pose_name asset.name = pose_name
@ -214,7 +230,7 @@ class POSELIB_OT_update_pose_asset(Operator):
@classmethod @classmethod
def poll(cls, context: Context) -> bool: def poll(cls, context: Context) -> bool:
if not isinstance(getattr(context, "id", None), Action): if not _get_action(context):
return False return False
if context.object is None or context.object.mode != "POSE": if context.object is None or context.object.mode != "POSE":
@ -231,7 +247,8 @@ class POSELIB_OT_update_pose_asset(Operator):
def execute(self, context: Context) -> Set[str]: def execute(self, context: Context) -> Set[str]:
# Make sure the new keys are on the same frame as the old keys. # Make sure the new keys are on the same frame as the old keys.
storage_frame_nr = self._find_frame_number(context) original_pose: Action = _get_action(context)
storage_frame_nr = self._find_frame_number(original_pose, context.scene.frame_current)
params = pose_creation.params_for_selected_bones( params = pose_creation.params_for_selected_bones(
context, context,
@ -247,7 +264,6 @@ class POSELIB_OT_update_pose_asset(Operator):
return {"CANCELLED"} return {"CANCELLED"}
try: try:
original_pose: Action = context.id
was_updated = self._update_pose_asset(original_pose, new_pose) was_updated = self._update_pose_asset(original_pose, new_pose)
finally: finally:
# Clean up by removing the temporary pose Action. # Clean up by removing the temporary pose Action.
@ -306,12 +322,11 @@ class POSELIB_OT_update_pose_asset(Operator):
return True return True
@staticmethod @staticmethod
def _find_frame_number(context: Context) -> float: def _find_frame_number(action: Action, fallback_frame: float) -> float:
original_pose: Action = context.id assert isinstance(action, Action), f"expected an Action, got {action}"
assert isinstance(original_pose, Action), f"expected an Action, got {original_pose}" if action.fcurves and action.fcurves[0].keyframe_points:
if original_pose.fcurves and original_pose.fcurves[0].keyframe_points: return action.fcurves[0].keyframe_points[0].co.x
return original_pose.fcurves[0].keyframe_points[0].co.x return fallback_frame
return context.scene.frame_current
@staticmethod @staticmethod
def _create_new_fcurve(action: Action, fcu_to_copy: FCurve) -> None: def _create_new_fcurve(action: Action, fcu_to_copy: FCurve) -> None: