Compare commits
10 Commits
blender3.6
...
master
Author | SHA1 | Date |
---|---|---|
christophe.seux | 6798053389 | |
christophe.seux | de3b78e741 | |
christophe.seux | 76ea717df8 | |
ChristopheSeux | 7d2d69ba0f | |
ChristopheSeux | 75bc414e26 | |
ChristopheSeux | 150bae5920 | |
ChristopheSeux | 917531f59a | |
ChristopheSeux | c31a73f80f | |
florentin.luce | b855e8ac37 | |
florentin.luce | 7627b76d64 |
|
@ -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
|
||||||
|
|
|
@ -15,7 +15,7 @@ import importlib
|
||||||
modules = (
|
modules = (
|
||||||
'.operators',
|
'.operators',
|
||||||
'.properties',
|
'.properties',
|
||||||
'.panels',
|
'.ui',
|
||||||
'.area',
|
'.area',
|
||||||
'.gizmo',
|
'.gizmo',
|
||||||
'.draw_handlers'
|
'.draw_handlers'
|
||||||
|
|
102
area.py
102
area.py
|
@ -1,5 +1,6 @@
|
||||||
import bpy
|
import bpy
|
||||||
from bpy.types import NodeTree, NODE_PT_tools_active, NODE_HT_header
|
from bpy.types import NodeTree, NODE_PT_tools_active, NODE_HT_header, Menu
|
||||||
|
from .constants import PICKERS
|
||||||
|
|
||||||
|
|
||||||
# Derived from the NodeTree base type, similar to Menu, Operator, Panel, etc.
|
# Derived from the NodeTree base type, similar to Menu, Operator, Panel, etc.
|
||||||
|
@ -13,17 +14,78 @@ class RigPickerTree(NodeTree):
|
||||||
bl_icon = 'OUTLINER_DATA_ARMATURE'
|
bl_icon = 'OUTLINER_DATA_ARMATURE'
|
||||||
|
|
||||||
|
|
||||||
|
class RP_MT_picker(Menu):
|
||||||
|
"""Picker"""
|
||||||
|
bl_label = "Picker"
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
scn = context.scene
|
||||||
|
|
||||||
|
|
||||||
|
layout.operator('rigpicker.reload_picker', icon='FILE_REFRESH')
|
||||||
|
row = layout.row(align=True)
|
||||||
|
|
||||||
|
# Has at least one picker collection in the scene
|
||||||
|
if not [c.rig_picker.enabled for c in scn.collection.children_recursive]:
|
||||||
|
row.enabled = False
|
||||||
|
row.prop(scn.rig_picker, 'use_pick_bone', text='Auto Bone Assign')
|
||||||
|
|
||||||
|
|
||||||
|
class RP_MT_animation(Menu):
|
||||||
|
"""Picker"""
|
||||||
|
bl_label = "Animation"
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
|
||||||
|
layout.operator("rigpicker.toogle_bone_layer", icon="MOUSE_LMB_DRAG")
|
||||||
|
|
||||||
|
row = layout.row()
|
||||||
|
op = row.operator("node.context_menu_picker", icon="MOUSE_RMB")
|
||||||
|
if not context.active_pose_bone:
|
||||||
|
row.enabled = False
|
||||||
|
|
||||||
|
layout.separator()
|
||||||
|
op = layout.operator("rigpicker.call_operator", text='Select All')
|
||||||
|
op.operator = "pose.select_all"
|
||||||
|
op.arguments = '{"action": "SELECT"}'
|
||||||
|
|
||||||
|
op = layout.operator("rigpicker.call_operator", text='Select All')
|
||||||
|
op.operator = "pose.select_all"
|
||||||
|
op.arguments = '{"action": "DESELECT"}'
|
||||||
|
|
||||||
|
op = layout.operator("rigpicker.call_operator", text='Frame Selected')
|
||||||
|
op.operator = "view3d.view_selected"
|
||||||
|
op.view_3d = True
|
||||||
|
|
||||||
|
layout.separator()
|
||||||
|
layout.operator("rigpicker.call_operator", text='Insert Keyframe').operator="animtoolbox.insert_keyframe"
|
||||||
|
layout.operator("anim.keyframe_delete_v3d", text='Delete Keyframe')
|
||||||
|
|
||||||
|
layout.separator()
|
||||||
|
op = layout.operator("rigpicker.call_operator", text='Move')
|
||||||
|
op.operator="transform.translate"
|
||||||
|
op.invoke = True
|
||||||
|
op.view_3d = True
|
||||||
|
|
||||||
|
layout.operator("node.picker_transform", text='Rotate').mode='ROTATE'
|
||||||
|
layout.operator("node.picker_transform", text='Scale').mode='SCALE'
|
||||||
|
layout.separator()
|
||||||
|
layout.operator("rigpicker.call_operator", text='Reset Bone').operator="animtoolbox.reset_bone"
|
||||||
|
layout.operator("rigpicker.call_operator", text='Clear Location').operator='pose.loc_clear'
|
||||||
|
layout.operator("rigpicker.call_operator", text='Clear Rotation').operator='pose.rot_clear'
|
||||||
|
layout.operator("rigpicker.call_operator", text='Clear Scale').operator='pose.scale_clear'
|
||||||
|
|
||||||
def draw_header(self, context):
|
def draw_header(self, context):
|
||||||
if context.space_data.tree_type == 'RigPickerTree':
|
if not context.space_data.tree_type == 'RigPickerTree':
|
||||||
|
self._draw(context)
|
||||||
|
return
|
||||||
|
|
||||||
|
scn = context.scene
|
||||||
|
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
layout.template_header()
|
layout.template_header()
|
||||||
#layout.separator_spacer()
|
|
||||||
|
|
||||||
if not context.space_data.node_tree:
|
if not context.space_data.node_tree:
|
||||||
ntree = bpy.data.node_groups.get('.rig_picker')
|
ntree = bpy.data.node_groups.get('.rig_picker')
|
||||||
|
@ -32,17 +94,36 @@ def draw_header(self, context):
|
||||||
|
|
||||||
context.space_data.node_tree = ntree
|
context.space_data.node_tree = ntree
|
||||||
|
|
||||||
#layout.template_ID(context.space_data, "node_tree", new="node.new_node_tree")
|
row = layout.row(align=True)
|
||||||
#layout.separator_spacer()
|
row.menu("RP_MT_picker")
|
||||||
|
row.menu("RP_MT_animation")
|
||||||
|
|
||||||
layout.operator('rigpicker.reload_picker', icon='FILE_REFRESH', text='')
|
ob = context.object
|
||||||
|
if not ob or not ob.type == 'ARMATURE':
|
||||||
|
return
|
||||||
|
|
||||||
#layout.prop('rigpicker.reload_picker', icon='FILE_REFRESH', text='')
|
picker_group = PICKERS.get(ob)
|
||||||
|
if not picker_group:
|
||||||
|
return
|
||||||
|
|
||||||
|
if scn.rig_picker.use_pick_bone:
|
||||||
|
layout.alert = True
|
||||||
|
layout.label(text='Auto Bone Assign')
|
||||||
|
layout.prop(scn.rig_picker, 'use_pick_bone', icon='PANEL_CLOSE', text='', emboss=False)
|
||||||
|
|
||||||
layout.separator_spacer()
|
layout.separator_spacer()
|
||||||
|
row = layout.row()
|
||||||
|
row.enabled = False
|
||||||
|
row.label(text=ob.name)
|
||||||
|
|
||||||
|
layout.separator_spacer()
|
||||||
|
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.operator('rigpicker.fit_picker', text='', icon='FULLSCREEN_ENTER').index = -1
|
||||||
|
|
||||||
else:
|
|
||||||
self._draw(context)
|
|
||||||
|
|
||||||
def tools_from_context(context, mode=None):
|
def tools_from_context(context, mode=None):
|
||||||
sp = context.space_data
|
sp = context.space_data
|
||||||
|
@ -68,10 +149,13 @@ def poll(cls, context):
|
||||||
|
|
||||||
classes = (
|
classes = (
|
||||||
RigPickerTree,
|
RigPickerTree,
|
||||||
|
RP_MT_picker,
|
||||||
|
RP_MT_animation
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
|
|
27
constants.py
27
constants.py
|
@ -1,2 +1,29 @@
|
||||||
|
import gpu
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
class LazyDict(dict):
|
||||||
|
def __getitem__(self, k):
|
||||||
|
v = super().__getitem__(k)
|
||||||
|
if callable(v):
|
||||||
|
v = v()
|
||||||
|
super().__setitem__(k, v)
|
||||||
|
return v
|
||||||
|
|
||||||
|
def get(self, k, default=None):
|
||||||
|
if k in self:
|
||||||
|
return self.__getitem__(k)
|
||||||
|
return default
|
||||||
|
|
||||||
|
|
||||||
PICKERS = {}
|
PICKERS = {}
|
||||||
|
|
||||||
|
|
||||||
|
MODULE_DIR = Path(__file__).parent
|
||||||
|
SHADER_DIR = MODULE_DIR / 'shaders'
|
||||||
|
|
||||||
|
SHADERS = LazyDict()
|
||||||
|
|
||||||
|
vertex_shader = Path(SHADER_DIR, "dash_shader.vert").read_text(encoding='utf-8')
|
||||||
|
fragment_shader = Path(SHADER_DIR, "dash_shader.frag").read_text(encoding='utf-8')
|
||||||
|
|
||||||
|
SHADERS['dashed_line'] = lambda : gpu.types.GPUShader(vertex_shader, fragment_shader)
|
||||||
|
|
|
@ -1,6 +1,21 @@
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
from .bl_utils import get_mat
|
from .bl_utils import get_mat, get_collection_parents
|
||||||
|
|
||||||
|
|
||||||
|
def get_picker_collection(ob=None):
|
||||||
|
"""Return the picker collection of an object"""
|
||||||
|
|
||||||
|
if not ob:
|
||||||
|
ob = bpy.context.object
|
||||||
|
|
||||||
|
for col in ob.users_collection:
|
||||||
|
if col.rig_picker.enabled:
|
||||||
|
return col
|
||||||
|
|
||||||
|
if picker_col := next((c for c in get_collection_parents(col) if c.rig_picker.enabled), None):
|
||||||
|
return picker_col
|
||||||
|
|
||||||
|
|
||||||
def is_shape(ob):
|
def is_shape(ob):
|
||||||
scn = bpy.context.scene
|
scn = bpy.context.scene
|
||||||
|
@ -15,6 +30,7 @@ def is_shape(ob):
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def get_object_color(ob):
|
def get_object_color(ob):
|
||||||
if not ob.data.materials:
|
if not ob.data.materials:
|
||||||
return
|
return
|
||||||
|
@ -29,6 +45,7 @@ def get_object_color(ob):
|
||||||
|
|
||||||
return emit_node.inputs['Color'].default_value
|
return emit_node.inputs['Color'].default_value
|
||||||
|
|
||||||
|
|
||||||
def get_operator_from_id(idname):
|
def get_operator_from_id(idname):
|
||||||
if not '.' in idname:
|
if not '.' in idname:
|
||||||
return
|
return
|
||||||
|
|
|
@ -2,6 +2,34 @@
|
||||||
import bpy
|
import bpy
|
||||||
|
|
||||||
|
|
||||||
|
def get_collection_parents(col, root=None, cols=None):
|
||||||
|
"""Return all direct collection parents
|
||||||
|
|
||||||
|
Args:
|
||||||
|
col (bpy.types.Collection): collection to get parents from
|
||||||
|
root (bpy.types.Collection, optional): collection to search in (recursive) instead of the scene collection.
|
||||||
|
Defaults to None.
|
||||||
|
cols (_type_, optional): for recursivity, store the parent collections.
|
||||||
|
Defaults to None.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list[bpy.types.Collection]: a list of direct parents
|
||||||
|
"""
|
||||||
|
|
||||||
|
if cols is None:
|
||||||
|
cols = []
|
||||||
|
|
||||||
|
if root is None:
|
||||||
|
root = bpy.context.scene.collection
|
||||||
|
|
||||||
|
for sub in root.children:
|
||||||
|
if sub == col:
|
||||||
|
cols.append(root)
|
||||||
|
|
||||||
|
if len(sub.children):
|
||||||
|
cols = get_collection_parents(col, root=sub, cols=cols)
|
||||||
|
return cols
|
||||||
|
|
||||||
def get_view_3d_override():
|
def get_view_3d_override():
|
||||||
windows = bpy.context.window_manager.windows
|
windows = bpy.context.window_manager.windows
|
||||||
areas = [a for w in windows for a in w.screen.areas if a.type == 'VIEW_3D']
|
areas = [a for w in windows for a in w.screen.areas if a.type == 'VIEW_3D']
|
||||||
|
@ -31,40 +59,34 @@ def link_mat_to_object(ob):
|
||||||
sl.link = 'OBJECT'
|
sl.link = 'OBJECT'
|
||||||
sl.material = m
|
sl.material = m
|
||||||
|
|
||||||
def find_mirror(name):
|
def eval_attr(ob, name):
|
||||||
mirror = None
|
resolved = ob
|
||||||
prop= False
|
for o in name.split("."):
|
||||||
|
resolved = getattr(resolved, o)
|
||||||
|
return resolved
|
||||||
|
|
||||||
if name:
|
def flip_name(name):
|
||||||
|
if not name:
|
||||||
if name.startswith('[')and name.endswith(']'):
|
return
|
||||||
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
|
|
||||||
|
|
||||||
|
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:
|
else:
|
||||||
return None
|
return bpy.utils.flip_name(name)
|
||||||
|
|
||||||
|
def split_path(path) :
|
||||||
|
try :
|
||||||
|
bone_name = path.split('["')[1].split('"]')[0]
|
||||||
|
except Exception:
|
||||||
|
bone_name = None
|
||||||
|
|
||||||
|
try :
|
||||||
|
prop_name = path.split('["')[2].split('"]')[0]
|
||||||
|
except Exception:
|
||||||
|
prop_name = None
|
||||||
|
|
||||||
|
return bone_name, prop_name
|
||||||
|
|
||||||
def hide_layers(args):
|
def hide_layers(args):
|
||||||
""" """
|
""" """
|
||||||
|
|
|
@ -21,15 +21,25 @@ def bound_box_center(ob):
|
||||||
|
|
||||||
return (sum(x) / len(points), sum(y) / len(points),sum(z) / len(points))
|
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
|
return [
|
||||||
dx = min(border[1][0],bound[1][0]) - max(border[0][0],bound[0][0])
|
Vector((x_points[0], y_points[-1])),
|
||||||
dy = min(border[0][1],bound[0][1]) - max(border[2][1],bound[2][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):
|
if (dx>=0) and (dy>=0):
|
||||||
return dx*dy
|
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]
|
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)):
|
def point_over_shape(point,verts,loops,outside_point=(-1,-1)):
|
||||||
|
|
569
core/picker.py
569
core/picker.py
|
@ -3,10 +3,12 @@ import bpy
|
||||||
import gpu
|
import gpu
|
||||||
from gpu_extras.batch import batch_for_shader
|
from gpu_extras.batch import batch_for_shader
|
||||||
import blf
|
import blf
|
||||||
from mathutils import bvhtree, Vector
|
from mathutils import bvhtree, Vector, Color
|
||||||
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 ..constants import PICKERS
|
||||||
from .addon_utils import get_operator_from_id
|
from .addon_utils import get_operator_from_id
|
||||||
|
from .geometry_utils import bounding_rect
|
||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import re
|
import re
|
||||||
|
@ -16,7 +18,6 @@ import os
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Shape:
|
class Shape:
|
||||||
def __init__(self, picker, points, polygons=None, edges=None, tooltip='', color=None, source_name=''):
|
def __init__(self, picker, points, polygons=None, edges=None, tooltip='', color=None, source_name=''):
|
||||||
|
|
||||||
|
@ -28,47 +29,37 @@ class Shape:
|
||||||
self.hover = False
|
self.hover = False
|
||||||
self.press = False
|
self.press = False
|
||||||
|
|
||||||
|
self.shader = gpu.shader.from_builtin('UNIFORM_COLOR')
|
||||||
|
self.shader.bind()
|
||||||
|
|
||||||
self.shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR')
|
#self.hover_shader = gpu.shader.from_builtin('UNIFORM_COLOR')
|
||||||
|
|
||||||
#self.hover_shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR')
|
|
||||||
#self.hover_shader.uniform_float("color", [1, 1, 1, 0.1])
|
#self.hover_shader.uniform_float("color", [1, 1, 1, 0.1])
|
||||||
self.hover_color = [1, 1, 1, 0.1]
|
|
||||||
|
self.color = color
|
||||||
|
self.hover_color = self.brighten_color(self.color)
|
||||||
|
|
||||||
self.tooltip = tooltip
|
self.tooltip = tooltip
|
||||||
self.color = color
|
|
||||||
self.points = points
|
self.points = points
|
||||||
self.polygons = polygons or []
|
self.polygons = polygons or []
|
||||||
self.edges = edges 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.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)
|
self.e_batch = batch_for_shader(self.shader, 'LINES', {"pos": self.points}, indices=self.edges)
|
||||||
|
|
||||||
#if polygons:
|
self.rect = bounding_rect(points)
|
||||||
# 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))
|
|
||||||
]
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def color(self):
|
def color(self):
|
||||||
return self._color
|
return self._color
|
||||||
|
|
||||||
|
def brighten_color(self, color):
|
||||||
|
brighten_color = Color(color[:3])
|
||||||
|
brighten_color.v += 0.05
|
||||||
|
brighten_color.v *= 1.1
|
||||||
|
brighten_color.v = max(brighten_color.v, 0.15)
|
||||||
|
|
||||||
|
return [*brighten_color, color[3]]
|
||||||
|
|
||||||
@color.setter
|
@color.setter
|
||||||
def color(self, color=None):
|
def color(self, color=None):
|
||||||
if not color:
|
if not color:
|
||||||
|
@ -88,16 +79,17 @@ class Shape:
|
||||||
|
|
||||||
def draw(self):
|
def draw(self):
|
||||||
|
|
||||||
self.shader.bind()
|
#self.shader.bind()
|
||||||
self.shader.uniform_float("color", self.color)
|
self.shader.uniform_float("color", self.color)
|
||||||
|
|
||||||
if self.polygons:
|
if self.polygons:
|
||||||
self.p_batch.draw(self.shader)
|
self.p_batch.draw(self.shader)
|
||||||
|
|
||||||
if self.edges:
|
if self.edges:
|
||||||
self.e_batch.draw(self.shader)
|
self.e_batch.draw(self.shader)
|
||||||
|
|
||||||
def move_event(self, location):
|
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
|
self.hover = False
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -124,33 +116,9 @@ class BoneShape(Shape):
|
||||||
self.type = 'bone'
|
self.type = 'bone'
|
||||||
self.bone = bone
|
self.bone = bone
|
||||||
self.active_color = [1, 1, 1, 0.1]
|
self.active_color = [1, 1, 1, 0.1]
|
||||||
#self.contour_shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR')
|
self.select_color = self.brighten_color(self.hover_color)
|
||||||
|
|
||||||
#self.contour_batches = []
|
self.bone_colors = self.get_bone_colors()
|
||||||
|
|
||||||
#self.line_batch =
|
|
||||||
#for loop in edges:
|
|
||||||
# loop_points = [points[i] for i in loop]
|
|
||||||
# batch = batch_for_shader(self.shader, 'LINE_LOOP', {"pos": loop_points})
|
|
||||||
# #self.contour_batches.append(batch)
|
|
||||||
|
|
||||||
theme = bpy.context.preferences.themes['Default']
|
|
||||||
self.bone_colors = {
|
|
||||||
'select': [*theme.view_3d.bone_pose, 1],
|
|
||||||
'normal': [0.05, 0.05, 0.05, 1],
|
|
||||||
'active': [*theme.view_3d.bone_pose_active, 1],
|
|
||||||
'hide': [0.85, 0.85, 0.85, 0.2],
|
|
||||||
}
|
|
||||||
|
|
||||||
if bone and bone.bone_group:
|
|
||||||
normal_color = bone.bone_group.colors.normal.copy()
|
|
||||||
normal_color.s *= 0.75
|
|
||||||
self.bone_colors['normal'] = [*normal_color, 1]
|
|
||||||
self.bone_colors['select'] = [*bone.bone_group.colors.select, 1]
|
|
||||||
self.bone_colors['active'] = [*bone.bone_group.colors.active, 1]
|
|
||||||
self.bone_colors['hide'] = [*normal_color, 0.1]
|
|
||||||
|
|
||||||
#self.color = [i for i in self.color]
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def select(self):
|
def select(self):
|
||||||
|
@ -172,20 +140,14 @@ class BoneShape(Shape):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
#return self.bone not in (bpy.context.visible_pose_bones or [])
|
#return self.bone not in (bpy.context.visible_pose_bones or [])
|
||||||
bl = [i for i, l in enumerate(self.bone.bone.layers) if l]
|
|
||||||
rl = [i for i, l in enumerate(self.rig.data.layers) if l]
|
return self.bone.bone.hide or not any(l.is_visible for l in self.bone.bone.collections)
|
||||||
return self.bone.bone.hide or not len(set(bl).intersection(rl))
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def bone_color(self):
|
def bone_color(self):
|
||||||
if not self.bone:
|
if not self.bone:
|
||||||
return [0, 0, 0, 1]
|
return [0, 0, 0, 1]
|
||||||
|
|
||||||
bone = self.bone.bone
|
|
||||||
|
|
||||||
bl = bone.layers
|
|
||||||
rl = self.rig.data.layers
|
|
||||||
|
|
||||||
if self.select and self.active:
|
if self.select and self.active:
|
||||||
return self.bone_colors['active']
|
return self.bone_colors['active']
|
||||||
elif self.select:
|
elif self.select:
|
||||||
|
@ -195,58 +157,110 @@ class BoneShape(Shape):
|
||||||
else:
|
else:
|
||||||
return self.bone_colors['normal']
|
return self.bone_colors['normal']
|
||||||
|
|
||||||
|
def get_bone_colors(self):
|
||||||
|
|
||||||
|
theme = bpy.context.preferences.themes['Default']
|
||||||
|
bone_colors = {
|
||||||
|
'select': [*theme.view_3d.bone_pose, 1],
|
||||||
|
'normal': [0.05, 0.05, 0.05, 1],
|
||||||
|
'active': [*theme.view_3d.bone_pose_active, 1],
|
||||||
|
'hide': [0.85, 0.85, 0.85, 0.2],
|
||||||
|
}
|
||||||
|
|
||||||
|
if not self.bone:
|
||||||
|
return bone_colors
|
||||||
|
|
||||||
|
if self.bone.color.palette == 'CUSTOM':
|
||||||
|
bone_color = self.bone
|
||||||
|
|
||||||
|
elif self.bone.color.palette == 'DEFAULT':
|
||||||
|
bone_color = self.bone.bone
|
||||||
|
|
||||||
|
normal_color = bone_color.color.custom.normal.copy()
|
||||||
|
normal_color.s *= 0.75
|
||||||
|
|
||||||
|
bone_colors['normal'] = [*normal_color, 1]
|
||||||
|
bone_colors['select'] = [*bone_color.color.custom.select, 1]
|
||||||
|
bone_colors['active'] = [*bone_color.color.custom.active, 1]
|
||||||
|
bone_colors['hide'] = [*normal_color, 0.1]
|
||||||
|
|
||||||
|
return bone_colors
|
||||||
|
|
||||||
|
def draw_hided(self):
|
||||||
|
gpu.state.blend_set('ALPHA')
|
||||||
|
gpu.state.line_width_set(1.0)
|
||||||
|
|
||||||
|
line_color = (1, 1, 1, 0.1)
|
||||||
|
color = Color(self.color[:3])
|
||||||
|
if self.hover:
|
||||||
|
color.v += 0.05
|
||||||
|
color.v *= 1.1
|
||||||
|
|
||||||
|
line_color = (1, 1, 1, 0.3)
|
||||||
|
|
||||||
|
self.shader.uniform_float("color", (*color[:3], 0.5))
|
||||||
|
self.p_batch.draw(self.shader)
|
||||||
|
|
||||||
|
self.shader.uniform_float("color", line_color)
|
||||||
|
self.e_batch.draw(self.shader)
|
||||||
|
|
||||||
|
gpu.state.blend_set('NONE')
|
||||||
|
|
||||||
|
def draw_zero_scaled(self):
|
||||||
|
gpu.state.blend_set('ALPHA')
|
||||||
|
gpu.state.line_width_set(1.0)
|
||||||
|
|
||||||
|
line_color = (*self.bone_colors['normal'][:3], 0.2)
|
||||||
|
color = Color(self.color[:3])
|
||||||
|
if self.hover or self.select:
|
||||||
|
color.v += 0.05
|
||||||
|
color.v *= 1.1
|
||||||
|
line_color = (*self.bone_color[:3], 0.66)
|
||||||
|
|
||||||
|
self.shader.uniform_float("color", (*color[:3], 0.5))
|
||||||
|
self.p_batch.draw(self.shader)
|
||||||
|
|
||||||
|
self.shader.uniform_float("color", line_color)
|
||||||
|
self.e_batch.draw(self.shader)
|
||||||
|
|
||||||
|
gpu.state.blend_set('NONE')
|
||||||
|
|
||||||
def draw(self):
|
def draw(self):
|
||||||
gpu.state.blend_set('ALPHA')
|
gpu.state.blend_set('ALPHA')
|
||||||
|
gpu.state.line_width_set(1.0)
|
||||||
|
|
||||||
if self.hide:
|
if self.hide:
|
||||||
self.shader.uniform_float("color", (*self.color[:3], 0.4))
|
return self.draw_hided()
|
||||||
self.p_batch.draw(self.shader)
|
|
||||||
#elif self.select:
|
|
||||||
# self.shader.uniform_float("color", self.bone_color)
|
|
||||||
# self.p_batch.draw(self.shader)
|
|
||||||
else:
|
|
||||||
super().draw()
|
|
||||||
|
|
||||||
# Overlay the fill slightly with the bone color
|
elif self.bone and self.bone.custom_shape_scale_xyz.length < 0.00001:
|
||||||
#self.shader.uniform_float("color", (*self.bone_color[:3], 0.1))
|
return self.draw_zero_scaled()
|
||||||
#self.p_batch.draw(self.shader)
|
|
||||||
|
|
||||||
|
# Draw Fill
|
||||||
|
color = self.color
|
||||||
if self.select:
|
if self.select:
|
||||||
|
color = self.select_color
|
||||||
|
elif self.hover:
|
||||||
color = self.hover_color
|
color = self.hover_color
|
||||||
|
|
||||||
if self.select or self.hover:
|
|
||||||
color = self.hover_color
|
|
||||||
|
|
||||||
if self.select and self.hover:
|
|
||||||
color = self.active_color
|
|
||||||
|
|
||||||
self.shader.uniform_float("color", color)
|
self.shader.uniform_float("color", color)
|
||||||
self.p_batch.draw(self.shader)
|
self.p_batch.draw(self.shader)
|
||||||
|
|
||||||
#Overlay the fill slightly with the bone color
|
# Draw Outline
|
||||||
self.shader.uniform_float("color", (*self.bone_colors['normal'][:3], 0.1))
|
gpu.state.blend_set('NONE')
|
||||||
self.p_batch.draw(self.shader)
|
|
||||||
|
|
||||||
|
|
||||||
#self.contour_shader.bind()
|
|
||||||
|
|
||||||
#print(self.bone_color)
|
|
||||||
if self.select or self.active:
|
if self.select or self.active:
|
||||||
gpu.state.line_width_set(2.0)
|
gpu.state.line_width_set(2.0)
|
||||||
|
|
||||||
#if not self.hide:
|
|
||||||
self.shader.uniform_float("color", self.bone_color)
|
self.shader.uniform_float("color", self.bone_color)
|
||||||
#for b in self.contour_batches:
|
|
||||||
self.e_batch.draw(self.shader)
|
self.e_batch.draw(self.shader)
|
||||||
|
|
||||||
gpu.state.line_width_set(1.0)
|
gpu.state.line_width_set(1.0)
|
||||||
gpu.state.blend_set('NONE')
|
|
||||||
|
|
||||||
def assign_bone_event(self):
|
def assign_bone_event(self):
|
||||||
|
|
||||||
#print('assign_bone_event', self)
|
#print('assign_bone_event', self)
|
||||||
|
|
||||||
scn = bpy.context.scene
|
scn = bpy.context.scene
|
||||||
rig = scn.rig_picker.rig
|
rig = self.picker.rig
|
||||||
source_object = scn.objects.get(self.source_name)
|
source_object = scn.objects.get(self.source_name)
|
||||||
if not source_object:
|
if not source_object:
|
||||||
print(f'Source object {self.source_name} not found')
|
print(f'Source object {self.source_name} not found')
|
||||||
|
@ -257,6 +271,9 @@ class BoneShape(Shape):
|
||||||
print('You need to have an active bone')
|
print('You need to have an active bone')
|
||||||
return
|
return
|
||||||
|
|
||||||
|
#print(active_bone, source_object)
|
||||||
|
#print(rig)
|
||||||
|
source_object.name = rig.data.bones.active.name
|
||||||
source_object.rig_picker.name = rig.data.bones.active.name
|
source_object.rig_picker.name = rig.data.bones.active.name
|
||||||
|
|
||||||
def release_event(self, mode='SET'):
|
def release_event(self, mode='SET'):
|
||||||
|
@ -277,11 +294,6 @@ class BoneShape(Shape):
|
||||||
|
|
||||||
|
|
||||||
def border_select(self, border, mode='SET'):
|
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:
|
if not self.bone:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -289,16 +301,16 @@ class BoneShape(Shape):
|
||||||
self.bone.bone.select = False
|
self.bone.bone.select = False
|
||||||
return
|
return
|
||||||
|
|
||||||
bound_tri1 = self.bound[0], self.bound[1], self.bound[2]
|
rect_tri1 = self.rect[0], self.rect[1], self.rect[2]
|
||||||
bound_tri2 = self.bound[2], self.bound[3], self.bound[0]
|
rect_tri2 = self.rect[2], self.rect[3], self.rect[0]
|
||||||
|
|
||||||
border_tri1 = border[0], border[1], border[2]
|
border_tri1 = border[0], border[1], border[2]
|
||||||
border_tri2 = border[2], border[3], border[0]
|
border_tri2 = border[2], border[3], border[0]
|
||||||
|
|
||||||
if (not intersect_tri_tri_2d(*border_tri1, *bound_tri1) and
|
if (not intersect_tri_tri_2d(*border_tri1, *rect_tri1) and
|
||||||
not intersect_tri_tri_2d(*border_tri1, *bound_tri2) and
|
not intersect_tri_tri_2d(*border_tri1, *rect_tri2) and
|
||||||
not intersect_tri_tri_2d(*border_tri2, *bound_tri1) and
|
not intersect_tri_tri_2d(*border_tri2, *rect_tri1) and
|
||||||
not intersect_tri_tri_2d(*border_tri2, *bound_tri2)):
|
not intersect_tri_tri_2d(*border_tri2, *rect_tri2)):
|
||||||
return
|
return
|
||||||
|
|
||||||
select = True
|
select = True
|
||||||
|
@ -385,121 +397,94 @@ class OperatorShape(Shape):
|
||||||
|
|
||||||
|
|
||||||
class Picker:
|
class Picker:
|
||||||
def __init__(self, rig, shapes):
|
def __init__(self, parent, rig, shapes):
|
||||||
|
self.parent = parent
|
||||||
self.region = bpy.context.region
|
self.region = bpy.context.region
|
||||||
self.rig = rig
|
self.rig = rig
|
||||||
|
|
||||||
|
self.translation = Vector((0, 0))
|
||||||
self.shapes = []
|
self.shapes = []
|
||||||
self.box_select = None
|
self.box_select = None
|
||||||
self.hover_shape = None
|
self.hover_shape = None
|
||||||
self.shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR')
|
self.shader = gpu.shader.from_builtin('UNIFORM_COLOR')
|
||||||
|
|
||||||
self.tooltip_shape = None
|
|
||||||
self.tooltip_mouse = None
|
|
||||||
self.tooltip = ''
|
|
||||||
|
|
||||||
self.timer = None
|
|
||||||
self.mouse = None
|
self.mouse = None
|
||||||
|
|
||||||
for s in shapes:
|
for shape_data in shapes:
|
||||||
if not s['points']:
|
if not shape_data['points']:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if s['type'] in ('CANVAS', 'DISPLAY'):
|
if shape_data['type'] in ('CANVAS', 'DISPLAY'):
|
||||||
shape = Shape(
|
shape = Shape(
|
||||||
self,
|
self,
|
||||||
points=s['points'],
|
points=shape_data['points'],
|
||||||
polygons=s['polygons'],
|
polygons=shape_data['polygons'],
|
||||||
edges=s['edges'],
|
edges=shape_data['edges'],
|
||||||
color=s['color']
|
color=shape_data['color']
|
||||||
)
|
)
|
||||||
|
|
||||||
elif s['type'] == 'BONE':
|
elif shape_data['type'] == 'BONE':
|
||||||
bone = rig.pose.bones.get(s['bone'])
|
bone = rig.pose.bones.get(shape_data['bone'])
|
||||||
#if not bone:
|
#if not bone:
|
||||||
# print(f'Bone {s["bone"]} not exist')
|
# print(f'Bone {shape_data["bone"]} not exist')
|
||||||
# continue
|
# continue
|
||||||
|
|
||||||
shape = BoneShape(
|
shape = BoneShape(
|
||||||
self,
|
self,
|
||||||
source_name=s['source_name'],
|
source_name=shape_data['source_name'],
|
||||||
points=s['points'],
|
points=shape_data['points'],
|
||||||
polygons=s['polygons'],
|
polygons=shape_data['polygons'],
|
||||||
edges=s['edges'],
|
edges=shape_data['edges'],
|
||||||
bone=bone,
|
bone=bone,
|
||||||
color=s['color']
|
color=shape_data['color']
|
||||||
)
|
)
|
||||||
|
|
||||||
elif s['type'] == 'OPERATOR':
|
elif shape_data['type'] == 'OPERATOR':
|
||||||
shape = OperatorShape(
|
shape = OperatorShape(
|
||||||
self,
|
self,
|
||||||
source_name=s['source_name'],
|
source_name=shape_data['source_name'],
|
||||||
points=s['points'],
|
points=shape_data['points'],
|
||||||
polygons=s['polygons'],
|
polygons=shape_data['polygons'],
|
||||||
operator=s['operator'],
|
operator=shape_data['operator'],
|
||||||
color=s['color'],
|
color=shape_data['color'],
|
||||||
tooltip=s['tooltip'],
|
tooltip=shape_data['tooltip'],
|
||||||
)
|
)
|
||||||
|
|
||||||
self.shapes.append(shape)
|
self.shapes.append(shape)
|
||||||
|
|
||||||
def assign_bone_event(self):
|
self.rect = bounding_rect([p for s in self.shapes for p in s.points])
|
||||||
for s in self.shapes:
|
|
||||||
if s.type=='bone' and s.hover:
|
|
||||||
s.assign_bone_event()
|
|
||||||
|
|
||||||
bpy.ops.rigpicker.save_picker()
|
def assign_bone_event(self):
|
||||||
|
for shape in self.shapes:
|
||||||
|
if shape.type == 'bone' and shape.hover:
|
||||||
|
shape.assign_bone_event()
|
||||||
|
|
||||||
|
bpy.ops.rigpicker.save_picker(index=self.index)
|
||||||
|
|
||||||
def press_event(self, mode='SET'):
|
def press_event(self, mode='SET'):
|
||||||
for s in self.shapes:
|
for shape in self.shapes:
|
||||||
if s.hover:
|
#print(s)
|
||||||
s.press_event(mode)
|
if shape.hover:
|
||||||
|
shape.press_event(mode)
|
||||||
else:
|
else:
|
||||||
s.press = False
|
shape.press = False
|
||||||
|
|
||||||
def release_event(self, mode='SET'):
|
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')
|
#bpy.ops.pose.select_all(action='DESELECT')
|
||||||
|
|
||||||
#print('PICKER release event', mode)
|
#print('PICKER release event', mode)
|
||||||
#print(f'type={event.type}, value={event.value}, ctrl={event.ctrl}, shift={event.shift}, alt={event.alt}')
|
#print(f'type={event.type}, value={event.value}, ctrl={event.ctrl}, shift={event.shift}, alt={event.alt}')
|
||||||
|
|
||||||
for s in self.shapes:
|
for shape in self.shapes:
|
||||||
if s.hover:
|
if shape.hover:
|
||||||
s.release_event(mode)
|
shape.release_event(mode)
|
||||||
|
|
||||||
s.press = False
|
shape.press = False
|
||||||
|
|
||||||
#bpy.context.area.tag_redraw()
|
#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')
|
picker.tooltip_event(event='SHOW')
|
||||||
region.tag_redraw()
|
region.tag_redraw()
|
||||||
|
@ -526,27 +511,27 @@ class Picker:
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def border_select(self, border, mode):
|
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 shape in self.shapes:
|
||||||
for b in self.rig.pose.bones:
|
shape.press = False
|
||||||
b.bone.select = False
|
shape.hover = False
|
||||||
|
if shape.type != 'bone':
|
||||||
|
continue
|
||||||
|
|
||||||
for s in (s for s in self.shapes if s.type=='bone'):
|
shape.border_select(border, mode)
|
||||||
s.border_select(border, mode)
|
|
||||||
|
|
||||||
def move_event(self, location):
|
def move_event(self, location):
|
||||||
self.mouse = location
|
self.mouse = Vector(location) - Vector(self.translation)
|
||||||
location = self.region.view2d.region_to_view(*location)
|
|
||||||
|
|
||||||
self.hover_shape = None
|
self.hover_shape = None
|
||||||
for shape in reversed(self.shapes):
|
for shape in reversed(self.shapes):
|
||||||
if self.hover_shape:
|
if self.hover_shape:
|
||||||
shape.hover = False
|
shape.hover = False
|
||||||
elif shape.move_event(location):
|
elif shape.move_event(self.mouse):
|
||||||
self.hover_shape = shape
|
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)
|
# over = point_over_shape(self.end,points, edges)
|
||||||
|
|
||||||
#if bpy.app.timers.is_registered(self.tooltip_event):
|
#if bpy.app.timers.is_registered(self.tooltip_event):
|
||||||
|
@ -554,57 +539,221 @@ class Picker:
|
||||||
# bpy.app.timers.unregister(self.tooltip_event)
|
# bpy.app.timers.unregister(self.tooltip_event)
|
||||||
#except:
|
#except:
|
||||||
# pass
|
# 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
|
||||||
|
|
||||||
|
@property
|
||||||
|
def index(self):
|
||||||
|
return self.parent.pickers.index(self)
|
||||||
|
|
||||||
|
|
||||||
|
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.location = 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, 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
|
||||||
|
self.location = self.region.view2d.region_to_view(*mouse)
|
||||||
|
|
||||||
|
# Try to detect view pan to remove tooltip
|
||||||
|
# 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(self.location):
|
||||||
|
continue
|
||||||
|
|
||||||
|
picker.move_event(self.location)
|
||||||
|
|
||||||
|
if picker.hover_shape:
|
||||||
|
hover_shape = picker.hover_shape
|
||||||
|
|
||||||
|
self.hover_shape = hover_shape
|
||||||
|
|
||||||
if self.tooltip_shape is not self.hover_shape:
|
if self.tooltip_shape is not self.hover_shape:
|
||||||
self.tooltip = ''
|
self.tooltip = ''
|
||||||
|
|
||||||
if self.timer:
|
if self.timer:
|
||||||
self.timer.cancel()
|
self.timer.cancel()
|
||||||
|
|
||||||
self.timer = threading.Timer(0.5, self.tooltip_event)
|
self.timer = threading.Timer(0.4, self.tooltip_event)
|
||||||
self.timer.start()
|
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:
|
||||||
|
if picker.under_mouse(self.location):
|
||||||
|
picker.press_event(mode)
|
||||||
|
else:
|
||||||
|
for shape in picker.shapes:
|
||||||
|
shape.press = False
|
||||||
|
|
||||||
def draw(self):
|
def release_event(self, mode='SET'):
|
||||||
for s in self.shapes:
|
if mode == 'SET':
|
||||||
s.draw()
|
for bone in self.rig.data.bones:
|
||||||
'''
|
bone.select = False
|
||||||
if self.box_select:
|
|
||||||
|
|
||||||
self.box_shader.uniform_float("color", self.box_select_color)
|
for picker in self.pickers:
|
||||||
batch = batch_for_shader(self.shader, 'LINE_LOOP', {"pos": []})
|
if picker.under_mouse(self.location):
|
||||||
|
picker.release_event(mode)
|
||||||
|
else:
|
||||||
|
for shape in picker.shapes:
|
||||||
|
shape.press = False
|
||||||
|
|
||||||
|
def assign_bone_event(self):
|
||||||
|
for picker in self.pickers:
|
||||||
|
if picker.under_mouse(self.location):
|
||||||
|
picker.assign_bone_event()
|
||||||
|
|
||||||
|
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' and self.hover_shape.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:
|
self.tooltip_mouse = self.mouse
|
||||||
b.draw(self.contour_shader)
|
|
||||||
|
|
||||||
self.batch.draw(self.shader)
|
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 load_picker_data(rig):
|
||||||
'''
|
if 'pickers' in rig.data.rig_picker:
|
||||||
|
picker_datas = [[s.to_dict() for s in p] for p in rig.data.rig_picker['pickers']]
|
||||||
|
else:
|
||||||
|
picker_datas = []
|
||||||
|
|
||||||
def get_picker_path(rig, start=None):
|
for picker in rig.data.rig_picker.sources:
|
||||||
picker_path = rig.data.get('rig_picker', {}).get('source')
|
picker_path = Path(bpy.path.abspath(picker.source, library=rig.data.library))
|
||||||
if not picker_path:
|
|
||||||
return
|
|
||||||
|
|
||||||
picker_path = bpy.path.abspath(picker_path, library=rig.data.library, start=start)
|
if not picker_path.exists():
|
||||||
|
print(f'Picker path not exists: {picker_path.resolve()}')
|
||||||
|
continue
|
||||||
|
|
||||||
|
print('Load picker from', picker_path.resolve())
|
||||||
|
picker_data = json.loads(picker_path.read_text(encoding='utf-8'))
|
||||||
|
picker_datas.append(picker_data)
|
||||||
|
|
||||||
|
PICKERS[rig] = PickerGroup(rig, picker_datas)
|
||||||
|
|
||||||
|
|
||||||
|
def get_picker_path(rig, source, start=None):
|
||||||
|
picker_path = bpy.path.abspath(source, library=rig.data.library, start=start)
|
||||||
return Path(os.path.abspath(picker_path))
|
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())
|
def pack_picker(rig, start=None):
|
||||||
|
if not 'rig_picker' in rig.data:
|
||||||
|
return
|
||||||
|
|
||||||
|
pickers = []
|
||||||
|
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')
|
||||||
|
continue
|
||||||
|
picker_data = json.loads(picker_path.read_text(encoding='utf-8'))
|
||||||
|
pickers.append(picker_data)
|
||||||
|
|
||||||
|
rig.data['rig_picker']['pickers'] = pickers
|
||||||
|
|
||||||
|
|
||||||
def unpack_picker(rig):
|
def unpack_picker(rig):
|
||||||
if 'rig_picker' not in rig.data.keys():
|
if 'rig_picker' not in rig.data.keys():
|
||||||
return
|
return
|
||||||
|
|
||||||
if 'picker' in rig.data['rig_picker'].keys():
|
if 'pickers' in rig.data['rig_picker'].keys():
|
||||||
del rig.data['rig_picker']['picker']
|
del rig.data['rig_picker']['pickers']
|
120
core/shape.py
120
core/shape.py
|
@ -20,7 +20,7 @@ def border_over_shape(border,verts,loops):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
for point in verts:
|
for point in verts:
|
||||||
if point_inside_rectangle(point,border):
|
if point_inside_rect(point,border):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
for point in border:
|
for point in border:
|
||||||
|
@ -29,7 +29,7 @@ def border_over_shape(border,verts,loops):
|
||||||
|
|
||||||
|
|
||||||
def border_loop(vert, loop):
|
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:
|
if border_edge:
|
||||||
for edge in border_edge:
|
for edge in border_edge:
|
||||||
|
@ -61,67 +61,50 @@ def contour_loops(bm, vert_index=0, loops=None, vert_indices=None):
|
||||||
|
|
||||||
return loops
|
return loops
|
||||||
|
|
||||||
def get_picker_datas(objects, canvas, rig):
|
|
||||||
picker_datas = []
|
def get_shape_data(ob, matrix=None, depsgraph=None):
|
||||||
|
if not matrix:
|
||||||
|
matrix = Matrix()
|
||||||
|
if not depsgraph:
|
||||||
|
depsgraph = bpy.context.evaluated_depsgraph_get()
|
||||||
|
|
||||||
gamma = 1 / 2.2
|
gamma = 1 / 2.2
|
||||||
|
eval_ob = ob.evaluated_get(depsgraph)
|
||||||
if canvas.type =='CURVE':
|
mesh = eval_ob.to_mesh()
|
||||||
canvas_points = canvas.data.splines[0].points
|
|
||||||
else:
|
|
||||||
canvas_points = canvas.data.vertices
|
|
||||||
|
|
||||||
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]
|
|
||||||
|
|
||||||
canvas_center = sum(canvas_coords, Vector()) / len(canvas_coords)
|
|
||||||
canvas_scale_fac = 4096 / (max(canvas_y) - min(canvas_y))# Reference height for the canvas
|
|
||||||
|
|
||||||
objects.append(canvas)
|
|
||||||
|
|
||||||
dg = bpy.context.evaluated_depsgraph_get()
|
|
||||||
|
|
||||||
#sorted by their z axes
|
|
||||||
for ob in sorted(objects, key=lambda x: bound_box_center(x)[2]):
|
|
||||||
|
|
||||||
|
|
||||||
print('Storing shape', ob.name)
|
|
||||||
mesh = bpy.data.meshes.new_from_object(ob.evaluated_get(dg))
|
|
||||||
|
|
||||||
bm = bmesh.new()
|
bm = bmesh.new()
|
||||||
bm.from_mesh(mesh)
|
bm.from_mesh(mesh)
|
||||||
|
|
||||||
|
|
||||||
bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.002)
|
bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.002)
|
||||||
bmesh.ops.dissolve_limit(bm, angle_limit=0.001745, verts=bm.verts, edges=bm.edges)
|
bmesh.ops.dissolve_limit(bm, angle_limit=0.001745, verts=bm.verts, edges=bm.edges)
|
||||||
|
|
||||||
bmesh.ops.connect_verts_concave(bm, faces=bm.faces)
|
bmesh.ops.connect_verts_concave(bm, faces=bm.faces)
|
||||||
bmesh.ops.triangulate(bm, faces=bm.faces)
|
bmesh.ops.triangulate(bm, faces=bm.faces)
|
||||||
|
|
||||||
#bm_loops = contour_loops(bm)
|
|
||||||
#loops = [[l.index for l in loop] for loop in bm_loops]
|
|
||||||
loops = []
|
|
||||||
|
|
||||||
edges = [[v.index for v in e.verts] for e in bm.edges if len(e.link_faces)<=1]
|
edges = [[v.index for v in e.verts] for e in bm.edges if len(e.link_faces)<=1]
|
||||||
|
|
||||||
bm.to_mesh(mesh)
|
bm.to_mesh(mesh)
|
||||||
mesh.update()
|
mesh.update()
|
||||||
bm.clear()
|
bm.clear()
|
||||||
|
|
||||||
points = []
|
points = []
|
||||||
#edges = []
|
|
||||||
polygons = []
|
polygons = []
|
||||||
|
depths = []
|
||||||
|
|
||||||
for p in mesh.vertices:
|
for vert in mesh.vertices:
|
||||||
point = ob.matrix_world@Vector((p.co))
|
co = matrix @ (ob.matrix_world @ Vector(vert.co))
|
||||||
point = (point - canvas_center) * canvas_scale_fac
|
points.append([round(co[0], 1), round(co[1], 1)])
|
||||||
points.append([round(point[0]), round(point[1])])
|
|
||||||
|
|
||||||
for f in mesh.polygons:
|
depths += [co[2]]
|
||||||
polygons.append([v for v in f.vertices])
|
|
||||||
|
|
||||||
#for e in mesh.edges:
|
if depths:
|
||||||
#
|
depth = max(depths)
|
||||||
# edges.append([v for v in e.vertices])
|
else:
|
||||||
|
print(f'{ob.name} has no vertices')
|
||||||
|
depth = 0
|
||||||
|
|
||||||
|
for face in mesh.polygons:
|
||||||
|
polygons.append([v for v in face.vertices])
|
||||||
|
|
||||||
color = get_object_color(ob)
|
color = get_object_color(ob)
|
||||||
if color:
|
if color:
|
||||||
|
@ -132,28 +115,65 @@ def get_picker_datas(objects, canvas, rig):
|
||||||
shape = {
|
shape = {
|
||||||
'source_name': ob.name,
|
'source_name': ob.name,
|
||||||
'tooltip': ob.rig_picker.name,
|
'tooltip': ob.rig_picker.name,
|
||||||
|
'depth': depth,
|
||||||
'points': points,
|
'points': points,
|
||||||
'polygons': polygons,
|
'polygons': polygons,
|
||||||
'edges': edges,
|
'edges': edges,
|
||||||
'loops': loops,
|
|
||||||
'color': color,
|
'color': color,
|
||||||
'type': 'CANVAS' if ob == canvas else ob.rig_picker.shape_type
|
'type': ob.rig_picker.shape_type
|
||||||
}
|
}
|
||||||
|
|
||||||
if shape['type'] =='OPERATOR':
|
if shape['type'] =='OPERATOR':
|
||||||
shape['operator'] = ob.rig_picker.operator
|
shape['operator'] = ob.rig_picker.operator
|
||||||
#if ob.rig_picker.arguments:
|
|
||||||
#shape['arguments'] = ob.rig_picker.arguments
|
|
||||||
#if ob.rig_picker.shortcut:
|
|
||||||
shape['shortcut'] = ob.rig_picker.shortcut
|
shape['shortcut'] = ob.rig_picker.shortcut
|
||||||
|
|
||||||
elif shape['type'] =='BONE':
|
elif shape['type'] =='BONE':
|
||||||
shape['bone'] = ob.rig_picker.name
|
shape['bone'] = ob.rig_picker.name
|
||||||
|
|
||||||
picker_datas.append(shape)
|
eval_ob.to_mesh_clear()
|
||||||
|
|
||||||
|
return shape
|
||||||
|
|
||||||
|
|
||||||
|
def get_picker_data(collection):
|
||||||
|
picker_data = []
|
||||||
|
|
||||||
|
depsgraph = bpy.context.evaluated_depsgraph_get()
|
||||||
|
canvas = collection.rig_picker.canvas
|
||||||
|
|
||||||
|
if canvas.type == 'CURVE':
|
||||||
|
canvas_points = canvas.data.splines[0].points
|
||||||
|
else:
|
||||||
|
canvas_points = canvas.data.vertices
|
||||||
|
|
||||||
|
canvas_coords = [canvas.matrix_world@Vector((p.co)) for p in canvas_points]
|
||||||
|
|
||||||
|
height = abs(max(canvas_coords).y - min(canvas_coords).y)
|
||||||
|
width = abs(max(canvas_coords).x - min(canvas_coords).x)
|
||||||
|
|
||||||
|
center = sum(canvas_coords, Vector()) / len(canvas_coords)
|
||||||
|
scale = 2048 / max(height, width)# Reference height for the canvas
|
||||||
|
|
||||||
|
matrix = Matrix.Translation(-center)
|
||||||
|
matrix = Matrix.Scale(scale, 4) @ matrix
|
||||||
|
|
||||||
|
#sorted by their z axes
|
||||||
|
for ob in collection.all_objects:
|
||||||
|
if ob.instance_collection:
|
||||||
|
for shape in ob.instance_collection.all_objects:
|
||||||
|
picker_data.append(get_shape_data(shape, matrix=matrix@ob.matrix_world, depsgraph=depsgraph))
|
||||||
|
|
||||||
|
elif ob.type in ('MESH', 'CURVE', 'FONT'):
|
||||||
|
picker_data.append(get_shape_data(ob, matrix=matrix, depsgraph=depsgraph))
|
||||||
|
|
||||||
|
else:
|
||||||
|
#print(f'{ob.name} of type {ob.type} not supported')
|
||||||
|
continue
|
||||||
|
|
||||||
|
|
||||||
|
picker_data.sort(key=lambda x : x['depth'])
|
||||||
|
|
||||||
#print(picker_datas)
|
#print(picker_datas)
|
||||||
|
|
||||||
return picker_datas
|
return picker_data
|
||||||
#rig.data.rig_picker['shapes'] = picker_datas
|
#rig.data.rig_picker['shapes'] = picker_datas
|
||||||
|
|
|
@ -1,39 +1,63 @@
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
|
import gpu
|
||||||
|
from gpu_extras.batch import batch_for_shader
|
||||||
import blf
|
import blf
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from .constants import PICKERS
|
from .constants import PICKERS
|
||||||
from .core.picker import Picker
|
from .core.picker import Picker, PickerGroup, load_picker_data
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
|
||||||
def draw_callback_view():
|
def draw_rect_2d(position, width, height, color):
|
||||||
sp = bpy.context.space_data
|
"""
|
||||||
|
Draw a 2d rectangele.
|
||||||
|
|
||||||
if not sp or not sp.tree_type == 'RigPickerTree':
|
: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():
|
||||||
|
space_data = bpy.context.space_data
|
||||||
|
|
||||||
|
if not space_data or not space_data.tree_type == 'RigPickerTree':
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Use the pin to know if this is the first time this picker window in created to hide the n panel
|
||||||
|
if not space_data.pin:
|
||||||
|
space_data.pin = True
|
||||||
|
space_data.show_region_ui = False
|
||||||
|
|
||||||
ob = bpy.context.object
|
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
|
return
|
||||||
|
|
||||||
if ob not in PICKERS:
|
if ob not in PICKERS:
|
||||||
if 'picker' in ob.data.rig_picker:
|
load_picker_data(ob)
|
||||||
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():
|
picker_group = PICKERS[ob]
|
||||||
print(f'Picker path not exists: {picker_path.resolve()}')
|
picker_group.draw()
|
||||||
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 = PICKERS.get(ob)
|
|
||||||
picker.draw()
|
|
||||||
|
|
||||||
def draw_callback_px():
|
def draw_callback_px():
|
||||||
sp = bpy.context.space_data
|
sp = bpy.context.space_data
|
||||||
|
@ -42,24 +66,37 @@ def draw_callback_px():
|
||||||
|
|
||||||
ob = bpy.context.object
|
ob = bpy.context.object
|
||||||
|
|
||||||
picker = PICKERS.get(ob)
|
picker_group = PICKERS.get(ob)
|
||||||
if not picker or not picker.tooltip:
|
if not picker_group or not picker_group.tooltip:
|
||||||
return
|
return
|
||||||
|
|
||||||
text = picker.tooltip
|
region = bpy.context.region
|
||||||
|
text = picker_group.tooltip
|
||||||
|
ui_scale = bpy.context.preferences.system.ui_scale
|
||||||
|
|
||||||
#print('Draw text', text)
|
#print('Draw text', text)
|
||||||
|
|
||||||
font_id = 0
|
font_id = 0
|
||||||
|
blf.size(font_id, int(13 * ui_scale))
|
||||||
|
|
||||||
|
margins = [12, 5]
|
||||||
|
text_size = blf.dimensions(font_id, text)
|
||||||
|
text_pos = (region.width - text_size[0]-margins[0], margins[1])
|
||||||
|
|
||||||
|
bg_pos = (text_pos[0] - margins[0], text_pos[1] - margins[1]-1)
|
||||||
|
bg_size = (text_size[0] + 2*margins[0], text_size[1] + 2*margins[1])
|
||||||
|
|
||||||
|
gpu.state.blend_set('ALPHA')
|
||||||
|
draw_rect_2d(bg_pos, *bg_size, (0.1, 0.1, 0.1, 0.66))
|
||||||
|
gpu.state.blend_set('NONE')
|
||||||
|
|
||||||
|
|
||||||
#blf.dimensions(font_id, text)
|
#blf.dimensions(font_id, text)
|
||||||
blf.enable(font_id, blf.SHADOW)
|
blf.enable(font_id, blf.SHADOW)
|
||||||
#gpu.state.blend_set('ALPHA')
|
|
||||||
|
|
||||||
|
|
||||||
# BLF drawing routine
|
# BLF drawing routine
|
||||||
blf.position(font_id, picker.tooltip_mouse[0]-5, picker.tooltip_mouse[1]+5, 0)
|
blf.position(font_id, round(text_pos[0]), round(text_pos[1]), 0)
|
||||||
blf.size(font_id, 14)
|
blf.color(font_id, 0.85, 0.85, 0.85, 1)
|
||||||
blf.color(font_id, 1, 1, 1, 1)
|
|
||||||
|
|
||||||
blf.shadow(font_id , 5, 0.0, 0.0, 0.0, 1)
|
blf.shadow(font_id , 5, 0.0, 0.0, 0.0, 1)
|
||||||
blf.shadow_offset(font_id, 2, -2)
|
blf.shadow_offset(font_id, 2, -2)
|
||||||
|
@ -67,7 +104,6 @@ def draw_callback_px():
|
||||||
blf.draw(font_id, text)
|
blf.draw(font_id, text)
|
||||||
|
|
||||||
blf.disable(font_id, blf.SHADOW)
|
blf.disable(font_id, blf.SHADOW)
|
||||||
#gpu.state.blend_set('NONE')
|
|
||||||
|
|
||||||
handle_view = None
|
handle_view = None
|
||||||
handle_pixel = None
|
handle_pixel = None
|
||||||
|
|
2
gizmo.py
2
gizmo.py
|
@ -68,6 +68,8 @@ class RP_GT_gizmo(Gizmo):
|
||||||
|
|
||||||
context.region.tag_redraw()
|
context.region.tag_redraw()
|
||||||
|
|
||||||
|
#print(location)
|
||||||
|
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,13 +1,18 @@
|
||||||
|
|
||||||
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
|
import json
|
||||||
|
from os.path import abspath
|
||||||
|
from pathlib import Path
|
||||||
|
import bpy
|
||||||
|
from bpy.types import Operator
|
||||||
|
from bpy.props import IntProperty
|
||||||
|
from mathutils import Matrix
|
||||||
|
|
||||||
|
from ..core.shape import get_picker_data
|
||||||
|
from ..core.addon_utils import is_shape, get_picker_collection
|
||||||
|
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_label = 'Create UI shape'
|
||||||
bl_idname = 'rigpicker.create_shape'
|
bl_idname = 'rigpicker.create_shape'
|
||||||
#bl_options = {'REGISTER', 'UNDO'}
|
#bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
@ -52,7 +57,7 @@ class RP_OT_create_shape(bpy.types.Operator):
|
||||||
return {'FINISHED'}
|
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_label = 'Name Shape from selected bones'
|
||||||
bl_idname = 'rigpicker.name_from_bone'
|
bl_idname = 'rigpicker.name_from_bone'
|
||||||
#bl_options = {'REGISTER', 'UNDO'}
|
#bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
@ -69,67 +74,67 @@ class RP_OT_name_from_bone(bpy.types.Operator):
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
class RP_OT_mirror_shape(bpy.types.Operator):
|
class RP_OT_mirror_shape(Operator):
|
||||||
bl_label = 'Mirror UI shape'
|
bl_label = 'Mirror UI shape'
|
||||||
bl_idname = 'rigpicker.mirror_shape'
|
bl_idname = 'rigpicker.mirror_shape'
|
||||||
#bl_options = {'REGISTER', 'UNDO'}
|
#bl_options = g
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
return (context.object and context.object.type in ('MESH', 'CURVE', 'TEXT'))
|
||||||
|
|
||||||
def execute(self,context):
|
def execute(self,context):
|
||||||
scn = context.scene
|
scn = context.scene
|
||||||
|
ob = context.object
|
||||||
|
collection = get_picker_collection(ob)
|
||||||
|
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)
|
for mod in ob.modifiers:
|
||||||
link_mat_to_object(ob)
|
if (mod.type == 'NODES' and mod.node_group.name == 'Symmetrize' and
|
||||||
|
mod.get('Socket_2') in objects):
|
||||||
|
|
||||||
if not name:
|
bpy.data.objects.remove(ob)
|
||||||
name = ob.name + '_flip'
|
|
||||||
|
|
||||||
old_shape = bpy.data.objects.get(name)
|
if collection.rig_picker.symmetry:
|
||||||
old_mat = None
|
x_axis = collection.rig_picker.symmetry.matrix_world.to_translation()[0]
|
||||||
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)
|
|
||||||
|
|
||||||
mirror_ob = ob.copy()
|
|
||||||
mirror_ob.data = ob.data
|
|
||||||
mirror_ob.name = name
|
|
||||||
|
|
||||||
if old_mat:
|
|
||||||
#mirror_ob.data.materials.clear()
|
|
||||||
#mirror_ob.data.materials.append(None)
|
|
||||||
#mirror_ob.material_slots[0].link = 'OBJECT'
|
|
||||||
|
|
||||||
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:
|
else:
|
||||||
symmetry_loc = 0
|
x_axis = 0
|
||||||
|
|
||||||
#print(symmetry_loc)
|
for ob in objects:
|
||||||
mirror_ob.matrix_world = ob.matrix_world
|
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
|
||||||
|
|
||||||
#if mirror_ob.location[0] < symmetry_loc:
|
flipped_name = f'{ob.name}{flipped_suffix}'
|
||||||
mirror_ob.location.x = symmetry_loc + (symmetry_loc - ob.location.x)
|
ob.name = f'{ob.name}{suffix}'
|
||||||
#else:
|
|
||||||
# mirror_ob.location[0]= symmetry_loc+ (symmetry_loc- ob.location[0])
|
|
||||||
|
|
||||||
mirror_ob.rotation_euler.y = -ob.rotation_euler.y
|
flipped_object = ob.copy()
|
||||||
mirror_ob.rotation_euler.z = -ob.rotation_euler.z
|
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)
|
||||||
|
|
||||||
mirror_ob.scale.x = -ob.scale.x
|
# 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
|
||||||
|
|
||||||
for key, value in ob.items():
|
for col in ob.users_collection:
|
||||||
if key not in ['_RNA_UI','cycles']:
|
col.objects.link(flipped_object)
|
||||||
mirror_ob[key] = value
|
|
||||||
|
|
||||||
if ob.rig_picker.shape_type == 'BONE':
|
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':
|
elif ob.rig_picker.shape_type == 'FUNCTION':
|
||||||
args = {}
|
args = {}
|
||||||
|
@ -137,18 +142,18 @@ class RP_OT_mirror_shape(bpy.types.Operator):
|
||||||
if type(value) == list:
|
if type(value) == list:
|
||||||
mirrored_value = []
|
mirrored_value = []
|
||||||
for item in value:
|
for item in value:
|
||||||
mirrored_value.append(find_mirror(item))
|
mirrored_value.append(flip_name(item))
|
||||||
|
|
||||||
elif type(value) == str:
|
elif type(value) == str:
|
||||||
mirrored_value = find_mirror(value)
|
mirrored_value = flip_name(value)
|
||||||
args[key] = mirrored_value
|
args[key] = mirrored_value
|
||||||
|
|
||||||
mirror_ob.rig_picker.arguments = str(args)
|
flipped_object.rig_picker.arguments = str(args)
|
||||||
|
|
||||||
return {'FINISHED'}
|
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_label = 'Select Shape by Type'
|
||||||
bl_idname = 'rigpicker.select_shape_type'
|
bl_idname = 'rigpicker.select_shape_type'
|
||||||
#bl_options = {'REGISTER', 'UNDO'}
|
#bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
@ -177,33 +182,46 @@ class RP_OT_select_shape_type(bpy.types.Operator):
|
||||||
return wm.invoke_props_dialog(self, width=150)
|
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_label = 'Store UI Data'
|
||||||
bl_idname = 'rigpicker.save_picker'
|
bl_idname = 'rigpicker.save_picker'
|
||||||
|
|
||||||
|
index : IntProperty(default=-1)
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
scn = context.scene
|
scn = context.scene
|
||||||
canvas = scn.rig_picker.canvas
|
ob = context.object
|
||||||
rig = scn.rig_picker.rig
|
|
||||||
shapes = [o for o in scn.objects if o != canvas and is_shape(o)]
|
|
||||||
|
|
||||||
if not rig:
|
if self.index == -1:
|
||||||
self.report({'ERROR'}, 'Choose a Rig')
|
collection = get_picker_collection(ob)
|
||||||
|
else:
|
||||||
|
source = context.active_object.data.rig_picker.sources[self.index].source
|
||||||
|
source = Path(bpy.path.abspath(source, library=ob.data.library)).resolve()
|
||||||
|
collection = next((c for c in set(scn.collection.children_recursive) if c.rig_picker.enabled
|
||||||
|
and Path(bpy.path.abspath(c.rig_picker.destination)).resolve() == source), None)
|
||||||
|
|
||||||
|
if not collection:
|
||||||
|
self.report({"ERROR"}, 'No Picker found')
|
||||||
return {'CANCELLED'}
|
return {'CANCELLED'}
|
||||||
|
|
||||||
|
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 canvas:
|
if not canvas:
|
||||||
self.report({'ERROR'}, 'Choose a Canvas')
|
self.report({'ERROR'}, 'Choose a Canvas')
|
||||||
return {'CANCELLED'}
|
return {'CANCELLED'}
|
||||||
|
|
||||||
|
|
||||||
data = get_picker_datas(shapes, canvas, rig)
|
picker_data = get_picker_data(collection)
|
||||||
|
|
||||||
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))
|
|
||||||
|
print(f'Save Picker to {picker_path}')
|
||||||
|
picker_path.write_text(json.dumps(picker_data))
|
||||||
|
|
||||||
bpy.ops.rigpicker.reload_picker()
|
bpy.ops.rigpicker.reload_picker()
|
||||||
|
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
classes = (
|
classes = (
|
||||||
|
|
117
panels.py
117
panels.py
|
@ -1,117 +0,0 @@
|
||||||
import bpy
|
|
||||||
#import collections
|
|
||||||
#import inspect
|
|
||||||
from .core.addon_utils import get_operator_from_id
|
|
||||||
from .core.bl_utils import get_mat
|
|
||||||
|
|
||||||
import re
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
layout = self.layout
|
|
||||||
col = layout.column(align=False)
|
|
||||||
col.prop_search(scn.rig_picker, 'rig', scn, 'objects', text='Rig ')
|
|
||||||
col.prop_search(scn.rig_picker, 'canvas', scn, 'objects', text='Canvas ')
|
|
||||||
col.prop_search(scn.rig_picker, 'symmetry', scn, 'objects', text='Symmetry ')
|
|
||||||
|
|
||||||
if ob.type == 'ARMATURE':
|
|
||||||
row = layout.row(align=True)
|
|
||||||
is_packed = ('picker' in ob.data.rig_picker.keys())
|
|
||||||
sub_row = row.row(align=True)
|
|
||||||
sub_row.prop(ob.data.rig_picker, 'source', text='Picker')
|
|
||||||
sub_row.enabled = not is_packed
|
|
||||||
row.operator('rigpicker.pack_picker', icon='PACKAGE' if is_packed else 'UGLYPACKAGE', text='')
|
|
||||||
|
|
||||||
|
|
||||||
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='ARROW_LEFTRIGHT', 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 scn.rig_picker.rig:
|
|
||||||
col.prop_search(ob.rig_picker, 'name', scn.rig_picker.rig.pose, 'bones', text='Bone')
|
|
||||||
|
|
||||||
#layout.separator()
|
|
||||||
layout.prop(scn.rig_picker, 'destination', text='Filepath')
|
|
||||||
layout.operator('rigpicker.save_picker', icon='PASTEDOWN', text='Save Picker')
|
|
||||||
|
|
||||||
|
|
||||||
classes = (
|
|
||||||
RP_PT_picker_maker_panel,
|
|
||||||
)
|
|
||||||
|
|
||||||
register, unregister = bpy.utils.register_classes_factory(classes)
|
|
|
@ -1,5 +1,7 @@
|
||||||
import bpy
|
import bpy
|
||||||
from bpy.props import EnumProperty, StringProperty, PointerProperty, BoolProperty
|
from bpy.props import (EnumProperty, StringProperty, PointerProperty, BoolProperty,
|
||||||
|
CollectionProperty, IntProperty)
|
||||||
|
|
||||||
import inspect
|
import inspect
|
||||||
|
|
||||||
'''
|
'''
|
||||||
|
@ -22,9 +24,15 @@ def bones_item(self,context):
|
||||||
items.append((bone.name,bone.name,''))
|
items.append((bone.name,bone.name,''))
|
||||||
return items
|
return items
|
||||||
|
|
||||||
|
|
||||||
|
class RP_PG_picker_source(bpy.types.PropertyGroup):
|
||||||
|
source : StringProperty(subtype='FILE_PATH')
|
||||||
|
|
||||||
|
|
||||||
class RP_PG_armature_ui_settings(bpy.types.PropertyGroup):
|
class RP_PG_armature_ui_settings(bpy.types.PropertyGroup):
|
||||||
name: StringProperty()
|
name: StringProperty()
|
||||||
source: StringProperty(subtype='FILE_PATH')
|
source: StringProperty(subtype='FILE_PATH')
|
||||||
|
sources: CollectionProperty(type=RP_PG_picker_source)
|
||||||
|
|
||||||
|
|
||||||
class RP_PG_object_ui_settings(bpy.types.PropertyGroup):
|
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):
|
class RP_PG_scene_ui_settings(bpy.types.PropertyGroup):
|
||||||
|
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)
|
||||||
|
@ -67,6 +76,7 @@ class RP_OT_operator_selector(bpy.types.Operator):
|
||||||
|
|
||||||
|
|
||||||
classes = (
|
classes = (
|
||||||
|
RP_PG_picker_source,
|
||||||
RP_PG_object_ui_settings,
|
RP_PG_object_ui_settings,
|
||||||
RP_PG_scene_ui_settings,
|
RP_PG_scene_ui_settings,
|
||||||
RP_PG_armature_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.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.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
|
||||||
|
|
Binary file not shown.
|
@ -0,0 +1,19 @@
|
||||||
|
|
||||||
|
flat in vec2 startPos;
|
||||||
|
in vec2 vertPos;
|
||||||
|
|
||||||
|
out vec4 fragColor;
|
||||||
|
|
||||||
|
uniform vec4 color;
|
||||||
|
uniform float dashSize;
|
||||||
|
uniform float gapSize;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec2 dir = (vertPos.xy - startPos.xy);
|
||||||
|
float dist = length(dir);
|
||||||
|
|
||||||
|
if (fract(dist / (dashSize + gapSize)) > dashSize/(dashSize + gapSize))
|
||||||
|
discard;
|
||||||
|
fragColor = color;
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
|
||||||
|
layout (location = 0) in vec2 pos;
|
||||||
|
|
||||||
|
flat out vec2 startPos;
|
||||||
|
out vec2 vertPos;
|
||||||
|
|
||||||
|
uniform mat4 viewMatrix;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec4 outPos = viewMatrix * vec4(pos.x, pos.y, 0.0, 1.0);
|
||||||
|
gl_Position = outPos;
|
||||||
|
vertPos = pos.xy / outPos.w;
|
||||||
|
startPos = vertPos;
|
||||||
|
}
|
|
@ -0,0 +1,144 @@
|
||||||
|
import bpy
|
||||||
|
from bpy.types import UIList
|
||||||
|
#import collections
|
||||||
|
#import inspect
|
||||||
|
from .core.addon_utils import get_operator_from_id, get_picker_collection
|
||||||
|
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_header_preset(self, context):
|
||||||
|
self.layout.operator('rigpicker.add_picker_collection', text="", icon='ADD', emboss=False)
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
ob = context.object
|
||||||
|
scn = context.scene
|
||||||
|
collection = get_picker_collection(ob)
|
||||||
|
|
||||||
|
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 = ('pickers' 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)
|
||||||
|
row.enabled = not is_packed
|
||||||
|
row.prop(item, 'source', text='')
|
||||||
|
row.operator("rigpicker.remove_picker_source", icon ='PANEL_CLOSE', text="", emboss=False).index=i
|
||||||
|
|
||||||
|
if collection:
|
||||||
|
#layout.separator()
|
||||||
|
layout.prop(collection.rig_picker, 'destination', text='Filepath')
|
||||||
|
layout.operator('rigpicker.save_picker', icon='PASTEDOWN', text='Save Picker')
|
||||||
|
|
||||||
|
|
||||||
|
class RP_PT_shape(bpy.types.Panel):
|
||||||
|
bl_label = 'Shape'
|
||||||
|
bl_category = 'Rigging'
|
||||||
|
bl_space_type = 'VIEW_3D'
|
||||||
|
bl_region_type = 'UI'
|
||||||
|
bl_parent_id = "RP_PT_picker_maker_panel"
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
ob = context.object
|
||||||
|
scn = context.scene
|
||||||
|
collection = get_picker_collection(ob)
|
||||||
|
|
||||||
|
layout = self.layout
|
||||||
|
col = layout.column(align=False)
|
||||||
|
|
||||||
|
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')
|
||||||
|
|
||||||
|
elif 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')
|
||||||
|
|
||||||
|
classes = (
|
||||||
|
#RP_UL_picker_source,
|
||||||
|
RP_PT_picker_maker_panel,
|
||||||
|
RP_PT_shape
|
||||||
|
)
|
||||||
|
|
||||||
|
register, unregister = bpy.utils.register_classes_factory(classes)
|
Loading…
Reference in New Issue