1 Commits

Author SHA1 Message Date
79be3983db Remove dsplvars 2024-12-28 04:05:44 -06:00
5 changed files with 207 additions and 208 deletions

View File

@ -1,48 +1,6 @@
# Damn Simple Pose Library # Damn Simple Pose Library
In Blender 3.x, a new Asset-based Pose system was introduced. That's fine and dandy, but it was not (and still is not as of Jan 2025) ready to replace the old Action-based Pose Library system. In Blender 3.x, a new Asset-based Pose system was introduced.
That's fine and dandy, however because of that, the old Pose Library was deprecated and gutted out real quick-like. Too quick.
The former Pose Library was deprecated and gutted rapidly during Blender 3.x, leaving legacy users high and dry. This addon brings the feature back as best as it can in a modern panel. Most, if not all existing Pose Libraries should work without any modifications, little new is introduced outside of string suffixes and mitigations (read further). This brings it back as best as it can.
## Features
- Supports Blender 3.3 - 4.3 (4.1 recommended).
- New panel interface for interacting with pose libraries, inspired by [gret's Actions Panel feature](https://github.com/greisane/gret?tab=readme-ov-file#animation-actions-panel).
- The older Pose Library list layout is provided as option.
- Operators and data property [that were removed in 3.5](https://projects.blender.org/blender/blender/issues/93406) are ported from C to Python:
- `dspl.apply_pose`
- `dspl.browse_poses`
- `dspl.create_pose_library`
- `dspl.convert_pose_library`
- `dspl.add_pose`
- `dspl.move_pose`
- `dspl.remove_pose`
- `dspl.rename_pose`
- `dspl.unlink_pose_library`
- `Object.pose_library`
## Installation
1. [Download the repository as a zip](https://git.bkspl.me/breakingspell/DamnSimplePoseLibrary/archive/develop.zip), or otherwise clone the repository.
2. Install as an Add-on in Blender via Install -> Zip, and enable.
3. Optionally configure the suffix strings to fit your workflow, and whether the orhpan checker should run at startup.
## Usage
1. Open Sidebar (`N`), and choose the `Pose` tab.
2. Select existing Pose Library from drop-down, or create a new library.
### Hotkeys:
- `Shift + L` - Add/Replace Pose
- `Alt + L` - Browse Poses with Arrow Keys
### Menu Controls
- `Single click` - Apply Pose
- `Shift + Click` - Rename Pose
- `Alt + Click` - Remove Pose
- `Ctrl + Click ` - Select Pose
- Choose `Edit` for fast Move/Rename/Removal
## Considerations
- Opening older scenes will cause existing Pose Libraries to unlink from their former targets and fall into orphan state. They can be re-linked and will retain their link when saved afterwards, but would otherwise disappear if saved without linking or protecting the datablock.
- This is due to the core DNA type `poselib` having been removed, so objects will drop the data. [There is no way to prevent this](https://developer.blender.org/docs/features/core/rna/#internals) as Python ID properties cannot be present at program runtime, only once the add-on is initialized.
- To mitgate, an operator is provided to protect orphaned pose libraries: `dspl.protect_orphan_pose_library`.
- This add-on was made much easier by the `pose_markers` property being retained for converting old pose libraries to the new asset system. If they decide to remove that property as well, there will be a need to improvise an index-based lookup.

View File

@ -3,10 +3,10 @@ from . import gui, operators, common, keymaps
bl_info = \ bl_info = \
{ {
"name": "Damn Simple Pose Library", "name": "Damn Simple Pose Library",
"author": "breakingspell", "author": "jackie",
"version": (0, 1, 0), "version": (4, 1, 0),
"blender": (3, 6, 0), "blender": (3, 5, 0),
"description": "Re-implement Pose Library", "description": "woah",
"category": "Object Data", "category": "Object Data",
} }
@ -22,27 +22,38 @@ if _need_reload:
operators = importlib.reload(operators) operators = importlib.reload(operators)
class dsplObj(bpy.types.PropertyGroup):
pose_library: bpy.props.PointerProperty(
name="Active Pose Library", description="",
override={'LIBRARY_OVERRIDABLE'}, type=bpy.types.Action)
# update = common.poselib_update)
# , update = anim_layers.layer_name_update
class dsplSettings(bpy.types.PropertyGroup): class dsplSettings(bpy.types.PropertyGroup):
new_menu: bpy.props.BoolProperty( new_menu: bpy.props.BoolProperty(
name="New Menu", description="Toggle New Menu", default=True) name="New Menu", description="Toggle New Menu", default=False)
edit_mode: bpy.props.BoolProperty( edit_mode: bpy.props.BoolProperty(
name="Edit Mode", description="Toggle Edit Mode", default=False) name="Edit Mode", description="Toggle Edit Mode", default=False)
classes = dsplSettings classes = (dsplObj, dsplSettings)
def pose_libraries_poll(self, action):
if getattr(action, "pose_markers", None):
return True
def register(): def register():
from bpy.utils import register_class from bpy.utils import register_class
register_class(dsplSettings) for cls in classes:
register_class(cls)
bpy.types.Object.pose_library = bpy.props.PointerProperty( # bpy.types.Armature.pose_library = bpy.props.PointerProperty(
name="Active Pose Library", description="", # type=dsplObj, override={'LIBRARY_OVERRIDABLE'})
type=bpy.types.Action, override={'LIBRARY_OVERRIDABLE'},
poll=pose_libraries_poll)
# bpy.types.Object.pose_library = bpy.props.PointerProperty(
# type=bpy.types.Action, options={'LIBRARY_EDITABLE'}, override={'LIBRARY_OVERRIDABLE'})
bpy.types.Object.dspl = bpy.props.PointerProperty(
type=dsplObj, override={'LIBRARY_OVERRIDABLE'})
bpy.types.Object.dsplvars = bpy.props.PointerProperty(
type=dsplVars, override={'LIBRARY_OVERRIDABLE'})
bpy.types.Scene.dsplSettings = bpy.props.PointerProperty( bpy.types.Scene.dsplSettings = bpy.props.PointerProperty(
type=dsplSettings, override={'LIBRARY_OVERRIDABLE'}) type=dsplSettings, override={'LIBRARY_OVERRIDABLE'})
@ -50,12 +61,14 @@ def register():
operators.register() operators.register()
keymaps.register() keymaps.register()
def unregister(): def unregister() -> None:
from bpy.utils import unregister_class from bpy.utils import unregister_class
unregister_class(dsplSettings) for cls in classes:
unregister_class(cls)
del bpy.types.Object.dspl
del bpy.types.Object.dsplvars
del bpy.types.Scene.dsplSettings del bpy.types.Scene.dsplSettings
del bpy.types.Object.pose_library
keymaps.unregister() keymaps.unregister()
operators.unregister() operators.unregister()

125
common.py
View File

@ -2,31 +2,51 @@ import bpy
import mathutils import mathutils
def getArmatureData(context): def getArmatureObject(context):
try: obj = context.active_object
arm_object = context.active_object if obj:
if arm_object and arm_object.type == "ARMATURE": if obj.type == "ARMATURE":
return arm_object, getattr(arm_object, "pose_library", None) return obj
elif arm_object and arm_object.parent and arm_object.parent.type == "ARMATURE": elif obj.parent is not None and obj.parent.type == "ARMATURE":
return arm_object.parent, getattr(arm_object.parent, "pose_library", None) return obj.parent
else: else:
return None, None return None
except:
return None, None
def getLegacyPoseLibrary(context):
arm = getArmatureObject(context)
return getattr(arm, "pose_library", None)
def getArmatureAction(context): def getArmatureAction(context):
try: arm = getArmatureObject(context)
arm_object, pose_library = getArmatureData(context) return getattr(arm.animation_data, "action", None)
if getattr(arm_object.animation_data.action, "pose_markers", None):
return getattr(arm_object.animation_data, "action", None)
except: def getDsplAction(context):
pass arm = getArmatureObject(context)
return getattr(arm.dspl, "pose_library", None)
def getPoseLib(context):
arm = getArmatureObject(context)
pose_library_dspl = getDsplAction(context)
pose_library_action = getArmatureAction(context)
pose_library_legacy = getLegacyPoseLibrary(context)
if pose_library_dspl:
return pose_library_dspl
elif pose_library_action and not pose_library_dspl:
return pose_library_action
elif pose_library_legacy and not pose_library_dspl:
return pose_library_legacy
else:
return None
def searchPoseMarker(context, posename, type): def searchPoseMarker(context, posename, type):
try: try:
arm_object, pose_library = getArmatureData(context) pose_library = getPoseLib(context)
if type == "marker": if type == "marker":
return pose_library.pose_markers.get(posename, None) return pose_library.pose_markers.get(posename, None)
if type == "frame": if type == "frame":
@ -37,23 +57,13 @@ def searchPoseMarker(context, posename, type):
pass 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): def findFcurve(context, bone_name, transform, index_int):
arm_object, pose_library = getArmatureData(context) arm_object = getArmatureObject(context)
pose_markers = pose_library.pose_markers action_object = getPoseLib(context)
pose_markers = action_object.pose_markers
active_frame = pose_markers.active.frame active_frame = pose_markers.active.frame
fcurve_object = pose_library.fcurves.find( fcurve_object = action_object.fcurves.find(
'pose.bones["'+bone_name+'"].'+transform+'', index=index_int) 'pose.bones["'+bone_name+'"].'+transform+'', index=index_int)
if hasattr(fcurve_object, 'evaluate'): if hasattr(fcurve_object, 'evaluate'):
return fcurve_object.evaluate(active_frame) return fcurve_object.evaluate(active_frame)
@ -62,33 +72,25 @@ def findFcurve(context, bone_name, transform, index_int):
def createFcurve(context, bone_name, transform, index_int): def createFcurve(context, bone_name, transform, index_int):
arm_object, pose_library = getArmatureData(context) arm_object = getArmatureObject(context)
pose_markers = pose_library.pose_markers action_object = getPoseLib(context)
pose_markers = action_object.pose_markers
try: try:
pose_library.fcurves.new( action_object.fcurves.new(
'pose.bones["'+bone_name+'"].'+transform+'', index=index_int, action_group=bone_name) 'pose.bones["'+bone_name+'"].'+transform+'', index=index_int, action_group=bone_name)
return return
except: except:
pass 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): def createKeyframe(context, bone_name, transform, index_int, new_marker, loc):
arm_object, pose_library = getArmatureData(context) arm_object = getArmatureObject(context)
pose_markers = pose_library.pose_markers action_object = getPoseLib(context)
pose_markers = action_object.pose_markers
try: try:
pose_library.fcurves.find( action_object.fcurves.find(
'pose.bones["'+bone_name+'"].'+transform+'', index=index_int).keyframe_points.insert(new_marker, loc) 'pose.bones["'+bone_name+'"].'+transform+'', index=index_int).keyframe_points.insert(new_marker, loc)
return return
except: except:
@ -105,7 +107,11 @@ def setKeyframesFromBones(context, arm_object, new_marker):
if bone.bone.select or none_selected == True: if bone.bone.select or none_selected == True:
bone_name = bone.name bone_name = bone.name
if bone.rotation_mode in ("XYZ", "XZY", "YXZ", "YZX", "ZXY", "ZYX"): 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" rot_mode = "rotation_euler"
elif bone.rotation_mode == "QUATERNION": elif bone.rotation_mode == "QUATERNION":
rot_mode = "rotation_quaternion" rot_mode = "rotation_quaternion"
@ -166,10 +172,11 @@ def setBonesfromKeyframes(context, arm_object, active_marker):
if bone.bone.select or none_selected == True: if bone.bone.select or none_selected == True:
bone_name = bone.name bone_name = bone.name
if findKeyframe(context, bone, active_marker.frame) is None: if bone.rotation_mode == "XYZ":
continue rot_mode = "rotation_euler"
elif bone.rotation_mode == "YZX":
if bone.rotation_mode in ("XYZ", "XZY", "YXZ", "YZX", "ZXY", "ZYX"): rot_mode = "rotation_euler"
elif bone.rotation_mode == "ZXY":
rot_mode = "rotation_euler" rot_mode = "rotation_euler"
elif bone.rotation_mode == "QUATERNION": elif bone.rotation_mode == "QUATERNION":
rot_mode = "rotation_quaternion" rot_mode = "rotation_quaternion"
@ -194,9 +201,21 @@ def setBonesfromKeyframes(context, arm_object, active_marker):
scl_z = findFcurve(context, bone_name, "scale", 2) or 1.0 scl_z = findFcurve(context, bone_name, "scale", 2) or 1.0
bone.location = mathutils.Vector((loc_x, loc_y, loc_z)) bone.location = mathutils.Vector((loc_x, loc_y, loc_z))
if rot_mode == "rotation_euler": if bone.rotation_mode == "XYZ":
bone.rotation_euler = mathutils.Euler( bone.rotation_euler = mathutils.Euler(
(rot_x, rot_y, rot_z)) (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": elif rot_mode == "rotation_quaternion":
bone.rotation_quaternion = mathutils.Quaternion( bone.rotation_quaternion = mathutils.Quaternion(
(rot_w, rot_x, rot_y, rot_z)) (rot_w, rot_x, rot_y, rot_z))

65
gui.py
View File

@ -20,8 +20,7 @@ class DATA_PT_DSPLPanel(bpy.types.Panel):
# Detect Armature object and parent # Detect Armature object and parent
armature_layout = dspl_panel_layout.column(align=True) armature_layout = dspl_panel_layout.column(align=True)
active_obj = context.active_object active_obj = context.active_object
arm_object, pose_library = getArmatureData(context) arm_object = getArmatureObject(context)
pose_library_action = getArmatureAction(context)
if arm_object: if arm_object:
if arm_object == active_obj: if arm_object == active_obj:
@ -30,32 +29,38 @@ class DATA_PT_DSPLPanel(bpy.types.Panel):
armature_layout.label(text=arm_object.name + " (Parent)") armature_layout.label(text=arm_object.name + " (Parent)")
# Attach or create pose library # Attach or create pose library
if pose_library or pose_library_action: pose_library_dspl = getDsplAction(context)
if pose_library and not pose_library_action: pose_library_action = getArmatureAction(context)
armature_layout.template_ID(
arm_object, "pose_library", new="dspl.create_pose_library", unlink="dspl.unlink_pose_library")
elif pose_library_action and not pose_library: if pose_library_dspl or pose_library_action:
if pose_library_dspl and not pose_library_action:
active_pose_library = pose_library_dspl
armature_layout.template_ID(
arm_object.dspl, "pose_library", new="dspl.create_pose_library", unlink="dspl.unlink_pose_library")
elif pose_library_action and not pose_library_dspl:
active_pose_library = pose_library_action
armature_layout.template_ID( armature_layout.template_ID(
arm_object.animation_data, "action", new="dspl.create_pose_library") arm_object.animation_data, "action", new="dspl.create_pose_library")
armature_layout.label( armature_layout.label(
text="Pose Library detected as Action") text="Legacy Pose Library detected.")
armature_layout.label( armature_layout.label(
text="You should convert to avoid problems") text="You may convert to avoid problems")
armature_layout.operator( armature_layout.operator(
"dspl.convert_pose_library", icon='PLUGIN', text="Convert to Pose Library") "dspl.convert_pose_library", icon='PLUGIN', text="Convert to DSPL")
elif pose_library and pose_library_action: elif pose_library_action and pose_library_dspl:
armature_layout.template_ID( armature_layout.template_ID(
arm_object, "pose_library", new="dspl.create_pose_library") arm_object.dspl, "pose_library", new="dspl.create_pose_library")
armature_layout.label(text="Pose Library is opened as Action") armature_layout.label(text="Double pose configuration!!")
armature_layout.label(text="Keyframes will affect pose") armature_layout.label(text="You should not proceed")
armature_layout.operator( armature_layout.operator(
"dspl.convert_pose_library", icon='PLUGIN', text="Unlink from Action") "dspl.convert_pose_library", icon='PLUGIN', text="Convert to DSPL")
active_pose_library = pose_library_dspl
# List poses in pose library # List poses in pose library
if pose_library: if active_pose_library:
pose_box_layout = dspl_panel_layout.column() pose_box_layout = dspl_panel_layout.column()
# Menu switcher # Menu switcher
@ -67,14 +72,14 @@ class DATA_PT_DSPLPanel(bpy.types.Panel):
quick_pose_controls_layout = pose_box_layout.column() quick_pose_controls_layout = pose_box_layout.column()
quick_pose_controls_layout.menu( quick_pose_controls_layout.menu(
OBJECT_MT_AddPoseMenu.bl_idname, icon='ADD', text="New Pose") OBJECT_MT_AddPoseMenu.bl_idname, icon='ADD', text="New Pose")
if pose_library.pose_markers.active: if active_pose_library.pose_markers.active:
quick_apply_layout = quick_pose_controls_layout.split( quick_apply_layout = quick_pose_controls_layout.split(
align=True) align=True)
quick_apply_layout.operator( quick_apply_layout.operator(
"dspl.browse_poses", icon='CON_ARMATURE', text="Browse") "dspl.browse_poses", icon='CON_ARMATURE', text="Browse")
if dsplsettings.new_menu == False: if dsplsettings.new_menu == False:
quick_apply_layout.operator( quick_apply_layout.operator(
"dspl.apply_pose", icon='ARMATURE_DATA', text="Apply Pose").posename = pose_library.pose_markers.active.name "dspl.apply_pose", icon='ARMATURE_DATA', text="Apply Pose").posename = active_pose_library.pose_markers.active.name
else: else:
quick_apply_layout.prop(dsplsettings, quick_apply_layout.prop(dsplsettings,
"edit_mode", icon='GREASEPENCIL', text="Edit", toggle=True) "edit_mode", icon='GREASEPENCIL', text="Edit", toggle=True)
@ -83,11 +88,11 @@ class DATA_PT_DSPLPanel(bpy.types.Panel):
if dsplsettings.new_menu == True: if dsplsettings.new_menu == True:
pose_button_layout = pose_box_layout.row() pose_button_layout = pose_box_layout.row()
pose_button_entries_layout = pose_button_layout.column(align=True) pose_button_entries_layout = pose_button_layout.column(align=True)
for pm in pose_library.pose_markers: for pm in active_pose_library.pose_markers:
row = pose_button_entries_layout.row(align=True) row = pose_button_entries_layout.row(align=True)
# Selected indicator # Selected indicator
selected = pm.frame == pose_library.pose_markers.active.frame selected = pm.frame == active_pose_library.pose_markers.active.frame
if dsplsettings.edit_mode == False: if dsplsettings.edit_mode == False:
row.label(text="", icon='PMARKER_ACT' if selected else 'PMARKER_SEL') row.label(text="", icon='PMARKER_ACT' if selected else 'PMARKER_SEL')
@ -114,19 +119,19 @@ class DATA_PT_DSPLPanel(bpy.types.Panel):
# Pose list # Pose list
pose_list_entries_layout = pose_list_layout.row() pose_list_entries_layout = pose_list_layout.row()
pose_list_entries_layout.template_list("UI_UL_list","pose_markers", pose_list_entries_layout.template_list("UI_UL_list","pose_markers",
pose_library, "pose_markers", active_pose_library, "pose_markers",
pose_library.pose_markers, "active_index", rows=4) active_pose_library.pose_markers, "active_index", rows=4)
# Pose operators # Pose operators
pose_ops_layout = pose_list_entries_layout.column(align=True) pose_ops_layout = pose_list_entries_layout.column(align=True)
pose_ops_layout.operator( pose_ops_layout.operator(
"wm.call_menu", icon='ADD', text="").name = "OBJECT_MT_AddPoseMenu" "wm.call_menu", icon='ADD', text="").name = "OBJECT_MT_AddPoseMenu"
if pose_library.pose_markers.active: if active_pose_library.pose_markers.active:
pose_ops_layout.operator( pose_ops_layout.operator(
"dspl.remove_pose", icon='REMOVE', text="") "dspl.remove_pose", icon='REMOVE', text="")
pose_ops_layout.operator( pose_ops_layout.operator(
"dspl.apply_pose", icon='ARMATURE_DATA', text="" "dspl.apply_pose", icon='ARMATURE_DATA', text=""
).posename = pose_library.pose_markers.active.name ).posename = active_pose_library.pose_markers.active.name
pose_ops_layout.operator( pose_ops_layout.operator(
"dspl.move_pose", icon='TRIA_UP', text="").direction = "UP" "dspl.move_pose", icon='TRIA_UP', text="").direction = "UP"
pose_ops_layout.operator( pose_ops_layout.operator(
@ -136,7 +141,7 @@ class DATA_PT_DSPLPanel(bpy.types.Panel):
armature_layout.label( armature_layout.label(
text="No Action or Pose Library detected") text="No Action or Pose Library detected")
armature_layout.template_ID( armature_layout.template_ID(
arm_object, "pose_library", new="dspl.create_pose_library") arm_object.dspl, "pose_library", new="dspl.create_pose_library")
else: else:
armature_layout.label(text="No armature or parent selected") armature_layout.label(text="No armature or parent selected")
@ -147,12 +152,13 @@ class OBJECT_MT_AddPoseMenu(bpy.types.Menu):
bl_label = "Add Pose" bl_label = "Add Pose"
def draw(self, context): def draw(self, context):
arm_object, pose_library = getArmatureData(context) arm_object = getArmatureObject(context)
action_object = getPoseLib(context)
dspl_add_menu_layout = self.layout dspl_add_menu_layout = self.layout
dspl_add_menu_layout.operator( dspl_add_menu_layout.operator(
"dspl.add_pose", icon='ADD', text="Add New Pose") "dspl.add_pose", icon='ADD', text="Add New Pose")
if len(pose_library.pose_markers): if len(action_object.pose_markers):
dspl_add_menu_layout.menu( dspl_add_menu_layout.menu(
"OBJECT_MT_ReplacePoseMenu", text="Replace Existing Pose", icon="DECORATE_OVERRIDE") "OBJECT_MT_ReplacePoseMenu", text="Replace Existing Pose", icon="DECORATE_OVERRIDE")
@ -162,10 +168,11 @@ class OBJECT_MT_ReplacePoseMenu(bpy.types.Menu):
bl_label = "Add Pose" bl_label = "Add Pose"
def draw(self, context): def draw(self, context):
arm_object, pose_library = getArmatureData(context) arm_object = getArmatureObject(context)
action_object = getPoseLib(context)
dspl_replace_menu_layout = self.layout dspl_replace_menu_layout = self.layout
for pm in pose_library.pose_markers: for pm in action_object.pose_markers:
op = dspl_replace_menu_layout.operator("dspl.add_pose", text=pm.name, icon="PMARKER") op = dspl_replace_menu_layout.operator("dspl.add_pose", text=pm.name, icon="PMARKER")
op.replace = True op.replace = True
op.posename = pm.name op.posename = pm.name

View File

@ -13,15 +13,15 @@ class DSPL_OT_CreatePoseLibrary(bpy.types.Operator):
bl_options = {'REGISTER', 'UNDO'} bl_options = {'REGISTER', 'UNDO'}
def execute(self, context): def execute(self, context):
arm_object, pose_library = getArmatureData(context) arm_object = getArmatureObject(context)
arm_object.pose_library = bpy.data.actions.new( arm_object.dspl.pose_library = bpy.data.actions.new(
name=arm_object.name + "_PoseLib") name=arm_object.name + "_PoseLib")
arm_object.pose_library.use_fake_user = True arm_object.dspl.pose_library.use_fake_user = True
return {'FINISHED'} return {'FINISHED'}
# Operator to convert an action to pose library # Operator to convert a pose library to dspl property
class DSPL_OT_ConvertPoseLibrary(bpy.types.Operator): class DSPL_OT_ConvertPoseLibrary(bpy.types.Operator):
@ -31,9 +31,8 @@ class DSPL_OT_ConvertPoseLibrary(bpy.types.Operator):
bl_options = {'REGISTER', 'UNDO'} bl_options = {'REGISTER', 'UNDO'}
def execute(self, context): def execute(self, context):
arm_object, pose_library = getArmatureData(context) arm_object = getArmatureObject(context)
if pose_library is None: arm_object.dspl.pose_library = arm_object.animation_data.action
arm_object.pose_library = arm_object.animation_data.action
arm_object.animation_data.action = None arm_object.animation_data.action = None
return {'FINISHED'} return {'FINISHED'}
@ -52,9 +51,11 @@ class DSPL_OT_AddPose(bpy.types.Operator):
replace: bpy.props.BoolProperty(name="Replace", description="Replace existing pose", default=False, options={'SKIP_SAVE'}) replace: bpy.props.BoolProperty(name="Replace", description="Replace existing pose", default=False, options={'SKIP_SAVE'})
def execute(self, context): def execute(self, context):
arm_object, pose_library = getArmatureData(context)
if self.replace == False: if self.replace == False:
pose_markers = pose_library.pose_markers arm_object = getArmatureObject(context)
action_object = getPoseLib(context)
pose_markers = action_object.pose_markers
new_name = self.posename new_name = self.posename
counter = 1 counter = 1
@ -82,13 +83,15 @@ class DSPL_OT_AddPose(bpy.types.Operator):
setKeyframesFromBones(context, arm_object, new_marker) setKeyframesFromBones(context, arm_object, new_marker)
pose_library.pose_markers.active = pose_markers[pose_name] action_object.pose_markers.active = pose_markers[pose_name]
bpy.context.area.tag_redraw() bpy.context.area.tag_redraw()
self.report({'INFO'}, "DSPL: Added " + pose_markers[new_name].name + " to frame " + str(pose_markers[new_name].frame)) self.report({'INFO'}, "DSPL: Added " + pose_markers[new_name].name + " to frame " + str(pose_markers[new_name].frame))
else: else:
pose_markers = pose_library.pose_markers arm_object = getArmatureObject(context)
action_object = getPoseLib(context)
pose_markers = action_object.pose_markers
active_marker = pose_markers.active active_marker = pose_markers.active
new_name = self.posename new_name = self.posename
counter = 1 counter = 1
@ -124,17 +127,18 @@ class DSPL_OT_RemovePose(bpy.types.Operator):
posename: bpy.props.StringProperty() posename: bpy.props.StringProperty()
def execute(self, context): def execute(self, context):
arm_object, pose_library = getArmatureData(context) arm_object = getArmatureObject(context)
pose_markers = pose_library.pose_markers action_object = getPoseLib(context)
pose_markers = action_object.pose_markers
if self.posename: if self.posename:
pose_library.pose_markers.active_index = searchPoseMarker(context, posename=self.posename, type="index") action_object.pose_markers.active_index = searchPoseMarker(context, posename=self.posename, type="index")
active_index = pose_library.pose_markers.active_index active_index = action_object.pose_markers.active_index
else: else:
active_index = pose_markers.active_index active_index = pose_markers.active_index
active_marker = pose_markers.active active_marker = pose_markers.active
active_frame = active_marker.frame active_frame = active_marker.frame
fcurves = pose_library.fcurves fcurves = action_object.fcurves
for fcu in fcurves: for fcu in fcurves:
for kf in fcu.keyframe_points.values(): for kf in fcu.keyframe_points.values():
if kf.co.x == active_frame: if kf.co.x == active_frame:
@ -152,8 +156,8 @@ class DSPL_OT_RemovePose(bpy.types.Operator):
pose_markers.remove(marker=active_marker) pose_markers.remove(marker=active_marker)
pose_library.pose_markers.active = next_marker action_object.pose_markers.active = next_marker
pose_library.pose_markers.active_index = next_index action_object.pose_markers.active_index = next_index
self.report({'INFO'}, "DSPL: Removed " + self.posename) self.report({'INFO'}, "DSPL: Removed " + self.posename)
@ -173,8 +177,10 @@ class DSPL_OT_RenamePose(bpy.types.Operator):
pose_new_name: bpy.props.StringProperty() pose_new_name: bpy.props.StringProperty()
def execute(self, context): def execute(self, context):
arm_object, pose_library = getArmatureData(context)
pose_markers = pose_library.pose_markers arm_object = getArmatureObject(context)
action_object = getPoseLib(context)
pose_markers = action_object.pose_markers
active_marker = pose_markers.active active_marker = pose_markers.active
if self.posename: if self.posename:
@ -211,8 +217,10 @@ class DSPL_OT_MovePose(bpy.types.Operator):
posename: bpy.props.StringProperty(name="Pose Name", default="", options={'SKIP_SAVE'}) posename: bpy.props.StringProperty(name="Pose Name", default="", options={'SKIP_SAVE'})
def execute(self, context): def execute(self, context):
arm_object, pose_library = getArmatureData(context) arm_object = getArmatureObject(context)
pose_markers = pose_library.pose_markers action_object = getPoseLib(context)
pose_lib = getPoseLib(context)
pose_markers = action_object.pose_markers
if self.posename: if self.posename:
active_index = searchPoseMarker(context, posename=self.posename, type="index") active_index = searchPoseMarker(context, posename=self.posename, type="index")
@ -220,6 +228,7 @@ class DSPL_OT_MovePose(bpy.types.Operator):
active_frame = active_marker.frame active_frame = active_marker.frame
active_posename = active_marker.name active_posename = active_marker.name
else: else:
arm_object = getArmatureObject(context)
active_marker = pose_markers.active active_marker = pose_markers.active
active_index = pose_markers.active_index active_index = pose_markers.active_index
@ -231,19 +240,21 @@ class DSPL_OT_MovePose(bpy.types.Operator):
swap_index = active_index + move_dir swap_index = active_index + move_dir
if swap_index < 0: if swap_index < 0:
# swap_index = len(pose_lib.pose_markers) - 1
return {'FINISHED'} return {'FINISHED'}
elif swap_index >= len(pose_library.pose_markers): elif swap_index >= len(pose_lib.pose_markers):
# swap_index = 0
return {'FINISHED'} return {'FINISHED'}
swap_marker = pose_markers[swap_index] swap_marker = pose_markers[swap_index]
tmp_marker_name = swap_marker.name tmp_marker_name = swap_marker.name
tmp_marker_frame = swap_marker.frame tmp_marker_frame = swap_marker.frame
pose_library.pose_markers[swap_index].name = active_marker.name action_object.pose_markers[swap_index].name = active_marker.name
pose_library.pose_markers[swap_index].frame = active_marker.frame action_object.pose_markers[swap_index].frame = active_marker.frame
pose_library.pose_markers[active_index].name = tmp_marker_name action_object.pose_markers[active_index].name = tmp_marker_name
pose_library.pose_markers[active_index].frame = tmp_marker_frame action_object.pose_markers[active_index].frame = tmp_marker_frame
pose_library.pose_markers.active_index = swap_index action_object.pose_markers.active_index = swap_index
return {'FINISHED'} return {'FINISHED'}
@ -254,21 +265,22 @@ class DSPL_OT_MovePose(bpy.types.Operator):
class DSPL_OT_ApplyPose(bpy.types.Operator): class DSPL_OT_ApplyPose(bpy.types.Operator):
bl_idname = "dspl.apply_pose" bl_idname = "dspl.apply_pose"
bl_label = "Apply Pose" bl_label = "Apply Pose"
bl_description = "Apply Pose (Ctrl+Click to select bones, Shift+Click to rename, Alt+Click to remove)" bl_description = "Apply Pose (Ctrl+Click to select, Alt+Click to remove)"
bl_options = {'REGISTER', 'UNDO'} bl_options = {'REGISTER', 'UNDO'}
posename: bpy.props.StringProperty() posename: bpy.props.StringProperty()
def execute(self, context): def execute(self, context):
arm_object, pose_library = getArmatureData(context) arm_object = getArmatureObject(context)
pose_markers = pose_library.pose_markers action_object = getPoseLib(context)
pose_markers = action_object.pose_markers
if self.posename: if self.posename:
active_marker = searchPoseMarker(context, posename=self.posename, type="marker") active_marker = searchPoseMarker(context, posename=self.posename, type="marker")
active_frame = active_marker.frame active_frame = active_marker.frame
active_posename = active_marker.name active_posename = active_marker.name
pose_library.pose_markers.active_index = searchPoseMarker(context, posename=self.posename, type="index") action_object.pose_markers.active_index = searchPoseMarker(context, posename=self.posename, type="index")
else: else:
active_index = pose_markers.active_index active_index = pose_markers.active_index
active_marker = pose_markers.active active_marker = pose_markers.active
@ -283,16 +295,9 @@ class DSPL_OT_ApplyPose(bpy.types.Operator):
def invoke(self, context, event): def invoke(self, context, event):
if event.ctrl: if event.ctrl:
# Select bones # Select
arm_object, pose_library = getArmatureData(context) action_object = getPoseLib(context)
active_marker = searchPoseMarker(context, posename=self.posename, type="marker") action_object.pose_markers.active_index = searchPoseMarker(context, posename=self.posename, type="index")
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'} return {'FINISHED'}
elif event.alt: elif event.alt:
# Remove # Remove
@ -322,7 +327,7 @@ class DSPL_OT_BrowsePoses(bpy.types.Operator):
font_dpi = 72 font_dpi = 72
blf.position(font_id, 15, 30, 0) blf.position(font_id, 15, 30, 0)
mod_var = self.pose_library.pose_markers.active.name mod_var = self.pose_lib.pose_markers.active.name
blf.color(font_id, 1.0, 1.0, 1.0, 1.0) blf.color(font_id, 1.0, 1.0, 1.0, 1.0)
blf.size(font_id, font_size) blf.size(font_id, font_size)
blf.draw(font_id, "Previewing pose: " + mod_var) blf.draw(font_id, "Previewing pose: " + mod_var)
@ -333,16 +338,16 @@ class DSPL_OT_BrowsePoses(bpy.types.Operator):
if event.value == 'PRESS': if event.value == 'PRESS':
if event.type in {'LEFT_ARROW', 'UP_ARROW'}: if event.type in {'LEFT_ARROW', 'UP_ARROW'}:
if self.pose_library.pose_markers.active_index <= 0: if self.pose_lib.pose_markers.active_index <= 0:
self.pose_library.pose_markers.active_index = len(self.pose_library.pose_markers) - 1 self.pose_lib.pose_markers.active_index = len(self.pose_lib.pose_markers) - 1
else: else:
self.pose_library.pose_markers.active_index = self.pose_library.pose_markers.active_index - 1 self.pose_lib.pose_markers.active_index = self.pose_lib.pose_markers.active_index - 1
bpy.ops.dspl.apply_pose() bpy.ops.dspl.apply_pose()
elif event.type in {'RIGHT_ARROW', 'DOWN_ARROW'}: elif event.type in {'RIGHT_ARROW', 'DOWN_ARROW'}:
if self.pose_library.pose_markers.active_index + 1 >= len(self.pose_library.pose_markers): if self.pose_lib.pose_markers.active_index + 1 >= len(self.pose_lib.pose_markers):
self.pose_library.pose_markers.active_index = 0 self.pose_lib.pose_markers.active_index = 0
else: else:
self.pose_library.pose_markers.active_index = self.pose_library.pose_markers.active_index + 1 self.pose_lib.pose_markers.active_index = self.pose_lib.pose_markers.active_index + 1
bpy.ops.dspl.apply_pose() bpy.ops.dspl.apply_pose()
if event.type in {'LEFTMOUSE', 'RET', 'NUMPAD_ENTER'}: if event.type in {'LEFTMOUSE', 'RET', 'NUMPAD_ENTER'}:
@ -351,7 +356,7 @@ class DSPL_OT_BrowsePoses(bpy.types.Operator):
elif event.type in {'RIGHTMOUSE', 'ESC'}: elif event.type in {'RIGHTMOUSE', 'ESC'}:
self.arm_object.pose.backup_restore() self.arm_object.pose.backup_restore()
self.pose_library.pose_markers.active_index = self.backup_index self.pose_lib.pose_markers.active_index = self.backup_index
bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW') bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
return {'CANCELLED'} return {'CANCELLED'}
@ -361,14 +366,15 @@ class DSPL_OT_BrowsePoses(bpy.types.Operator):
def invoke(self, context, event): def invoke(self, context, event):
bpy.context.area.tag_redraw() bpy.context.area.tag_redraw()
self.arm_object, self.pose_library = getArmatureData(context) self.arm_object = getArmatureObject(context)
self.pose_lib = getPoseLib(context)
if self.pose_library is None: if self.pose_lib is None:
self.report({'WARNING'}, "DSPL: Pose Library not active") self.report({'WARNING'}, "DSPL: Pose Library not active")
return {'CANCELLED'} return {'CANCELLED'}
self.arm_object.pose.backup_create(self.pose_library) self.arm_object.pose.backup_create(self.pose_lib)
self.backup_index = self.pose_library.pose_markers.active_index self.backup_index = self.pose_lib.pose_markers.active_index
bpy.ops.dspl.apply_pose() bpy.ops.dspl.apply_pose()
if context.area.type == 'VIEW_3D': if context.area.type == 'VIEW_3D':
@ -390,15 +396,11 @@ class DSPL_OT_UnlinkPoseLibrary(bpy.types.Operator):
bl_options = {'REGISTER', 'UNDO'} bl_options = {'REGISTER', 'UNDO'}
def execute(self, context): def execute(self, context):
arm_object, pose_library = getArmatureData(context) arm_object = getArmatureObject(context)
pose_library = getPoseLib(context)
try: arm_object.dspl.pose_library.name = "del_" + arm_object.dspl.pose_library.name
arm_object.pose_library = None arm_object.dspl.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'} return {'FINISHED'}