diff --git a/__init__.py b/__init__.py index 34e9890..2479b1b 100644 --- a/__init__.py +++ b/__init__.py @@ -15,7 +15,7 @@ import importlib modules = ( '.operators', '.properties', - '.panels', + '.ui', '.area', '.gizmo', '.draw_handlers' diff --git a/area.py b/area.py index b3e5dd3..3b5a358 100644 --- a/area.py +++ b/area.py @@ -1,5 +1,6 @@ import bpy from bpy.types import NodeTree, NODE_PT_tools_active, NODE_HT_header +from .constants import PICKERS # Derived from the NodeTree base type, similar to Menu, Operator, Panel, etc. @@ -13,36 +14,52 @@ class RigPickerTree(NodeTree): bl_icon = 'OUTLINER_DATA_ARMATURE' - - - - - - def draw_header(self, context): - if context.space_data.tree_type == 'RigPickerTree': - layout = self.layout - layout.template_header() - #layout.separator_spacer() - - if not context.space_data.node_tree: - ntree = bpy.data.node_groups.get('.rig_picker') - if not ntree: - ntree = bpy.data.node_groups.new('.rig_picker', 'RigPickerTree') - - context.space_data.node_tree = ntree - - #layout.template_ID(context.space_data, "node_tree", new="node.new_node_tree") - #layout.separator_spacer() - - layout.operator('rigpicker.reload_picker', icon='FILE_REFRESH', text='') - - #layout.prop('rigpicker.reload_picker', icon='FILE_REFRESH', text='') - - layout.separator_spacer() - - else: + if not context.space_data.tree_type == 'RigPickerTree': self._draw(context) + return + + layout = self.layout + layout.template_header() + #layout.separator_spacer() + + if not context.space_data.node_tree: + ntree = bpy.data.node_groups.get('.rig_picker') + if not ntree: + ntree = bpy.data.node_groups.new('.rig_picker', 'RigPickerTree') + + context.space_data.node_tree = ntree + + #layout.template_ID(context.space_data, "node_tree", new="node.new_node_tree") + #layout.separator_spacer() + + #layout.operator('rigpicker.reload_picker', icon='FILE_REFRESH', text='') + ob = context.object + if not ob or not ob.type == 'ARMATURE': + return + + picker_group = PICKERS.get(ob) + if not picker_group: + return + + layout.separator_spacer() + layout.label(text=ob.name) + + layout.separator_spacer() + #row.alignment = 'RIGHT' + + row = layout.row(align=True) + + + + for i, picker in enumerate(picker_group.pickers): + row.operator('rigpicker.fit_picker', text=f'{i+1}').index=i + #row.separator_spacer() + row.operator('rigpicker.fit_picker', text='', icon='FULLSCREEN_ENTER').index = -1 + + #layout.prop('rigpicker.reload_picker', icon='FILE_REFRESH', text='') + + def tools_from_context(context, mode=None): sp = context.space_data diff --git a/core/bl_utils.py b/core/bl_utils.py index 4a08126..1f858ac 100644 --- a/core/bl_utils.py +++ b/core/bl_utils.py @@ -31,40 +31,15 @@ def link_mat_to_object(ob): sl.link = 'OBJECT' sl.material = m -def find_mirror(name): - mirror = None - prop= False - - if name: - - if name.startswith('[')and name.endswith(']'): - prop = True - name= name[:-2][2:] - - match={ - 'R': 'L', - 'r': 'l', - 'L': 'R', - 'l': 'r', - } - - separator=['.','_'] - - if name.startswith(tuple(match.keys())): - if name[1] in separator: - mirror = match[name[0]]+name[1:] - - if name.endswith(tuple(match.keys())): - if name[-2] in separator: - mirror = name[:-1]+match[name[-1]] - - if mirror and prop == True: - mirror='["%s"]'%mirror - - return mirror +def flip_name(name): + if not name: + return + if name.startswith('[') and name.endswith(']'): #It's a custom property + flipped_name = bpy.utils.flip_name(name[:-2][2:]) + return f'["{flipped_name}"]' else: - return None + return bpy.utils.flip_name(name) def hide_layers(args): """ """ diff --git a/core/geometry_utils.py b/core/geometry_utils.py index f061898..ec2ea5c 100644 --- a/core/geometry_utils.py +++ b/core/geometry_utils.py @@ -21,15 +21,25 @@ def bound_box_center(ob): return (sum(x) / len(points), sum(y) / len(points),sum(z) / len(points)) +def bounding_rect(points): + x_points = sorted(p[0] for p in points) + y_points = sorted(p[1] for p in points) -def intersect_rectangles(bound, border): # returns None if rectangles don't intersect - dx = min(border[1][0],bound[1][0]) - max(border[0][0],bound[0][0]) - dy = min(border[0][1],bound[0][1]) - max(border[2][1],bound[2][1]) + return [ + Vector((x_points[0], y_points[-1])), + Vector((x_points[-1], y_points[-1])), + Vector((x_points[-1], y_points[0])), + Vector((x_points[0], y_points[0])), + ] + +def intersect_rects(rect, border): # returns None if rectangles don't intersect + dx = min(border[1][0],rect[1][0]) - max(border[0][0],rect[0][0]) + dy = min(border[0][1],rect[0][1]) - max(border[2][1],rect[2][1]) if (dx>=0) and (dy>=0): return dx*dy -def point_inside_rectangle(point, rect): +def point_inside_rect(point, rect): return rect[0][0]< point[0]< rect[1][0] and rect[2][1]< point[1]< rect[0][1] def point_over_shape(point,verts,loops,outside_point=(-1,-1)): diff --git a/core/picker.py b/core/picker.py index 83623ca..e6b3659 100644 --- a/core/picker.py +++ b/core/picker.py @@ -4,9 +4,11 @@ import gpu from gpu_extras.batch import batch_for_shader import blf from mathutils import bvhtree, Vector -from mathutils.geometry import intersect_point_quad_2d, intersect_point_tri_2d, intersect_tri_tri_2d +from mathutils.geometry import (intersect_point_quad_2d, intersect_point_tri_2d, + intersect_tri_tri_2d) from ..constants import PICKERS from .addon_utils import get_operator_from_id +from .geometry_utils import bounding_rect from pathlib import Path import re @@ -16,7 +18,6 @@ import os import threading - class Shape: def __init__(self, picker, points, polygons=None, edges=None, tooltip='', color=None, source_name=''): @@ -28,7 +29,6 @@ class Shape: self.hover = False self.press = False - self.shader = gpu.shader.from_builtin('UNIFORM_COLOR') #self.hover_shader = gpu.shader.from_builtin('UNIFORM_COLOR') @@ -41,29 +41,10 @@ class Shape: self.polygons = polygons or [] self.edges = edges or [] - #print(points, self.polygons) - self.p_batch = batch_for_shader(self.shader, 'TRIS', {"pos": self.points}, indices=self.polygons) self.e_batch = batch_for_shader(self.shader, 'LINES', {"pos": self.points}, indices=self.edges) - #if polygons: - # self.batch = batch_for_shader(self.shader, 'TRIS', {"pos": points}, indices=polygons) - #else: - #pts = [] - #for loop in self.edges: - # pts += [self.points[i] for i in loop] - - # self.batch = batch_for_shader(self.shader, 'LINES', {"pos": points}, indices=indices) - - points_x = [v[0] for v in points] - points_y = [v[1] for v in points] - - self.bound = [ - (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.rect = bounding_rect(points) @property def color(self): @@ -97,7 +78,7 @@ class Shape: self.e_batch.draw(self.shader) def move_event(self, location): - if not intersect_point_quad_2d(location, *self.bound): + if not intersect_point_quad_2d(location, *self.rect): self.hover = False return False @@ -224,6 +205,7 @@ class BoneShape(Shape): #Overlay the fill slightly with the bone color self.shader.uniform_float("color", (*self.bone_colors['normal'][:3], 0.1)) self.p_batch.draw(self.shader) + gpu.state.blend_set('NONE') #self.contour_shader.bind() @@ -238,7 +220,6 @@ class BoneShape(Shape): self.e_batch.draw(self.shader) gpu.state.line_width_set(1.0) - gpu.state.blend_set('NONE') def assign_bone_event(self): @@ -275,11 +256,6 @@ class BoneShape(Shape): def border_select(self, border, mode='SET'): - ''' - if ( not any(intersect_point_quad_2d(b, *self.bound) for b in border) and - not any(intersect_point_quad_2d(b, *border) for b in self.bound) ): - return - ''' if not self.bone: return @@ -287,16 +263,16 @@ class BoneShape(Shape): self.bone.bone.select = False return - bound_tri1 = self.bound[0], self.bound[1], self.bound[2] - bound_tri2 = self.bound[2], self.bound[3], self.bound[0] + rect_tri1 = self.rect[0], self.rect[1], self.rect[2] + rect_tri2 = self.rect[2], self.rect[3], self.rect[0] border_tri1 = border[0], border[1], border[2] border_tri2 = border[2], border[3], border[0] - if (not intersect_tri_tri_2d(*border_tri1, *bound_tri1) and - not intersect_tri_tri_2d(*border_tri1, *bound_tri2) and - not intersect_tri_tri_2d(*border_tri2, *bound_tri1) and - not intersect_tri_tri_2d(*border_tri2, *bound_tri2)): + if (not intersect_tri_tri_2d(*border_tri1, *rect_tri1) and + not intersect_tri_tri_2d(*border_tri1, *rect_tri2) and + not intersect_tri_tri_2d(*border_tri2, *rect_tri1) and + not intersect_tri_tri_2d(*border_tri2, *rect_tri2)): return select = True @@ -388,116 +364,89 @@ class Picker: self.region = bpy.context.region self.rig = rig + self.translation = Vector((0, 0)) self.shapes = [] self.box_select = None self.hover_shape = None self.shader = gpu.shader.from_builtin('UNIFORM_COLOR') - self.tooltip_shape = None - self.tooltip_mouse = None - self.tooltip = '' - - self.timer = None self.mouse = None - for s in shapes: - if not s['points']: + for shape_data in shapes: + if not shape_data['points']: continue - if s['type'] in ('CANVAS', 'DISPLAY'): + if shape_data['type'] in ('CANVAS', 'DISPLAY'): shape = Shape( self, - points=s['points'], - polygons=s['polygons'], - edges=s['edges'], - color=s['color'] + points=shape_data['points'], + polygons=shape_data['polygons'], + edges=shape_data['edges'], + color=shape_data['color'] ) - elif s['type'] == 'BONE': - bone = rig.pose.bones.get(s['bone']) + elif shape_data['type'] == 'BONE': + bone = rig.pose.bones.get(shape_data['bone']) #if not bone: - # print(f'Bone {s["bone"]} not exist') + # print(f'Bone {shape_data["bone"]} not exist') # continue shape = BoneShape( self, - source_name=s['source_name'], - points=s['points'], - polygons=s['polygons'], - edges=s['edges'], + source_name=shape_data['source_name'], + points=shape_data['points'], + polygons=shape_data['polygons'], + edges=shape_data['edges'], bone=bone, - color=s['color'] + color=shape_data['color'] ) - elif s['type'] == 'OPERATOR': + elif shape_data['type'] == 'OPERATOR': shape = OperatorShape( self, - source_name=s['source_name'], - points=s['points'], - polygons=s['polygons'], - operator=s['operator'], - color=s['color'], - tooltip=s['tooltip'], + source_name=shape_data['source_name'], + points=shape_data['points'], + polygons=shape_data['polygons'], + operator=shape_data['operator'], + color=shape_data['color'], + tooltip=shape_data['tooltip'], ) self.shapes.append(shape) + self.rect = bounding_rect([p for s in self.shapes for p in s.points]) + def assign_bone_event(self): - for s in self.shapes: - if s.type=='bone' and s.hover: - s.assign_bone_event() + for shape in self.shapes: + if shape.type=='bone' and shape.hover: + shape.assign_bone_event() bpy.ops.rigpicker.save_picker() def press_event(self, mode='SET'): - for s in self.shapes: - if s.hover: - s.press_event(mode) + for shape in self.shapes: + #print(s) + if shape.hover: + shape.press_event(mode) else: - s.press = False + shape.press = False def release_event(self, mode='SET'): - if mode == 'SET': - for b in self.rig.pose.bones: - b.bone.select = False + #bpy.ops.pose.select_all(action='DESELECT') #print('PICKER release event', mode) #print(f'type={event.type}, value={event.value}, ctrl={event.ctrl}, shift={event.shift}, alt={event.alt}') - for s in self.shapes: - if s.hover: - s.release_event(mode) + for shape in self.shapes: + if shape.hover: + shape.release_event(mode) - s.press = False + shape.press = False #bpy.context.area.tag_redraw() - - def tooltip_event(self): - #print('Tooltip Event', self) - - #print(self.hover_shape, self.hover_shape.type) - - if self.hover_shape and self.hover_shape.type != 'display': - if self.hover_shape.type == 'bone': - self.tooltip = self.hover_shape.bone.name - else: - self.tooltip = self.hover_shape.tooltip - self.tooltip_shape = self.hover_shape - else: - self.tooltip = '' - self.tooltip_shape = None - - self.tooltip_mouse = self.mouse - - self.timer.cancel() - - #print(self.tooltip) - - self.region.tag_redraw() - ''' picker.tooltip_event(event='SHOW') region.tag_redraw() @@ -524,27 +473,27 @@ class Picker: ''' def border_select(self, border, mode): - border = [bpy.context.region.view2d.region_to_view(*b) for b in border] + border = [Vector(b) - Vector(self.translation) for b in border] - if mode == 'SET': - for b in self.rig.pose.bones: - b.bone.select = False - for s in (s for s in self.shapes if s.type=='bone'): - s.border_select(border, mode) + + for shape in self.shapes: + if shape.type != 'bone': + continue + + shape.border_select(border, mode) def move_event(self, location): - self.mouse = location - location = self.region.view2d.region_to_view(*location) + self.mouse = Vector(location) - Vector(self.translation) self.hover_shape = None for shape in reversed(self.shapes): if self.hover_shape: shape.hover = False - elif shape.move_event(location): + elif shape.move_event(self.mouse): self.hover_shape = shape - #if point_inside_rectangle(self.end, bound): + #if point_inside_rect(self.end, rect): # over = point_over_shape(self.end,points, edges) #if bpy.app.timers.is_registered(self.tooltip_event): @@ -552,6 +501,85 @@ class Picker: # bpy.app.timers.unregister(self.tooltip_event) #except: # pass + + #bpy.app.timers.register(self.tooltip_event, first_interval=1) + + def draw(self): + with gpu.matrix.push_pop(): + gpu.matrix.translate(self.translation) + for shape in self.shapes: + shape.draw() + + def under_mouse(self, location): + location = Vector(location) - Vector(self.translation) + return intersect_point_quad_2d(location, *self.rect) + + @property + def center(self): + relative_center = (self.rect[1] + self.rect[3]) * 0.5 + return relative_center + self.translation + + +class PickerGroup: + def __init__(self, rig, pickers): + + self.view_location = Vector((0, 0)) + self.region = bpy.context.region + self.rig = rig + self.pickers = [] + + self.mouse = Vector((0, 0)) + + self.hover_shape = None + + self.tooltip_shape = None + self.tooltip_mouse = Vector((0, 0)) + self.tooltip = '' + + self.timer = None + + for picker in pickers: + self.add_picker(picker) + + def add_picker(self, picker): + self.pickers.append(Picker(self.rig, picker)) + + def draw(self): + y = 0 + for picker in self.pickers: + height = picker.rect[1][1] - picker.rect[-1][1] + + picker.translation = Vector((0, -y-height*0.5)) + picker.draw() + + y += height + 50 + + #break #TODO for now only draw first picker + + def move_event(self, mouse): + self.mouse = mouse + location = self.region.view2d.region_to_view(*mouse) + + view_location = self.region.view2d.region_to_view(0, 0) + if view_location != self.view_location: + self.view_location = view_location + self.tooltip = '' + if self.timer: + self.timer.cancel() + return + + hover_shape = None + for picker in self.pickers: + if not picker.under_mouse(location): + continue + + picker.move_event(location) + + if picker.hover_shape: + hover_shape = picker.hover_shape + + self.hover_shape = hover_shape + if self.tooltip_shape is not self.hover_shape: self.tooltip = '' @@ -561,27 +589,70 @@ class Picker: self.timer = threading.Timer(0.5, self.tooltip_event) self.timer.start() + def press_event(self, mode='SET'): + self.clear_tooltip() - #bpy.app.timers.register(self.tooltip_event, first_interval=1) + for picker in self.pickers: + picker.press_event(mode) - def draw(self): - for s in self.shapes: - s.draw() - ''' - if self.box_select: + def release_event(self, mode='SET'): - self.box_shader.uniform_float("color", self.box_select_color) - batch = batch_for_shader(self.shader, 'LINE_LOOP', {"pos": []}) + if mode == 'SET': + for bone in self.rig.data.bones: + bone.select = False + for picker in self.pickers: + picker.release_event(mode) + + def border_select(self, border, mode): + border = [self.region.view2d.region_to_view(*b) for b in border] + + if mode == 'SET': + for bone in self.rig.data.bones: + bone.select = False + + for picker in self.pickers: + picker.border_select(border, mode) + + def clear_tooltip(self): + self.tooltip = '' + self.tooltip_shape = None + self.timer.cancel() + self.region.tag_redraw() + + def tooltip_event(self): + #print('Tooltip Event', self) + + #print(self.hover_shape, self.hover_shape.type) + + if self.hover_shape and self.hover_shape.type != 'display': + if self.hover_shape.type == 'bone': + self.tooltip = self.hover_shape.bone.name + else: + self.tooltip = self.hover_shape.tooltip + self.tooltip_shape = self.hover_shape + else: + return self.clear_tooltip() - for b in self.contour_batches: - b.draw(self.contour_shader) - self.batch.draw(self.shader) + self.tooltip_mouse = self.mouse + + self.timer.cancel() + + #print(self.tooltip) + + self.region.tag_redraw() + + @property + def rect(self): + return bounding_rect([co - Vector(p.translation) for p in self.pickers for co in p.rect]) + + @property + def center(self): + center = sum(self.rect, Vector((0, 0))) / len(self.rect) + center[1] = -center[1] + return center - - gpu.state.blend_set('NONE') - ''' def get_picker_path(rig, start=None): picker_path = rig.data.get('rig_picker', {}).get('source') @@ -592,13 +663,15 @@ def get_picker_path(rig, start=None): return Path(os.path.abspath(picker_path)) + def pack_picker(rig, start=None): picker_path = get_picker_path(rig, start=start) if picker_path and picker_path.exists(): if 'rig_picker' not in rig.data.keys(): rig.data['rig_picker'] = {} - rig.data['rig_picker']['picker'] = json.loads(picker_path.read_text()) + rig.data['rig_picker']['picker'] = json.loads(picker_path.read_text(encoding='utf-8')) + def unpack_picker(rig): if 'rig_picker' not in rig.data.keys(): diff --git a/core/shape.py b/core/shape.py index dcc15ce..91690e4 100644 --- a/core/shape.py +++ b/core/shape.py @@ -20,7 +20,7 @@ def border_over_shape(border,verts,loops): return True for point in verts: - if point_inside_rectangle(point,border): + if point_inside_rect(point,border): return True for point in border: @@ -29,7 +29,7 @@ def border_over_shape(border,verts,loops): def border_loop(vert, loop): - border_edge =[e for e in vert.link_edges if e.is_boundary] + border_edge =[e for e in vert.link_edges if e.is_rectary] if border_edge: for edge in border_edge: @@ -61,7 +61,7 @@ def contour_loops(bm, vert_index=0, loops=None, vert_indices=None): return loops -def get_picker_datas(objects, canvas, rig): +def get_picker_data(objects, canvas, rig): picker_datas = [] gamma = 1 / 2.2 @@ -72,11 +72,11 @@ def get_picker_datas(objects, canvas, rig): canvas_coords = [canvas.matrix_world@Vector((p.co)) for p in canvas_points] - canvas_x = [p[0] for p in canvas_coords] - canvas_y = [p[1] for p in canvas_coords] + height = max(canvas_coords).y - min(canvas_coords).y + width = max(canvas_coords).x - min(canvas_coords).x canvas_center = sum(canvas_coords, Vector()) / len(canvas_coords) - canvas_scale_fac = 4096 / (max(canvas_y) - min(canvas_y))# Reference height for the canvas + canvas_scale_fac = 4096 / max(height, width)# Reference height for the canvas objects.append(canvas) diff --git a/draw_handlers.py b/draw_handlers.py index 901b62c..42a070a 100644 --- a/draw_handlers.py +++ b/draw_handlers.py @@ -1,39 +1,91 @@ import bpy +import gpu +from gpu_extras.batch import batch_for_shader import blf from pathlib import Path from .constants import PICKERS -from .core.picker import Picker +from .core.picker import Picker, PickerGroup import json +def draw_rect_2d(position, width, height, color): + """ + Draw a 2d rectangele. + + :arg position: Position of the lower left corner. + :type position: 2D Vector + :arg width: Width of the rect. + :type width: float + :arg height: Height of the rect. + :type height: float + """ + + coords = ((0, 0), (1, 0), (1, 1), (0, 1)) + + shader = gpu.shader.from_builtin('UNIFORM_COLOR') + batch = batch_for_shader( + shader, 'TRI_FAN', + {"pos": coords}, + ) + + with gpu.matrix.push_pop(): + gpu.matrix.translate(position) + gpu.matrix.scale((width, height)) + shader.uniform_float("color", color) + + batch.draw(shader) + + def draw_callback_view(): sp = bpy.context.space_data if not sp or not sp.tree_type == 'RigPickerTree': return + if not sp.pin: + sp.pin = True + sp.show_region_ui = False + ob = bpy.context.object - if not ob or ob.type !='ARMATURE' or not ob.data.rig_picker.source: + if not ob or ob.type !='ARMATURE' or not ob.data.rig_picker.sources: return if ob not in PICKERS: - if 'picker' in ob.data.rig_picker: - picker_datas = [s.to_dict() for s in ob.data.rig_picker['picker']] + if 'pickers' in ob.data.rig_picker: + picker_datas = [s.to_dict() for s in ob.data.rig_picker['pickers']] else: - picker_path = Path(bpy.path.abspath(ob.data.rig_picker.source, library=ob.data.library)) + picker_datas = [] + for picker in ob.data.rig_picker.sources: + picker_path = Path(bpy.path.abspath(picker.source, library=ob.data.library)) - if not picker_path.exists(): - print(f'Picker path not exists: {picker_path.resolve()}') - return + if not picker_path.exists(): + print(f'Picker path not exists: {picker_path.resolve()}') + continue - print('Load picker from', picker_path.resolve()) - picker_datas = json.loads(picker_path.read_text()) - #shapes = [s.to_dict() for s in ob.data.rig_picker['shapes']] - PICKERS[ob] = Picker(ob, shapes=picker_datas) + print('Load picker from', picker_path.resolve()) + picker_data = json.loads(picker_path.read_text(encoding='utf-8')) + picker_datas.append(picker_data) + #shapes = [s.to_dict() for s in ob.data.rig_picker['shapes']] - picker = PICKERS.get(ob) - picker.draw() + PICKERS[ob] = PickerGroup(ob, picker_datas)#[Picker(ob, shapes=picker_data) for picker_data in picker_datas] + + # if 'picker' in ob.data.rig_picker: + # picker_datas = [s.to_dict() for s in ob.data.rig_picker['picker']] + # else: + # picker_path = Path(bpy.path.abspath(ob.data.rig_picker.source, library=ob.data.library)) + + # if not picker_path.exists(): + # print(f'Picker path not exists: {picker_path.resolve()}') + # return + + # print('Load picker from', picker_path.resolve()) + # picker_datas = json.loads(picker_path.read_text()) + # #shapes = [s.to_dict() for s in ob.data.rig_picker['shapes']] + # PICKERS[ob] = Picker(ob, shapes=picker_datas) + + picker_group = PICKERS.get(ob) + picker_group.draw() def draw_callback_px(): sp = bpy.context.space_data @@ -42,23 +94,36 @@ def draw_callback_px(): ob = bpy.context.object - picker = PICKERS.get(ob) - if not picker or not picker.tooltip: + picker_group = PICKERS.get(ob) + if not picker_group or not picker_group.tooltip: return - text = picker.tooltip + text = picker_group.tooltip + mouse = picker_group.tooltip_mouse + ui_scale = bpy.context.preferences.system.ui_scale #print('Draw text', text) - font_id = 0 + blf.size(font_id, int(13 * ui_scale)) + + text_size = blf.dimensions(font_id, text) + text_pos = (mouse[0] - text_size[0]*0.33, mouse[1] - (34*ui_scale)) + + bg_margins = [8, 4] + bg_pos = (text_pos[0] - bg_margins[0], text_pos[1] - bg_margins[1]-1) + bg_size = (text_size[0] + 2*bg_margins[0], text_size[1] + 2*bg_margins[1]) + + gpu.state.blend_set('ALPHA') + draw_rect_2d(bg_pos, *bg_size, (0, 0, 0, 0.75)) + gpu.state.blend_set('NONE') + + #blf.dimensions(font_id, text) blf.enable(font_id, blf.SHADOW) - #gpu.state.blend_set('ALPHA') # BLF drawing routine - blf.position(font_id, picker.tooltip_mouse[0]-5, picker.tooltip_mouse[1]+5, 0) - blf.size(font_id, 14) + blf.position(font_id, round(text_pos[0]), round(text_pos[1]), 0) blf.color(font_id, 1, 1, 1, 1) blf.shadow(font_id , 5, 0.0, 0.0, 0.0, 1) @@ -67,7 +132,6 @@ def draw_callback_px(): blf.draw(font_id, text) blf.disable(font_id, blf.SHADOW) - #gpu.state.blend_set('NONE') handle_view = None handle_pixel = None diff --git a/operators/picker.py b/operators/picker.py index e5006f0..db5b443 100644 --- a/operators/picker.py +++ b/operators/picker.py @@ -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 ) diff --git a/operators/shape.py b/operators/shape.py index b9fa8ef..cafb196 100644 --- a/operators/shape.py +++ b/operators/shape.py @@ -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() diff --git a/properties.py b/properties.py index 21efaf5..66caa77 100644 --- a/properties.py +++ b/properties.py @@ -1,5 +1,7 @@ import bpy -from bpy.props import EnumProperty, StringProperty, PointerProperty, BoolProperty +from bpy.props import (EnumProperty, StringProperty, PointerProperty, BoolProperty, + CollectionProperty, IntProperty) + import inspect ''' @@ -22,9 +24,15 @@ def bones_item(self,context): items.append((bone.name,bone.name,'')) return items + +class RP_PG_picker_source(bpy.types.PropertyGroup): + source : StringProperty(subtype='FILE_PATH') + + class RP_PG_armature_ui_settings(bpy.types.PropertyGroup): name: StringProperty() source: StringProperty(subtype='FILE_PATH') + sources: CollectionProperty(type=RP_PG_picker_source) class RP_PG_object_ui_settings(bpy.types.PropertyGroup): @@ -37,6 +45,7 @@ class RP_PG_object_ui_settings(bpy.types.PropertyGroup): class RP_PG_scene_ui_settings(bpy.types.PropertyGroup): + enabled : BoolProperty(default=False) rig: PointerProperty(type=bpy.types.Object) canvas: PointerProperty(type=bpy.types.Object) symmetry: PointerProperty(type=bpy.types.Object) @@ -67,6 +76,7 @@ class RP_OT_operator_selector(bpy.types.Operator): classes = ( + RP_PG_picker_source, RP_PG_object_ui_settings, RP_PG_scene_ui_settings, RP_PG_armature_ui_settings, @@ -80,6 +90,7 @@ def register(): 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.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(): del bpy.types.Scene.rig_picker diff --git a/resources/KohinoorZerone-Zero.otf b/resources/KohinoorZerone-Zero.otf new file mode 100644 index 0000000..594e3b1 Binary files /dev/null and b/resources/KohinoorZerone-Zero.otf differ diff --git a/ui.py b/ui.py new file mode 100644 index 0000000..8215ab5 --- /dev/null +++ b/ui.py @@ -0,0 +1,142 @@ +import bpy +from bpy.types import UIList +#import collections +#import inspect +from .core.addon_utils import get_operator_from_id +from .core.bl_utils import get_mat + +import re + + +# class RP_UL_picker_source(UIList): +# def draw_item(self, context, layout, data, item, _icon, _active_data, _active_propname, _index): +# ob = context.object +# row = layout.row(align=True) +# is_packed = ('picker' in ob.data.rig_picker.keys()) +# sub_row = row.row(align=True) +# sub_row.prop(item, 'source', text='') +# sub_row.enabled = not is_packed +# #row.operator('rigpicker.pack_picker', icon='PACKAGE' if is_packed else 'UGLYPACKAGE', text='') + + +class RP_PT_picker_maker_panel(bpy.types.Panel): + bl_label = 'Rig Picker' + bl_category = 'Rigging' + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + + @classmethod + def poll(cls, context): + return context.object + + def draw(self, context): + ob = context.object + scn = context.scene + collection = next((c for c in ob.users_collection if c.rig_picker.enabled), None) + + layout = self.layout + col = layout.column(align=False) + + if collection: + col.prop_search(collection.rig_picker, 'rig', scn, 'objects', text='Rig ') + col.prop_search(collection.rig_picker, 'canvas', scn, 'objects', text='Canvas ') + col.prop_search(collection.rig_picker, 'symmetry', scn, 'objects', text='Symmetry ') + + if ob.type == 'ARMATURE': + box = col.box() + box.enabled = not ob.data.library + col = box.column(align=False) + + row = col.row(align=True) + row.separator(factor=0.5) + row.label(text="Sources") + + is_packed = ('picker' in ob.data.rig_picker.keys()) + row.operator('rigpicker.pack_picker', icon='PACKAGE' if is_packed else 'UGLYPACKAGE', text='', emboss=False) + row.operator("rigpicker.add_picker_source", icon ='ADD', text="", emboss=False) + + for i, item in enumerate(ob.data.rig_picker.sources): + row = col.row(align=True) + is_packed = ('pickers' in ob.data.rig_picker.keys()) + row.prop(item, 'source', text='') + row.operator("rigpicker.remove_picker_source", icon ='PANEL_CLOSE', text="", emboss=False).index=i + + if collection: + #if context.collection and context.collection.rig_picker.enabled: + col = layout.column(align=True) + row = col.row(align=True) + row.operator('rigpicker.create_shape', icon='MESH_DATA', text='Create 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') + + if ob.type != 'ARMATURE': + box = layout.box() + col = box.column(align=False) + + material_row = col.row(align=True) + material_row.operator('rigpicker.remove_mat', icon='REMOVE', text='') + material_row.operator('rigpicker.add_mat', icon='ADD', text='') + mat = False + if ob.type in ('MESH', 'CURVE', 'FONT') and ob.data.materials: + mat = get_mat(ob) + if mat and mat.node_tree: + emission_nodes = [n for n in mat.node_tree.nodes if n.type =='EMISSION'] + if emission_nodes: + material_row.prop(emission_nodes[0].inputs[0], 'default_value', text='') + mat = True + if not mat: + material_row.label(text='No Material') + material_row.operator('rigpicker.eyedropper_mat', icon='EYEDROPPER', text='') + + shape_type_row = col.row(align=True) + shape_type_row.prop(ob.rig_picker,'shape_type',expand = True) + shape_type_row.operator('rigpicker.select_shape_type', text='', icon='RESTRICT_SELECT_OFF') + + + if ob.rig_picker.shape_type == 'OPERATOR': + + op_row = col.row(align=True) + op_row.prop(ob.rig_picker, 'operator', text='') + op_row.operator('rigpicker.operator_selector', text='', icon='COLLAPSEMENU') + + if ob.rig_picker.operator: + col.prop(ob.rig_picker, 'name', text='Tooltip') + col.prop(ob.rig_picker,'shortcut', text='Shortcut') + + else: + col.prop(ob.rig_picker, 'name', text='Tooltip') + + ''' + if ob.rig_picker.operator: + op = get_operator_from_id(ob.rig_picker.idname) + if op: + doc = re.findall(r'\(([^\)]+)\)', op.__doc__)[0] + else: + doc = 'Operator not found' + + col.prop(ob.rig_picker,'name', text='Tooltip') + if op: + col.prop(ob.rig_picker,'shortcut', text='Shortcut') + col.prop(ob.rig_picker,'arguments', text='') + col.label(text=doc) + else: + col.label(text=doc) + ''' + + + if ob.rig_picker.shape_type == 'BONE': + if collection and collection.rig_picker.rig: + col.prop_search(ob.rig_picker, 'name', collection.rig_picker.rig.pose, 'bones', text='Bone') + + #layout.separator() + layout.prop(collection.rig_picker, 'destination', text='Filepath') + layout.operator('rigpicker.save_picker', icon='PASTEDOWN', text='Save Picker') + + +classes = ( + #RP_UL_picker_source, + RP_PT_picker_maker_panel, +) + +register, unregister = bpy.utils.register_classes_factory(classes) \ No newline at end of file