13 Commits
9 changed files with 77 additions and 36 deletions
+7
View File
@@ -2,6 +2,8 @@
> Blender addon for picking rig contollers > Blender addon for picking rig contollers
Rig_picker is an OpenGl tool for having a 2d interface for the 3d animators allowing them to pick a controller easily. Rig_picker is an OpenGl tool for having a 2d interface for the 3d animators allowing them to pick a controller easily.
The addon is drawing 2d shapes inside a dedicated Node Editor Area using the gpu module.
You can use multiple pickers for one rig, each picker shapes are in there own collection.
Video of the previous version : https://vimeo.com/241970235 Video of the previous version : https://vimeo.com/241970235
@@ -23,7 +25,12 @@ Video of the previous version : https://vimeo.com/241970235
<!-- INSTALLATION --> <!-- INSTALLATION -->
## Installation ## Installation
For external user, you can clone the repository using:
```sh
git clone https://git.autourdeminuit.com/autour_de_minuit/rig_picker.git
```
For Internal user:
1. Create your own local directory in 1. Create your own local directory in
```sh ```sh
/home/<USER>/dev /home/<USER>/dev
+1
View File
@@ -155,6 +155,7 @@ classes = (
def register(): def register():
# Remove the tools inside the picker space
bpy.types.WM_OT_tool_set_by_id._execute = bpy.types.WM_OT_tool_set_by_id.execute #tool_set_by_id bpy.types.WM_OT_tool_set_by_id._execute = bpy.types.WM_OT_tool_set_by_id.execute #tool_set_by_id
bpy.types.WM_OT_tool_set_by_id.execute = tool_set_by_id bpy.types.WM_OT_tool_set_by_id.execute = tool_set_by_id
+6 -3
View File
@@ -459,7 +459,7 @@ class Picker:
if shape.type == 'bone' and shape.hover: if shape.type == 'bone' and shape.hover:
shape.assign_bone_event() shape.assign_bone_event()
bpy.ops._rigpicker.save_picker(index=self.index) bpy.ops.rigpicker.save_picker(index=self.index)
def press_event(self, mode='SET'): def press_event(self, mode='SET'):
for shape in self.shapes: for shape in self.shapes:
@@ -712,7 +712,7 @@ class PickerGroup:
def load_picker_data(rig): def load_picker_data(rig):
if 'pickers' in rig.data.rig_picker: if 'pickers' in rig.data.rig_picker:
picker_datas = [p.to_dict() for p in rig.rig_picker['pickers']] picker_datas = [[s.to_dict() for s in p] for p in rig.data.rig_picker['pickers']]
else: else:
picker_datas = [] picker_datas = []
@@ -736,8 +736,11 @@ def get_picker_path(rig, source, start=None):
def pack_picker(rig, start=None): def pack_picker(rig, start=None):
if not 'rig_picker' in rig.data:
return
pickers = [] pickers = []
for picker_source in rig.data.get('rig_picker', {}).get('sources', {}): for picker_source in rig.data['rig_picker'].get('sources', []):
picker_path = get_picker_path(rig, picker_source['source'], start) picker_path = get_picker_path(rig, picker_source['source'], start)
if not picker_path.exists(): if not picker_path.exists():
print(f'{picker_path} not exists') print(f'{picker_path} not exists')
+3 -3
View File
@@ -93,7 +93,7 @@ def get_shape_data(ob, matrix=None, depsgraph=None):
for vert in mesh.vertices: for vert in mesh.vertices:
co = matrix @ (ob.matrix_world @ Vector(vert.co)) co = matrix @ (ob.matrix_world @ Vector(vert.co))
points.append([round(co[0]), round(co[1])]) points.append([round(co[0], 1), round(co[1], 1)])
depths += [co[2]] depths += [co[2]]
@@ -148,8 +148,8 @@ def get_picker_data(collection):
canvas_coords = [canvas.matrix_world@Vector((p.co)) for p in canvas_points] canvas_coords = [canvas.matrix_world@Vector((p.co)) for p in canvas_points]
height = abs(max(canvas_coords).y - min(canvas_coords).y) height = abs(max(co.y for co in canvas_coords) - min(co.y for co in canvas_coords))
width = abs(max(canvas_coords).x - min(canvas_coords).x) width = abs(max(co.x for co in canvas_coords) - min(co.x for co in canvas_coords))
center = sum(canvas_coords, Vector()) / len(canvas_coords) center = sum(canvas_coords, Vector()) / len(canvas_coords)
scale = 2048 / max(height, width)# Reference height for the canvas scale = 2048 / max(height, width)# Reference height for the canvas
+2
View File
@@ -68,6 +68,8 @@ class RP_GT_gizmo(Gizmo):
context.region.tag_redraw() context.region.tag_redraw()
#print(location)
return -1 return -1
+22 -11
View File
@@ -130,9 +130,8 @@ class RP_OT_box_select(Operator):
return {'RUNNING_MODAL'} return {'RUNNING_MODAL'}
def release_event(self, context): def release_event(self, context):
scn = context.scene
if scn.rig_picker.use_pick_bone: if get_picker_collection():
self.picker.assign_bone_event() self.picker.assign_bone_event()
elif (self.start_mouse[0] != self.mouse[0] or self.start_mouse[1] != self.mouse[1]): elif (self.start_mouse[0] != self.mouse[0] or self.start_mouse[1] != self.mouse[1]):
@@ -392,24 +391,19 @@ class RP_OT_reload_picker(Operator):
# return # return
def execute(self, context): def execute(self, context):
#PICKERS.clear()
if context.object.type == 'ARMATURE': if context.object.type == 'ARMATURE':
rig = context.object rig = context.object
else: else:
collection = get_picker_collection(context.object) collection = get_picker_collection(context.object)
rig = collection.rig_picker.rig rig = collection.rig_picker.rig
#print('Reload', rig)
load_picker_data(rig) load_picker_data(rig)
'''
for area in context.screen.areas: for area in context.screen.areas:
#print(area.type, is_picker_space(area.spaces.active))
if is_picker_space(area.spaces.active): if is_picker_space(area.spaces.active):
print('Tag Redraw Region', area.type)
area.regions[0].tag_redraw()
area.tag_redraw() area.tag_redraw()
'''
return {"FINISHED"} return {"FINISHED"}
@@ -551,7 +545,8 @@ class RP_MT_context_menu(Menu):
def draw(self, context): def draw(self, context):
layout = self.layout layout = self.layout
col = layout.column() col = layout.column()
col.use_property_split = True col.operator_context = 'INVOKE_DEFAULT'
#col.use_property_split = True
ob = context.object ob = context.object
picker = PICKERS.get(ob) picker = PICKERS.get(ob)
@@ -561,9 +556,25 @@ class RP_MT_context_menu(Menu):
else: else:
bone = context.active_pose_bone bone = context.active_pose_bone
# Draw Space Switch Operator
if getattr(ob.data, 'space_switch'):
space_switch = ob.data.space_switch
data_paths = [f'pose.bones["{bone.name}"]["{k}"]' for k in bone.keys()]
space_bone = next((s for s in space_switch.bones if s.data_path in data_paths), None)
if space_bone:
index = list(space_switch.bones).index(space_bone)
value = ob.path_resolve(space_bone.data_path)
space = next((s.name for s in space_bone.spaces if s.value == value), None)
op = col.operator("spaceswitch.change_space", text=f'({space})', icon='PINNED')
op.index=index
col.separator()
if bone: if bone:
for key in bone.keys(): for key in bone.keys():
layout.prop(bone,f'["{key}"]', slider=True) col.prop(bone,f'["{key}"]', slider=True)
#layout.operator("rigpicker.show_bone_layer", text="Show Bone Layer", ).type = 'ACTIVE' #layout.operator("rigpicker.show_bone_layer", text="Show Bone Layer", ).type = 'ACTIVE'
#layout.operator("rigidbody.objects_add", text="B Add Passive").type = 'PASSIVE' #layout.operator("rigidbody.objects_add", text="B Add Passive").type = 'PASSIVE'
+22 -14
View File
@@ -20,6 +20,14 @@ class RP_OT_create_shape(Operator):
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
return (context.object and context.object.mode == 'POSE') return (context.object and context.object.mode == 'POSE')
def invoke(self, context, event):
return context.window_manager.invoke_props_dialog(self)
def draw(self, context):
for col in bpy.data.collections:
if col.rig_picker.enabled:
self.layout.prop(col.rig_picker, 'link_shape', text=col.name)
def execute(self,context): def execute(self,context):
scn = context.scene scn = context.scene
@@ -40,8 +48,11 @@ class RP_OT_create_shape(Operator):
mesh.from_pydata(verts, edges, faces) mesh.from_pydata(verts, edges, faces)
for c in scn.rig_picker.canvas.users_collection: for col in bpy.data.collections:
c.objects.link(ob) if col.rig_picker.link_shape:
col.objects.link(ob)
else:
scn.collection.objects.link(ob)
ob.location.z = 0.05 ob.location.z = 0.05
ob.location.x = offset ob.location.x = offset
@@ -60,16 +71,16 @@ class RP_OT_create_shape(Operator):
class RP_OT_name_from_bone(Operator): class RP_OT_name_from_bone(Operator):
bl_label = 'Name Shape from selected bones' bl_label = 'Name Shape from selected bones'
bl_idname = 'rigpicker.name_from_bone' bl_idname = 'rigpicker.name_from_bone'
bl_description = 'Rename all shapes from related bones name'
#bl_options = {'REGISTER', 'UNDO'} #bl_options = {'REGISTER', 'UNDO'}
def execute(self,context): def execute(self,context):
scene = context.scene
rig = scene.rig_picker.rig
bone = rig.data.bones.active
if bone: col = get_picker_collection(context.object)
context.object.name = bone.name
context.object.rig_picker.name = bone.name for ob in col.all_objects:
if ob.rig_picker.shape_type == 'BONE':
ob.name = ob.rig_picker.name
return {'FINISHED'} return {'FINISHED'}
@@ -84,9 +95,7 @@ class RP_OT_mirror_shape(Operator):
return (context.object and context.object.type in ('MESH', 'CURVE', 'TEXT')) return (context.object and context.object.type in ('MESH', 'CURVE', 'TEXT'))
def execute(self,context): def execute(self,context):
scn = context.scene collection = get_picker_collection(context.object)
ob = context.object
collection = get_picker_collection(ob)
objects = context.selected_objects objects = context.selected_objects
# Remove mirror object: # Remove mirror object:
@@ -95,7 +104,7 @@ class RP_OT_mirror_shape(Operator):
continue continue
for mod in ob.modifiers: for mod in ob.modifiers:
if (mod.type == 'NODES' and mod.node_group.name == 'Symmetrize' and if (mod.type == 'NODES' and mod.node_group and mod.node_group.name == 'Symmetrize' and
mod.get('Socket_2') in objects): mod.get('Socket_2') in objects):
bpy.data.objects.remove(ob) bpy.data.objects.remove(ob)
@@ -124,7 +133,7 @@ class RP_OT_mirror_shape(Operator):
for mod in list(flipped_object.modifiers): for mod in list(flipped_object.modifiers):
flipped_object.modifiers.remove(mod) flipped_object.modifiers.remove(mod)
# Add symmetrize modifier # Add symmetrize modifier TODO add it into a resource
mod = flipped_object.modifiers.new(name='Symmetrize', type='NODES') mod = flipped_object.modifiers.new(name='Symmetrize', type='NODES')
mod.node_group = bpy.data.node_groups['Symmetrize'] mod.node_group = bpy.data.node_groups['Symmetrize']
mod['Socket_2'] = ob mod['Socket_2'] = ob
@@ -222,7 +231,6 @@ class RP_OT_save_picker(Operator):
bpy.ops.rigpicker.reload_picker() bpy.ops.rigpicker.reload_picker()
return {'FINISHED'} return {'FINISHED'}
classes = ( classes = (
+9 -4
View File
@@ -44,14 +44,17 @@ class RP_PG_object_ui_settings(bpy.types.PropertyGroup):
name: StringProperty() name: StringProperty()
class RP_PG_scene_ui_settings(bpy.types.PropertyGroup): class RP_PG_collection_ui_settings(bpy.types.PropertyGroup):
enabled : BoolProperty(default=False) enabled : BoolProperty(default=False)
rig: PointerProperty(type=bpy.types.Object) rig: PointerProperty(type=bpy.types.Object)
canvas: PointerProperty(type=bpy.types.Object) canvas: PointerProperty(type=bpy.types.Object)
symmetry: PointerProperty(type=bpy.types.Object) symmetry: PointerProperty(type=bpy.types.Object)
#idname: EnumProperty(items=[])
destination: StringProperty(subtype='FILE_PATH') destination: StringProperty(subtype='FILE_PATH')
#bone_list: bpy.props.EnumProperty(items = bones_item use_pick_bone : BoolProperty(default=False)
link_shape : BoolProperty(default=False)
class RP_PG_scene_ui_settings(bpy.types.PropertyGroup):
use_pick_bone : BoolProperty(default=False) use_pick_bone : BoolProperty(default=False)
@@ -78,6 +81,7 @@ class RP_OT_operator_selector(bpy.types.Operator):
classes = ( classes = (
RP_PG_picker_source, RP_PG_picker_source,
RP_PG_object_ui_settings, RP_PG_object_ui_settings,
RP_PG_collection_ui_settings,
RP_PG_scene_ui_settings, RP_PG_scene_ui_settings,
RP_PG_armature_ui_settings, RP_PG_armature_ui_settings,
RP_OT_operator_selector, RP_OT_operator_selector,
@@ -89,11 +93,12 @@ def register():
bpy.types.Armature.rig_picker = bpy.props.PointerProperty(type=RP_PG_armature_ui_settings) bpy.types.Armature.rig_picker = bpy.props.PointerProperty(type=RP_PG_armature_ui_settings)
bpy.types.Object.rig_picker = bpy.props.PointerProperty(type=RP_PG_object_ui_settings) bpy.types.Object.rig_picker = bpy.props.PointerProperty(type=RP_PG_object_ui_settings)
bpy.types.Collection.rig_picker = bpy.props.PointerProperty(type=RP_PG_collection_ui_settings)
bpy.types.Scene.rig_picker = bpy.props.PointerProperty(type=RP_PG_scene_ui_settings) bpy.types.Scene.rig_picker = bpy.props.PointerProperty(type=RP_PG_scene_ui_settings)
bpy.types.Collection.rig_picker = bpy.props.PointerProperty(type=RP_PG_scene_ui_settings)
def unregister(): def unregister():
del bpy.types.Scene.rig_picker del bpy.types.Scene.rig_picker
del bpy.types.Collection.rig_picker
del bpy.types.Object.rig_picker del bpy.types.Object.rig_picker
del bpy.types.Armature.rig_picker del bpy.types.Armature.rig_picker
+5 -1
View File
@@ -91,8 +91,12 @@ class RP_PT_shape(bpy.types.Panel):
row = col.row(align=True) row = col.row(align=True)
row.operator('rigpicker.create_shape', icon='MESH_DATA', text='Create Shape') row.operator('rigpicker.create_shape', icon='MESH_DATA', text='Create Shape')
row.operator('rigpicker.mirror_shape', icon='MOD_MIRROR', text='Mirror Shape') row.operator('rigpicker.mirror_shape', icon='MOD_MIRROR', text='Mirror Shape')
col.operator('rigpicker.name_from_bone', icon='SORTALPHA' , text='Name From Bones')
col.prop(scn.rig_picker, 'use_pick_bone', icon='EYEDROPPER', text='Auto Bone Assign') col.prop(scn.rig_picker, 'use_pick_bone', icon='EYEDROPPER', text='Auto Bone Assign')
col.operator('rigpicker.name_from_bone', icon='SORTALPHA' , text='Name From Bones')
else:
col = layout.column(align=True)
row = col.row(align=True)
row.operator('rigpicker.create_shape', icon='MESH_DATA', text='Create Shape')
if ob.type != 'ARMATURE': if ob.type != 'ARMATURE':
box = layout.box() box = layout.box()