Picker Group

This commit is contained in:
ChristopheSeux
2024-02-19 11:53:15 +01:00
parent b855e8ac37
commit c31a73f80f
12 changed files with 690 additions and 418 deletions
+109 -135
View File
@@ -1,7 +1,8 @@
import bpy
from bpy.props import EnumProperty
from bpy.props import EnumProperty, IntProperty
from ..constants import PICKERS
from ..core.bl_utils import get_view_3d_override
from ..core.geometry_utils import bounding_rect
from ..core.picker import get_picker_path, pack_picker, unpack_picker
#from .func_bgl import draw_callback_px
#from .func_bgl import select_bone
@@ -151,15 +152,8 @@ class RP_OT_box_select(bpy.types.Operator):
def modal(self, context, event):
self.mouse = event.mouse_region_x, event.mouse_region_y
points_x = [v[0] for v in (self.start_mouse, self.mouse)]
points_y = [v[1] for v in (self.start_mouse, self.mouse)]
self.border = [
(min(points_x), max(points_y)),
(max(points_x), max(points_y)),
(max(points_x), min(points_y)),
(min(points_x), min(points_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})
@@ -179,7 +173,7 @@ class RP_OT_box_select(bpy.types.Operator):
if scn.rig_picker.use_pick_bone:
self.picker.assign_bone_event()
elif (self.start_mouse[0] != self.mouse[0] and self.start_mouse[1] != self.mouse[1]):
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)
@@ -255,7 +249,7 @@ class RP_OT_function_execute(bpy.types.Operator):
bl_idname = "rigpicker.function_execute"
bl_label = 'Function Execute'
shape_index = bpy.props.IntProperty()
shape_index = IntProperty()
@classmethod
def poll(cls, context):
@@ -295,7 +289,7 @@ class RP_OT_reload_picker(bpy.types.Operator):
def execute(self,context):
PICKERS.clear()
for a in context.screen.areas:
for a in context.screen.areas:
a.tag_redraw()
return {"FINISHED"}
@@ -379,24 +373,35 @@ class RP_OT_pack_picker(bpy.types.Operator):
class RP_OT_call_operator(bpy.types.Operator):
bl_idname = "rigpicker.call_operator"
bl_label = 'Toogle Bone Layer'
bl_label = 'Call operator'
operator: bpy.props.StringProperty()
operator : bpy.props.StringProperty()
view_3d : bpy.props.BoolProperty()
@classmethod
def poll(cls, context):
return is_picker_space(context)
def execute(self, context):
print('CALL OPERATOR', self.operator)
try:
exec(self.operator)
except Exception as e:
self.report({"ERROR"}, e)
if not self.operator.startswith('bpy.ops'):
print("Operator call_operator can only execute operator")
return {"FINISHED"}
context.region.tag_redraw()
print('CAll Operator')
context.area.tag_redraw()
override = {}
if self.view_3d:
override = get_view_3d_override()
with context.temp_override(**override):
try:
exec(self.operator)
except Exception as e:
self.report({"ERROR"}, e)
context.area.tag_redraw()
return {"FINISHED"}
@@ -423,127 +428,80 @@ class RP_MT_context_menu(bpy.types.Menu):
#layout.operator("rigpicker.show_bone_layer", text="Show Bone Layer", ).type = 'ACTIVE'
#layout.operator("rigidbody.objects_add", text="B Add Passive").type = 'PASSIVE'
'''
class RP_OT_ui_draw(bpy.types.Operator):
bl_idname = "rigpicker.ui_draw"
bl_label = "Rig UI Draw"
_handle = None
tmp_ob = None
tmp_bones = []
start = (0,0)
end = (0,0)
border = ((0,0),(0,0),(0,0),(0,0))
is_border = False
press = False
scale = 1
offset = 0
outside_point = (-1,-1)
addon_keymaps = []
class RP_OT_add_picker_source(bpy.types.Operator):
bl_idname = "rigpicker.add_picker_source"
bl_label = "Add a Picker source"
bl_description = "Add a Picker source"
bl_options = {'UNDO', 'REGISTER'}
def set_shorcut(self,context):
def execute(self, context):
arm = context.object.data
arm.rig_picker.sources.add()
return {'FINISHED'}
class RP_OT_remove_picker_source(bpy.types.Operator):
bl_idname = "rigpicker.remove_picker_source"
bl_label = "Delete a Picker source"
bl_description = "Delete a Picker source"
bl_options = {'UNDO', 'REGISTER'}
index : IntProperty(default=-1)
def execute(self, context):
arm = context.object.data
arm.rig_picker.sources.remove(self.index)
return {'FINISHED'}
class RP_OT_fit_picker(bpy.types.Operator):
bl_idname = "rigpicker.fit_picker"
bl_label = "Fit Picker"
bl_description = "Fit Picker in 2d view"
bl_options = {'UNDO', 'REGISTER'}
index : IntProperty()
@classmethod
def poll(cls, context):
return is_picker_space(context) and PICKERS.get(context.object)
def execute(self, context):
ob = context.object
addon = bpy.context.window_manager.keyconfigs.addon
picker_group = PICKERS[ob]
if ob and ob.type =='ARMATURE' and ob.data.rig_picker and addon:
for i,shape in [(i,s) for i,s in enumerate(ob.data.rig_picker['shapes']) if s.get('function') and s.get('shortcut')]:
km = addon.keymaps.new(name = 'Image Generic', space_type = 'IMAGE_EDITOR',region_type = 'WINDOW')
if self.index >= len(picker_group.pickers):
return {"CANCELLED"}
split = shape["shortcut"].split(' ')
if len(split)==1:
shortcut = shape["shortcut"].upper()
modifier = None
else:
shortcut = split[1].upper()
modifier = split[0].lower()
picker = picker_group.pickers[self.index]
view2d = context.region.view2d
kmi = km.keymap_items.new("rigpicker.function_execute", type = shortcut, value = "CLICK")
kmi.properties.shape_index = i
if self.index == -1:
#print('Picker Group')
picker = picker_group
if modifier:
setattr(kmi,modifier,True)
view_rect = [view2d.view_to_region(*co, clip=False) for co in picker.rect]
bpy.ops.view2d.zoom_border(
xmin=round(view_rect[3][0]), xmax=round(view_rect[1][0]),
ymin=round(view_rect[3][1]), ymax=round(view_rect[1][1]),
wait_for_input=False, zoom_out=False
)
self.addon_keymaps.append(km)
region_center = Vector((context.region.width * 0.5, context.region.height * 0.5))
view_center = view2d.region_to_view(*region_center)
def remove_shorcut(self,context):
# Remove Shortcut
wm = bpy.context.window_manager
for km in self.addon_keymaps:
for kmi in km.keymap_items:
km.keymap_items.remove(kmi)
view_offset = Vector(view_center) + (picker.center - Vector(view_center))
region_offset = view2d.view_to_region(*view_offset, clip=False)
self.addon_keymaps.clear()
delta = Vector(region_offset) - region_center
def modal(self, context, event):
inside = is_over_region(self,context,event)
bpy.ops.view2d.pan(deltax=round(delta[0]), deltay=round(delta[1]))
if context.object and context.object.type == 'ARMATURE' and context.area:
if not context.screen.is_animation_playing:
if self.tmp_ob != context.object:
context.area.tag_redraw()
self.remove_shorcut(context)
self.set_shorcut(context)
self.tmp_ob = context.object
return {'FINISHED'}
if self.tmp_bones != context.selected_pose_bones:
context.area.tag_redraw()
self.tmp_bones = context.selected_pose_bones
if inside:
context.area.tag_redraw()
if event.type == 'LEFTMOUSE':
if event.value == 'PRESS': # start selection
if inside:
self.start = (event.mouse_region_x,event.mouse_region_y)
self.press = True
elif event.value == 'RELEASE' and self.press:
self.end = (event.mouse_region_x, event.mouse_region_y)
select_bone(self, context, event)
bpy.ops.ed.undo_push()
self.is_border= False
self.press = False
if event.type == 'MOUSEMOVE':
self.end = (event.mouse_region_x, event.mouse_region_y)
if self.press:
b_x = (min(self.start[0], self.end[0]), max(self.start[0], self.end[0]))
b_y = (min(self.start[1], self.end[1]), max(self.start[1], self.end[1]))
self.border = ((b_x[0], b_y[1]), (b_x[1], b_y[1]), (b_x[1], b_y[0]), (b_x[0], b_y[0]))
self.is_border = True if (b_x[1]-b_x[0])+(b_y[1]-b_y[0]) > 4 else False
if self.is_border:
select_bone(self, context, event)
elif event.type in {'ESC',} and inside:
bpy.types.SpaceImageEditor.draw_handler_remove(self._handle, 'WINDOW')
self.remove_shorcut(context)
return {'CANCELLED'}
return {'PASS_THROUGH'}
def invoke(self, context, event):
#shortcut Creation
context.space_data.image = None
self.adress = context.area.as_pointer()
args = (self, context)
self._handle = bpy.types.SpaceImageEditor.draw_handler_add(draw_callback_px, args, 'WINDOW', 'POST_PIXEL')
context.window_manager.modal_handler_add(self)
return {'RUNNING_MODAL'}
'''
keymaps = []
def register_keymaps():
@@ -551,10 +509,16 @@ def register_keymaps():
km = wm.keyconfigs.addon.keymaps.new(name="Node Editor", space_type="NODE_EDITOR")
kmi = km.keymap_items.new("rigpicker.call_operator", type="X", value="PRESS")
kmi.properties.operator = "bpy.ops.animtoolbox.reset_bone()"
for i in range(10):
kmi = km.keymap_items.new("rigpicker.fit_picker", type=f"NUMPAD_{i}", value="PRESS")
kmi.properties.index = i - 1
keymaps.append((km, kmi))
kmi = km.keymap_items.new("rigpicker.call_operator", type="NUMPAD_PERIOD", value="PRESS")
kmi.properties.operator = "bpy.ops.view3d.view_selected()"
kmi.properties.view_3d = True
keymaps.append((km, kmi))
kmi = km.keymap_items.new("rigpicker.call_operator", type="A", value="PRESS")
kmi.properties.operator = "bpy.ops.pose.select_all(action='SELECT')"
keymaps.append((km, kmi))
@@ -581,6 +545,8 @@ 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))
@@ -592,9 +558,14 @@ def register_keymaps():
#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')"
keymaps.append((km, kmi))
def unregister_keymaps():
for km, kmi in keymaps:
km.keymap_items.remove(kmi)
@@ -609,7 +580,10 @@ classes = (
RP_MT_context_menu,
RP_OT_picker_transform,
RP_OT_context_menu_picker,
RP_OT_pack_picker
RP_OT_pack_picker,
RP_OT_add_picker_source,
RP_OT_remove_picker_source,
RP_OT_fit_picker
#RP_OT_ui_draw
)
+69 -63
View File
@@ -1,13 +1,16 @@
import bpy
from ..core.shape import get_picker_datas
from ..core.addon_utils import is_shape
from ..core.bl_utils import link_mat_to_object, find_mirror
from pathlib import Path
import json
from pathlib import Path
from mathutils import Matrix
import bpy
from bpy.types import Operator
from ..core.shape import get_picker_data
from ..core.addon_utils import is_shape
from ..core.bl_utils import link_mat_to_object, flip_name
class RP_OT_create_shape(bpy.types.Operator):
class RP_OT_create_shape(Operator):
bl_label = 'Create UI shape'
bl_idname = 'rigpicker.create_shape'
#bl_options = {'REGISTER', 'UNDO'}
@@ -52,7 +55,7 @@ class RP_OT_create_shape(bpy.types.Operator):
return {'FINISHED'}
class RP_OT_name_from_bone(bpy.types.Operator):
class RP_OT_name_from_bone(Operator):
bl_label = 'Name Shape from selected bones'
bl_idname = 'rigpicker.name_from_bone'
#bl_options = {'REGISTER', 'UNDO'}
@@ -69,67 +72,67 @@ class RP_OT_name_from_bone(bpy.types.Operator):
return {'FINISHED'}
class RP_OT_mirror_shape(bpy.types.Operator):
class RP_OT_mirror_shape(Operator):
bl_label = 'Mirror UI shape'
bl_idname = 'rigpicker.mirror_shape'
#bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
return (context.object and context.object.type in ('MESH', 'CURVE', 'TEXT'))
def execute(self,context):
scn = context.scene
ob = context.object
collection = next((c for c in ob.users_collection if c.rig_picker.enabled), None)
objects = context.selected_objects
for ob in bpy.context.selected_objects:
# Remove mirror object:
for ob in list(collection.all_objects):
if ob in objects:
continue
name = find_mirror(ob.name)
link_mat_to_object(ob)
for mod in ob.modifiers:
if (mod.type == 'NODES' and mod.node_group.name == 'Symmetrize' and
mod.get('Socket_2') in objects):
bpy.data.objects.remove(ob)
if not name:
name = ob.name + '_flip'
if collection.rig_picker.symmetry:
x_axis = collection.rig_picker.symmetry.matrix_world.to_translation()[0]
else:
x_axis = 0
old_shape = bpy.data.objects.get(name)
old_mat = None
if old_shape:
old_mat = next((sl.material for sl in old_shape.material_slots if sl.material), None)
bpy.data.objects.remove(old_shape)
for ob in objects:
flipped_name = flip_name(ob.name)
if flipped_name == ob.name:
suffix = '.L'
flipped_suffix = '.R'
# Determine side of the object
if ob.matrix_world.to_translation()[0] < x_axis:
suffix, flipped_suffix = flipped_suffix, suffix
flipped_name = f'{ob.name}{flipped_suffix}'
ob.name = f'{ob.name}{suffix}'
mirror_ob = ob.copy()
mirror_ob.data = ob.data
mirror_ob.name = name
flipped_object = ob.copy()
flipped_object.name = flipped_name
flipped_object.parent = None
flipped_object.matrix_world = Matrix()
for mod in list(flipped_object.modifiers):
flipped_object.modifiers.remove(mod)
if old_mat:
#mirror_ob.data.materials.clear()
#mirror_ob.data.materials.append(None)
#mirror_ob.material_slots[0].link = 'OBJECT'
# Add symmetrize modifier
mod = flipped_object.modifiers.new(name='Symmetrize', type='NODES')
mod.node_group = bpy.data.node_groups['Symmetrize']
mod['Socket_2'] = ob
mod['Socket_3'] = collection.rig_picker.symmetry
mirror_ob.material_slots[0].material = old_mat
#mirror_ob = bpy.data.objects.new(name,ob.data.copy())
for c in ob.users_collection:
c.objects.link(mirror_ob)
if scn.rig_picker.symmetry:
symmetry_loc = scn.rig_picker.symmetry.matrix_world.to_translation()[0]
else:
symmetry_loc = 0
#print(symmetry_loc)
mirror_ob.matrix_world = ob.matrix_world
#if mirror_ob.location[0] < symmetry_loc:
mirror_ob.location.x = symmetry_loc + (symmetry_loc - ob.location.x)
#else:
# mirror_ob.location[0]= symmetry_loc+ (symmetry_loc- ob.location[0])
mirror_ob.rotation_euler.y = -ob.rotation_euler.y
mirror_ob.rotation_euler.z = -ob.rotation_euler.z
mirror_ob.scale.x = -ob.scale.x
for key, value in ob.items():
if key not in ['_RNA_UI','cycles']:
mirror_ob[key] = value
for col in ob.users_collection:
col.objects.link(flipped_object)
if ob.rig_picker.shape_type == 'BONE':
mirror_ob.rig_picker.name = find_mirror(ob.rig_picker.name)
flipped_object.rig_picker.name = flip_name(ob.rig_picker.name)
elif ob.rig_picker.shape_type == 'FUNCTION':
args = {}
@@ -137,18 +140,18 @@ class RP_OT_mirror_shape(bpy.types.Operator):
if type(value) == list:
mirrored_value = []
for item in value:
mirrored_value.append(find_mirror(item))
mirrored_value.append(flip_name(item))
elif type(value) == str:
mirrored_value = find_mirror(value)
mirrored_value = flip_name(value)
args[key] = mirrored_value
mirror_ob.rig_picker.arguments = str(args)
flipped_object.rig_picker.arguments = str(args)
return {'FINISHED'}
class RP_OT_select_shape_type(bpy.types.Operator):
class RP_OT_select_shape_type(Operator):
bl_label = 'Select Shape by Type'
bl_idname = 'rigpicker.select_shape_type'
#bl_options = {'REGISTER', 'UNDO'}
@@ -177,15 +180,18 @@ class RP_OT_select_shape_type(bpy.types.Operator):
return wm.invoke_props_dialog(self, width=150)
class RP_OT_save_picker(bpy.types.Operator):
class RP_OT_save_picker(Operator):
bl_label = 'Store UI Data'
bl_idname = 'rigpicker.save_picker'
def execute(self, context):
scn = context.scene
canvas = scn.rig_picker.canvas
rig = scn.rig_picker.rig
shapes = [o for o in scn.objects if o != canvas and is_shape(o)]
ob = context.object
collection = next((c for c in ob.users_collection if c.rig_picker.enabled), None)
canvas = collection.rig_picker.canvas
rig = collection.rig_picker.rig
shapes = [o for o in collection.all_objects if o != canvas and o.type in ('MESH', 'CURVE', 'FONT')]
if not rig:
self.report({'ERROR'}, 'Choose a Rig')
@@ -196,9 +202,9 @@ class RP_OT_save_picker(bpy.types.Operator):
return {'CANCELLED'}
data = get_picker_datas(shapes, canvas, rig)
data = get_picker_data(shapes, canvas, rig)
picker_path = Path(bpy.path.abspath(scn.rig_picker.destination))
picker_path = Path(bpy.path.abspath(collection.rig_picker.destination))
picker_path.write_text(json.dumps(data))
bpy.ops.rigpicker.reload_picker()