expre-gen/blender/3.4/scripts/addons/amaranth/animation/motion_paths.py

133 lines
4.0 KiB
Python
Raw Permalink Normal View History

2025-10-17 09:47:23 +08:00
# SPDX-License-Identifier: GPL-2.0-or-later
"""
Bone Motion Paths:
Match Frame Range + Clear All Paths
* Clear All Paths:
Silly operator to loop through all bones and clear their paths, useful
when having hidden bones (othrewise you have to go through each one of
them and clear manually)
*Match Current Frame Range:
Set the current frame range as motion path range.
Both requests by Hjalti from Project Pampa
Thanks to Bassam Kurdali for helping finding out the weirdness behind
Motion Paths bpy.
Developed during Caminandes Open Movie Project
"""
import bpy
class AMTH_POSE_OT_paths_clear_all(bpy.types.Operator):
"""Clear motion paths from all bones"""
bl_idname = "pose.paths_clear_all"
bl_label = "Clear All Motion Paths"
bl_options = {"UNDO"}
@classmethod
def poll(cls, context):
return context.mode == "POSE"
def execute(self, context):
# silly but works
for b in context.object.data.bones:
b.select = True
bpy.ops.pose.paths_clear()
b.select = False
return {"FINISHED"}
class AMTH_POSE_OT_paths_frame_match(bpy.types.Operator):
"""Match Start/End frame of scene to motion path range"""
bl_idname = "pose.paths_frame_match"
bl_label = "Match Frame Range"
bl_options = {"UNDO"}
def execute(self, context):
avs = context.object.pose.animation_visualization
scene = context.scene
if avs.motion_path.type == "RANGE":
if scene.use_preview_range:
avs.motion_path.frame_start = scene.frame_preview_start
avs.motion_path.frame_end = scene.frame_preview_end
else:
avs.motion_path.frame_start = scene.frame_start
avs.motion_path.frame_end = scene.frame_end
else:
if scene.use_preview_range:
avs.motion_path.frame_before = scene.frame_preview_start
avs.motion_path.frame_after = scene.frame_preview_end
else:
avs.motion_path.frame_before = scene.frame_start
avs.motion_path.frame_after = scene.frame_end
return {"FINISHED"}
def pose_motion_paths_ui(self, context):
layout = self.layout
scene = context.scene
avs = context.object.pose.animation_visualization
if context.active_pose_bone:
mpath = context.active_pose_bone.motion_path
layout.separator()
layout.label(text="Motion Paths Extras:")
split = layout.split()
col = split.column(align=True)
if context.selected_pose_bones:
if mpath:
sub = col.row(align=True)
sub.operator(
"pose.paths_update", text="Update Path", icon="BONE_DATA")
sub.operator("pose.paths_clear", text="", icon="X")
else:
col.operator(
"pose.paths_calculate",
text="Calculate Path",
icon="BONE_DATA")
else:
col.label(text="Select Bones First", icon="ERROR")
col = split.column(align=True)
col.operator(
AMTH_POSE_OT_paths_frame_match.bl_idname,
text="Set Preview Frame Range" if scene.use_preview_range else "Set Frame Range",
icon="PREVIEW_RANGE" if scene.use_preview_range else "TIME")
col = layout.column()
row = col.row(align=True)
if avs.motion_path.type == "RANGE":
row.prop(avs.motion_path, "frame_start", text="Start")
row.prop(avs.motion_path, "frame_end", text="End")
else:
row.prop(avs.motion_path, "frame_before", text="Before")
row.prop(avs.motion_path, "frame_after", text="After")
layout.separator()
layout.operator(AMTH_POSE_OT_paths_clear_all.bl_idname, icon="X")
def register():
bpy.utils.register_class(AMTH_POSE_OT_paths_clear_all)
bpy.utils.register_class(AMTH_POSE_OT_paths_frame_match)
bpy.types.DATA_PT_display.append(pose_motion_paths_ui)
def unregister():
bpy.utils.unregister_class(AMTH_POSE_OT_paths_clear_all)
bpy.utils.unregister_class(AMTH_POSE_OT_paths_frame_match)
bpy.types.DATA_PT_display.remove(pose_motion_paths_ui)