Compare commits

..

5 Commits

Author SHA1 Message Date
christophe.seux 6798053389 Update README.md 2024-10-30 17:36:07 +01:00
christophe.seux de3b78e741 Update README.md 2024-10-30 17:35:16 +01:00
christophe.seux 76ea717df8 Update README.md 2024-10-30 17:34:17 +01:00
ChristopheSeux 7d2d69ba0f Fix pack picker if missing 2024-03-04 09:55:46 +01:00
ChristopheSeux 75bc414e26 bone space in context menu 2024-02-28 09:59:11 +01:00
6 changed files with 61 additions and 232 deletions

View File

@ -2,6 +2,8 @@
> 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.
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
@ -23,7 +25,12 @@ Video of the previous version : https://vimeo.com/241970235
<!-- 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
```sh
/home/<USER>/dev

View File

@ -459,7 +459,7 @@ class Picker:
if shape.type == 'bone' and shape.hover:
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'):
for shape in self.shapes:
@ -712,7 +712,7 @@ class PickerGroup:
def load_picker_data(rig):
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:
picker_datas = []
@ -736,8 +736,11 @@ def get_picker_path(rig, source, start=None):
def pack_picker(rig, start=None):
if not 'rig_picker' in rig.data:
return
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)
if not picker_path.exists():
print(f'{picker_path} not exists')

View File

@ -93,7 +93,7 @@ def get_shape_data(ob, matrix=None, depsgraph=None):
for vert in mesh.vertices:
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]]

235
gizmo.py
View File

@ -3,13 +3,10 @@ import bpy
from bpy.props import (IntProperty, EnumProperty, BoolProperty)
from bpy.types import (AddonPreferences, GizmoGroup, Operator, Gizmo)
import gpu
from mathutils import Vector, Matrix, Euler
from gpu_extras.batch import batch_for_shader
from .constants import PICKERS, SHADERS
from mathutils import Vector, Matrix, Euler
from .constants import PICKERS
from .core.picker import Picker
from .core.geometry_utils import bounding_rect
@ -41,133 +38,14 @@ class RP_OT_simple_operator(bpy.types.Operator):
print('Select Shape')
return {'FINISHED'}
'''
'''
class RP_OT_box_select(Operator):
"""Box Select bones in the picker view"""
bl_idname = "node.rp_box_select"
bl_label = "Picker Box Select"
mode: EnumProperty(items=[(i, i.title(), '') for i in ('SET', 'EXTEND', 'SUBSTRACT')])
@classmethod
def poll(cls, context):
if not is_picker_space(context.space_data):
return
ob = context.object
return ob and ob in PICKERS
def draw_callback(self):
#print('draw callback border')
if not self.draw_border:
return
gpu.state.blend_set('ALPHA')
#print('DRAW BORDER')
self.color_shader.bind()
self.color_shader.uniform_float("color", self.bg_color)
self.bg_batch.draw(self.color_shader)
self.dash_shader.bind()
matrix = gpu.matrix.get_projection_matrix()
self.dash_shader.uniform_float("color", self.border_color)
self.dash_shader.uniform_float("viewMatrix", matrix)
self.dash_shader.uniform_float("dashSize", 5)
self.dash_shader.uniform_float("gapSize", 4)
self.contour_batch.draw(self.dash_shader)
gpu.state.blend_set('NONE')
def invoke(self, context, event):
#print(f'invoke: type={event.type}, value={event.value}, ctrl={event.ctrl}, shift={event.shift}, alt={event.alt}')
if context.object.mode != 'POSE':
bpy.ops.object.posemode_toggle()
self.timer = None
#self.mode = self.mode_from_event(event)
#self.invoke_event = event.copy()
self.region = context.region
self.draw_border = False
self.picker = PICKERS[context.object]
self.start_mouse = event.mouse_region_x, event.mouse_region_y
#self.shader = line_strip_shader
self.border_color = [1, 1, 1, 1]
self.bg_color = [1, 1, 1, 0.05]
#args = (self, context)
self.color_shader = gpu.shader.from_builtin('UNIFORM_COLOR')
self.dash_shader = SHADERS['dashed_line']
self._handle = bpy.types.SpaceNodeEditor.draw_handler_add(self.draw_callback, (), 'WINDOW', 'POST_PIXEL')
context.window_manager.modal_handler_add(self)
self.picker.press_event(self.mode)
self.region.tag_redraw()
return {'RUNNING_MODAL'}
def modal(self, context, event):
self.mouse = event.mouse_region_x, event.mouse_region_y
self.border = bounding_rect((self.start_mouse, self.mouse))
self.bg_batch = batch_for_shader(self.color_shader, 'TRI_FAN', {"pos": self.border})
self.contour_batch = batch_for_shader(self.dash_shader, 'LINE_LOOP', {"pos": self.border})
self.draw_border = True
self.region.tag_redraw()
if event.value == 'RELEASE':
return self.release_event(context)
return {'RUNNING_MODAL'}
def release_event(self, context):
scn = context.scene
if scn.rig_picker.use_pick_bone:
self.picker.assign_bone_event()
elif (self.start_mouse[0] != self.mouse[0] or self.start_mouse[1] != self.mouse[1]):
self.picker.border_select(self.border, self.mode)
else:
self.picker.move_event(self.mouse)
self.picker.release_event(self.mode)
bpy.ops.ed.undo_push(message="Box Select")
return self.exit(context)
def exit(self, context):
bpy.types.SpaceNodeEditor.draw_handler_remove(self._handle, 'WINDOW')
context.region.tag_redraw()
return {'FINISHED'}
'''
class RP_GT_gizmo(Gizmo):
def mode_from_event(self, event):
if event.alt:
return 'SUBSTRACT'
elif event.ctrl or event.shift:
return 'EXTEND'
else:
return 'SET'
def test_select(self, context, location):
ob = context.object
@ -176,109 +54,41 @@ class RP_GT_gizmo(Gizmo):
return -1
picker.move_event(location)
#if bpy.app.timers.is_registered(tooltip):
# bpy.app.timers.unregister(tooltip)
#context.region.tag_redraw()
#picker.tooltip_event(event='START')
#bpy.app.timers.register(partial(tooltip, context.region), first_interval=1)
#print(location)
context.region.tag_redraw()
return 1
#print(location)
return -1
def draw(self, context):
print('DRAW_SELECT', self.border)
if not self.draw_border:
return
gpu.state.blend_set('ALPHA')
gpu.state.depth_test_set('ALWAYS')
gpu.state.depth_mask_set(False)
#print('DRAW BORDER')
self.color_shader.bind()
self.color_shader.uniform_float("color", self.bg_color)
self.bg_batch.draw(self.color_shader)
self.dash_shader.bind()
matrix = gpu.matrix.get_projection_matrix()
self.dash_shader.uniform_float("color", self.border_color)
self.dash_shader.uniform_float("viewMatrix", matrix)
self.dash_shader.uniform_float("dashSize", 5)
self.dash_shader.uniform_float("gapSize", 4)
self.contour_batch.draw(self.dash_shader)
#gpu.state.depth_mask_set(False)
gpu.state.blend_set('NONE')
'''
def invoke(self, context, event):
print(f'invoke: type={event.type}, value={event.value}, ctrl={event.ctrl}, shift={event.shift}, alt={event.alt}')
print(self)
if context.object.mode != 'POSE':
bpy.ops.object.posemode_toggle()
#self.timer = None
#self.mode = self.mode_from_event(event)
#self.invoke_event = event.copy()
#self.region = context.region
self.draw_border = False
self.picker = PICKERS[context.object]
self.start_mouse = event.mouse_region_x, event.mouse_region_y
#self.shader = line_strip_shader
self.border_color = [1, 1, 1, 1]
self.bg_color = [1, 1, 1, 0.05]
#args = (self, context)
self.color_shader = gpu.shader.from_builtin('UNIFORM_COLOR')
self.dash_shader = SHADERS['dashed_line']
#self._handle = bpy.types.SpaceNodeEditor.draw_handler_add(self.draw_callback, (), 'WINDOW', 'POST_PIXEL')
self.border = [(0, 0), (0, 0), (0, 0), (0, 0)]
#context.window_manager.modal_handler_add(self)
self.mode = self.mode_from_event(event)
self.picker.press_event(self.mode)
context.region.tag_redraw()
return {'RUNNING_MODAL'}
def modal(self, context, event, tweak):
#print(f'invoke: type={event.type}, value={event.value}, ctrl={event.ctrl}, shift={event.shift}, alt={event.alt}')
self.mouse = event.mouse_region_x, event.mouse_region_y
self.border = bounding_rect((self.start_mouse, self.mouse))
self.bg_batch = batch_for_shader(self.color_shader, 'TRI_FAN', {"pos": self.border})
self.contour_batch = batch_for_shader(self.dash_shader, 'LINE_LOOP', {"pos": self.border})
self.draw_border = True
context.region.tag_redraw()
#self.draw(context)
print(f'invoke: type={event.type}, value={event.value}, ctrl={event.ctrl}, shift={event.shift}, alt={event.alt}')
return {'RUNNING_MODAL'}
def exit(self, context, cancel):
scn = context.scene
if scn.rig_picker.use_pick_bone:
self.picker.assign_bone_event()
elif (self.start_mouse[0] != self.mouse[0] or self.start_mouse[1] != self.mouse[1]):
self.picker.border_select(self.border, self.mode)
else:
self.picker.move_event(self.mouse)
self.picker.release_event(self.mode)
bpy.ops.ed.undo_push(message="Box Select")
self.draw_border = False
context.region.tag_redraw()
print('EXIT')
'''
@ -296,9 +106,6 @@ class RP_GT_gizmogroup(GizmoGroup):
def setup(self, context):
self.gizmo = self.gizmos.new("RP_GT_gizmo")
self.gizmo.draw_border = False
self.gizmo.border = []
self.gizmo.use_draw_modal = True

View File

@ -392,24 +392,19 @@ class RP_OT_reload_picker(Operator):
# return
def execute(self, context):
#PICKERS.clear()
if context.object.type == 'ARMATURE':
rig = context.object
else:
collection = get_picker_collection(context.object)
rig = collection.rig_picker.rig
#print('Reload', rig)
load_picker_data(rig)
'''
for area in context.screen.areas:
#print(area.type, 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()
'''
return {"FINISHED"}
@ -551,7 +546,8 @@ class RP_MT_context_menu(Menu):
def draw(self, context):
layout = self.layout
col = layout.column()
col.use_property_split = True
col.operator_context = 'INVOKE_DEFAULT'
#col.use_property_split = True
ob = context.object
picker = PICKERS.get(ob)
@ -561,9 +557,25 @@ class RP_MT_context_menu(Menu):
else:
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:
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("rigidbody.objects_add", text="B Add Passive").type = 'PASSIVE'
@ -755,7 +767,7 @@ def register_keymaps():
kmi = km.keymap_items.new("node.context_menu_picker", type="RIGHTMOUSE", value="PRESS")
keymaps.append((km, kmi))
'''
kmi = km.keymap_items.new("node.rp_box_select", type="LEFTMOUSE", value="PRESS")
kmi.properties.mode = 'SET'
keymaps.append((km, kmi))
@ -764,10 +776,11 @@ def register_keymaps():
kmi.properties.mode = 'EXTEND'
keymaps.append((km, kmi))
#kmi = km.keymap_items.new("node.rp_box_select", type="LEFTMOUSE", value="PRESS", ctrl=True)
kmi = km.keymap_items.new("node.rp_box_select", type="LEFTMOUSE", value="PRESS", alt=True)
kmi.properties.mode = 'SUBSTRACT'
keymaps.append((km, kmi))
'''
#km = wm.keyconfigs.addon.keymaps.new(name="View2D")
#kmi = km.keymap_items.new("rigpicker.call_operator", type="MIDDLEMOUSE", value="PRESS")
#kmi.properties.operator = "bpy.ops.view2d.pan('INVOKE_DEFAULT')"

View File

@ -222,7 +222,6 @@ class RP_OT_save_picker(Operator):
bpy.ops.rigpicker.reload_picker()
return {'FINISHED'}
classes = (