Skip to content
Snippets Groups Projects
Commit ce871b0b authored by Ines Almeida's avatar Ines Almeida
Browse files

Bone Selection Sets: Temporary solution for copy/pasting multiple sets.

Copy/Pasting multiple sets was requested by the Spring Animation Team.
Currently UILists don't support multiple selection, though :(
I added a checkbox to represent additionally selected sets, which is far from
ideal in terms of UI and understanding what the operators should apply to.
The checkboxes currently only affect copy/pasting, which should be reviewed later.
parent 21466a9e
No related branches found
No related tags found
No related merge requests found
...@@ -41,6 +41,7 @@ from bpy.props import ( ...@@ -41,6 +41,7 @@ from bpy.props import (
StringProperty, StringProperty,
IntProperty, IntProperty,
EnumProperty, EnumProperty,
BoolProperty,
CollectionProperty, CollectionProperty,
) )
...@@ -56,6 +57,7 @@ class SelectionEntry(PropertyGroup): ...@@ -56,6 +57,7 @@ class SelectionEntry(PropertyGroup):
class SelectionSet(PropertyGroup): class SelectionSet(PropertyGroup):
name: StringProperty(name="Set Name") name: StringProperty(name="Set Name")
bone_ids: CollectionProperty(type=SelectionEntry) bone_ids: CollectionProperty(type=SelectionEntry)
is_selected: BoolProperty(name="Is Selected")
# UI Panel w/ UIList ########################################################## # UI Panel w/ UIList ##########################################################
...@@ -127,7 +129,10 @@ class POSE_PT_selection_sets(Panel): ...@@ -127,7 +129,10 @@ class POSE_PT_selection_sets(Panel):
class POSE_UL_selection_set(UIList): class POSE_UL_selection_set(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
sel_set = item
layout.prop(item, "name", text="", icon='GROUP_BONE', emboss=False) layout.prop(item, "name", text="", icon='GROUP_BONE', emboss=False)
if self.layout_type in ('DEFAULT', 'COMPACT'):
layout.prop(item, "is_selected", text="")
class POSE_MT_selection_set_create(Menu): class POSE_MT_selection_set_create(Menu):
...@@ -157,6 +162,7 @@ class POSE_MT_selection_sets_select(Menu): ...@@ -157,6 +162,7 @@ class POSE_MT_selection_sets_select(Menu):
# Operators ################################################################### # Operators ###################################################################
class PluginOperator(Operator): class PluginOperator(Operator):
"""Operator only available for objects of type armature in pose mode."""
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
return (context.object and return (context.object and
...@@ -165,6 +171,7 @@ class PluginOperator(Operator): ...@@ -165,6 +171,7 @@ class PluginOperator(Operator):
class NeedSelSetPluginOperator(PluginOperator): class NeedSelSetPluginOperator(PluginOperator):
"""Operator only available if the armature has a selected selection set."""
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
if not super().poll(context): if not super().poll(context):
...@@ -187,8 +194,8 @@ class POSE_OT_selection_set_delete_all(PluginOperator): ...@@ -187,8 +194,8 @@ class POSE_OT_selection_set_delete_all(PluginOperator):
class POSE_OT_selection_set_remove_bones(PluginOperator): class POSE_OT_selection_set_remove_bones(PluginOperator):
bl_idname = "pose.selection_set_remove_bones" bl_idname = "pose.selection_set_remove_bones"
bl_label = "Remove Bones from Sets" bl_label = "Remove Selected Bones from All Sets"
bl_description = "Removes the Active Bones from All Sets" bl_description = "Removes the Selected Bones from All Sets"
bl_options = {'UNDO', 'REGISTER'} bl_options = {'UNDO', 'REGISTER'}
def execute(self, context): def execute(self, context):
...@@ -389,20 +396,20 @@ class POSE_OT_selection_set_add_and_assign(PluginOperator): ...@@ -389,20 +396,20 @@ class POSE_OT_selection_set_add_and_assign(PluginOperator):
class POSE_OT_selection_set_copy(NeedSelSetPluginOperator): class POSE_OT_selection_set_copy(NeedSelSetPluginOperator):
bl_idname = "pose.selection_set_copy" bl_idname = "pose.selection_set_copy"
bl_label = "Copy Selection Set to Clipboard" bl_label = "Copy Selection Set(s)"
bl_description = "Converts the Selection Set to JSON and places it on the clipboard" bl_description = "Copies the selected Selection Set(s) to the clipboard"
bl_options = {'UNDO', 'REGISTER'} bl_options = {'UNDO', 'REGISTER'}
def execute(self, context): def execute(self, context):
context.window_manager.clipboard = to_json(context) context.window_manager.clipboard = to_json(context)
self.report({'INFO'}, 'Copied Selection Set to Clipboard') self.report({'INFO'}, 'Copied Selection Set(s) to Clipboard')
return {'FINISHED'} return {'FINISHED'}
class POSE_OT_selection_set_paste(PluginOperator): class POSE_OT_selection_set_paste(PluginOperator):
bl_idname = "pose.selection_set_paste" bl_idname = "pose.selection_set_paste"
bl_label = "Paste Selection Set from Clipboard" bl_label = "Paste Selection Set(s)"
bl_description = "Adds a new Selection Set from copied JSON on the clipboard" bl_description = "Adds new Selection Set(s) from the Clipboard"
bl_options = {'UNDO', 'REGISTER'} bl_options = {'UNDO', 'REGISTER'}
def execute(self, context): def execute(self, context):
...@@ -426,32 +433,37 @@ def menu_func_select_selection_set(self, context): ...@@ -426,32 +433,37 @@ def menu_func_select_selection_set(self, context):
def to_json(context) -> str: def to_json(context) -> str:
"""Convert the active bone selection set of the current rig to JSON.""" """Convert the selected Selection Sets of the current rig to JSON.
Selected Sets are the active_selection_set determined by the UIList
plus any with the is_selected checkbox on."""
import json import json
arm = context.object arm = context.object
active_idx = arm.active_selection_set active_idx = arm.active_selection_set
sel_set = arm.selection_sets[active_idx]
return json.dumps({ json_obj = {}
'name': sel_set.name, for idx, sel_set in enumerate(context.object.selection_sets):
'bones': [bone_id.name for bone_id in sel_set.bone_ids] if idx == active_idx or sel_set.is_selected:
}) bones = [bone_id.name for bone_id in sel_set.bone_ids]
json_obj[sel_set.name] = bones
return json.dumps(json_obj)
def from_json(context, as_json: str): def from_json(context, as_json: str):
"""Add the single bone selection set from JSON to the current rig.""" """Add the selection sets (one or more) from JSON to the current rig."""
import json import json
sel_set = json.loads(as_json) json_obj = json.loads(as_json)
arm_sel_sets = context.object.selection_sets
sel_sets = context.object.selection_sets
new_sel_set = sel_sets.add()
new_sel_set.name = uniqify(sel_set['name'], sel_sets.keys())
for bone_name in sel_set['bones']: for name, bones in json_obj.items():
bone_id = new_sel_set.bone_ids.add() new_sel_set = arm_sel_sets.add()
bone_id.name = bone_name new_sel_set.name = uniqify(name, arm_sel_sets.keys())
for bone_name in bones:
bone_id = new_sel_set.bone_ids.add()
bone_id.name = bone_name
def uniqify(name: str, other_names: list) -> str: def uniqify(name: str, other_names: list) -> str:
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment