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
|
import importlib
|
||||||
|
|
||||||
modules = [
|
modules = (
|
||||||
'.op_material',
|
'.operators',
|
||||||
'.op_picker',
|
|
||||||
'.op_shape',
|
|
||||||
'.properties',
|
'.properties',
|
||||||
'.panels',
|
'.panels',
|
||||||
'.area',
|
'.area',
|
||||||
'.gizmo',
|
'.gizmo',
|
||||||
'.picker'
|
'.draw_handlers'
|
||||||
]
|
)
|
||||||
|
|
||||||
functions = [
|
|
||||||
#'.func_picker',
|
|
||||||
'.func_shape',
|
|
||||||
'.snapping_utils',
|
|
||||||
'.utils'
|
|
||||||
]
|
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
|
|
||||||
if "bpy" in locals():
|
if "bpy" in locals():
|
||||||
if not bpy.app.background:
|
if not bpy.app.background:
|
||||||
for name in modules + functions:
|
for name in modules:
|
||||||
module = importlib.import_module(name, __name__)
|
module = importlib.import_module(name, __name__)
|
||||||
importlib.reload(module)
|
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 bpy
|
||||||
import gpu
|
import gpu
|
||||||
from gpu_extras.batch import batch_for_shader
|
from gpu_extras.batch import batch_for_shader
|
||||||
import gpu
|
import blf
|
||||||
from mathutils import bvhtree, Vector
|
from mathutils import bvhtree, Vector
|
||||||
from mathutils.geometry import intersect_point_quad_2d, intersect_point_tri_2d, intersect_tri_tri_2d
|
from mathutils.geometry import intersect_point_quad_2d, intersect_point_tri_2d, intersect_tri_tri_2d
|
||||||
from .constants import PICKERS
|
from ..constants import PICKERS
|
||||||
from .utils import get_operator_from_id
|
from .addon_utils import get_operator_from_id
|
||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import re
|
import re
|
||||||
import json
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
|
@ -383,8 +384,6 @@ class OperatorShape(Shape):
|
||||||
gpu.state.blend_set('NONE')
|
gpu.state.blend_set('NONE')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Picker:
|
class Picker:
|
||||||
def __init__(self, rig, shapes):
|
def __init__(self, rig, shapes):
|
||||||
|
|
||||||
|
@ -475,6 +474,8 @@ class Picker:
|
||||||
|
|
||||||
s.press = False
|
s.press = False
|
||||||
|
|
||||||
|
#bpy.context.area.tag_redraw()
|
||||||
|
|
||||||
|
|
||||||
def tooltip_event(self):
|
def tooltip_event(self):
|
||||||
#print('Tooltip Event', self)
|
#print('Tooltip Event', self)
|
||||||
|
@ -538,9 +539,12 @@ class Picker:
|
||||||
self.mouse = location
|
self.mouse = location
|
||||||
location = self.region.view2d.region_to_view(*location)
|
location = self.region.view2d.region_to_view(*location)
|
||||||
|
|
||||||
for s in self.shapes:
|
self.hover_shape = None
|
||||||
if s.move_event(location):
|
for shape in reversed(self.shapes):
|
||||||
self.hover_shape = s
|
if self.hover_shape:
|
||||||
|
shape.hover = False
|
||||||
|
elif shape.move_event(location):
|
||||||
|
self.hover_shape = shape
|
||||||
|
|
||||||
#if point_inside_rectangle(self.end, bound):
|
#if point_inside_rectangle(self.end, bound):
|
||||||
# over = point_over_shape(self.end,points, edges)
|
# over = point_over_shape(self.end,points, edges)
|
||||||
|
@ -581,86 +585,26 @@ class Picker:
|
||||||
gpu.state.blend_set('NONE')
|
gpu.state.blend_set('NONE')
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
def get_picker_path(rig, start=None):
|
||||||
def draw_callback_view():
|
picker_path = rig.data.get('rig_picker', {}).get('source')
|
||||||
sp = bpy.context.space_data
|
if not picker_path:
|
||||||
|
|
||||||
if not sp or not sp.tree_type == 'RigPickerTree':
|
|
||||||
return
|
return
|
||||||
|
|
||||||
ob = bpy.context.object
|
picker_path = bpy.path.abspath(picker_path, library=rig.data.library, start=start)
|
||||||
if not ob or ob.type !='ARMATURE' or not ob.data.rig_picker.source:
|
|
||||||
|
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
|
return
|
||||||
|
|
||||||
if ob not in PICKERS:
|
if 'picker' in rig.data['rig_picker'].keys():
|
||||||
if 'picker' in ob.data.rig_picker:
|
del rig.data['rig_picker']['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
|
|
|
@ -2,9 +2,65 @@ import bpy
|
||||||
import bmesh
|
import bmesh
|
||||||
from mathutils import Vector, Matrix
|
from mathutils import Vector, Matrix
|
||||||
from bpy_extras import mesh_utils
|
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):
|
def get_picker_datas(objects, canvas, rig):
|
||||||
picker_datas = []
|
picker_datas = []
|
||||||
gamma = 1 / 2.2
|
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 mathutils import Vector, Matrix, Euler
|
||||||
from .constants import PICKERS
|
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
|
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 draw_callback_px
|
||||||
#from .func_bgl import select_bone
|
#from .func_bgl import select_bone
|
||||||
#from .func_picker import *
|
#from core.picker import *
|
||||||
#from .utils import is_over_region
|
#from .utils import is_over_region
|
||||||
import gpu
|
import gpu
|
||||||
from mathutils import Vector
|
from mathutils import Vector
|
||||||
from gpu_extras.batch import batch_for_shader
|
from gpu_extras.batch import batch_for_shader
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import json
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
vertex_shader = '''
|
vertex_shader = '''
|
||||||
|
@ -92,7 +96,7 @@ class RP_OT_box_select(bpy.types.Operator):
|
||||||
bl_idname = "node.rp_box_select"
|
bl_idname = "node.rp_box_select"
|
||||||
bl_label = "Picker 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')
|
color_shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR')
|
||||||
dash_shader = gpu.types.GPUShader(vertex_shader, fragment_shader)
|
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"""
|
"""Tooltip"""
|
||||||
bl_idname = "node.move_bone"
|
bl_idname = "node.picker_transform"
|
||||||
bl_label = "Move Bone in Picker View"
|
bl_label = "Move Bone in Picker View"
|
||||||
|
|
||||||
|
mode : EnumProperty(items=[(m, m.title(), '') for m in ('TRANSLATE', 'ROTATE', 'SCALE')])
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
if not is_picker_space(context):
|
return is_picker_space(context)
|
||||||
return
|
|
||||||
|
|
||||||
def invoke(self, context, event):
|
def execute(self, context):
|
||||||
self.mouse = event.mouse_region_x, event.mouse_region_y
|
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)
|
# def invoke(self, context, event):
|
||||||
return {'RUNNING_MODAL'}
|
# 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
|
# context.window_manager.modal_handler_add(self)
|
||||||
delta_y = (event.mouse_region_y - self.mouse[1]) / 1000
|
# 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():
|
# if event.type=="LEFTMOUSE" and event.value == 'RELEASE':
|
||||||
bone.matrix.translation = matrix.translation + Vector((delta_x, 0, delta_y))
|
# 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 {'RUNNING_MODAL'}
|
||||||
return {'FINISHED'}
|
|
||||||
|
|
||||||
if event.type=="RIGHTMOUSE":
|
|
||||||
for bone, matrix in self.bone_data.items():
|
|
||||||
bone.matrix = matrix
|
|
||||||
|
|
||||||
return {'CANCELLED'}
|
|
||||||
|
|
||||||
return {'RUNNING_MODAL'}
|
|
||||||
|
|
||||||
|
|
||||||
class RP_OT_function_execute(bpy.types.Operator):
|
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
|
bone = picker.hover_shape.bone
|
||||||
hide = picker.hover_shape.hide
|
hide = picker.hover_shape.hide
|
||||||
|
|
||||||
|
|
||||||
if bone:
|
if bone:
|
||||||
for i, l in enumerate(bone.bone.layers):
|
for i, l in enumerate(bone.bone.layers):
|
||||||
if l:
|
if l:
|
||||||
ob.data.layers[i] = hide
|
ob.data.layers[i] = hide
|
||||||
|
|
||||||
print('Bone Layer toogle')
|
|
||||||
|
|
||||||
#if picker.hover_bone:
|
|
||||||
context.region.tag_redraw()
|
context.region.tag_redraw()
|
||||||
|
|
||||||
return {"FINISHED"}
|
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):
|
class RP_OT_pack_picker(bpy.types.Operator):
|
||||||
"""Pack Unpack the picker on the rig"""
|
"""Pack Unpack the picker on the rig"""
|
||||||
bl_idname = "rigpicker.pack_picker"
|
bl_idname = "rigpicker.pack_picker"
|
||||||
|
@ -336,18 +360,20 @@ class RP_OT_pack_picker(bpy.types.Operator):
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
print('Pack Picker')
|
print('Pack Picker')
|
||||||
|
|
||||||
ob = context.object
|
rig = context.object
|
||||||
picker_src = Path(bpy.path.abspath(ob.data.rig_picker.source, library=ob.data.library))
|
picker_path = get_picker_path(rig)
|
||||||
|
|
||||||
if 'picker' in ob.data.rig_picker.keys():
|
if 'picker' in rig.data.rig_picker.keys():
|
||||||
del ob.data.rig_picker['picker']
|
unpack_picker(rig)
|
||||||
|
self.report({"INFO"}, f'The picker is unpacked')
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
if not picker_src.exists():
|
if not picker_path.exists():
|
||||||
self.report({"ERROR"}, f'The path of the picker not exist: {picker_src}')
|
self.report({"ERROR"}, f'The path of the picker not exist: {picker_path}')
|
||||||
return {"CANCELLED"}
|
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"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
@ -383,8 +409,19 @@ class RP_MT_context_menu(bpy.types.Menu):
|
||||||
# Set the menu operators and draw functions
|
# Set the menu operators and draw functions
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
layout = self.layout
|
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'
|
#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")
|
kmi = km.keymap_items.new("rigpicker.toogle_bone_layer", type="LEFTMOUSE", value="DOUBLE_CLICK")
|
||||||
keymaps.append((km, kmi))
|
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))
|
keymaps.append((km, kmi))
|
||||||
|
|
||||||
kmi = km.keymap_items.new("wm.call_menu", type="RIGHTMOUSE", value="PRESS")
|
kmi = km.keymap_items.new("node.picker_transform", type="R", value="PRESS")
|
||||||
kmi.properties.name = "RP_MT_context_menu"
|
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))
|
keymaps.append((km, kmi))
|
||||||
|
|
||||||
kmi = km.keymap_items.new("node.rp_box_select", type="LEFTMOUSE", value="PRESS")
|
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_toogle_bone_layer,
|
||||||
RP_OT_call_operator,
|
RP_OT_call_operator,
|
||||||
RP_MT_context_menu,
|
RP_MT_context_menu,
|
||||||
RP_OT_move_bone,
|
RP_OT_picker_transform,
|
||||||
|
RP_OT_context_menu_picker,
|
||||||
RP_OT_pack_picker
|
RP_OT_pack_picker
|
||||||
#RP_OT_ui_draw
|
#RP_OT_ui_draw
|
||||||
)
|
)
|
|
@ -1,8 +1,8 @@
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
from .func_shape import get_picker_datas
|
from ..core.shape import get_picker_datas
|
||||||
from .utils import is_shape, find_mirror, link_mat_to_object
|
from ..core.addon_utils import is_shape
|
||||||
#import os
|
from ..core.bl_utils import link_mat_to_object, find_mirror
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import json
|
import json
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import bpy
|
import bpy
|
||||||
#import collections
|
#import collections
|
||||||
#import inspect
|
#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
|
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