From 1ed8b8f208e7b2cb225dd601a6a059120a124381 Mon Sep 17 00:00:00 2001 From: Blazer Silving Date: Sun, 19 Jan 2025 03:17:45 -0600 Subject: [PATCH] Function to select bones in pose --- common.py | 433 ++++++++++++------------ operators.py | 923 ++++++++++++++++++++++++++------------------------- 2 files changed, 687 insertions(+), 669 deletions(-) diff --git a/common.py b/common.py index 3987db2..d53fcbb 100644 --- a/common.py +++ b/common.py @@ -1,212 +1,223 @@ -import bpy -import mathutils - - -def getArmatureData(context): - try: - arm_object = context.active_object - if arm_object and arm_object.type == "ARMATURE": - return arm_object, getattr(arm_object, "pose_library", None) - elif arm_object and arm_object.parent and arm_object.parent.type == "ARMATURE": - return arm_object.parent, getattr(arm_object.parent, "pose_library", None) - else: - return None, None - except: - return None, None - - -def getArmatureAction(context): - try: - arm_object, pose_library = getArmatureData(context) - if getattr(arm_object.animation_data.action, "pose_markers", None): - return getattr(arm_object.animation_data, "action", None) - except: - pass - - -def searchPoseMarker(context, posename, type): - try: - arm_object, pose_library = getArmatureData(context) - if type == "marker": - return pose_library.pose_markers.get(posename, None) - if type == "frame": - return pose_library.pose_markers.get(posename, None).frame - if type == "index": - return pose_library.pose_markers.find(posename) - except: - pass - - -def findFcurve(context, bone_name, transform, index_int): - arm_object, pose_library = getArmatureData(context) - pose_markers = pose_library.pose_markers - active_frame = pose_markers.active.frame - - fcurve_object = pose_library.fcurves.find( - 'pose.bones["'+bone_name+'"].'+transform+'', index=index_int) - if hasattr(fcurve_object, 'evaluate'): - return fcurve_object.evaluate(active_frame) - else: - return None - - -def createFcurve(context, bone_name, transform, index_int): - arm_object, pose_library = getArmatureData(context) - pose_markers = pose_library.pose_markers - - try: - pose_library.fcurves.new( - 'pose.bones["'+bone_name+'"].'+transform+'', index=index_int, action_group=bone_name) - return - except: - pass - - -def findKeyframe(context, bone, active_frame): - arm_object, pose_library = getArmatureData(context) - - for fcu in pose_library.fcurves: - if fcu.data_path.startswith('pose.bones["'+bone.name+'"]'): - for kp in fcu.keyframe_points: - if kp.co.x == active_frame: - return fcu.data_path - - -def createKeyframe(context, bone_name, transform, index_int, new_marker, loc): - arm_object, pose_library = getArmatureData(context) - pose_markers = pose_library.pose_markers - - try: - pose_library.fcurves.find( - 'pose.bones["'+bone_name+'"].'+transform+'', index=index_int).keyframe_points.insert(new_marker, loc) - return - except: - pass - - -def setKeyframesFromBones(context, arm_object, new_marker): - none_selected = True - for bone in arm_object.pose.bones: - if bone.bone.select: - none_selected = False - - for bone in arm_object.pose.bones: - if bone.bone.select or none_selected == True: - bone_name = bone.name - - if bone.rotation_mode == "XYZ": - rot_mode = "rotation_euler" - elif bone.rotation_mode == "YZX": - rot_mode = "rotation_euler" - elif bone.rotation_mode == "ZXY": - rot_mode = "rotation_euler" - elif bone.rotation_mode == "QUATERNION": - rot_mode = "rotation_quaternion" - else: - self.report({'WARNING'}, "DSPL: Unsupported bone: " + bone.name + ": " + bone.rotation_mode) - rot_mode = None - - loc_x = bone.location[0] - loc_y = bone.location[1] - loc_z = bone.location[2] - createFcurve(context, bone_name, "location", 0) - createFcurve(context, bone_name, "location", 1) - createFcurve(context, bone_name, "location", 2) - createKeyframe(context, bone_name, "location", 0, new_marker, loc_x) - createKeyframe(context, bone_name, "location", 1, new_marker, loc_y) - createKeyframe(context, bone_name, "location", 2, new_marker, loc_z) - if rot_mode == "rotation_quaternion": - rot_w = bone.rotation_quaternion[0] - rot_x = bone.rotation_quaternion[1] - rot_y = bone.rotation_quaternion[2] - rot_z = bone.rotation_quaternion[3] - createFcurve(context, bone_name, rot_mode, 0) - createFcurve(context, bone_name, rot_mode, 1) - createFcurve(context, bone_name, rot_mode, 2) - createFcurve(context, bone_name, rot_mode, 3) - createKeyframe(context, bone_name, rot_mode, 0, new_marker, rot_w) - createKeyframe(context, bone_name, rot_mode, 1, new_marker, rot_x) - createKeyframe(context, bone_name, rot_mode, 2, new_marker, rot_y) - createKeyframe(context, bone_name, rot_mode, 3, new_marker, rot_z) - elif rot_mode == "rotation_euler": - rot_x = bone.rotation_euler[0] - rot_y = bone.rotation_euler[1] - rot_z = bone.rotation_euler[2] - createFcurve(context, bone_name, rot_mode, 0) - createFcurve(context, bone_name, rot_mode, 1) - createFcurve(context, bone_name, rot_mode, 2) - createKeyframe(context, bone_name, rot_mode, 0, new_marker, rot_x) - createKeyframe(context, bone_name, rot_mode, 1, new_marker, rot_y) - createKeyframe(context, bone_name, rot_mode, 2, new_marker, rot_z) - scl_x = bone.scale[0] - scl_y = bone.scale[1] - scl_z = bone.scale[2] - createFcurve(context, bone_name, "scale", 0) - createFcurve(context, bone_name, "scale", 1) - createFcurve(context, bone_name, "scale", 2) - createKeyframe(context, bone_name, "scale", 0, new_marker, scl_x) - createKeyframe(context, bone_name, "scale", 1, new_marker, scl_y) - createKeyframe(context, bone_name, "scale", 2, new_marker, scl_z) - - -def setBonesfromKeyframes(context, arm_object, active_marker): - none_selected = True - for bone in arm_object.pose.bones: - if bone.bone.select: - none_selected = False - - for bone in arm_object.pose.bones: - if bone.bone.select or none_selected == True: - bone_name = bone.name - - if findKeyframe(context, bone, active_marker.frame) is None: - continue - - if bone.rotation_mode == "XYZ": - rot_mode = "rotation_euler" - elif bone.rotation_mode == "YZX": - rot_mode = "rotation_euler" - elif bone.rotation_mode == "ZXY": - rot_mode = "rotation_euler" - elif bone.rotation_mode == "QUATERNION": - rot_mode = "rotation_quaternion" - else: - self.report({'WARNING'}, "DSPL: Unsupported bone: " + bone.name + ": " + bone.rotation_mode) - rot_mode = None - - loc_x = findFcurve(context, bone_name, "location", 0) or 0.0 - loc_y = findFcurve(context, bone_name, "location", 1) or 0.0 - loc_z = findFcurve(context, bone_name, "location", 2) or 0.0 - if rot_mode == "rotation_quaternion": - rot_w = findFcurve(context, bone_name, rot_mode, 0) or 1.0 - rot_x = findFcurve(context, bone_name, rot_mode, 1) or 0.0 - rot_y = findFcurve(context, bone_name, rot_mode, 2) or 0.0 - rot_z = findFcurve(context, bone_name, rot_mode, 3) or 0.0 - elif rot_mode == "rotation_euler": - rot_x = findFcurve(context, bone_name, rot_mode, 0) or 0.0 - rot_y = findFcurve(context, bone_name, rot_mode, 1) or 0.0 - rot_z = findFcurve(context, bone_name, rot_mode, 2) or 0.0 - scl_x = findFcurve(context, bone_name, "scale", 0) or 1.0 - scl_y = findFcurve(context, bone_name, "scale", 1) or 1.0 - scl_z = findFcurve(context, bone_name, "scale", 2) or 1.0 - - bone.location = mathutils.Vector((loc_x, loc_y, loc_z)) - if bone.rotation_mode == "XYZ": - bone.rotation_euler = mathutils.Euler( - (rot_x, rot_y, rot_z)) - elif bone.rotation_mode == "YZX": - bone.rotation_euler = mathutils.Euler( - (rot_x, rot_y, rot_z)) - elif bone.rotation_mode == "ZXY": - bone.rotation_euler = mathutils.Euler( - (rot_z, rot_x, rot_y)) - elif bone.rotation_mode == "YXZ": - bone.rotation_euler = mathutils.Euler( - (rot_y, rot_x, rot_z)) - elif bone.rotation_mode == "XZY": - bone.rotation_euler = mathutils.Euler( - (rot_x, rot_z, rot_y)) - elif rot_mode == "rotation_quaternion": - bone.rotation_quaternion = mathutils.Quaternion( - (rot_w, rot_x, rot_y, rot_z)) +import bpy +import mathutils + + +def getArmatureData(context): + try: + arm_object = context.active_object + if arm_object and arm_object.type == "ARMATURE": + return arm_object, getattr(arm_object, "pose_library", None) + elif arm_object and arm_object.parent and arm_object.parent.type == "ARMATURE": + return arm_object.parent, getattr(arm_object.parent, "pose_library", None) + else: + return None, None + except: + return None, None + + +def getArmatureAction(context): + try: + arm_object, pose_library = getArmatureData(context) + if getattr(arm_object.animation_data.action, "pose_markers", None): + return getattr(arm_object.animation_data, "action", None) + except: + pass + + +def searchPoseMarker(context, posename, type): + try: + arm_object, pose_library = getArmatureData(context) + if type == "marker": + return pose_library.pose_markers.get(posename, None) + if type == "frame": + return pose_library.pose_markers.get(posename, None).frame + if type == "index": + return pose_library.pose_markers.find(posename) + except: + pass + + +def selectBonesinPose(context, posename, active_marker): + try: + arm_object, pose_library = getArmatureData(context) + for bone in arm_object.pose.bones: + bone.bone.select = False + if findKeyframe(context, bone, active_marker.frame): + bone.bone.select = True + except: + pass + + +def findFcurve(context, bone_name, transform, index_int): + arm_object, pose_library = getArmatureData(context) + pose_markers = pose_library.pose_markers + active_frame = pose_markers.active.frame + + fcurve_object = pose_library.fcurves.find( + 'pose.bones["'+bone_name+'"].'+transform+'', index=index_int) + if hasattr(fcurve_object, 'evaluate'): + return fcurve_object.evaluate(active_frame) + else: + return None + + +def createFcurve(context, bone_name, transform, index_int): + arm_object, pose_library = getArmatureData(context) + pose_markers = pose_library.pose_markers + + try: + pose_library.fcurves.new( + 'pose.bones["'+bone_name+'"].'+transform+'', index=index_int, action_group=bone_name) + return + except: + pass + + +def findKeyframe(context, bone, active_frame): + arm_object, pose_library = getArmatureData(context) + + for fcu in pose_library.fcurves: + if fcu.data_path.startswith('pose.bones["'+bone.name+'"]'): + for kp in fcu.keyframe_points: + if kp.co.x == active_frame: + return fcu.data_path + + +def createKeyframe(context, bone_name, transform, index_int, new_marker, loc): + arm_object, pose_library = getArmatureData(context) + pose_markers = pose_library.pose_markers + + try: + pose_library.fcurves.find( + 'pose.bones["'+bone_name+'"].'+transform+'', index=index_int).keyframe_points.insert(new_marker, loc) + return + except: + pass + + +def setKeyframesFromBones(context, arm_object, new_marker): + none_selected = True + for bone in arm_object.pose.bones: + if bone.bone.select: + none_selected = False + + for bone in arm_object.pose.bones: + if bone.bone.select or none_selected == True: + bone_name = bone.name + + if bone.rotation_mode == "XYZ": + rot_mode = "rotation_euler" + elif bone.rotation_mode == "YZX": + rot_mode = "rotation_euler" + elif bone.rotation_mode == "ZXY": + rot_mode = "rotation_euler" + elif bone.rotation_mode == "QUATERNION": + rot_mode = "rotation_quaternion" + else: + self.report({'WARNING'}, "DSPL: Unsupported bone: " + bone.name + ": " + bone.rotation_mode) + rot_mode = None + + loc_x = bone.location[0] + loc_y = bone.location[1] + loc_z = bone.location[2] + createFcurve(context, bone_name, "location", 0) + createFcurve(context, bone_name, "location", 1) + createFcurve(context, bone_name, "location", 2) + createKeyframe(context, bone_name, "location", 0, new_marker, loc_x) + createKeyframe(context, bone_name, "location", 1, new_marker, loc_y) + createKeyframe(context, bone_name, "location", 2, new_marker, loc_z) + if rot_mode == "rotation_quaternion": + rot_w = bone.rotation_quaternion[0] + rot_x = bone.rotation_quaternion[1] + rot_y = bone.rotation_quaternion[2] + rot_z = bone.rotation_quaternion[3] + createFcurve(context, bone_name, rot_mode, 0) + createFcurve(context, bone_name, rot_mode, 1) + createFcurve(context, bone_name, rot_mode, 2) + createFcurve(context, bone_name, rot_mode, 3) + createKeyframe(context, bone_name, rot_mode, 0, new_marker, rot_w) + createKeyframe(context, bone_name, rot_mode, 1, new_marker, rot_x) + createKeyframe(context, bone_name, rot_mode, 2, new_marker, rot_y) + createKeyframe(context, bone_name, rot_mode, 3, new_marker, rot_z) + elif rot_mode == "rotation_euler": + rot_x = bone.rotation_euler[0] + rot_y = bone.rotation_euler[1] + rot_z = bone.rotation_euler[2] + createFcurve(context, bone_name, rot_mode, 0) + createFcurve(context, bone_name, rot_mode, 1) + createFcurve(context, bone_name, rot_mode, 2) + createKeyframe(context, bone_name, rot_mode, 0, new_marker, rot_x) + createKeyframe(context, bone_name, rot_mode, 1, new_marker, rot_y) + createKeyframe(context, bone_name, rot_mode, 2, new_marker, rot_z) + scl_x = bone.scale[0] + scl_y = bone.scale[1] + scl_z = bone.scale[2] + createFcurve(context, bone_name, "scale", 0) + createFcurve(context, bone_name, "scale", 1) + createFcurve(context, bone_name, "scale", 2) + createKeyframe(context, bone_name, "scale", 0, new_marker, scl_x) + createKeyframe(context, bone_name, "scale", 1, new_marker, scl_y) + createKeyframe(context, bone_name, "scale", 2, new_marker, scl_z) + + +def setBonesfromKeyframes(context, arm_object, active_marker): + none_selected = True + for bone in arm_object.pose.bones: + if bone.bone.select: + none_selected = False + + for bone in arm_object.pose.bones: + if bone.bone.select or none_selected == True: + bone_name = bone.name + + if findKeyframe(context, bone, active_marker.frame) is None: + continue + + if bone.rotation_mode == "XYZ": + rot_mode = "rotation_euler" + elif bone.rotation_mode == "YZX": + rot_mode = "rotation_euler" + elif bone.rotation_mode == "ZXY": + rot_mode = "rotation_euler" + elif bone.rotation_mode == "QUATERNION": + rot_mode = "rotation_quaternion" + else: + self.report({'WARNING'}, "DSPL: Unsupported bone: " + bone.name + ": " + bone.rotation_mode) + rot_mode = None + + loc_x = findFcurve(context, bone_name, "location", 0) or 0.0 + loc_y = findFcurve(context, bone_name, "location", 1) or 0.0 + loc_z = findFcurve(context, bone_name, "location", 2) or 0.0 + if rot_mode == "rotation_quaternion": + rot_w = findFcurve(context, bone_name, rot_mode, 0) or 1.0 + rot_x = findFcurve(context, bone_name, rot_mode, 1) or 0.0 + rot_y = findFcurve(context, bone_name, rot_mode, 2) or 0.0 + rot_z = findFcurve(context, bone_name, rot_mode, 3) or 0.0 + elif rot_mode == "rotation_euler": + rot_x = findFcurve(context, bone_name, rot_mode, 0) or 0.0 + rot_y = findFcurve(context, bone_name, rot_mode, 1) or 0.0 + rot_z = findFcurve(context, bone_name, rot_mode, 2) or 0.0 + scl_x = findFcurve(context, bone_name, "scale", 0) or 1.0 + scl_y = findFcurve(context, bone_name, "scale", 1) or 1.0 + scl_z = findFcurve(context, bone_name, "scale", 2) or 1.0 + + bone.location = mathutils.Vector((loc_x, loc_y, loc_z)) + if bone.rotation_mode == "XYZ": + bone.rotation_euler = mathutils.Euler( + (rot_x, rot_y, rot_z)) + elif bone.rotation_mode == "YZX": + bone.rotation_euler = mathutils.Euler( + (rot_x, rot_y, rot_z)) + elif bone.rotation_mode == "ZXY": + bone.rotation_euler = mathutils.Euler( + (rot_z, rot_x, rot_y)) + elif bone.rotation_mode == "YXZ": + bone.rotation_euler = mathutils.Euler( + (rot_y, rot_x, rot_z)) + elif bone.rotation_mode == "XZY": + bone.rotation_euler = mathutils.Euler( + (rot_x, rot_z, rot_y)) + elif rot_mode == "rotation_quaternion": + bone.rotation_quaternion = mathutils.Quaternion( + (rot_w, rot_x, rot_y, rot_z)) bone.scale = mathutils.Vector((scl_x, scl_y, scl_z)) \ No newline at end of file diff --git a/operators.py b/operators.py index e161cc4..bc49b72 100644 --- a/operators.py +++ b/operators.py @@ -1,458 +1,465 @@ -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, pose_library = getArmatureData(context) - arm_object.pose_library = bpy.data.actions.new( - name=arm_object.name + "_PoseLib") - arm_object.pose_library.use_fake_user = True - - return {'FINISHED'} - - -# Operator to convert an action to pose library - - -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, pose_library = getArmatureData(context) - if pose_library is None: - arm_object.pose_library = arm_object.animation_data.action - arm_object.animation_data.action = None - - return {'FINISHED'} - - -# 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(default="Pose") - replace: bpy.props.BoolProperty(name="Replace", description="Replace existing pose", default=False, options={'SKIP_SAVE'}) - - def execute(self, context): - arm_object, pose_library = getArmatureData(context) - if self.replace == False: - pose_markers = pose_library.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) - - pose_library.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: - pose_markers = pose_library.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, pose_library = getArmatureData(context) - pose_markers = pose_library.pose_markers - if self.posename: - pose_library.pose_markers.active_index = searchPoseMarker(context, posename=self.posename, type="index") - active_index = pose_library.pose_markers.active_index - else: - active_index = pose_markers.active_index - active_marker = pose_markers.active - active_frame = active_marker.frame - - fcurves = pose_library.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) - - pose_library.pose_markers.active = next_marker - pose_library.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, pose_library = getArmatureData(context) - pose_markers = pose_library.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, pose_library = getArmatureData(context) - pose_markers = pose_library.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: - 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: - return {'FINISHED'} - elif swap_index >= len(pose_library.pose_markers): - return {'FINISHED'} - - swap_marker = pose_markers[swap_index] - - tmp_marker_name = swap_marker.name - tmp_marker_frame = swap_marker.frame - pose_library.pose_markers[swap_index].name = active_marker.name - pose_library.pose_markers[swap_index].frame = active_marker.frame - pose_library.pose_markers[active_index].name = tmp_marker_name - pose_library.pose_markers[active_index].frame = tmp_marker_frame - pose_library.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, Shift+Click to rename, Alt+Click to remove)" - bl_options = {'REGISTER', 'UNDO'} - - posename: bpy.props.StringProperty() - - - def execute(self, context): - arm_object, pose_library = getArmatureData(context) - pose_markers = pose_library.pose_markers - - if self.posename: - active_marker = searchPoseMarker(context, posename=self.posename, type="marker") - active_frame = active_marker.frame - active_posename = active_marker.name - pose_library.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 - arm_object, pose_library = getArmatureData(context) - pose_library.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_library.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_library.pose_markers.active_index <= 0: - self.pose_library.pose_markers.active_index = len(self.pose_library.pose_markers) - 1 - else: - self.pose_library.pose_markers.active_index = self.pose_library.pose_markers.active_index - 1 - bpy.ops.dspl.apply_pose() - elif event.type in {'RIGHT_ARROW', 'DOWN_ARROW'}: - if self.pose_library.pose_markers.active_index + 1 >= len(self.pose_library.pose_markers): - self.pose_library.pose_markers.active_index = 0 - else: - self.pose_library.pose_markers.active_index = self.pose_library.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_library.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, self.pose_library = getArmatureData(context) - - if self.pose_library is None: - self.report({'WARNING'}, "DSPL: Pose Library not active") - return {'CANCELLED'} - - self.arm_object.pose.backup_create(self.pose_library) - self.backup_index = self.pose_library.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, pose_library = getArmatureData(context) - - try: - arm_object.pose_library = None - # arm_object.pose_library.use_fake_user = False - # if not arm_object.pose_library.name.startswith("del_"): - # arm_object.pose_library.name = "del_{}".format(arm_object.pose_library.name) - except: - pass - - 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_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) +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, pose_library = getArmatureData(context) + arm_object.pose_library = bpy.data.actions.new( + name=arm_object.name + "_PoseLib") + arm_object.pose_library.use_fake_user = True + + return {'FINISHED'} + + +# Operator to convert an action to pose library + + +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, pose_library = getArmatureData(context) + if pose_library is None: + arm_object.pose_library = arm_object.animation_data.action + arm_object.animation_data.action = None + + return {'FINISHED'} + + +# 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(default="Pose") + replace: bpy.props.BoolProperty(name="Replace", description="Replace existing pose", default=False, options={'SKIP_SAVE'}) + + def execute(self, context): + arm_object, pose_library = getArmatureData(context) + if self.replace == False: + pose_markers = pose_library.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) + + pose_library.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: + pose_markers = pose_library.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, pose_library = getArmatureData(context) + pose_markers = pose_library.pose_markers + if self.posename: + pose_library.pose_markers.active_index = searchPoseMarker(context, posename=self.posename, type="index") + active_index = pose_library.pose_markers.active_index + else: + active_index = pose_markers.active_index + active_marker = pose_markers.active + active_frame = active_marker.frame + + fcurves = pose_library.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) + + pose_library.pose_markers.active = next_marker + pose_library.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, pose_library = getArmatureData(context) + pose_markers = pose_library.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, pose_library = getArmatureData(context) + pose_markers = pose_library.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: + 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: + return {'FINISHED'} + elif swap_index >= len(pose_library.pose_markers): + return {'FINISHED'} + + swap_marker = pose_markers[swap_index] + + tmp_marker_name = swap_marker.name + tmp_marker_frame = swap_marker.frame + pose_library.pose_markers[swap_index].name = active_marker.name + pose_library.pose_markers[swap_index].frame = active_marker.frame + pose_library.pose_markers[active_index].name = tmp_marker_name + pose_library.pose_markers[active_index].frame = tmp_marker_frame + pose_library.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 bones, Shift+Click to rename, Alt+Click to remove)" + bl_options = {'REGISTER', 'UNDO'} + + posename: bpy.props.StringProperty() + + + def execute(self, context): + arm_object, pose_library = getArmatureData(context) + pose_markers = pose_library.pose_markers + + if self.posename: + active_marker = searchPoseMarker(context, posename=self.posename, type="marker") + active_frame = active_marker.frame + active_posename = active_marker.name + pose_library.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 bones + arm_object, pose_library = getArmatureData(context) + active_marker = searchPoseMarker(context, posename=self.posename, type="marker") + + arm_object.select = True + bpy.context.view_layer.objects.active = arm_object + bpy.ops.object.mode_set(mode='POSE') + selectBonesinPose(context, self.posename, active_marker) + + self.execute(context) + 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_library.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_library.pose_markers.active_index <= 0: + self.pose_library.pose_markers.active_index = len(self.pose_library.pose_markers) - 1 + else: + self.pose_library.pose_markers.active_index = self.pose_library.pose_markers.active_index - 1 + bpy.ops.dspl.apply_pose() + elif event.type in {'RIGHT_ARROW', 'DOWN_ARROW'}: + if self.pose_library.pose_markers.active_index + 1 >= len(self.pose_library.pose_markers): + self.pose_library.pose_markers.active_index = 0 + else: + self.pose_library.pose_markers.active_index = self.pose_library.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_library.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, self.pose_library = getArmatureData(context) + + if self.pose_library is None: + self.report({'WARNING'}, "DSPL: Pose Library not active") + return {'CANCELLED'} + + self.arm_object.pose.backup_create(self.pose_library) + self.backup_index = self.pose_library.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, pose_library = getArmatureData(context) + + try: + arm_object.pose_library = None + # arm_object.pose_library.use_fake_user = False + # if not arm_object.pose_library.name.startswith("del_"): + # arm_object.pose_library.name = "del_{}".format(arm_object.pose_library.name) + except: + pass + + 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_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)