refacto
parent
57efcc2998
commit
b28e80d6d5
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"python.linting.pylintEnabled": true,
|
||||
"python.linting.enabled": true
|
||||
}
|
19
__init__.py
19
__init__.py
|
@ -12,29 +12,20 @@ bl_info = {
|
|||
|
||||
import importlib
|
||||
|
||||
modules = [
|
||||
'.op_material',
|
||||
'.op_picker',
|
||||
'.op_shape',
|
||||
modules = (
|
||||
'.operators',
|
||||
'.properties',
|
||||
'.panels',
|
||||
'.area',
|
||||
'.gizmo',
|
||||
'.picker'
|
||||
]
|
||||
|
||||
functions = [
|
||||
#'.func_picker',
|
||||
'.func_shape',
|
||||
'.snapping_utils',
|
||||
'.utils'
|
||||
]
|
||||
'.draw_handlers'
|
||||
)
|
||||
|
||||
import bpy
|
||||
|
||||
if "bpy" in locals():
|
||||
if not bpy.app.background:
|
||||
for name in modules + functions:
|
||||
for name in modules:
|
||||
module = importlib.import_module(name, __name__)
|
||||
importlib.reload(module)
|
||||
|
||||
|
|
289
_draw_handler.py
289
_draw_handler.py
|
@ -1,289 +0,0 @@
|
|||
import bpy
|
||||
import blf
|
||||
import gpu
|
||||
|
||||
from mathutils import Vector
|
||||
from .func_picker import *
|
||||
from .utils import intersect_rectangles, point_inside_rectangle,\
|
||||
point_over_shape, border_over_shape, canvas_space
|
||||
|
||||
from gpu_extras.batch import batch_for_shader
|
||||
from .constants import PICKERS
|
||||
from .picker import Picker
|
||||
|
||||
"""
|
||||
def draw_polygon_2d(verts, indices, loops, color, contour, width=None):
|
||||
dpi = int(bpy.context.user_preferences.system.pixel_size)
|
||||
|
||||
bgl.glColor4f(*color)
|
||||
gpu.state.blend_set('ALPHA')
|
||||
#bgl.glEnable(bgl.GL_LINE_SMOOTH)
|
||||
|
||||
bgl.glColor4f(*color)
|
||||
for face in faces:
|
||||
bgl.glBegin(bgl.GL_POLYGON)
|
||||
for v_index in face:
|
||||
coord = verts[v_index]
|
||||
bgl.glVertex2f(coord[0],coord[1])
|
||||
bgl.glEnd()
|
||||
|
||||
if width:
|
||||
gpu.state.line_width_set(width*dpi)
|
||||
bgl.glColor4f(*contour)
|
||||
|
||||
for loop in loops:
|
||||
if faces:
|
||||
bgl.glBegin(bgl.GL_LINE_LOOP)
|
||||
else:
|
||||
bgl.glBegin(bgl.GL_LINE_STRIP)
|
||||
for v_index in loop:
|
||||
coord = verts[v_index]
|
||||
bgl.glVertex2f(coord[0],coord[1])
|
||||
bgl.glEnd()
|
||||
|
||||
gpu.state.blend_set('NONE')
|
||||
#bgl.glDisable(bgl.GL_LINE_SMOOTH)
|
||||
bgl.glEnd()
|
||||
|
||||
return
|
||||
|
||||
def draw_border(border):
|
||||
gpu.state.blend_set('ALPHA')
|
||||
bgl.glColor4f(1,1,1,0.2)
|
||||
bgl.glBegin(bgl.GL_POLYGON)
|
||||
for v in border:
|
||||
bgl.glVertex2f(v[0],v[1])
|
||||
|
||||
bgl.glEnd()
|
||||
|
||||
bgl.glColor4f(0.0, 0.0, 0.0, 0.5)
|
||||
gpu.state.line_width_set(2.0)
|
||||
bgl.glLineStipple(3, 0xAAAA)
|
||||
bgl.glEnable(bgl.GL_LINE_STIPPLE)
|
||||
|
||||
bgl.glBegin(bgl.GL_LINE_LOOP)
|
||||
|
||||
for v in border:
|
||||
bgl.glVertex2f(v[0],v[1])
|
||||
|
||||
bgl.glEnd()
|
||||
|
||||
bgl.glColor4f(1.0, 1.0, 1.0, 1)
|
||||
gpu.state.line_width_set(1.0)
|
||||
bgl.glBegin(bgl.GL_LINE_LOOP)
|
||||
for v in border:
|
||||
bgl.glVertex2f(v[0],v[1])
|
||||
|
||||
bgl.glEnd()
|
||||
bgl.glDisable(bgl.GL_LINE_STIPPLE)
|
||||
gpu.state.blend_set('NONE')
|
||||
|
||||
def draw_text(mouse,text,color):
|
||||
dpi = int(bpy.context.user_preferences.system.pixel_size)
|
||||
|
||||
gpu.state.blend_set('ALPHA')
|
||||
font_id =0 # XXX, need to find out how best to get this.
|
||||
# draw some text
|
||||
bgl.glColor4f(0,0,0,0.75)
|
||||
blf.blur(font_id,5)
|
||||
blf.position(font_id, mouse[0]+10*dpi, mouse[1]-20*dpi, 0)
|
||||
blf.size(font_id, 9*dpi, 96)
|
||||
blf.draw(font_id, text)
|
||||
|
||||
bgl.glEnd()
|
||||
bgl.glColor4f(*color)
|
||||
blf.blur(font_id,0)
|
||||
blf.draw(font_id, text)
|
||||
gpu.state.blend_set('NONE')
|
||||
|
||||
|
||||
def select_bone(self,context,event):
|
||||
ob = context.object
|
||||
if ob and ob.type =='ARMATURE' and ob.data.rig_picker:
|
||||
shape_data = ob.data.rig_picker
|
||||
selected_bones = [b for b in ob.pose.bones if b.bone.select]
|
||||
|
||||
if not event.shift and not event.alt:
|
||||
for b in ob.pose.bones:
|
||||
b.bone.select= False
|
||||
|
||||
for shape in [s for s in shape_data['shapes'] if not s['shape_type']==["DISPLAY"]]:
|
||||
points = [canvas_space(p,self.scale,self.offset) for p in shape['points']]
|
||||
bound = [canvas_space(p,self.scale,self.offset) for p in shape['bound']]
|
||||
loops = shape['loops']
|
||||
|
||||
## Colision check
|
||||
over = False
|
||||
if self.is_border:
|
||||
if intersect_rectangles(self.border,bound): #start check on over bound_box
|
||||
over = border_over_shape(self.border,points,loops)
|
||||
else:
|
||||
if point_inside_rectangle(self.end,bound):
|
||||
over = point_over_shape(self.end,points,loops)
|
||||
|
||||
if over:
|
||||
if shape['shape_type'] == 'BONE':
|
||||
bone = context.object.pose.bones.get(shape['bone'])
|
||||
if bone:
|
||||
if event.alt:
|
||||
bone.bone.select = False
|
||||
else:
|
||||
bone.bone.select = True
|
||||
context.object.data.bones.active = bone.bone
|
||||
|
||||
if shape['shape_type'] == 'FUNCTION' and event.value== 'RELEASE' and not self.is_border:
|
||||
# restore selection
|
||||
for b in selected_bones:
|
||||
b.bone.select = True
|
||||
|
||||
function = shape['function']
|
||||
if shape.get('variables'):
|
||||
variables=shape['variables'].to_dict()
|
||||
|
||||
else:
|
||||
variables={}
|
||||
|
||||
variables['event']=event
|
||||
|
||||
print(variables)
|
||||
|
||||
globals()[function](variables)
|
||||
|
||||
"""
|
||||
|
||||
|
||||
|
||||
def draw_callback_view():
|
||||
ob = bpy.context.object
|
||||
|
||||
if not ob or ob.type !='ARMATURE' or 'shapes' not in ob.data.rig_picker.keys():
|
||||
return
|
||||
|
||||
if ob not in PICKERS:
|
||||
shapes = [s.to_dict() for s in ob.data.rig_picker['shapes']]
|
||||
PICKERS[ob] = Picker(ob, shapes=shapes)
|
||||
|
||||
picker = PICKERS.get(ob)
|
||||
picker.draw()
|
||||
|
||||
handle_view = None
|
||||
handle_pixel = None
|
||||
|
||||
def register():
|
||||
global handle_view, handle_pixel
|
||||
|
||||
handle_view = bpy.types.SpaceNodeEditor.draw_handler_add(draw_callback_view, (), 'WINDOW', 'POST_VIEW')
|
||||
handle_pixel = bpy.types.SpaceNodeEditor.draw_handler_add(draw_callback_px, (), 'WINDOW', 'POST_PIXEL')
|
||||
|
||||
def unregister():
|
||||
global handle_view, handle_pixel
|
||||
|
||||
bpy.types.SpaceNodeEditor.draw_handler_remove(handle_view, 'WINDOW')
|
||||
bpy.types.SpaceNodeEditor.draw_handler_remove(handle_pixel, 'WINDOW')
|
||||
|
||||
handle_view = None
|
||||
handle_pixel = None
|
||||
|
||||
'''
|
||||
return
|
||||
|
||||
shape_data = ob.data.rig_picker
|
||||
rig_layers = [i for i,l in enumerate(ob.data.layers) if l]
|
||||
|
||||
r = context.region
|
||||
#self.scale = region.height
|
||||
|
||||
#draw BG
|
||||
canvas = shape_data['shapes'][0]
|
||||
#bg_point = [(0, r.height), (r.width, r.height), (r.width, 0),(0, 0)]
|
||||
bg_color = [*canvas['color'], 1]
|
||||
draw_polygon_2d(bg_point,[[0,1,2,3]],[[0,1,2,3]], bg_color,(0,0,0,1),0)
|
||||
|
||||
show_tooltip = False
|
||||
for shape in shape_data['shapes']:
|
||||
|
||||
color = shape['color']
|
||||
points = [canvas_space(p,self.scale,self.offset) for p in shape['points']]
|
||||
bound = [canvas_space(p,self.scale,self.offset) for p in shape['bound']]
|
||||
loops = shape['loops']
|
||||
faces = shape['faces']
|
||||
|
||||
select=None
|
||||
contour_color = [0,0,0]
|
||||
contour_alpha = 1
|
||||
width = 0
|
||||
shape_color = [c for c in color]
|
||||
shape_alpha = 1
|
||||
|
||||
|
||||
if shape['shape_type'] == 'DISPLAY' and not faces:
|
||||
width = 1
|
||||
|
||||
if shape['shape_type'] != 'DISPLAY':
|
||||
if shape['shape_type'] == 'BONE':
|
||||
bone = ob.pose.bones.get(shape['bone'])
|
||||
if bone:
|
||||
b_layers = [i for i,l in enumerate(bone.bone.layers) if l]
|
||||
|
||||
if bone.bone_group:
|
||||
group_color = list(bone.bone_group.colors.normal)
|
||||
contour_color = [c*0.85 for c in group_color]
|
||||
width = 1
|
||||
|
||||
if bone.bone.select :
|
||||
shape_color = [c*1.2+0.1 for c in color]
|
||||
if bone.bone_group:
|
||||
contour_color = [0.05,0.95,0.95]
|
||||
|
||||
if ob.data.bones.active and shape['bone'] == ob.data.bones.active.name:
|
||||
if bone.bone_group:
|
||||
if bone.bone.select :
|
||||
shape_color = [c*1.2+0.2 for c in color]
|
||||
contour_color = [1,1,1]
|
||||
width = 1.5
|
||||
else:
|
||||
shape_color = [c*1.2+0.15 for c in color]
|
||||
contour_color = [0.9,0.9,0.9]
|
||||
width = 1
|
||||
|
||||
|
||||
if bone.bone.hide or not len(set(b_layers).intersection(rig_layers)):
|
||||
shape_alpha = 0.33
|
||||
contour_alpha = 0.33
|
||||
|
||||
elif shape['shape_type'] == 'FUNCTION':
|
||||
if shape['function'] == 'boolean':
|
||||
path = shape['variables']['data_path']
|
||||
|
||||
if ob.path_resolve(path):
|
||||
shape_color = [c*1.4+0.08 for c in color]
|
||||
else:
|
||||
shape_color = [color[0],color[1],color[2]]
|
||||
|
||||
|
||||
## On mouse over checking
|
||||
over = False
|
||||
if self.is_border:
|
||||
if intersect_rectangles(self.border,bound): #start check on over bound_box
|
||||
over = border_over_shape(self.border,points,loops)
|
||||
else:
|
||||
if point_inside_rectangle(self.end,bound):
|
||||
over = point_over_shape(self.end,points,loops)
|
||||
|
||||
if over:
|
||||
show_tooltip = True
|
||||
tooltip = shape['tooltip']
|
||||
if not self.press:
|
||||
shape_color = [c*1.02+0.05 for c in shape_color]
|
||||
contour_color = [c*1.03+0.05 for c in contour_color]
|
||||
|
||||
shape_color.append(shape_alpha)
|
||||
contour_color.append(contour_alpha)
|
||||
draw_polygon_2d(points,faces,loops,shape_color,contour_color,width)
|
||||
|
||||
if show_tooltip:
|
||||
draw_text(self.end,tooltip,(1,1,1,1))
|
||||
|
||||
if self.is_border:
|
||||
draw_border(self.border)
|
||||
'''
|
|
@ -0,0 +1,43 @@
|
|||
|
||||
import bpy
|
||||
from .bl_utils import get_mat
|
||||
|
||||
def is_shape(ob):
|
||||
scn = bpy.context.scene
|
||||
canvas = scn.rig_picker.canvas
|
||||
if not canvas or ob.hide_render:
|
||||
return False
|
||||
|
||||
shapes = {ob for col in canvas.users_collection for ob in col.all_objects}
|
||||
|
||||
if ob.type in ('MESH', 'CURVE', 'FONT') and ob in shapes:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def get_object_color(ob):
|
||||
if not ob.data.materials:
|
||||
return
|
||||
|
||||
mat = get_mat(ob)
|
||||
if not mat or not mat.node_tree or not mat.node_tree.nodes:
|
||||
return
|
||||
|
||||
emit_node = mat.node_tree.nodes.get('Emission')
|
||||
if not emit_node:
|
||||
return
|
||||
|
||||
return emit_node.inputs['Color'].default_value
|
||||
|
||||
def get_operator_from_id(idname):
|
||||
if not '.' in idname:
|
||||
return
|
||||
|
||||
m, o = idname.split(".")
|
||||
try:
|
||||
op = getattr(getattr(bpy.ops, m), o)
|
||||
op.get_rna_type()
|
||||
except Exception:
|
||||
return
|
||||
|
||||
return op
|
|
@ -0,0 +1,119 @@
|
|||
|
||||
import bpy
|
||||
|
||||
|
||||
def get_view_3d_override():
|
||||
windows = bpy.context.window_manager.windows
|
||||
areas = [a for w in windows for a in w.screen.areas if a.type == 'VIEW_3D']
|
||||
|
||||
if not areas:
|
||||
print('No view 3d found')
|
||||
return
|
||||
|
||||
view_3d = None
|
||||
for area in areas:
|
||||
if area.spaces.active.camera:
|
||||
view_3d = area
|
||||
|
||||
if not view_3d:
|
||||
view_3d = max(areas, key=lambda x :x.width)
|
||||
|
||||
return {'area': view_3d, 'region': view_3d.regions[-1]}
|
||||
|
||||
def get_mat(ob):
|
||||
for sl in ob.material_slots:
|
||||
if sl.material:
|
||||
return sl.material
|
||||
|
||||
def link_mat_to_object(ob):
|
||||
for sl in ob.material_slots:
|
||||
m = sl.material
|
||||
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
|
||||
|
||||
else:
|
||||
return None
|
||||
|
||||
def hide_layers(args):
|
||||
""" """
|
||||
ob = bpy.context.object
|
||||
|
||||
layers = []
|
||||
for bone in [b for b in ob.pose.bones if b.bone.select]:
|
||||
for i,l in enumerate(bone.bone.layers):
|
||||
if l and i not in layers:
|
||||
layers.append(i)
|
||||
|
||||
for i in layers:
|
||||
ob.data.layers[i] = not ob.data.layers[i]
|
||||
|
||||
def select_layer(args):
|
||||
ob = bpy.context.object
|
||||
|
||||
layers =[]
|
||||
for bone in [b for b in ob.pose.bones if b.bone.select]:
|
||||
bone_layers = [i for i,l in enumerate(bone.bone.layers) if l]
|
||||
|
||||
for l in bone_layers:
|
||||
if l not in layers:
|
||||
layers.append(l)
|
||||
|
||||
for bone in ob.pose.bones:
|
||||
bone_layers = [i for i,l in enumerate(bone.bone.layers) if l]
|
||||
|
||||
if len(set(bone_layers).intersection(layers)):
|
||||
bone.bone.select = True
|
||||
|
||||
def hide_bones(args):
|
||||
ob = bpy.context.object
|
||||
selected_bone = [b for b in ob.pose.bones if b.bone.select]
|
||||
|
||||
hide = [b.bone.hide for b in selected_bone if not b.bone.hide]
|
||||
|
||||
visibility = True if len(hide) else False
|
||||
|
||||
for bone in selected_bone:
|
||||
bone.bone.hide = visibility
|
||||
|
||||
def select_all(args):
|
||||
ob = bpy.context.object
|
||||
shapes = ob.data.rig_picker['shapes']
|
||||
bones = [s['bone'] for s in shapes if s['shape_type']=='BONE']
|
||||
|
||||
for bone_name in bones:
|
||||
bone = ob.pose.bones.get(bone_name)
|
||||
|
||||
if bone:
|
||||
bone.bone.select = True
|
|
@ -0,0 +1,49 @@
|
|||
|
||||
import bpy
|
||||
from mathutils import Vector
|
||||
from mathutils.geometry import intersect_line_line_2d
|
||||
|
||||
|
||||
def is_over_region(self,context,event):
|
||||
inside = 2 < event.mouse_region_x < context.region.width -2 and \
|
||||
2 < event.mouse_region_y < context.region.height -2 and \
|
||||
[a for a in context.screen.areas if a.as_pointer()==self.adress] and \
|
||||
not context.screen.show_fullscreen
|
||||
|
||||
return inside
|
||||
|
||||
def bound_box_center(ob):
|
||||
points = [ob.matrix_world@Vector(p) for p in ob.bound_box]
|
||||
|
||||
x = [v[0] for v in points]
|
||||
y = [v[1] for v in points]
|
||||
z = [v[2] for v in points]
|
||||
|
||||
return (sum(x) / len(points), sum(y) / len(points),sum(z) / len(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])
|
||||
|
||||
if (dx>=0) and (dy>=0):
|
||||
return dx*dy
|
||||
|
||||
def point_inside_rectangle(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)):
|
||||
out = Vector(outside_point)
|
||||
pt = Vector(point)
|
||||
|
||||
intersections = 0
|
||||
for loop in loops:
|
||||
for i,p in enumerate(loop):
|
||||
a = Vector(verts[loop[i-1]])
|
||||
b = Vector(verts[p])
|
||||
|
||||
if intersect_line_line_2d(pt,out,a,b):
|
||||
intersections += 1
|
||||
|
||||
if intersections%2 == 1: #chek if the nb of intersection is odd
|
||||
return True
|
|
@ -2,15 +2,16 @@
|
|||
import bpy
|
||||
import gpu
|
||||
from gpu_extras.batch import batch_for_shader
|
||||
import gpu
|
||||
import blf
|
||||
from mathutils import bvhtree, Vector
|
||||
from mathutils.geometry import intersect_point_quad_2d, intersect_point_tri_2d, intersect_tri_tri_2d
|
||||
from .constants import PICKERS
|
||||
from .utils import get_operator_from_id
|
||||
from ..constants import PICKERS
|
||||
from .addon_utils import get_operator_from_id
|
||||
|
||||
from pathlib import Path
|
||||
import re
|
||||
import json
|
||||
import os
|
||||
|
||||
import threading
|
||||
|
||||
|
@ -383,8 +384,6 @@ class OperatorShape(Shape):
|
|||
gpu.state.blend_set('NONE')
|
||||
|
||||
|
||||
|
||||
|
||||
class Picker:
|
||||
def __init__(self, rig, shapes):
|
||||
|
||||
|
@ -475,6 +474,8 @@ class Picker:
|
|||
|
||||
s.press = False
|
||||
|
||||
#bpy.context.area.tag_redraw()
|
||||
|
||||
|
||||
def tooltip_event(self):
|
||||
#print('Tooltip Event', self)
|
||||
|
@ -538,9 +539,12 @@ class Picker:
|
|||
self.mouse = location
|
||||
location = self.region.view2d.region_to_view(*location)
|
||||
|
||||
for s in self.shapes:
|
||||
if s.move_event(location):
|
||||
self.hover_shape = s
|
||||
self.hover_shape = None
|
||||
for shape in reversed(self.shapes):
|
||||
if self.hover_shape:
|
||||
shape.hover = False
|
||||
elif shape.move_event(location):
|
||||
self.hover_shape = shape
|
||||
|
||||
#if point_inside_rectangle(self.end, bound):
|
||||
# over = point_over_shape(self.end,points, edges)
|
||||
|
@ -581,86 +585,26 @@ class Picker:
|
|||
gpu.state.blend_set('NONE')
|
||||
'''
|
||||
|
||||
|
||||
def draw_callback_view():
|
||||
sp = bpy.context.space_data
|
||||
|
||||
if not sp or not sp.tree_type == 'RigPickerTree':
|
||||
def get_picker_path(rig, start=None):
|
||||
picker_path = rig.data.get('rig_picker', {}).get('source')
|
||||
if not picker_path:
|
||||
return
|
||||
|
||||
ob = bpy.context.object
|
||||
if not ob or ob.type !='ARMATURE' or not ob.data.rig_picker.source:
|
||||
picker_path = bpy.path.abspath(picker_path, library=rig.data.library, start=start)
|
||||
|
||||
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 unpack_picker(rig):
|
||||
if 'rig_picker' not in rig.data.keys():
|
||||
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']]
|
||||
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 = PICKERS.get(ob)
|
||||
picker.draw()
|
||||
|
||||
def draw_callback_px():
|
||||
sp = bpy.context.space_data
|
||||
if not sp.tree_type == 'RigPickerTree':
|
||||
return
|
||||
|
||||
ob = bpy.context.object
|
||||
|
||||
picker = PICKERS.get(ob)
|
||||
if not picker or not picker.tooltip:
|
||||
return
|
||||
|
||||
text = picker.tooltip
|
||||
|
||||
#print('Draw text', text)
|
||||
|
||||
font_id = 0
|
||||
#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.color(font_id, 1, 1, 1, 1)
|
||||
|
||||
blf.shadow(font_id , 5, 0.0, 0.0, 0.0, 1)
|
||||
blf.shadow_offset(font_id, 2, -2)
|
||||
|
||||
blf.draw(font_id, text)
|
||||
|
||||
blf.disable(font_id, blf.SHADOW)
|
||||
#gpu.state.blend_set('NONE')
|
||||
|
||||
handle_view = None
|
||||
handle_pixel = None
|
||||
|
||||
|
||||
def register():
|
||||
global handle_view
|
||||
global handle_pixel
|
||||
|
||||
handle_view = bpy.types.SpaceNodeEditor.draw_handler_add(draw_callback_view, (), 'WINDOW', 'POST_VIEW')
|
||||
handle_pixel = bpy.types.SpaceNodeEditor.draw_handler_add(draw_callback_px, (), 'WINDOW', 'POST_PIXEL')
|
||||
|
||||
def unregister():
|
||||
global handle_view
|
||||
global handle_pixel
|
||||
|
||||
bpy.types.SpaceNodeEditor.draw_handler_remove(handle_view, 'WINDOW')
|
||||
bpy.types.SpaceNodeEditor.draw_handler_remove(handle_pixel, 'WINDOW')
|
||||
|
||||
handle_view = None
|
||||
handle_pixel = None
|
||||
if 'picker' in rig.data['rig_picker'].keys():
|
||||
del rig.data['rig_picker']['picker']
|
|
@ -2,9 +2,65 @@ import bpy
|
|||
import bmesh
|
||||
from mathutils import Vector, Matrix
|
||||
from bpy_extras import mesh_utils
|
||||
from .utils import bound_box_center, contour_loops, get_object_color
|
||||
from .geometry_utils import bound_box_center
|
||||
from .addon_utils import get_object_color
|
||||
|
||||
|
||||
|
||||
def border_over_shape(border,verts,loops):
|
||||
for loop in loops:
|
||||
for i,p in enumerate(loop):
|
||||
a = Vector(verts[loop[i-1]])
|
||||
b = Vector(verts[p])
|
||||
|
||||
for j in range(0,4):
|
||||
c = border[j-1]
|
||||
d = border[j]
|
||||
if intersect_line_line_2d(a,b,c,d):
|
||||
return True
|
||||
|
||||
for point in verts:
|
||||
if point_inside_rectangle(point,border):
|
||||
return True
|
||||
|
||||
for point in border:
|
||||
if point_over_shape(point,verts,loops):
|
||||
return True
|
||||
|
||||
|
||||
def border_loop(vert, loop):
|
||||
border_edge =[e for e in vert.link_edges if e.is_boundary]
|
||||
|
||||
if border_edge:
|
||||
for edge in border_edge:
|
||||
other_vert = edge.other_vert(vert)
|
||||
|
||||
if not other_vert in loop:
|
||||
loop.append(other_vert)
|
||||
border_loop(other_vert, loop)
|
||||
|
||||
return loop
|
||||
else:
|
||||
return [vert]
|
||||
|
||||
def contour_loops(bm, vert_index=0, loops=None, vert_indices=None):
|
||||
loops = loops or []
|
||||
vert_indices = vert_indices or [v.index for v in bm.verts]
|
||||
|
||||
bm.verts.ensure_lookup_table()
|
||||
|
||||
loop = border_loop(bm.verts[vert_index], [bm.verts[vert_index]])
|
||||
if len(loop) >1:
|
||||
loops.append(loop)
|
||||
|
||||
for v in loop:
|
||||
vert_indices.remove(v.index)
|
||||
|
||||
if len(vert_indices):
|
||||
contour_loops(bm, vert_indices[0], loops, vert_indices)
|
||||
|
||||
return loops
|
||||
|
||||
def get_picker_datas(objects, canvas, rig):
|
||||
picker_datas = []
|
||||
gamma = 1 / 2.2
|
|
@ -0,0 +1,91 @@
|
|||
|
||||
import bpy
|
||||
import blf
|
||||
from pathlib import Path
|
||||
from .constants import PICKERS
|
||||
from .core.picker import Picker
|
||||
import json
|
||||
|
||||
|
||||
def draw_callback_view():
|
||||
sp = bpy.context.space_data
|
||||
|
||||
if not sp or not sp.tree_type == 'RigPickerTree':
|
||||
return
|
||||
|
||||
ob = bpy.context.object
|
||||
if not ob or ob.type !='ARMATURE' or not ob.data.rig_picker.source:
|
||||
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']]
|
||||
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 = PICKERS.get(ob)
|
||||
picker.draw()
|
||||
|
||||
def draw_callback_px():
|
||||
sp = bpy.context.space_data
|
||||
if not sp.tree_type == 'RigPickerTree':
|
||||
return
|
||||
|
||||
ob = bpy.context.object
|
||||
|
||||
picker = PICKERS.get(ob)
|
||||
if not picker or not picker.tooltip:
|
||||
return
|
||||
|
||||
text = picker.tooltip
|
||||
|
||||
#print('Draw text', text)
|
||||
|
||||
font_id = 0
|
||||
#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.color(font_id, 1, 1, 1, 1)
|
||||
|
||||
blf.shadow(font_id , 5, 0.0, 0.0, 0.0, 1)
|
||||
blf.shadow_offset(font_id, 2, -2)
|
||||
|
||||
blf.draw(font_id, text)
|
||||
|
||||
blf.disable(font_id, blf.SHADOW)
|
||||
#gpu.state.blend_set('NONE')
|
||||
|
||||
handle_view = None
|
||||
handle_pixel = None
|
||||
|
||||
|
||||
def register():
|
||||
global handle_view
|
||||
global handle_pixel
|
||||
|
||||
handle_view = bpy.types.SpaceNodeEditor.draw_handler_add(draw_callback_view, (), 'WINDOW', 'POST_VIEW')
|
||||
handle_pixel = bpy.types.SpaceNodeEditor.draw_handler_add(draw_callback_px, (), 'WINDOW', 'POST_PIXEL')
|
||||
|
||||
def unregister():
|
||||
global handle_view
|
||||
global handle_pixel
|
||||
|
||||
bpy.types.SpaceNodeEditor.draw_handler_remove(handle_view, 'WINDOW')
|
||||
bpy.types.SpaceNodeEditor.draw_handler_remove(handle_pixel, 'WINDOW')
|
||||
|
||||
handle_view = None
|
||||
handle_pixel = None
|
248
func_picker.py
248
func_picker.py
|
@ -1,248 +0,0 @@
|
|||
import bpy
|
||||
|
||||
#from. insert_keyframe import insert_keyframe
|
||||
from. snapping_utils import *
|
||||
from .utils import get_IK_bones
|
||||
|
||||
try:
|
||||
from rigutils.driver_utils import split_path
|
||||
from rigutils.insert_keyframe import insert_keyframe
|
||||
from rigutils.snap_ik_fk import snap_ik_fk
|
||||
from rigutils.utils import find_mirror
|
||||
except:
|
||||
print('You need to install the rigutils module in your blender modules path')
|
||||
|
||||
def hide_layers(args):
|
||||
""" """
|
||||
ob = bpy.context.object
|
||||
|
||||
layers=[]
|
||||
for bone in [b for b in ob.pose.bones if b.bone.select]:
|
||||
for i,l in enumerate(bone.bone.layers):
|
||||
if l and i not in layers:
|
||||
layers.append(i)
|
||||
|
||||
for i in layers:
|
||||
ob.data.layers[i] = not ob.data.layers[i]
|
||||
|
||||
|
||||
def boolean(args):
|
||||
""" data_path, keyable """
|
||||
ob = bpy.context.object
|
||||
data_path = args['data_path']
|
||||
keyable = args['keyable']
|
||||
|
||||
#bone,prop = split_path(data_path)
|
||||
|
||||
try:
|
||||
value = ob.path_resolve(data_path)
|
||||
#setattr(ob.pose.bones.get(bone),'["%s"]'%prop,not value)
|
||||
try:
|
||||
exec("ob.%s = %s"%(data_path,not value))
|
||||
except:
|
||||
exec("ob%s= %s"%(data_path,not value))
|
||||
|
||||
if keyable and bpy.context.scene.tool_settings.use_keyframe_insert_auto:
|
||||
|
||||
if not ob.animation_data:
|
||||
ob.animation_data_create()
|
||||
|
||||
ob.keyframe_insert(data_path = data_path ,group = bone)
|
||||
|
||||
except ValueError:
|
||||
print("Property don't exist")
|
||||
|
||||
|
||||
def select_layer(args):
|
||||
ob = bpy.context.object
|
||||
|
||||
layers =[]
|
||||
for bone in [b for b in ob.pose.bones if b.bone.select]:
|
||||
bone_layers = [i for i,l in enumerate(bone.bone.layers) if l]
|
||||
|
||||
for l in bone_layers:
|
||||
if l not in layers:
|
||||
layers.append(l)
|
||||
|
||||
for bone in ob.pose.bones:
|
||||
bone_layers = [i for i,l in enumerate(bone.bone.layers) if l]
|
||||
|
||||
if len(set(bone_layers).intersection(layers)):
|
||||
bone.bone.select = True
|
||||
|
||||
def hide_bones(args):
|
||||
ob = bpy.context.object
|
||||
selected_bone = [b for b in ob.pose.bones if b.bone.select]
|
||||
|
||||
hide = [b.bone.hide for b in selected_bone if not b.bone.hide]
|
||||
|
||||
visibility = True if len(hide) else False
|
||||
|
||||
for bone in selected_bone:
|
||||
bone.bone.hide = visibility
|
||||
|
||||
|
||||
def select_all(args):
|
||||
ob = bpy.context.object
|
||||
shapes = ob.data.rig_picker['shapes']
|
||||
bones = [s['bone'] for s in shapes if s['shape_type']=='BONE']
|
||||
|
||||
for bone_name in bones:
|
||||
bone = ob.pose.bones.get(bone_name)
|
||||
|
||||
if bone:
|
||||
bone.bone.select = True
|
||||
|
||||
def select_bones(args):
|
||||
"""bones (name list)"""
|
||||
ob = bpy.context.object
|
||||
pBones = ob.pose.bones
|
||||
bones_name =args['bones']
|
||||
event = args['event']
|
||||
if not event.shift:
|
||||
for bone in bpy.context.object.pose.bones:
|
||||
bone.bone.select = False
|
||||
|
||||
bones = [pBones.get(b) for b in bones_name]
|
||||
|
||||
select = False
|
||||
for bone in bones:
|
||||
if bone.bone.select == False:
|
||||
select =True
|
||||
break
|
||||
|
||||
for bone in bones:
|
||||
bone.bone.select = select
|
||||
ob.data.bones.active = bones[-1].bone
|
||||
|
||||
def keyframe_bones(args):
|
||||
print(args)
|
||||
event=args['event']
|
||||
bones=[]
|
||||
|
||||
for bone in bpy.context.object.pose.bones:
|
||||
if not bone.name.startswith(('DEF','ORG','MCH')) and not bone.get('_unkeyable_') ==1:
|
||||
if event.shift:
|
||||
bones.append(bone)
|
||||
elif not event.shift and bone.bone.select :
|
||||
bones.append(bone)
|
||||
|
||||
|
||||
for bone in bones:
|
||||
insert_keyframe(bone)
|
||||
|
||||
def reset_bones(args):
|
||||
event=args['event']
|
||||
avoid_value =args['avoid_value']
|
||||
|
||||
ob = bpy.context.object
|
||||
|
||||
bones=[]
|
||||
for bone in bpy.context.object.pose.bones:
|
||||
if not bone.name.startswith(('DEF','ORG','MCH')) and not bone.get('_unkeyable_') ==1:
|
||||
if event.shift:
|
||||
bones.append(bone)
|
||||
elif not event.shift and bone.bone.select :
|
||||
bones.append(bone)
|
||||
|
||||
|
||||
for bone in bones:
|
||||
if bone.rotation_mode =='QUATERNION':
|
||||
bone.rotation_quaternion = 1, 0, 0, 0
|
||||
|
||||
if bone.rotation_mode == 'AXIS_ANGLE':
|
||||
bone.rotation_axis_angle = 0, 0, 1, 0
|
||||
|
||||
else:
|
||||
bone.rotation_euler = 0, 0, 0
|
||||
|
||||
bone.location = 0, 0, 0
|
||||
bone.scale = 1, 1, 1
|
||||
|
||||
for key,value in bone.items():
|
||||
if key not in avoid_value and type(value) in (int,float):
|
||||
if ob.data.get("DefaultValues") and ob.data.DefaultValues['bones'].get(bone.name):
|
||||
|
||||
if key in ob.data.DefaultValues['bones'][bone.name]:
|
||||
bone[key] = ob.data.DefaultValues['bones'][bone.name][key]
|
||||
|
||||
else:
|
||||
if type(value)== int:
|
||||
bone[key]=0
|
||||
else:
|
||||
bone[key]=0.0
|
||||
else:
|
||||
if type(value)== int:
|
||||
bone[key]=0
|
||||
else:
|
||||
bone[key]=0.0
|
||||
|
||||
if bpy.context.scene.tool_settings.use_keyframe_insert_auto:
|
||||
insert_keyframe(bone)
|
||||
|
||||
|
||||
def flip_bones(args):
|
||||
event=args['event']
|
||||
|
||||
ob = bpy.context.object
|
||||
arm = bpy.context.object.pose.bones
|
||||
|
||||
selected_bones = [bone for bone in ob.pose.bones if bone.bone.select==True ]
|
||||
mirrorActive = None
|
||||
|
||||
for bone in selected_bones:
|
||||
boneName = bone.name
|
||||
mirrorBoneName= find_mirror(boneName)
|
||||
|
||||
mirrorBone = ob.pose.bones.get(mirrorBoneName) if mirrorBoneName else None
|
||||
|
||||
if bpy.context.active_pose_bone == bone:
|
||||
mirrorActive = mirrorBone
|
||||
|
||||
#print(mirrorBone)
|
||||
if not event.shift and mirrorBone:
|
||||
bone.bone.select = False
|
||||
|
||||
if mirrorBone:
|
||||
mirrorBone.bone.select = True
|
||||
if mirrorActive:
|
||||
ob.data.bones.active = mirrorActive.bone
|
||||
|
||||
def snap_ikfk(args):
|
||||
""" way, chain_index """
|
||||
|
||||
way =args['way']
|
||||
chain_index = args['chain_index']
|
||||
#auto_switch = self.auto_switch
|
||||
|
||||
ob = bpy.context.object
|
||||
armature = ob.data
|
||||
|
||||
SnappingChain = armature.get('SnappingChain')
|
||||
|
||||
poseBone = ob.pose.bones
|
||||
dataBone = ob.data.bones
|
||||
|
||||
IKFK_chain = SnappingChain['IKFK_bones'][chain_index]
|
||||
switch_prop = IKFK_chain['switch_prop']
|
||||
|
||||
FK_root = poseBone.get(IKFK_chain['FK_root'])
|
||||
FK_mid = [poseBone.get(b['name']) for b in IKFK_chain['FK_mid']]
|
||||
FK_tip = poseBone.get(IKFK_chain['FK_tip'])
|
||||
|
||||
IK_last = poseBone.get(IKFK_chain['IK_last'])
|
||||
IK_tip = poseBone.get(IKFK_chain['IK_tip'])
|
||||
IK_pole = poseBone.get(IKFK_chain['IK_pole'])
|
||||
|
||||
invert = IKFK_chain['invert_switch']
|
||||
|
||||
ik_fk_layer = (IKFK_chain['FK_layer'],IKFK_chain['IK_layer'])
|
||||
|
||||
|
||||
for lock in ('lock_ik_x','lock_ik_y','lock_ik_z'):
|
||||
if getattr(IK_last,lock):
|
||||
full_snapping = False
|
||||
break
|
||||
|
||||
|
||||
snap_ik_fk(ob,way,switch_prop,FK_root,FK_tip,IK_last,IK_tip,IK_pole,FK_mid,full_snapping,invert,ik_fk_layer,auto_switch=True)
|
2
gizmo.py
2
gizmo.py
|
@ -6,7 +6,7 @@ from bpy.types import (AddonPreferences, GizmoGroup, Operator, Gizmo)
|
|||
|
||||
from mathutils import Vector, Matrix, Euler
|
||||
from .constants import PICKERS
|
||||
from .picker import Picker
|
||||
from .core.picker import Picker
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
|
||||
|
||||
from . import material
|
||||
from . import picker
|
||||
from . import shape
|
||||
|
||||
|
||||
modules = (
|
||||
material,
|
||||
picker,
|
||||
shape
|
||||
)
|
||||
|
||||
def register():
|
||||
for mod in modules:
|
||||
mod.register()
|
||||
|
||||
def unregister():
|
||||
for mod in reversed(modules):
|
||||
mod.unregister()
|
|
@ -1,14 +1,18 @@
|
|||
import bpy
|
||||
from .constants import PICKERS
|
||||
from bpy.props import EnumProperty
|
||||
from ..constants import PICKERS
|
||||
from ..core.bl_utils import get_view_3d_override
|
||||
from ..core.picker import get_picker_path, pack_picker, unpack_picker
|
||||
#from .func_bgl import draw_callback_px
|
||||
#from .func_bgl import select_bone
|
||||
#from .func_picker import *
|
||||
#from core.picker import *
|
||||
#from .utils import is_over_region
|
||||
import gpu
|
||||
from mathutils import Vector
|
||||
from gpu_extras.batch import batch_for_shader
|
||||
from pathlib import Path
|
||||
import json
|
||||
import os
|
||||
|
||||
|
||||
vertex_shader = '''
|
||||
|
@ -92,7 +96,7 @@ class RP_OT_box_select(bpy.types.Operator):
|
|||
bl_idname = "node.rp_box_select"
|
||||
bl_label = "Picker Box Select"
|
||||
|
||||
mode: bpy.props.EnumProperty(items=[(i, i.title(), '') for i in ('SET', 'EXTEND', 'SUBSTRACT')])
|
||||
mode: EnumProperty(items=[(i, i.title(), '') for i in ('SET', 'EXTEND', 'SUBSTRACT')])
|
||||
|
||||
color_shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR')
|
||||
dash_shader = gpu.types.GPUShader(vertex_shader, fragment_shader)
|
||||
|
@ -194,47 +198,57 @@ class RP_OT_box_select(bpy.types.Operator):
|
|||
|
||||
|
||||
|
||||
class RP_OT_move_bone(bpy.types.Operator):
|
||||
class RP_OT_picker_transform(bpy.types.Operator):
|
||||
"""Tooltip"""
|
||||
bl_idname = "node.move_bone"
|
||||
bl_idname = "node.picker_transform"
|
||||
bl_label = "Move Bone in Picker View"
|
||||
|
||||
mode : EnumProperty(items=[(m, m.title(), '') for m in ('TRANSLATE', 'ROTATE', 'SCALE')])
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if not is_picker_space(context):
|
||||
return
|
||||
return is_picker_space(context)
|
||||
|
||||
def invoke(self, context, event):
|
||||
self.mouse = event.mouse_region_x, event.mouse_region_y
|
||||
def execute(self, context):
|
||||
with context.temp_override(**get_view_3d_override()):
|
||||
if self.mode == 'TRANSLATE':
|
||||
bpy.ops.transform.translate("INVOKE_DEFAULT")
|
||||
elif self.mode == 'ROTATE':
|
||||
bpy.ops.transform.rotate("INVOKE_DEFAULT")
|
||||
elif self.mode == 'SCALE':
|
||||
bpy.ops.transform.resize("INVOKE_DEFAULT")
|
||||
|
||||
self.bone_data = {b: b.matrix.copy() for b in context.selected_pose_bones}
|
||||
return {"FINISHED"}
|
||||
|
||||
context.window_manager.modal_handler_add(self)
|
||||
return {'RUNNING_MODAL'}
|
||||
# def invoke(self, context, event):
|
||||
# self.mouse = event.mouse_region_x, event.mouse_region_y
|
||||
|
||||
def modal(self, context, event):
|
||||
# self.bone_data = {b: b.matrix.copy() for b in context.selected_pose_bones}
|
||||
|
||||
delta_x = (event.mouse_region_x - self.mouse[0]) / 1000
|
||||
delta_y = (event.mouse_region_y - self.mouse[1]) / 1000
|
||||
# context.window_manager.modal_handler_add(self)
|
||||
# return {'RUNNING_MODAL'}
|
||||
|
||||
# def modal(self, context, event):
|
||||
|
||||
# delta_x = (event.mouse_region_x - self.mouse[0]) / 1000
|
||||
# delta_y = (event.mouse_region_y - self.mouse[1]) / 1000
|
||||
|
||||
# for bone, matrix in self.bone_data.items():
|
||||
# bone.matrix.translation = matrix.translation + Vector((delta_x, 0, delta_y))
|
||||
|
||||
|
||||
# #print(delta_x, delta_y)
|
||||
|
||||
for bone, matrix in self.bone_data.items():
|
||||
bone.matrix.translation = matrix.translation + Vector((delta_x, 0, delta_y))
|
||||
# if event.type=="LEFTMOUSE" and event.value == 'RELEASE':
|
||||
# return {'FINISHED'}
|
||||
|
||||
# if event.type=="RIGHTMOUSE":
|
||||
# for bone, matrix in self.bone_data.items():
|
||||
# bone.matrix = matrix
|
||||
|
||||
#print(delta_x, delta_y)
|
||||
# return {'CANCELLED'}
|
||||
|
||||
if event.type=="LEFTMOUSE" and event.value == 'RELEASE':
|
||||
return {'FINISHED'}
|
||||
|
||||
if event.type=="RIGHTMOUSE":
|
||||
for bone, matrix in self.bone_data.items():
|
||||
bone.matrix = matrix
|
||||
|
||||
return {'CANCELLED'}
|
||||
|
||||
return {'RUNNING_MODAL'}
|
||||
# return {'RUNNING_MODAL'}
|
||||
|
||||
|
||||
class RP_OT_function_execute(bpy.types.Operator):
|
||||
|
@ -309,20 +323,30 @@ class RP_OT_toogle_bone_layer(bpy.types.Operator):
|
|||
bone = picker.hover_shape.bone
|
||||
hide = picker.hover_shape.hide
|
||||
|
||||
|
||||
if bone:
|
||||
for i, l in enumerate(bone.bone.layers):
|
||||
if l:
|
||||
ob.data.layers[i] = hide
|
||||
|
||||
print('Bone Layer toogle')
|
||||
|
||||
#if picker.hover_bone:
|
||||
context.region.tag_redraw()
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class RP_OT_context_menu_picker(bpy.types.Operator):
|
||||
bl_idname = "node.context_menu_picker"
|
||||
bl_label = 'Context Menu Picker'
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return is_picker_space(context)
|
||||
|
||||
def execute(self, context):
|
||||
bpy.ops.wm.call_menu(name='RP_MT_context_menu')
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class RP_OT_pack_picker(bpy.types.Operator):
|
||||
"""Pack Unpack the picker on the rig"""
|
||||
bl_idname = "rigpicker.pack_picker"
|
||||
|
@ -336,18 +360,20 @@ class RP_OT_pack_picker(bpy.types.Operator):
|
|||
def execute(self, context):
|
||||
print('Pack Picker')
|
||||
|
||||
ob = context.object
|
||||
picker_src = Path(bpy.path.abspath(ob.data.rig_picker.source, library=ob.data.library))
|
||||
rig = context.object
|
||||
picker_path = get_picker_path(rig)
|
||||
|
||||
if 'picker' in ob.data.rig_picker.keys():
|
||||
del ob.data.rig_picker['picker']
|
||||
if 'picker' in rig.data.rig_picker.keys():
|
||||
unpack_picker(rig)
|
||||
self.report({"INFO"}, f'The picker is unpacked')
|
||||
return {"FINISHED"}
|
||||
|
||||
if not picker_src.exists():
|
||||
self.report({"ERROR"}, f'The path of the picker not exist: {picker_src}')
|
||||
if not picker_path.exists():
|
||||
self.report({"ERROR"}, f'The path of the picker not exist: {picker_path}')
|
||||
return {"CANCELLED"}
|
||||
|
||||
ob.data.rig_picker['picker'] = json.loads(picker_src.read_text())
|
||||
pack_picker(rig)
|
||||
self.report({"INFO"}, f'The picker is packed')
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
@ -383,8 +409,19 @@ class RP_MT_context_menu(bpy.types.Menu):
|
|||
# Set the menu operators and draw functions
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
col = layout.column()
|
||||
col.use_property_split = True
|
||||
|
||||
layout.operator("rigpicker.show_bone_layer", text="Show Bone Layer", ).type = 'ACTIVE'
|
||||
ob = context.object
|
||||
picker = PICKERS.get(ob)
|
||||
|
||||
if picker.hover_shape and picker.hover_shape.type == 'bone':
|
||||
bone = picker.hover_shape.bone
|
||||
|
||||
for key in bone.keys():
|
||||
layout.prop(bone,f'["{key}"]', slider=True)
|
||||
|
||||
#layout.operator("rigpicker.show_bone_layer", text="Show Bone Layer", ).type = 'ACTIVE'
|
||||
#layout.operator("rigidbody.objects_add", text="B Add Passive").type = 'PASSIVE'
|
||||
|
||||
'''
|
||||
|
@ -530,11 +567,19 @@ def register_keymaps():
|
|||
kmi = km.keymap_items.new("rigpicker.toogle_bone_layer", type="LEFTMOUSE", value="DOUBLE_CLICK")
|
||||
keymaps.append((km, kmi))
|
||||
|
||||
kmi = km.keymap_items.new("node.move_bone", type="G", value="PRESS")
|
||||
kmi = km.keymap_items.new("node.picker_transform", type="G", value="PRESS")
|
||||
kmi.properties.mode = 'TRANSLATE'
|
||||
keymaps.append((km, kmi))
|
||||
|
||||
kmi = km.keymap_items.new("wm.call_menu", type="RIGHTMOUSE", value="PRESS")
|
||||
kmi.properties.name = "RP_MT_context_menu"
|
||||
kmi = km.keymap_items.new("node.picker_transform", type="R", value="PRESS")
|
||||
kmi.properties.mode = 'ROTATE'
|
||||
keymaps.append((km, kmi))
|
||||
|
||||
kmi = km.keymap_items.new("node.picker_transform", type="S", value="PRESS")
|
||||
kmi.properties.mode = 'SCALE'
|
||||
keymaps.append((km, kmi))
|
||||
|
||||
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")
|
||||
|
@ -563,7 +608,8 @@ classes = (
|
|||
RP_OT_toogle_bone_layer,
|
||||
RP_OT_call_operator,
|
||||
RP_MT_context_menu,
|
||||
RP_OT_move_bone,
|
||||
RP_OT_picker_transform,
|
||||
RP_OT_context_menu_picker,
|
||||
RP_OT_pack_picker
|
||||
#RP_OT_ui_draw
|
||||
)
|
|
@ -1,8 +1,8 @@
|
|||
|
||||
import bpy
|
||||
from .func_shape import get_picker_datas
|
||||
from .utils import is_shape, find_mirror, link_mat_to_object
|
||||
#import os
|
||||
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
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
import bpy
|
||||
#import collections
|
||||
#import inspect
|
||||
from .utils import get_operator_from_id, get_mat
|
||||
from .core.addon_utils import get_operator_from_id
|
||||
from .core.bl_utils import get_mat
|
||||
|
||||
import re
|
||||
|
||||
|
|
|
@ -1,209 +0,0 @@
|
|||
import bpy
|
||||
from mathutils import Vector,Matrix
|
||||
from math import acos, pi
|
||||
#from .insert_keyframe import insert_keyframe
|
||||
|
||||
############################
|
||||
## Math utility functions ##
|
||||
############################
|
||||
|
||||
def perpendicular_vector(v):
|
||||
""" Returns a vector that is perpendicular to the one given.
|
||||
The returned vector is _not_ guaranteed to be normalized.
|
||||
"""
|
||||
# Create a vector that is not aligned with v.
|
||||
# It doesn't matter what vector. Just any vector
|
||||
# that's guaranteed to not be pointing in the same
|
||||
# direction.
|
||||
if abs(v[0]) < abs(v[1]):
|
||||
tv = Vector((1,0,0))
|
||||
else:
|
||||
tv = Vector((0,1,0))
|
||||
|
||||
# Use cross prouct to generate a vector perpendicular to
|
||||
# both tv and (more importantly) v.
|
||||
return v.cross(tv)
|
||||
|
||||
|
||||
def rotation_difference(mat1, mat2):
|
||||
""" Returns the shortest-path rotational difference between two
|
||||
matrices.
|
||||
"""
|
||||
q1 = mat1.to_quaternion()
|
||||
q2 = mat2.to_quaternion()
|
||||
angle = acos(min(1,max(-1,q1.dot(q2)))) * 2
|
||||
if angle > pi:
|
||||
angle = -angle + (2*pi)
|
||||
return angle
|
||||
|
||||
|
||||
#########################################
|
||||
## "Visual Transform" helper functions ##
|
||||
#########################################
|
||||
|
||||
def get_pose_matrix_in_other_space(mat, pose_bone):
|
||||
""" Returns the transform matrix relative to pose_bone's current
|
||||
transform space. In other words, presuming that mat is in
|
||||
armature space, slapping the returned matrix onto pose_bone
|
||||
should give it the armature-space transforms of mat.
|
||||
TODO: try to handle cases with axis-scaled parents better.
|
||||
"""
|
||||
rest = pose_bone.bone.matrix_local.copy()
|
||||
rest_inv = rest.inverted()
|
||||
if pose_bone.parent:
|
||||
par_mat = pose_bone.parent.matrix.copy()
|
||||
par_inv = par_mat.inverted()
|
||||
par_rest = pose_bone.parent.bone.matrix_local.copy()
|
||||
else:
|
||||
par_mat = Matrix()
|
||||
par_inv = Matrix()
|
||||
par_rest = Matrix()
|
||||
|
||||
# Get matrix in bone's current transform space
|
||||
smat = rest_inv * (par_rest * (par_inv * mat))
|
||||
|
||||
# Compensate for non-local location
|
||||
#if not pose_bone.bone.use_local_location:
|
||||
# loc = smat.to_translation() * (par_rest.inverted() * rest).to_quaternion()
|
||||
# smat.translation = loc
|
||||
|
||||
return smat
|
||||
|
||||
|
||||
def get_local_pose_matrix(pose_bone):
|
||||
""" Returns the local transform matrix of the given pose bone.
|
||||
"""
|
||||
return get_pose_matrix_in_other_space(pose_bone.matrix, pose_bone)
|
||||
|
||||
|
||||
def set_pose_translation(pose_bone, mat):
|
||||
""" Sets the pose bone's translation to the same translation as the given matrix.
|
||||
Matrix should be given in bone's local space.
|
||||
"""
|
||||
if pose_bone.bone.use_local_location is True:
|
||||
pose_bone.location = mat.to_translation()
|
||||
else:
|
||||
loc = mat.to_translation()
|
||||
|
||||
rest = pose_bone.bone.matrix_local.copy()
|
||||
if pose_bone.bone.parent:
|
||||
par_rest = pose_bone.bone.parent.matrix_local.copy()
|
||||
else:
|
||||
par_rest = Matrix()
|
||||
|
||||
q = (par_rest.inverted() * rest).to_quaternion()
|
||||
pose_bone.location = q * loc
|
||||
|
||||
|
||||
def set_pose_rotation(pose_bone, mat):
|
||||
""" Sets the pose bone's rotation to the same rotation as the given matrix.
|
||||
Matrix should be given in bone's local space.
|
||||
"""
|
||||
q = mat.to_quaternion()
|
||||
|
||||
if pose_bone.rotation_mode == 'QUATERNION':
|
||||
pose_bone.rotation_quaternion = q
|
||||
elif pose_bone.rotation_mode == 'AXIS_ANGLE':
|
||||
pose_bone.rotation_axis_angle[0] = q.angle
|
||||
pose_bone.rotation_axis_angle[1] = q.axis[0]
|
||||
pose_bone.rotation_axis_angle[2] = q.axis[1]
|
||||
pose_bone.rotation_axis_angle[3] = q.axis[2]
|
||||
else:
|
||||
pose_bone.rotation_euler = q.to_euler(pose_bone.rotation_mode)
|
||||
|
||||
|
||||
def set_pose_scale(pose_bone, mat):
|
||||
""" Sets the pose bone's scale to the same scale as the given matrix.
|
||||
Matrix should be given in bone's local space.
|
||||
"""
|
||||
pose_bone.scale = mat.to_scale()
|
||||
|
||||
|
||||
def match_pose_translation(pose_bone, target_bone):
|
||||
""" Matches pose_bone's visual translation to target_bone's visual
|
||||
translation.
|
||||
This function assumes you are in pose mode on the relevant armature.
|
||||
"""
|
||||
mat = get_pose_matrix_in_other_space(target_bone.matrix, pose_bone)
|
||||
set_pose_translation(pose_bone, mat)
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
bpy.ops.object.mode_set(mode='POSE')
|
||||
|
||||
|
||||
def match_pose_rotation(pose_bone, target_bone):
|
||||
""" Matches pose_bone's visual rotation to target_bone's visual
|
||||
rotation.
|
||||
This function assumes you are in pose mode on the relevant armature.
|
||||
"""
|
||||
mat = get_pose_matrix_in_other_space(target_bone.matrix, pose_bone)
|
||||
set_pose_rotation(pose_bone, mat)
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
bpy.ops.object.mode_set(mode='POSE')
|
||||
|
||||
|
||||
def match_pose_scale(pose_bone, target_bone):
|
||||
""" Matches pose_bone's visual scale to target_bone's visual
|
||||
scale.
|
||||
This function assumes you are in pose mode on the relevant armature.
|
||||
"""
|
||||
mat = get_pose_matrix_in_other_space(target_bone.matrix, pose_bone)
|
||||
set_pose_scale(pose_bone, mat)
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
bpy.ops.object.mode_set(mode='POSE')
|
||||
|
||||
|
||||
##############################
|
||||
## IK/FK snapping functions ##
|
||||
##############################
|
||||
|
||||
def match_pole_target(ik_first, ik_last, pole, match_bone, length):
|
||||
""" Places an IK chain's pole target to match ik_first's
|
||||
transforms to match_bone. All bones should be given as pose bones.
|
||||
You need to be in pose mode on the relevant armature object.
|
||||
ik_first: first bone in the IK chain
|
||||
ik_last: last bone in the IK chain
|
||||
pole: pole target bone for the IK chain
|
||||
match_bone: bone to match ik_first to (probably first bone in a matching FK chain)
|
||||
length: distance pole target should be placed from the chain center
|
||||
"""
|
||||
a = ik_first.matrix.to_translation()
|
||||
b = ik_last.matrix.to_translation() + ik_last.vector
|
||||
|
||||
# Vector from the head of ik_first to the
|
||||
# tip of ik_last
|
||||
ikv = b - a
|
||||
|
||||
# Get a vector perpendicular to ikv
|
||||
pv = perpendicular_vector(ikv).normalized() * length
|
||||
|
||||
def set_pole(pvi):
|
||||
""" Set pole target's position based on a vector
|
||||
from the arm center line.
|
||||
"""
|
||||
# Translate pvi into armature space
|
||||
ploc = a + (ikv/2) + pvi
|
||||
|
||||
# Set pole target to location
|
||||
mat = get_pose_matrix_in_other_space(Matrix.Translation(ploc), pole)
|
||||
set_pose_translation(pole, mat)
|
||||
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
bpy.ops.object.mode_set(mode='POSE')
|
||||
|
||||
set_pole(pv)
|
||||
|
||||
# Get the rotation difference between ik_first and match_bone
|
||||
angle = rotation_difference(ik_first.matrix, match_bone.matrix)
|
||||
|
||||
# Try compensating for the rotation difference in both directions
|
||||
pv1 = Matrix.Rotation(angle, 4, ikv) * pv
|
||||
set_pole(pv1)
|
||||
ang1 = rotation_difference(ik_first.matrix, match_bone.matrix)
|
||||
|
||||
pv2 = Matrix.Rotation(-angle, 4, ikv) * pv
|
||||
set_pole(pv2)
|
||||
ang2 = rotation_difference(ik_first.matrix, match_bone.matrix)
|
||||
|
||||
# Do the one with the smaller angle
|
||||
if ang1 < ang2:
|
||||
set_pole(pv1)
|
215
utils.py
215
utils.py
|
@ -1,215 +0,0 @@
|
|||
|
||||
import bpy
|
||||
from mathutils import Vector
|
||||
from mathutils.geometry import intersect_line_line_2d
|
||||
|
||||
|
||||
def get_mat(ob):
|
||||
for sl in ob.material_slots:
|
||||
if sl.material:
|
||||
return sl.material
|
||||
|
||||
def link_mat_to_object(ob):
|
||||
for sl in ob.material_slots:
|
||||
m = sl.material
|
||||
sl.link = 'OBJECT'
|
||||
sl.material = m
|
||||
|
||||
def get_operator_from_id(idname):
|
||||
if not '.' in idname:
|
||||
return
|
||||
|
||||
m, o = idname.split(".")
|
||||
try:
|
||||
op = getattr(getattr(bpy.ops, m), o)
|
||||
op.get_rna_type()
|
||||
except Exception:
|
||||
return
|
||||
|
||||
return op
|
||||
'''
|
||||
def canvas_space(point,scale,offset):
|
||||
return scale*Vector(point)+Vector(offset)
|
||||
'''
|
||||
|
||||
def get_object_color(ob):
|
||||
if not ob.data.materials:
|
||||
return
|
||||
|
||||
mat = get_mat(ob)
|
||||
if not mat or not mat.node_tree or not mat.node_tree.nodes:
|
||||
return
|
||||
|
||||
emit_node = mat.node_tree.nodes.get('Emission')
|
||||
if not emit_node:
|
||||
return
|
||||
|
||||
return emit_node.inputs['Color'].default_value
|
||||
|
||||
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])
|
||||
|
||||
if (dx>=0) and (dy>=0):
|
||||
return dx*dy
|
||||
|
||||
def point_inside_rectangle(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)):
|
||||
out = Vector(outside_point)
|
||||
pt = Vector(point)
|
||||
|
||||
intersections = 0
|
||||
for loop in loops:
|
||||
for i,p in enumerate(loop):
|
||||
a = Vector(verts[loop[i-1]])
|
||||
b = Vector(verts[p])
|
||||
|
||||
if intersect_line_line_2d(pt,out,a,b):
|
||||
intersections += 1
|
||||
|
||||
if intersections%2 == 1: #chek if the nb of intersection is odd
|
||||
return True
|
||||
|
||||
|
||||
def border_over_shape(border,verts,loops):
|
||||
for loop in loops:
|
||||
for i,p in enumerate(loop):
|
||||
a = Vector(verts[loop[i-1]])
|
||||
b = Vector(verts[p])
|
||||
|
||||
for j in range(0,4):
|
||||
c = border[j-1]
|
||||
d = border[j]
|
||||
if intersect_line_line_2d(a,b,c,d):
|
||||
return True
|
||||
|
||||
for point in verts:
|
||||
if point_inside_rectangle(point,border):
|
||||
return True
|
||||
|
||||
for point in border:
|
||||
if point_over_shape(point,verts,loops):
|
||||
return True
|
||||
|
||||
|
||||
|
||||
def border_loop(vert, loop):
|
||||
border_edge =[e for e in vert.link_edges if e.is_boundary]
|
||||
|
||||
if border_edge:
|
||||
for edge in border_edge:
|
||||
other_vert = edge.other_vert(vert)
|
||||
|
||||
if not other_vert in loop:
|
||||
loop.append(other_vert)
|
||||
border_loop(other_vert, loop)
|
||||
|
||||
return loop
|
||||
else:
|
||||
return [vert]
|
||||
|
||||
|
||||
def contour_loops(bm, vert_index=0, loops=None, vert_indices=None):
|
||||
loops = loops or []
|
||||
vert_indices = vert_indices or [v.index for v in bm.verts]
|
||||
|
||||
bm.verts.ensure_lookup_table()
|
||||
|
||||
loop = border_loop(bm.verts[vert_index], [bm.verts[vert_index]])
|
||||
if len(loop) >1:
|
||||
loops.append(loop)
|
||||
|
||||
for v in loop:
|
||||
vert_indices.remove(v.index)
|
||||
|
||||
if len(vert_indices):
|
||||
contour_loops(bm, vert_indices[0], loops, vert_indices)
|
||||
|
||||
return loops
|
||||
|
||||
|
||||
def get_IK_bones(IK_last):
|
||||
ik_chain = IK_last.parent_recursive
|
||||
ik_len = 0
|
||||
|
||||
#Get IK len:
|
||||
for c in IK_last.constraints:
|
||||
if c.type == 'IK':
|
||||
ik_len = c.chain_count -2
|
||||
break
|
||||
|
||||
IK_root = ik_chain[ik_len]
|
||||
|
||||
IK_mid= ik_chain[:ik_len]
|
||||
|
||||
IK_mid.reverse()
|
||||
IK_mid.append(IK_last)
|
||||
|
||||
return IK_root,IK_mid
|
||||
|
||||
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
|
||||
|
||||
else:
|
||||
return None
|
||||
|
||||
def is_shape(ob):
|
||||
scn = bpy.context.scene
|
||||
canvas = scn.rig_picker.canvas
|
||||
if not canvas or ob.hide_render:
|
||||
return False
|
||||
|
||||
shapes = {ob for col in canvas.users_collection for ob in col.all_objects}
|
||||
|
||||
if ob.type in ('MESH', 'CURVE', 'FONT') and ob in shapes:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def is_over_region(self,context,event):
|
||||
inside = 2 < event.mouse_region_x < context.region.width -2 and \
|
||||
2 < event.mouse_region_y < context.region.height -2 and \
|
||||
[a for a in context.screen.areas if a.as_pointer()==self.adress] and \
|
||||
not context.screen.show_fullscreen
|
||||
|
||||
return inside
|
||||
|
||||
def bound_box_center(ob):
|
||||
points = [ob.matrix_world@Vector(p) for p in ob.bound_box]
|
||||
|
||||
x = [v[0] for v in points]
|
||||
y = [v[1] for v in points]
|
||||
z = [v[2] for v in points]
|
||||
|
||||
return (sum(x) / len(points), sum(y) / len(points),sum(z) / len(points))
|
Loading…
Reference in New Issue