503 lines
17 KiB
Python
503 lines
17 KiB
Python
import bpy
|
|
import blf
|
|
from .common import *
|
|
|
|
|
|
# Operator to create a new pose library
|
|
|
|
|
|
class DSPL_OT_CreatePoseLibrary(bpy.types.Operator):
|
|
bl_idname = "dspl.create_pose_library"
|
|
bl_label = "Create Pose Library"
|
|
bl_description = "Create Pose Library"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
def execute(self, context):
|
|
arm_object = getArmatureObject(context)
|
|
arm_object.dspl.pose_library = bpy.data.actions.new(
|
|
name=arm_object.name + "_PoseLib")
|
|
arm_object.dspl.pose_library.use_fake_user = True
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
# Operator to convert a pose library to dspl property
|
|
|
|
|
|
class DSPL_OT_ConvertPoseLibrary(bpy.types.Operator):
|
|
bl_idname = "dspl.convert_pose_library"
|
|
bl_label = "Convert Pose Library"
|
|
bl_description = "Convert Pose Library"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
def execute(self, context):
|
|
arm_object = getArmatureObject(context)
|
|
arm_object.dspl.pose_library = arm_object.animation_data.action
|
|
arm_object.animation_data.action = None
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
# Operator to manage the big menu buttons
|
|
|
|
|
|
class DSPL_OT_MenuButtonHandler(bpy.types.Operator):
|
|
bl_idname = "dspl.menu_button_handler"
|
|
bl_label = "Button Handler"
|
|
bl_description = "Button Handler"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
posename: bpy.props.StringProperty()
|
|
|
|
def execute(self, context):
|
|
bpy.ops.wm.call_menu(name=self.menuID)
|
|
|
|
return {'FINISHED'}
|
|
|
|
def invoke(self, context, event):
|
|
if event.ctrl:
|
|
# Select
|
|
action_object = getPoseLib(context)
|
|
action_object.pose_markers.active_index = searchPoseMarker(context, posename=self.posename, type="index")
|
|
return {'FINISHED'}
|
|
elif event.alt:
|
|
# Remove
|
|
bpy.ops.dspl.remove_pose(posename = self.posename)
|
|
return {'FINISHED'}
|
|
elif event.shift:
|
|
# Rename
|
|
bpy.ops.dspl.rename_pose(posename = self.posename)
|
|
return {'FINISHED'}
|
|
else:
|
|
return self.execute(context)
|
|
|
|
# Operator to add keyframes and marker to pose library
|
|
|
|
|
|
class DSPL_OT_AddPose(bpy.types.Operator):
|
|
bl_idname = "dspl.add_pose"
|
|
bl_label = "Add Pose"
|
|
bl_description = "Add Pose"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
posename: bpy.props.StringProperty()
|
|
replace: bpy.props.BoolProperty(name="Replace", description="Replace existing pose", default=False, options={'SKIP_SAVE'})
|
|
|
|
def execute(self, context):
|
|
|
|
if self.replace == False:
|
|
arm_object = getArmatureObject(context)
|
|
action_object = getPoseLib(context)
|
|
pose_markers = action_object.pose_markers
|
|
new_name = self.posename
|
|
counter = 1
|
|
|
|
# Find first unused marker frame
|
|
for f in range(0, len(pose_markers) + 1):
|
|
f += 1
|
|
for pm in pose_markers:
|
|
name_check = pm.name
|
|
frame_check = pm.frame
|
|
if frame_check == f:
|
|
break
|
|
else:
|
|
new_marker = f
|
|
break
|
|
|
|
# Check for duplicate names
|
|
while pose_markers.find(new_name) > -1:
|
|
new_name = self.posename + ".{:03d}".format(counter)
|
|
counter += 1
|
|
else:
|
|
pose_name = new_name
|
|
|
|
pose_markers.new(name=pose_name)
|
|
pose_markers[pose_name].frame = new_marker
|
|
|
|
setKeyframesFromBones(context, arm_object, new_marker)
|
|
|
|
action_object.pose_markers.active = pose_markers[pose_name]
|
|
bpy.context.area.tag_redraw()
|
|
|
|
self.report({'INFO'}, "DSPL: Added " + pose_markers[new_name].name + " to frame " + str(pose_markers[new_name].frame))
|
|
|
|
else:
|
|
arm_object = getArmatureObject(context)
|
|
action_object = getPoseLib(context)
|
|
pose_markers = action_object.pose_markers
|
|
active_marker = pose_markers.active
|
|
new_name = self.posename
|
|
counter = 1
|
|
|
|
for pm in pose_markers:
|
|
name_check = pm.name
|
|
frame_check = pm.frame
|
|
if name_check == new_name:
|
|
target_name = name_check
|
|
target_frame = frame_check
|
|
break
|
|
else:
|
|
return
|
|
|
|
new_marker = target_frame
|
|
|
|
setKeyframesFromBones(context, arm_object, new_marker)
|
|
|
|
self.report({'INFO'}, "DSPL: Replaced " + pose_markers[new_name].name + " on frame " + str(pose_markers[new_name].frame))
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
# Operator to remove keyframes and marker
|
|
|
|
|
|
class DSPL_OT_RemovePose(bpy.types.Operator):
|
|
bl_idname = "dspl.remove_pose"
|
|
bl_label = "Remove Pose"
|
|
bl_description = "Remove Pose"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
posename: bpy.props.StringProperty()
|
|
|
|
def execute(self, context):
|
|
arm_object = getArmatureObject(context)
|
|
action_object = getPoseLib(context)
|
|
pose_markers = action_object.pose_markers
|
|
if self.posename:
|
|
action_object.pose_markers.active_index = searchPoseMarker(context, posename=self.posename, type="index")
|
|
active_index = action_object.pose_markers.active_index
|
|
else:
|
|
active_index = pose_markers.active_index
|
|
active_marker = pose_markers.active
|
|
active_frame = active_marker.frame
|
|
|
|
fcurves = action_object.fcurves
|
|
for fcu in fcurves:
|
|
for kf in fcu.keyframe_points.values():
|
|
if kf.co.x == active_frame:
|
|
fcu.keyframe_points.remove(kf)
|
|
|
|
if active_index <= 0:
|
|
next_index = active_index
|
|
next_marker = pose_markers[next_index]
|
|
elif (active_index + 1) >= len(pose_markers):
|
|
next_index = active_index - 1
|
|
next_marker = pose_markers[next_index]
|
|
else:
|
|
next_index = active_index
|
|
next_marker = pose_markers[next_index]
|
|
|
|
pose_markers.remove(marker=active_marker)
|
|
|
|
action_object.pose_markers.active = next_marker
|
|
action_object.pose_markers.active_index = next_index
|
|
|
|
self.report({'INFO'}, "DSPL: Removed " + self.posename)
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
# Operator to rename the current pose
|
|
|
|
|
|
class DSPL_OT_RenamePose(bpy.types.Operator):
|
|
bl_idname = "dspl.rename_pose"
|
|
bl_label = "Rename Pose"
|
|
bl_description = "Rename Pose"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
posename: bpy.props.StringProperty()
|
|
pose_new_name: bpy.props.StringProperty()
|
|
|
|
def execute(self, context):
|
|
|
|
arm_object = getArmatureObject(context)
|
|
action_object = getPoseLib(context)
|
|
pose_markers = action_object.pose_markers
|
|
active_marker = pose_markers.active
|
|
|
|
if self.posename:
|
|
target_marker = searchPoseMarker(context, posename=self.posename, type="marker")
|
|
else:
|
|
target_marker = active_marker
|
|
|
|
if self.pose_new_name:
|
|
target_marker.name = self.pose_new_name
|
|
context.area.tag_redraw()
|
|
|
|
self.report({'INFO'}, "DSPL: Renamed " + self.posename + " to " + self.pose_new_name + " on frame " + str(active_marker.frame))
|
|
|
|
return {'FINISHED'}
|
|
else:
|
|
return {'FINISHED'}
|
|
|
|
|
|
def invoke(self, context, event):
|
|
self.pose_new_name = self.posename
|
|
return context.window_manager.invoke_props_dialog(self)
|
|
|
|
|
|
# Operator to reorder pose markers
|
|
|
|
|
|
class DSPL_OT_MovePose(bpy.types.Operator):
|
|
bl_idname = "dspl.move_pose"
|
|
bl_label = "Move Pose"
|
|
bl_description = "Move pose"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
direction: bpy.props.StringProperty(name="Direction", default="DOWN")
|
|
posename: bpy.props.StringProperty(name="Pose Name", default="", options={'SKIP_SAVE'})
|
|
|
|
def execute(self, context):
|
|
arm_object = getArmatureObject(context)
|
|
action_object = getPoseLib(context)
|
|
pose_lib = getPoseLib(context)
|
|
pose_markers = action_object.pose_markers
|
|
|
|
if self.posename:
|
|
active_index = searchPoseMarker(context, posename=self.posename, type="index")
|
|
active_marker = searchPoseMarker(context, posename=self.posename, type="marker")
|
|
active_frame = active_marker.frame
|
|
active_posename = active_marker.name
|
|
else:
|
|
arm_object = getArmatureObject(context)
|
|
active_marker = pose_markers.active
|
|
active_index = pose_markers.active_index
|
|
|
|
if self.direction == "UP":
|
|
move_dir = -1
|
|
elif self.direction == "DOWN":
|
|
move_dir = 1
|
|
|
|
swap_index = active_index + move_dir
|
|
|
|
if swap_index < 0:
|
|
# swap_index = len(pose_lib.pose_markers) - 1
|
|
return {'FINISHED'}
|
|
elif swap_index >= len(pose_lib.pose_markers):
|
|
# swap_index = 0
|
|
return {'FINISHED'}
|
|
|
|
swap_marker = pose_markers[swap_index]
|
|
|
|
tmp_marker_name = swap_marker.name
|
|
tmp_marker_frame = swap_marker.frame
|
|
action_object.pose_markers[swap_index].name = active_marker.name
|
|
action_object.pose_markers[swap_index].frame = active_marker.frame
|
|
action_object.pose_markers[active_index].name = tmp_marker_name
|
|
action_object.pose_markers[active_index].frame = tmp_marker_frame
|
|
action_object.pose_markers.active_index = swap_index
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
# Operator to apply a pose from active marker
|
|
|
|
|
|
class DSPL_OT_ApplyPose(bpy.types.Operator):
|
|
bl_idname = "dspl.apply_pose"
|
|
bl_label = "Apply Pose"
|
|
bl_description = "Apply Pose (Ctrl+Click to select, Alt+Click to remove)"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
posename: bpy.props.StringProperty()
|
|
|
|
|
|
def execute(self, context):
|
|
arm_object = getArmatureObject(context)
|
|
action_object = getPoseLib(context)
|
|
pose_markers = action_object.pose_markers
|
|
|
|
if self.posename:
|
|
active_marker = searchPoseMarker(context, posename=self.posename, type="marker")
|
|
active_frame = active_marker.frame
|
|
active_posename = active_marker.name
|
|
action_object.pose_markers.active_index = searchPoseMarker(context, posename=self.posename, type="index")
|
|
else:
|
|
active_index = pose_markers.active_index
|
|
active_marker = pose_markers.active
|
|
active_frame = active_marker.frame
|
|
active_posename = active_marker.name
|
|
|
|
setBonesfromKeyframes(context, arm_object, active_marker)
|
|
|
|
self.report({'INFO'}, "DSPL: Applied " + active_posename)
|
|
|
|
return {'FINISHED'}
|
|
|
|
def invoke(self, context, event):
|
|
if event.ctrl:
|
|
# Select
|
|
action_object = getPoseLib(context)
|
|
action_object.pose_markers.active_index = searchPoseMarker(context, posename=self.posename, type="index")
|
|
return {'FINISHED'}
|
|
elif event.alt:
|
|
# Remove
|
|
bpy.ops.dspl.remove_pose(posename = self.posename)
|
|
return {'FINISHED'}
|
|
elif event.shift:
|
|
# Rename
|
|
bpy.ops.dspl.rename_pose('INVOKE_DEFAULT', posename = self.posename)
|
|
return {'FINISHED'}
|
|
else:
|
|
return self.execute(context)
|
|
|
|
|
|
# Operator to preview up and down pose list
|
|
|
|
|
|
class DSPL_OT_BrowsePoses(bpy.types.Operator):
|
|
bl_idname = "dspl.browse_poses"
|
|
bl_label = "Browse Poses"
|
|
bl_description = "Browse Poses"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
|
|
def draw_callback_px(self, context, test):
|
|
font_id = 1
|
|
font_size = 24
|
|
font_dpi = 72
|
|
|
|
blf.position(font_id, 15, 30, 0)
|
|
mod_var = self.pose_lib.pose_markers.active.name
|
|
blf.color(font_id, 1.0, 1.0, 1.0, 1.0)
|
|
blf.size(font_id, font_size)
|
|
blf.draw(font_id, "Previewing pose: " + mod_var)
|
|
|
|
|
|
def modal(self, context, event):
|
|
context.area.tag_redraw()
|
|
|
|
if event.value == 'PRESS':
|
|
if event.type in {'LEFT_ARROW', 'UP_ARROW'}:
|
|
if self.pose_lib.pose_markers.active_index <= 0:
|
|
self.pose_lib.pose_markers.active_index = len(self.pose_lib.pose_markers) - 1
|
|
else:
|
|
self.pose_lib.pose_markers.active_index = self.pose_lib.pose_markers.active_index - 1
|
|
bpy.ops.dspl.apply_pose()
|
|
elif event.type in {'RIGHT_ARROW', 'DOWN_ARROW'}:
|
|
if self.pose_lib.pose_markers.active_index + 1 >= len(self.pose_lib.pose_markers):
|
|
self.pose_lib.pose_markers.active_index = 0
|
|
else:
|
|
self.pose_lib.pose_markers.active_index = self.pose_lib.pose_markers.active_index + 1
|
|
bpy.ops.dspl.apply_pose()
|
|
|
|
if event.type in {'LEFTMOUSE', 'RET', 'NUMPAD_ENTER'}:
|
|
bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
|
|
return {'FINISHED'}
|
|
|
|
elif event.type in {'RIGHTMOUSE', 'ESC'}:
|
|
self.arm_object.pose.backup_restore()
|
|
self.pose_lib.pose_markers.active_index = self.backup_index
|
|
bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
|
|
return {'CANCELLED'}
|
|
|
|
return {'RUNNING_MODAL'}
|
|
|
|
|
|
def invoke(self, context, event):
|
|
bpy.context.area.tag_redraw()
|
|
|
|
self.arm_object = getArmatureObject(context)
|
|
self.pose_lib = getPoseLib(context)
|
|
|
|
if self.pose_lib is None:
|
|
self.report({'WARNING'}, "DSPL: Pose Library not active")
|
|
return {'CANCELLED'}
|
|
|
|
self.arm_object.pose.backup_create(self.pose_lib)
|
|
self.backup_index = self.pose_lib.pose_markers.active_index
|
|
bpy.ops.dspl.apply_pose()
|
|
|
|
if context.area.type == 'VIEW_3D':
|
|
self.report({'INFO'}, "DSPL: Browsing Poses")
|
|
args = (self, context)
|
|
self._handle = bpy.types.SpaceView3D.draw_handler_add(self.draw_callback_px, args, 'WINDOW', 'POST_PIXEL')
|
|
context.window_manager.modal_handler_add(self)
|
|
return {'RUNNING_MODAL'}
|
|
return {'CANCELLED'}
|
|
|
|
|
|
# Operator to unlink a pose library and mark for removal
|
|
|
|
|
|
class DSPL_OT_UnlinkPoseLibrary(bpy.types.Operator):
|
|
bl_idname = "dspl.unlink_pose_library"
|
|
bl_label = "Unlink Pose Library"
|
|
bl_description = "Unlink Pose Library"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
def execute(self, context):
|
|
arm_object = getArmatureObject(context)
|
|
pose_library = getPoseLib(context)
|
|
|
|
arm_object.dspl.pose_library.name = "del_" + arm_object.dspl.pose_library.name
|
|
arm_object.dspl.pose_library = None
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
# Operator to protect orphaned legacy pose libraries
|
|
|
|
|
|
class DSPL_OT_ProtectOrphanPoseLibrary(bpy.types.Operator):
|
|
bl_idname = "dspl.protect_orphan_pose_library"
|
|
bl_label = "Protect Orphaned Pose Libraries"
|
|
bl_description = "Protect Orphaned Pose Libraries"
|
|
bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
only_poselibs: bpy.props.BoolProperty(name="Only Poselibs", description="Only named poselibs", default=False, options={'SKIP_SAVE'})
|
|
protect: bpy.props.BoolProperty(name="Protect", description="Or not", default=True, options={'SKIP_SAVE'})
|
|
|
|
def check(self, context):
|
|
return True
|
|
|
|
def invoke(self, context, event):
|
|
return context.window_manager.invoke_props_dialog(self)
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
orphaned_act = [act for act in bpy.data.actions if act.users == 0]
|
|
if orphaned_act:
|
|
|
|
# bpy.types.Scene.my_prop = bpy.props.BoolVectorProperty(name="boo", size = len(orphaned_act), default=True)
|
|
col = layout.column()
|
|
col.label(text="Orphaned actions", icon="ORPHAN_DATA")
|
|
|
|
for act in orphaned_act:
|
|
entryrow = col.row()
|
|
protectbutton = entryrow.prop(self, "protect", text=act.name)
|
|
|
|
col.split()
|
|
else:
|
|
layout.label(text="No orphans here")
|
|
|
|
def execute(self, context):
|
|
orphaned_act = [act for act in bpy.data.actions if act.users == 0]
|
|
if orphaned_act:
|
|
for act in orphaned_act:
|
|
if "_loc" in act.name or "PoseLib" in act.name:
|
|
self.report({'INFO'}, "DSPL: Protecting orphaned action: " + act.name)
|
|
act.use_fake_user = True
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
classes = (
|
|
DSPL_OT_CreatePoseLibrary,
|
|
DSPL_OT_ConvertPoseLibrary,
|
|
DSPL_OT_CallPopupMenu,
|
|
DSPL_OT_MenuButtonHandler,
|
|
DSPL_OT_AddPose,
|
|
DSPL_OT_RemovePose,
|
|
DSPL_OT_RenamePose,
|
|
DSPL_OT_MovePose,
|
|
DSPL_OT_ApplyPose,
|
|
DSPL_OT_BrowsePoses,
|
|
DSPL_OT_UnlinkPoseLibrary,
|
|
DSPL_OT_ProtectOrphanPoseLibrary
|
|
)
|
|
|
|
register, unregister = bpy.utils.register_classes_factory(classes)
|