Add menu and some cleanup
parent
917531f59a
commit
150bae5920
87
area.py
87
area.py
|
@ -1,5 +1,5 @@
|
|||
import bpy
|
||||
from bpy.types import NodeTree, NODE_PT_tools_active, NODE_HT_header
|
||||
from bpy.types import NodeTree, NODE_PT_tools_active, NODE_HT_header, Menu
|
||||
from .constants import PICKERS
|
||||
|
||||
|
||||
|
@ -14,6 +14,69 @@ class RigPickerTree(NodeTree):
|
|||
bl_icon = 'OUTLINER_DATA_ARMATURE'
|
||||
|
||||
|
||||
class RP_MT_picker(Menu):
|
||||
"""Picker"""
|
||||
bl_label = "Picker"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
scn = context.scene
|
||||
|
||||
|
||||
layout.operator('rigpicker.reload_picker', icon='FILE_REFRESH')
|
||||
row = layout.row(align=True)
|
||||
|
||||
# Has at least one picker collection in the scene
|
||||
if not [c.rig_picker.enabled for c in scn.collection.children_recursive]:
|
||||
row.enabled = False
|
||||
row.prop(scn.rig_picker, 'use_pick_bone', text='Auto Bone Assign')
|
||||
|
||||
|
||||
class RP_MT_animation(Menu):
|
||||
"""Picker"""
|
||||
bl_label = "Animation"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
layout.operator("rigpicker.toogle_bone_layer", icon="MOUSE_LMB_DRAG")
|
||||
|
||||
row = layout.row()
|
||||
op = row.operator("node.context_menu_picker", icon="MOUSE_RMB")
|
||||
if not context.active_pose_bone:
|
||||
row.enabled = False
|
||||
|
||||
layout.separator()
|
||||
op = layout.operator("rigpicker.call_operator", text='Select All')
|
||||
op.operator = "pose.select_all"
|
||||
op.arguments = '{"action": "SELECT"}'
|
||||
|
||||
op = layout.operator("rigpicker.call_operator", text='Select All')
|
||||
op.operator = "pose.select_all"
|
||||
op.arguments = '{"action": "DESELECT"}'
|
||||
|
||||
op = layout.operator("rigpicker.call_operator", text='Frame Selected')
|
||||
op.operator = "view3d.view_selected"
|
||||
op.view_3d = True
|
||||
|
||||
layout.separator()
|
||||
layout.operator("rigpicker.call_operator", text='Insert Keyframe').operator="animtoolbox.insert_keyframe"
|
||||
layout.operator("anim.keyframe_delete_v3d", text='Delete Keyframe')
|
||||
|
||||
layout.separator()
|
||||
op = layout.operator("rigpicker.call_operator", text='Move')
|
||||
op.operator="transform.translate"
|
||||
op.invoke = True
|
||||
op.view_3d = True
|
||||
|
||||
layout.operator("node.picker_transform", text='Rotate').mode='ROTATE'
|
||||
layout.operator("node.picker_transform", text='Scale').mode='SCALE'
|
||||
layout.separator()
|
||||
layout.operator("rigpicker.call_operator", text='Reset Bone').operator="animtoolbox.reset_bone"
|
||||
layout.operator("rigpicker.call_operator", text='Clear Location').operator='pose.loc_clear'
|
||||
layout.operator("rigpicker.call_operator", text='Clear Rotation').operator='pose.rot_clear'
|
||||
layout.operator("rigpicker.call_operator", text='Clear Scale').operator='pose.scale_clear'
|
||||
|
||||
def draw_header(self, context):
|
||||
if not context.space_data.tree_type == 'RigPickerTree':
|
||||
self._draw(context)
|
||||
|
@ -23,7 +86,6 @@ def draw_header(self, context):
|
|||
|
||||
layout = self.layout
|
||||
layout.template_header()
|
||||
#layout.separator_spacer()
|
||||
|
||||
if not context.space_data.node_tree:
|
||||
ntree = bpy.data.node_groups.get('.rig_picker')
|
||||
|
@ -32,10 +94,10 @@ def draw_header(self, context):
|
|||
|
||||
context.space_data.node_tree = ntree
|
||||
|
||||
#layout.template_ID(context.space_data, "node_tree", new="node.new_node_tree")
|
||||
#layout.separator_spacer()
|
||||
row = layout.row(align=True)
|
||||
row.menu("RP_MT_picker")
|
||||
row.menu("RP_MT_animation")
|
||||
|
||||
#layout.operator('rigpicker.reload_picker', icon='FILE_REFRESH', text='')
|
||||
ob = context.object
|
||||
if not ob or not ob.type == 'ARMATURE':
|
||||
return
|
||||
|
@ -50,22 +112,17 @@ def draw_header(self, context):
|
|||
layout.prop(scn.rig_picker, 'use_pick_bone', icon='PANEL_CLOSE', text='', emboss=False)
|
||||
|
||||
layout.separator_spacer()
|
||||
layout.label(text=ob.name)
|
||||
row = layout.row()
|
||||
row.enabled = False
|
||||
row.label(text=ob.name)
|
||||
|
||||
layout.separator_spacer()
|
||||
#row.alignment = 'RIGHT'
|
||||
|
||||
row = layout.row(align=True)
|
||||
|
||||
|
||||
|
||||
for i, picker in enumerate(picker_group.pickers):
|
||||
row.operator('rigpicker.fit_picker', text=f'{i+1}').index=i
|
||||
#row.separator_spacer()
|
||||
|
||||
row.operator('rigpicker.fit_picker', text='', icon='FULLSCREEN_ENTER').index = -1
|
||||
|
||||
#layout.prop('rigpicker.reload_picker', icon='FILE_REFRESH', text='')
|
||||
|
||||
|
||||
|
||||
def tools_from_context(context, mode=None):
|
||||
|
@ -92,6 +149,8 @@ def poll(cls, context):
|
|||
|
||||
classes = (
|
||||
RigPickerTree,
|
||||
RP_MT_picker,
|
||||
RP_MT_animation
|
||||
)
|
||||
|
||||
|
||||
|
|
29
constants.py
29
constants.py
|
@ -1,2 +1,29 @@
|
|||
import gpu
|
||||
from pathlib import Path
|
||||
|
||||
PICKERS = {}
|
||||
class LazyDict(dict):
|
||||
def __getitem__(self, k):
|
||||
v = super().__getitem__(k)
|
||||
if callable(v):
|
||||
v = v()
|
||||
super().__setitem__(k, v)
|
||||
return v
|
||||
|
||||
def get(self, k, default=None):
|
||||
if k in self:
|
||||
return self.__getitem__(k)
|
||||
return default
|
||||
|
||||
|
||||
PICKERS = {}
|
||||
|
||||
|
||||
MODULE_DIR = Path(__file__).parent
|
||||
SHADER_DIR = MODULE_DIR / 'shaders'
|
||||
|
||||
SHADERS = LazyDict()
|
||||
|
||||
vertex_shader = Path(SHADER_DIR, "dash_shader.vert").read_text(encoding='utf-8')
|
||||
fragment_shader = Path(SHADER_DIR, "dash_shader.frag").read_text(encoding='utf-8')
|
||||
|
||||
SHADERS['dashed_line'] = lambda : gpu.types.GPUShader(vertex_shader, fragment_shader)
|
||||
|
|
|
@ -1,6 +1,21 @@
|
|||
|
||||
import bpy
|
||||
from .bl_utils import get_mat
|
||||
from .bl_utils import get_mat, get_collection_parents
|
||||
|
||||
|
||||
def get_picker_collection(ob=None):
|
||||
"""Return the picker collection of an object"""
|
||||
|
||||
if not ob:
|
||||
ob = bpy.context.object
|
||||
|
||||
for col in ob.users_collection:
|
||||
if col.rig_picker.enabled:
|
||||
return col
|
||||
|
||||
if picker_col := next((c for c in get_collection_parents(col) if c.rig_picker.enabled), None):
|
||||
return picker_col
|
||||
|
||||
|
||||
def is_shape(ob):
|
||||
scn = bpy.context.scene
|
||||
|
@ -15,6 +30,7 @@ def is_shape(ob):
|
|||
|
||||
return False
|
||||
|
||||
|
||||
def get_object_color(ob):
|
||||
if not ob.data.materials:
|
||||
return
|
||||
|
@ -29,6 +45,7 @@ def get_object_color(ob):
|
|||
|
||||
return emit_node.inputs['Color'].default_value
|
||||
|
||||
|
||||
def get_operator_from_id(idname):
|
||||
if not '.' in idname:
|
||||
return
|
||||
|
|
|
@ -2,6 +2,34 @@
|
|||
import bpy
|
||||
|
||||
|
||||
def get_collection_parents(col, root=None, cols=None):
|
||||
"""Return all direct collection parents
|
||||
|
||||
Args:
|
||||
col (bpy.types.Collection): collection to get parents from
|
||||
root (bpy.types.Collection, optional): collection to search in (recursive) instead of the scene collection.
|
||||
Defaults to None.
|
||||
cols (_type_, optional): for recursivity, store the parent collections.
|
||||
Defaults to None.
|
||||
|
||||
Returns:
|
||||
list[bpy.types.Collection]: a list of direct parents
|
||||
"""
|
||||
|
||||
if cols is None:
|
||||
cols = []
|
||||
|
||||
if root is None:
|
||||
root = bpy.context.scene.collection
|
||||
|
||||
for sub in root.children:
|
||||
if sub == col:
|
||||
cols.append(root)
|
||||
|
||||
if len(sub.children):
|
||||
cols = get_collection_parents(col, root=sub, cols=cols)
|
||||
return cols
|
||||
|
||||
def get_view_3d_override():
|
||||
windows = bpy.context.window_manager.windows
|
||||
areas = [a for w in windows for a in w.screen.areas if a.type == 'VIEW_3D']
|
||||
|
@ -31,6 +59,12 @@ def link_mat_to_object(ob):
|
|||
sl.link = 'OBJECT'
|
||||
sl.material = m
|
||||
|
||||
def eval_attr(ob, name):
|
||||
resolved = ob
|
||||
for o in name.split("."):
|
||||
resolved = getattr(resolved, o)
|
||||
return resolved
|
||||
|
||||
def flip_name(name):
|
||||
if not name:
|
||||
return
|
||||
|
@ -41,6 +75,19 @@ def flip_name(name):
|
|||
else:
|
||||
return bpy.utils.flip_name(name)
|
||||
|
||||
def split_path(path) :
|
||||
try :
|
||||
bone_name = path.split('["')[1].split('"]')[0]
|
||||
except Exception:
|
||||
bone_name = None
|
||||
|
||||
try :
|
||||
prop_name = path.split('["')[2].split('"]')[0]
|
||||
except Exception:
|
||||
prop_name = None
|
||||
|
||||
return bone_name, prop_name
|
||||
|
||||
def hide_layers(args):
|
||||
""" """
|
||||
ob = bpy.context.object
|
||||
|
|
|
@ -232,7 +232,7 @@ class BoneShape(Shape):
|
|||
if self.hide:
|
||||
return self.draw_hided()
|
||||
|
||||
elif self.bone.custom_shape_scale_xyz.length < 0.00001:
|
||||
elif self.bone and self.bone.custom_shape_scale_xyz.length < 0.00001:
|
||||
return self.draw_zero_scaled()
|
||||
|
||||
# Draw Fill
|
||||
|
@ -258,8 +258,9 @@ class BoneShape(Shape):
|
|||
def assign_bone_event(self):
|
||||
|
||||
#print('assign_bone_event', self)
|
||||
|
||||
scn = bpy.context.scene
|
||||
rig = scn.rig_picker.rig
|
||||
rig = self.picker.rig
|
||||
source_object = scn.objects.get(self.source_name)
|
||||
if not source_object:
|
||||
print(f'Source object {self.source_name} not found')
|
||||
|
@ -270,6 +271,9 @@ class BoneShape(Shape):
|
|||
print('You need to have an active bone')
|
||||
return
|
||||
|
||||
#print(active_bone, source_object)
|
||||
#print(rig)
|
||||
source_object.name = rig.data.bones.active.name
|
||||
source_object.rig_picker.name = rig.data.bones.active.name
|
||||
|
||||
def release_event(self, mode='SET'):
|
||||
|
@ -452,10 +456,10 @@ class Picker:
|
|||
|
||||
def assign_bone_event(self):
|
||||
for shape in self.shapes:
|
||||
if shape.type=='bone' and shape.hover:
|
||||
if shape.type == 'bone' and shape.hover:
|
||||
shape.assign_bone_event()
|
||||
|
||||
bpy.ops.rigpicker.save_picker(index=self.index)
|
||||
bpy.ops._rigpicker.save_picker(index=self.index)
|
||||
|
||||
def press_event(self, mode='SET'):
|
||||
for shape in self.shapes:
|
||||
|
@ -626,11 +630,11 @@ class PickerGroup:
|
|||
if self.timer:
|
||||
self.timer.cancel()
|
||||
|
||||
self.timer = threading.Timer(0.5, self.tooltip_event)
|
||||
self.timer = threading.Timer(0.4, self.tooltip_event)
|
||||
self.timer.start()
|
||||
|
||||
def press_event(self, mode='SET'):
|
||||
self.clear_tooltip()
|
||||
#self.clear_tooltip()
|
||||
|
||||
for picker in self.pickers:
|
||||
if picker.under_mouse(self.location):
|
||||
|
@ -678,7 +682,7 @@ class PickerGroup:
|
|||
#print(self.hover_shape, self.hover_shape.type)
|
||||
|
||||
if self.hover_shape and self.hover_shape.type != 'display':
|
||||
if self.hover_shape.type == 'bone':
|
||||
if self.hover_shape.type == 'bone' and self.hover_shape.bone:
|
||||
self.tooltip = self.hover_shape.bone.name
|
||||
else:
|
||||
self.tooltip = self.hover_shape.tooltip
|
||||
|
@ -706,28 +710,47 @@ class PickerGroup:
|
|||
return center
|
||||
|
||||
|
||||
def get_picker_path(rig, start=None):
|
||||
picker_path = rig.data.get('rig_picker', {}).get('source')
|
||||
if not picker_path:
|
||||
return
|
||||
def load_picker_data(rig):
|
||||
if 'pickers' in rig.data.rig_picker:
|
||||
picker_datas = [p.to_dict() for p in rig.rig_picker['pickers']]
|
||||
else:
|
||||
picker_datas = []
|
||||
|
||||
picker_path = bpy.path.abspath(picker_path, library=rig.data.library, start=start)
|
||||
for picker in rig.data.rig_picker.sources:
|
||||
picker_path = Path(bpy.path.abspath(picker.source, library=rig.data.library))
|
||||
|
||||
if not picker_path.exists():
|
||||
print(f'Picker path not exists: {picker_path.resolve()}')
|
||||
continue
|
||||
|
||||
print('Load picker from', picker_path.resolve())
|
||||
picker_data = json.loads(picker_path.read_text(encoding='utf-8'))
|
||||
picker_datas.append(picker_data)
|
||||
|
||||
PICKERS[rig] = PickerGroup(rig, picker_datas)
|
||||
|
||||
|
||||
def get_picker_path(rig, source, start=None):
|
||||
picker_path = bpy.path.abspath(source, library=rig.data.library, start=start)
|
||||
return Path(os.path.abspath(picker_path))
|
||||
|
||||
|
||||
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'] = {}
|
||||
pickers = []
|
||||
for picker_source in rig.data.get('rig_picker', {}).get('sources', {}):
|
||||
picker_path = get_picker_path(rig, picker_source['source'], start)
|
||||
if not picker_path.exists():
|
||||
print(f'{picker_path} not exists')
|
||||
continue
|
||||
picker_data = json.loads(picker_path.read_text(encoding='utf-8'))
|
||||
pickers.append(picker_data)
|
||||
|
||||
rig.data['rig_picker']['picker'] = json.loads(picker_path.read_text(encoding='utf-8'))
|
||||
rig.data['rig_picker']['pickers'] = pickers
|
||||
|
||||
|
||||
def unpack_picker(rig):
|
||||
if 'rig_picker' not in rig.data.keys():
|
||||
return
|
||||
|
||||
if 'picker' in rig.data['rig_picker'].keys():
|
||||
del rig.data['rig_picker']['picker']
|
||||
if 'pickers' in rig.data['rig_picker'].keys():
|
||||
del rig.data['rig_picker']['pickers']
|
174
core/shape.py
174
core/shape.py
|
@ -61,99 +61,119 @@ def contour_loops(bm, vert_index=0, loops=None, vert_indices=None):
|
|||
|
||||
return loops
|
||||
|
||||
def get_picker_data(objects, canvas, rig):
|
||||
picker_datas = []
|
||||
gamma = 1 / 2.2
|
||||
|
||||
if canvas.type =='CURVE':
|
||||
def get_shape_data(ob, matrix=None, depsgraph=None):
|
||||
if not matrix:
|
||||
matrix = Matrix()
|
||||
if not depsgraph:
|
||||
depsgraph = bpy.context.evaluated_depsgraph_get()
|
||||
|
||||
gamma = 1 / 2.2
|
||||
eval_ob = ob.evaluated_get(depsgraph)
|
||||
mesh = eval_ob.to_mesh()
|
||||
|
||||
bm = bmesh.new()
|
||||
bm.from_mesh(mesh)
|
||||
|
||||
|
||||
bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.002)
|
||||
bmesh.ops.dissolve_limit(bm, angle_limit=0.001745, verts=bm.verts, edges=bm.edges)
|
||||
|
||||
bmesh.ops.connect_verts_concave(bm, faces=bm.faces)
|
||||
bmesh.ops.triangulate(bm, faces=bm.faces)
|
||||
|
||||
edges = [[v.index for v in e.verts] for e in bm.edges if len(e.link_faces)<=1]
|
||||
bm.to_mesh(mesh)
|
||||
mesh.update()
|
||||
bm.clear()
|
||||
|
||||
points = []
|
||||
polygons = []
|
||||
depths = []
|
||||
|
||||
for vert in mesh.vertices:
|
||||
co = matrix @ (ob.matrix_world @ Vector(vert.co))
|
||||
points.append([round(co[0]), round(co[1])])
|
||||
|
||||
depths += [co[2]]
|
||||
|
||||
if depths:
|
||||
depth = max(depths)
|
||||
else:
|
||||
print(f'{ob.name} has no vertices')
|
||||
depth = 0
|
||||
|
||||
for face in mesh.polygons:
|
||||
polygons.append([v for v in face.vertices])
|
||||
|
||||
color = get_object_color(ob)
|
||||
if color:
|
||||
color = [round(pow(c, gamma), 4) for c in color]
|
||||
else:
|
||||
color = [0.5, 0.5, 0.5]
|
||||
|
||||
shape = {
|
||||
'source_name': ob.name,
|
||||
'tooltip': ob.rig_picker.name,
|
||||
'depth': depth,
|
||||
'points': points,
|
||||
'polygons': polygons,
|
||||
'edges': edges,
|
||||
'color': color,
|
||||
'type': ob.rig_picker.shape_type
|
||||
}
|
||||
|
||||
if shape['type'] =='OPERATOR':
|
||||
shape['operator'] = ob.rig_picker.operator
|
||||
shape['shortcut'] = ob.rig_picker.shortcut
|
||||
|
||||
elif shape['type'] =='BONE':
|
||||
shape['bone'] = ob.rig_picker.name
|
||||
|
||||
eval_ob.to_mesh_clear()
|
||||
|
||||
return shape
|
||||
|
||||
|
||||
def get_picker_data(collection):
|
||||
picker_data = []
|
||||
|
||||
depsgraph = bpy.context.evaluated_depsgraph_get()
|
||||
canvas = collection.rig_picker.canvas
|
||||
|
||||
if canvas.type == 'CURVE':
|
||||
canvas_points = canvas.data.splines[0].points
|
||||
else:
|
||||
canvas_points = canvas.data.vertices
|
||||
|
||||
canvas_coords = [canvas.matrix_world@Vector((p.co)) for p in canvas_points]
|
||||
|
||||
height = max(canvas_coords).y - min(canvas_coords).y
|
||||
width = max(canvas_coords).x - min(canvas_coords).x
|
||||
height = abs(max(canvas_coords).y - min(canvas_coords).y)
|
||||
width = abs(max(canvas_coords).x - min(canvas_coords).x)
|
||||
|
||||
canvas_center = sum(canvas_coords, Vector()) / len(canvas_coords)
|
||||
canvas_scale_fac = 4096 / max(height, width)# Reference height for the canvas
|
||||
center = sum(canvas_coords, Vector()) / len(canvas_coords)
|
||||
scale = 2048 / max(height, width)# Reference height for the canvas
|
||||
|
||||
objects.append(canvas)
|
||||
|
||||
dg = bpy.context.evaluated_depsgraph_get()
|
||||
matrix = Matrix.Translation(-center)
|
||||
matrix = Matrix.Scale(scale, 4) @ matrix
|
||||
|
||||
#sorted by their z axes
|
||||
for ob in sorted(objects, key=lambda x: bound_box_center(x)[2]):
|
||||
for ob in collection.all_objects:
|
||||
if ob.instance_collection:
|
||||
for shape in ob.instance_collection.all_objects:
|
||||
picker_data.append(get_shape_data(shape, matrix=matrix@ob.matrix_world, depsgraph=depsgraph))
|
||||
|
||||
elif ob.type in ('MESH', 'CURVE', 'FONT'):
|
||||
picker_data.append(get_shape_data(ob, matrix=matrix, depsgraph=depsgraph))
|
||||
|
||||
print('Storing shape', ob.name)
|
||||
mesh = bpy.data.meshes.new_from_object(ob.evaluated_get(dg))
|
||||
|
||||
bm = bmesh.new()
|
||||
bm.from_mesh(mesh)
|
||||
bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.002)
|
||||
bmesh.ops.dissolve_limit(bm, angle_limit=0.001745, verts=bm.verts, edges=bm.edges)
|
||||
|
||||
bmesh.ops.connect_verts_concave(bm, faces=bm.faces)
|
||||
bmesh.ops.triangulate(bm, faces=bm.faces)
|
||||
|
||||
#bm_loops = contour_loops(bm)
|
||||
#loops = [[l.index for l in loop] for loop in bm_loops]
|
||||
loops = []
|
||||
|
||||
edges = [[v.index for v in e.verts] for e in bm.edges if len(e.link_faces)<=1]
|
||||
|
||||
bm.to_mesh(mesh)
|
||||
mesh.update()
|
||||
bm.clear()
|
||||
|
||||
points = []
|
||||
#edges = []
|
||||
polygons = []
|
||||
|
||||
for p in mesh.vertices:
|
||||
point = ob.matrix_world@Vector((p.co))
|
||||
point = (point - canvas_center) * canvas_scale_fac
|
||||
points.append([round(point[0]), round(point[1])])
|
||||
|
||||
for f in mesh.polygons:
|
||||
polygons.append([v for v in f.vertices])
|
||||
|
||||
#for e in mesh.edges:
|
||||
#
|
||||
# edges.append([v for v in e.vertices])
|
||||
|
||||
color = get_object_color(ob)
|
||||
if color:
|
||||
color = [round(pow(c, gamma), 4) for c in color]
|
||||
else:
|
||||
color = [0.5, 0.5, 0.5]
|
||||
|
||||
shape = {
|
||||
'source_name': ob.name,
|
||||
'tooltip': ob.rig_picker.name,
|
||||
'points': points,
|
||||
'polygons': polygons,
|
||||
'edges': edges,
|
||||
'loops': loops,
|
||||
'color': color,
|
||||
'type': 'CANVAS' if ob == canvas else ob.rig_picker.shape_type
|
||||
}
|
||||
|
||||
if shape['type'] =='OPERATOR':
|
||||
shape['operator'] = ob.rig_picker.operator
|
||||
#if ob.rig_picker.arguments:
|
||||
#shape['arguments'] = ob.rig_picker.arguments
|
||||
#if ob.rig_picker.shortcut:
|
||||
shape['shortcut'] = ob.rig_picker.shortcut
|
||||
|
||||
elif shape['type'] =='BONE':
|
||||
shape['bone'] = ob.rig_picker.name
|
||||
|
||||
picker_datas.append(shape)
|
||||
#print(f'{ob.name} of type {ob.type} not supported')
|
||||
continue
|
||||
|
||||
|
||||
picker_data.sort(key=lambda x : x['depth'])
|
||||
|
||||
#print(picker_datas)
|
||||
|
||||
return picker_datas
|
||||
return picker_data
|
||||
#rig.data.rig_picker['shapes'] = picker_datas
|
||||
|
|
|
@ -5,7 +5,7 @@ from gpu_extras.batch import batch_for_shader
|
|||
import blf
|
||||
from pathlib import Path
|
||||
from .constants import PICKERS
|
||||
from .core.picker import Picker, PickerGroup
|
||||
from .core.picker import Picker, PickerGroup, load_picker_data
|
||||
import json
|
||||
|
||||
|
||||
|
@ -38,55 +38,27 @@ def draw_rect_2d(position, width, height, color):
|
|||
|
||||
|
||||
def draw_callback_view():
|
||||
sp = bpy.context.space_data
|
||||
space_data = bpy.context.space_data
|
||||
|
||||
if not sp or not sp.tree_type == 'RigPickerTree':
|
||||
if not space_data or not space_data.tree_type == 'RigPickerTree':
|
||||
return
|
||||
|
||||
if not sp.pin:
|
||||
sp.pin = True
|
||||
sp.show_region_ui = False
|
||||
# Use the pin to know if this is the first time this picker window in created to hide the n panel
|
||||
if not space_data.pin:
|
||||
space_data.pin = True
|
||||
space_data.show_region_ui = False
|
||||
|
||||
ob = bpy.context.object
|
||||
if not ob or ob.type !='ARMATURE' or not ob.data.rig_picker.sources:
|
||||
return
|
||||
|
||||
if ob not in PICKERS:
|
||||
if 'pickers' in ob.data.rig_picker:
|
||||
picker_datas = [s.to_dict() for s in ob.data.rig_picker['pickers']]
|
||||
else:
|
||||
picker_datas = []
|
||||
for picker in ob.data.rig_picker.sources:
|
||||
picker_path = Path(bpy.path.abspath(picker.source, library=ob.data.library))
|
||||
load_picker_data(ob)
|
||||
|
||||
if not picker_path.exists():
|
||||
print(f'Picker path not exists: {picker_path.resolve()}')
|
||||
continue
|
||||
|
||||
print('Load picker from', picker_path.resolve())
|
||||
picker_data = json.loads(picker_path.read_text(encoding='utf-8'))
|
||||
picker_datas.append(picker_data)
|
||||
#shapes = [s.to_dict() for s in ob.data.rig_picker['shapes']]
|
||||
|
||||
PICKERS[ob] = PickerGroup(ob, picker_datas)#[Picker(ob, shapes=picker_data) for picker_data in picker_datas]
|
||||
|
||||
# if 'picker' in ob.data.rig_picker:
|
||||
# picker_datas = [s.to_dict() for s in ob.data.rig_picker['picker']]
|
||||
# else:
|
||||
# picker_path = Path(bpy.path.abspath(ob.data.rig_picker.source, library=ob.data.library))
|
||||
|
||||
# if not picker_path.exists():
|
||||
# print(f'Picker path not exists: {picker_path.resolve()}')
|
||||
# return
|
||||
|
||||
# print('Load picker from', picker_path.resolve())
|
||||
# picker_datas = json.loads(picker_path.read_text())
|
||||
# #shapes = [s.to_dict() for s in ob.data.rig_picker['shapes']]
|
||||
# PICKERS[ob] = Picker(ob, shapes=picker_datas)
|
||||
|
||||
picker_group = PICKERS.get(ob)
|
||||
picker_group = PICKERS[ob]
|
||||
picker_group.draw()
|
||||
|
||||
|
||||
def draw_callback_px():
|
||||
sp = bpy.context.space_data
|
||||
if not sp.tree_type == 'RigPickerTree':
|
||||
|
@ -100,23 +72,21 @@ def draw_callback_px():
|
|||
|
||||
region = bpy.context.region
|
||||
text = picker_group.tooltip
|
||||
mouse = picker_group.tooltip_mouse
|
||||
ui_scale = bpy.context.preferences.system.ui_scale
|
||||
|
||||
#print('Draw text', text)
|
||||
font_id = 0
|
||||
blf.size(font_id, int(13 * ui_scale))
|
||||
|
||||
margins = [12, 4]
|
||||
margins = [12, 5]
|
||||
text_size = blf.dimensions(font_id, text)
|
||||
#text_pos = (mouse[0] - text_size[0]*0.33, mouse[1] - (34*ui_scale))
|
||||
text_pos = (region.width - text_size[0]-margins[0], margins[1])
|
||||
|
||||
bg_pos = (text_pos[0] - margins[0], text_pos[1] - margins[1]-1)
|
||||
bg_size = (text_size[0] + 2*margins[0], text_size[1] + 2*margins[1])
|
||||
|
||||
gpu.state.blend_set('ALPHA')
|
||||
draw_rect_2d(bg_pos, *bg_size, (0.1, 0.1, 0.1, 0.75))
|
||||
draw_rect_2d(bg_pos, *bg_size, (0.1, 0.1, 0.1, 0.66))
|
||||
gpu.state.blend_set('NONE')
|
||||
|
||||
|
||||
|
@ -126,7 +96,7 @@ def draw_callback_px():
|
|||
|
||||
# BLF drawing routine
|
||||
blf.position(font_id, round(text_pos[0]), round(text_pos[1]), 0)
|
||||
blf.color(font_id, 1, 1, 1, 1)
|
||||
blf.color(font_id, 0.85, 0.85, 0.85, 1)
|
||||
|
||||
blf.shadow(font_id , 5, 0.0, 0.0, 0.0, 1)
|
||||
blf.shadow_offset(font_id, 2, -2)
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import bpy
|
||||
from bpy.props import EnumProperty, IntProperty, BoolProperty
|
||||
from ..constants import PICKERS
|
||||
from ..core.bl_utils import get_view_3d_override
|
||||
from bpy.props import EnumProperty, IntProperty, BoolProperty, StringProperty
|
||||
from bpy.types import Operator, Menu
|
||||
from ..constants import PICKERS, SHADERS
|
||||
from ..core.addon_utils import get_picker_collection
|
||||
from ..core.bl_utils import get_view_3d_override, split_path, eval_attr
|
||||
from ..core.geometry_utils import bounding_rect
|
||||
from ..core.picker import get_picker_path, pack_picker, unpack_picker
|
||||
from ..core.picker import get_picker_path, pack_picker, unpack_picker, load_picker_data
|
||||
#from .func_bgl import draw_callback_px
|
||||
#from .func_bgl import select_bone
|
||||
#from core.picker import *
|
||||
|
@ -16,95 +18,28 @@ import json
|
|||
import os
|
||||
|
||||
|
||||
vertex_shader = '''
|
||||
layout (location = 0) in vec2 pos;
|
||||
|
||||
flat out vec2 startPos;
|
||||
out vec2 vertPos;
|
||||
|
||||
uniform mat4 viewMatrix;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 outPos = viewMatrix * vec4(pos.x, pos.y, 0.0, 1.0);
|
||||
gl_Position = outPos;
|
||||
vertPos = pos.xy / outPos.w;
|
||||
startPos = vertPos;
|
||||
}
|
||||
|
||||
'''
|
||||
|
||||
fragment_shader = '''
|
||||
flat in vec2 startPos;
|
||||
in vec2 vertPos;
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
uniform vec4 color;
|
||||
uniform float dashSize;
|
||||
uniform float gapSize;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec2 dir = (vertPos.xy - startPos.xy);
|
||||
float dist = length(dir);
|
||||
|
||||
if (fract(dist / (dashSize + gapSize)) > dashSize/(dashSize + gapSize))
|
||||
discard;
|
||||
fragColor = color;
|
||||
}
|
||||
'''
|
||||
|
||||
|
||||
|
||||
|
||||
def draw_callback(self):
|
||||
#print('draw callback border')
|
||||
if not self.draw_border:
|
||||
return
|
||||
|
||||
gpu.state.blend_set('ALPHA')
|
||||
|
||||
#print('DRAW BORDER')
|
||||
|
||||
self.color_shader.bind()
|
||||
self.color_shader.uniform_float("color", self.bg_color)
|
||||
self.bg_batch.draw(self.color_shader)
|
||||
|
||||
self.dash_shader.bind()
|
||||
matrix = gpu.matrix.get_projection_matrix()
|
||||
|
||||
self.dash_shader.uniform_float("color", self.border_color)
|
||||
self.dash_shader.uniform_float("viewMatrix", matrix)
|
||||
self.dash_shader.uniform_float("dashSize", 5)
|
||||
self.dash_shader.uniform_float("gapSize", 4)
|
||||
|
||||
self.contour_batch.draw(self.dash_shader)
|
||||
|
||||
gpu.state.blend_set('NONE')
|
||||
|
||||
|
||||
def is_picker_space(context):
|
||||
sp = context.space_data
|
||||
if sp and (sp.type == 'NODE_EDITOR' and sp.tree_type == 'RigPickerTree'):
|
||||
def is_picker_space(space_data=None):
|
||||
if not space_data:
|
||||
space_data = bpy.context.space_data
|
||||
if space_data and (space_data.type == 'NODE_EDITOR' and space_data.tree_type == 'RigPickerTree'):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
class RP_OT_box_select(bpy.types.Operator):
|
||||
"""Tooltip"""
|
||||
class RP_OT_box_select(Operator):
|
||||
"""Box Select bones in the picker view"""
|
||||
bl_idname = "node.rp_box_select"
|
||||
bl_label = "Picker Box Select"
|
||||
|
||||
mode: EnumProperty(items=[(i, i.title(), '') for i in ('SET', 'EXTEND', 'SUBSTRACT')])
|
||||
|
||||
color_shader = gpu.shader.from_builtin('UNIFORM_COLOR')
|
||||
dash_shader = gpu.types.GPUShader(vertex_shader, fragment_shader)
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if not is_picker_space(context):
|
||||
if not is_picker_space(context.space_data):
|
||||
return
|
||||
|
||||
ob = context.object
|
||||
|
@ -120,6 +55,31 @@ class RP_OT_box_select(bpy.types.Operator):
|
|||
return 'SET'
|
||||
'''
|
||||
|
||||
def draw_callback(self):
|
||||
#print('draw callback border')
|
||||
if not self.draw_border:
|
||||
return
|
||||
|
||||
gpu.state.blend_set('ALPHA')
|
||||
|
||||
#print('DRAW BORDER')
|
||||
|
||||
self.color_shader.bind()
|
||||
self.color_shader.uniform_float("color", self.bg_color)
|
||||
self.bg_batch.draw(self.color_shader)
|
||||
|
||||
self.dash_shader.bind()
|
||||
matrix = gpu.matrix.get_projection_matrix()
|
||||
|
||||
self.dash_shader.uniform_float("color", self.border_color)
|
||||
self.dash_shader.uniform_float("viewMatrix", matrix)
|
||||
self.dash_shader.uniform_float("dashSize", 5)
|
||||
self.dash_shader.uniform_float("gapSize", 4)
|
||||
|
||||
self.contour_batch.draw(self.dash_shader)
|
||||
|
||||
gpu.state.blend_set('NONE')
|
||||
|
||||
def invoke(self, context, event):
|
||||
#print(f'invoke: type={event.type}, value={event.value}, ctrl={event.ctrl}, shift={event.shift}, alt={event.alt}')
|
||||
|
||||
|
@ -140,7 +100,9 @@ class RP_OT_box_select(bpy.types.Operator):
|
|||
self.bg_color = [1, 1, 1, 0.05]
|
||||
|
||||
#args = (self, context)
|
||||
self._handle = bpy.types.SpaceNodeEditor.draw_handler_add(draw_callback, (self,), 'WINDOW', 'POST_PIXEL')
|
||||
self.color_shader = gpu.shader.from_builtin('UNIFORM_COLOR')
|
||||
self.dash_shader = SHADERS['dashed_line']
|
||||
self._handle = bpy.types.SpaceNodeEditor.draw_handler_add(self.draw_callback, (), 'WINDOW', 'POST_PIXEL')
|
||||
|
||||
context.window_manager.modal_handler_add(self)
|
||||
|
||||
|
@ -163,12 +125,12 @@ class RP_OT_box_select(bpy.types.Operator):
|
|||
self.region.tag_redraw()
|
||||
|
||||
if event.value == 'RELEASE':
|
||||
return self.release_event()
|
||||
return self.release_event(context)
|
||||
|
||||
return {'RUNNING_MODAL'}
|
||||
|
||||
def release_event(self):
|
||||
scn = bpy.context.scene
|
||||
def release_event(self, context):
|
||||
scn = context.scene
|
||||
|
||||
if scn.rig_picker.use_pick_bone:
|
||||
self.picker.assign_bone_event()
|
||||
|
@ -181,173 +143,246 @@ class RP_OT_box_select(bpy.types.Operator):
|
|||
|
||||
bpy.ops.ed.undo_push(message="Box Select")
|
||||
|
||||
return self.exit()
|
||||
|
||||
def exit(self):
|
||||
#print('Border Select Finished')
|
||||
return self.exit(context)
|
||||
|
||||
def exit(self, context):
|
||||
bpy.types.SpaceNodeEditor.draw_handler_remove(self._handle, 'WINDOW')
|
||||
self.region.tag_redraw()
|
||||
context.region.tag_redraw()
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
|
||||
class RP_OT_picker_transform(bpy.types.Operator):
|
||||
"""Tooltip"""
|
||||
class RP_OT_picker_transform(Operator):
|
||||
"""Transform Bones in the picker view"""
|
||||
bl_idname = "node.picker_transform"
|
||||
bl_label = "Move Bone in Picker View"
|
||||
bl_label = "Transform Bone in Picker View"
|
||||
|
||||
mode : EnumProperty(items=[(m, m.title(), '') for m in ('TRANSLATE', 'ROTATE', 'SCALE')])
|
||||
mode : EnumProperty(items=[(m, m.title(), '') for m in ('ROTATE', 'SCALE')])
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return is_picker_space(context)
|
||||
return context.selected_pose_bones and is_picker_space(context.space_data)
|
||||
|
||||
'''
|
||||
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")
|
||||
def draw_callback(self):
|
||||
gpu.state.blend_set('ALPHA')
|
||||
gpu.state.line_width_set(2)
|
||||
|
||||
return {"FINISHED"}
|
||||
self.dash_shader.bind()
|
||||
matrix = gpu.matrix.get_projection_matrix()
|
||||
|
||||
self.dash_shader.uniform_float("color", [0, 0, 0, 1])
|
||||
self.dash_shader.uniform_float("viewMatrix", matrix)
|
||||
self.dash_shader.uniform_float("dashSize", 5)
|
||||
self.dash_shader.uniform_float("gapSize", 4)
|
||||
|
||||
self.batch = batch_for_shader(self.dash_shader, 'LINE_LOOP', {"pos": [self.view_center, self.mouse]})
|
||||
self.batch.draw(self.dash_shader)
|
||||
|
||||
gpu.state.line_width_set(1)
|
||||
gpu.state.blend_set('NONE')
|
||||
'''
|
||||
|
||||
def invoke(self, context, event):
|
||||
self.override = get_view_3d_override()
|
||||
|
||||
if self.mode == 'TRANSLATE':
|
||||
with context.temp_override(**self.override):
|
||||
if event.alt:
|
||||
bpy.ops.pose.loc_clear()
|
||||
else:
|
||||
bpy.ops.transform.translate("INVOKE_DEFAULT")
|
||||
return {"FINISHED"}
|
||||
|
||||
elif self.mode == 'ROTATE' and event.alt:
|
||||
with context.temp_override(**self.override):
|
||||
bpy.ops.pose.rot_clear()
|
||||
return {"FINISHED"}
|
||||
|
||||
elif self.mode == 'SCALE' and event.alt:
|
||||
with context.temp_override(**self.override):
|
||||
bpy.ops.pose.scale_clear()
|
||||
return {"FINISHED"}
|
||||
|
||||
self.mouse = event.mouse_region_x, event.mouse_region_y
|
||||
|
||||
self.center = context.active_pose_bone.matrix.translation.copy()
|
||||
self.view_center = Vector((int(context.region.width / 2), int(context.region.height / 2)))
|
||||
self.mouse_start = Vector((event.mouse_region_x, event.mouse_region_y))
|
||||
self.mouse = Vector((0, 0))
|
||||
|
||||
transform_type = context.scene.tool_settings.transform_pivot_point
|
||||
self.center = context.active_pose_bone.matrix.to_translation()
|
||||
if transform_type == 'MEDIAN_POINT':
|
||||
origins = [b.matrix.to_translation() for b in context.selected_pose_bones]
|
||||
self.center = sum(origins, Vector()) / len(context.selected_pose_bones)
|
||||
# self.bone_data = {}
|
||||
# for bone in context.selected_pose_bones:
|
||||
# self.bone_data[bone] = bone.matrix.copy()
|
||||
self.bone_data = {b: b.matrix.copy() for b in context.selected_pose_bones}
|
||||
|
||||
space = self.override['area'].spaces.active
|
||||
|
||||
view_matrix = space.region_3d.view_matrix
|
||||
if space.region_3d.view_perspective == 'CAMERA':
|
||||
view_matrix = context.scene.camera.matrix_world
|
||||
|
||||
context.window.cursor_modal_set('MOVE_X')
|
||||
self.view_vector = Vector((0, 0, 1))
|
||||
if self.mode == 'ROTATE':
|
||||
self.view_vector.rotate(view_matrix)
|
||||
|
||||
|
||||
elif self.mode == 'SCALE':
|
||||
self.view_vector = Vector((0, 0, 0))
|
||||
|
||||
self.transform_type = "VIEW"
|
||||
self.transform_types = ["VIEW", 'GLOBAL', 'LOCAL']
|
||||
if context.scene.transform_orientation_slots[0].type == 'LOCAL':
|
||||
self.transform_types = ["VIEW", 'LOCAL', 'GLOBAL']
|
||||
|
||||
self.transform_orientation = context.scene.transform_orientation_slots[0].type
|
||||
|
||||
#self.dash_shader = SHADERS['dashed_line']
|
||||
|
||||
|
||||
#self._handle = bpy.types.SpaceNodeEditor.draw_handler_add(self.draw_callback, (), 'WINDOW', 'POST_PIXEL')
|
||||
context.window_manager.modal_handler_add(self)
|
||||
|
||||
return {'RUNNING_MODAL'}
|
||||
|
||||
def change_transform_type(self):
|
||||
indices = {0: 1, 1:2, 2:1}
|
||||
index = self.transform_types.index(self.transform_type)
|
||||
new_index = indices[index]
|
||||
self.transform_type = self.transform_types[new_index]
|
||||
|
||||
def release_event(self, context):
|
||||
scn = bpy.context.scene
|
||||
|
||||
# Insert keyframe
|
||||
#bpy.ops.ed.undo_push(message="Transform")
|
||||
if scn.tool_settings.use_keyframe_insert_auto:
|
||||
try:
|
||||
bpy.ops.animtoolbox.insert_keyframe()
|
||||
except Exception:
|
||||
self.report({"WARNING"}, 'You need animtoolbox addon for inserting keyframe')
|
||||
|
||||
self.exit(context)
|
||||
|
||||
def exit(self, context):
|
||||
#bpy.types.SpaceNodeEditor.draw_handler_remove(self._handle, 'WINDOW')
|
||||
context.window.cursor_modal_restore()
|
||||
#context.region.tag_redraw()
|
||||
|
||||
def modal(self, context, event):
|
||||
|
||||
delta_x = (event.mouse_region_x - self.mouse[0]) / 100
|
||||
#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))
|
||||
center = context.active_pose_bone.tail
|
||||
|
||||
scn = context.scene
|
||||
cam = scn.camera
|
||||
self.mouse = Vector((event.mouse_region_x, event.mouse_region_y))
|
||||
|
||||
if self.mode == 'ROTATE':
|
||||
delta = (event.mouse_region_x - self.mouse_start[0]) / 100
|
||||
elif self.mode == 'SCALE':
|
||||
delta = 1 + (event.mouse_region_x - self.mouse_start[0]) / 100
|
||||
#delta = (self.mouse - self.view_center).length / (self.mouse_start - self.view_center).length
|
||||
|
||||
|
||||
transform_type = self.transform_type
|
||||
#self.batch = batch_for_shader(self.dash_shader, 'LINE_STRIP', {"pos": [self.mouse_start, self.mouse]})
|
||||
#context.area.tag_redraw()
|
||||
|
||||
|
||||
#rotation_axis = cam.matrix_world.to_3x3().transposed().col[2]
|
||||
#rotation_matrix = Matrix.Rotation(delta_x, 4, Vector((0, 0, -1)) @ cam.matrix_world)
|
||||
view_vec = Vector((0, 0, 1))
|
||||
view_vec.rotate(cam.matrix_world)
|
||||
rotation_matrix = Matrix.Rotation(delta_x, 4, view_vec)
|
||||
|
||||
#rot_mat = Matrix.Rotation(delta_x, 4, "Z")
|
||||
#rot_mat = scn.camera.matrix_world.inverted() @ rot_mat
|
||||
#rot_mat = Matrix.LocRotScale(center, Euler((0, 0, delta_x)), (1, 1, 1))
|
||||
|
||||
if event.type == "MOUSEMOVE":
|
||||
for bone, matrix in self.bone_data.items():
|
||||
#center =
|
||||
|
||||
mat = matrix.copy()
|
||||
mat.translation -= self.center
|
||||
mat = rotation_matrix @ mat
|
||||
mat.translation += self.center
|
||||
|
||||
bone.matrix = mat
|
||||
#rotation_matrix.translation = matrix.translation
|
||||
|
||||
#mat = matrix.copy().to_3x3()
|
||||
#mat.rotate(rotation_matrix)
|
||||
#mat = mat.to_4x4()
|
||||
#mat.translation = matrix.translation
|
||||
|
||||
#bone.matrix = mat
|
||||
|
||||
#bone.matrix = matrix.inverted() @ (rotation_matrix @ bone_mat)
|
||||
#bone.matrix.translation = 0, 0, 0
|
||||
#bone.matrix = rotation_matrix @ matrix
|
||||
#bone.matrix = matrix @ (matrix.inverted() @ rotation_matrix)
|
||||
#bone.matrix.translation = matrix.translation
|
||||
|
||||
#print('Rotate', delta_x)
|
||||
#with context.temp_override(**get_view_3d_override()):
|
||||
#for bone, matrix in self.bone_data.items():
|
||||
# bone.rotation_euler.x = delta_x
|
||||
#bpy.ops.transform.rotate(value=delta_x)
|
||||
|
||||
#print(delta_x, delta_y)
|
||||
|
||||
if event.type=="LEFTMOUSE" and event.value == 'RELEASE':
|
||||
#print(event.type, event.value)
|
||||
if self.mode == 'ROTATE' and event.type == "R" and event.value == 'PRESS':
|
||||
with context.temp_override(**self.override):
|
||||
self.exit(context)
|
||||
bpy.ops.transform.trackball('INVOKE_DEFAULT')
|
||||
return {'FINISHED'}
|
||||
|
||||
if event.type == "RIGHTMOUSE":
|
||||
if event.type == "LEFTMOUSE" and event.value == 'RELEASE':
|
||||
self.release_event(context)
|
||||
return {'FINISHED'}
|
||||
|
||||
if event.type in {"RIGHTMOUSE", "ESC"}:
|
||||
for bone, matrix in self.bone_data.items():
|
||||
bone.matrix = matrix
|
||||
|
||||
self.exit(context)
|
||||
return {'CANCELLED'}
|
||||
|
||||
elif event.type == "X" and event.value == 'PRESS':
|
||||
self.change_transform_type()
|
||||
self.view_vector = Vector((1, 0, 0))
|
||||
#transform_type = self.transform_orientation
|
||||
|
||||
elif event.type == "Y" and event.value == 'PRESS':
|
||||
self.change_transform_type()
|
||||
self.view_vector = Vector((0, 1, 0))
|
||||
#transform_type = self.transform_orientation
|
||||
|
||||
elif event.type == "Z" and event.value == 'PRESS':
|
||||
self.change_transform_type()
|
||||
self.view_vector = Vector((0, 0, 1))
|
||||
#transform_type = self.transform_orientation
|
||||
|
||||
elif event.type == "MOUSEMOVE":
|
||||
|
||||
if self.mode == 'ROTATE':
|
||||
transform_matrix = Matrix.Rotation(delta, 4, self.view_vector)
|
||||
elif self.mode == 'SCALE':
|
||||
if self.view_vector.length:
|
||||
transform_matrix = Matrix.Scale(delta, 4, self.view_vector)
|
||||
else:
|
||||
transform_matrix = Matrix.Scale(delta, 4)
|
||||
|
||||
for bone, matrix in self.bone_data.items():
|
||||
center = self.center
|
||||
if scn.tool_settings.transform_pivot_point == "INDIVIDUAL_ORIGINS":
|
||||
center = matrix.to_translation()
|
||||
|
||||
if self.mode == 'ROTATE':
|
||||
if transform_type == 'LOCAL':
|
||||
view_vector = self.view_vector.copy()
|
||||
view_vector.rotate(matrix)
|
||||
transform_matrix = Matrix.Rotation(delta, 4, view_vector)
|
||||
|
||||
elif self.mode == 'SCALE':
|
||||
if transform_type == 'LOCAL':
|
||||
view_vector = self.view_vector.copy()
|
||||
view_vector.rotate(matrix)
|
||||
transform_matrix = Matrix.Scale(delta, 4, view_vector)
|
||||
|
||||
mat = matrix.copy()
|
||||
mat.translation -= center
|
||||
|
||||
mat = transform_matrix @ mat
|
||||
|
||||
mat.translation += center
|
||||
|
||||
bone.matrix = mat
|
||||
|
||||
return {'RUNNING_MODAL'}
|
||||
|
||||
|
||||
class RP_OT_function_execute(bpy.types.Operator):
|
||||
bl_idname = "rigpicker.function_execute"
|
||||
bl_label = 'Function Execute'
|
||||
class RP_OT_toogle_property(Operator):
|
||||
"""Invert a bone custom property"""
|
||||
|
||||
shape_index = IntProperty()
|
||||
bl_idname = "rigpicker.toogle_property"
|
||||
bl_label = 'Toogle Property'
|
||||
|
||||
data_path : StringProperty()
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if not is_picker_space(context):
|
||||
return
|
||||
return is_picker_space(context.space_data)
|
||||
|
||||
def execute(self,context):
|
||||
event = self.event
|
||||
ob = context.object
|
||||
shape = ob.data.rig_picker['shapes'][self.shape_index]
|
||||
|
||||
try:
|
||||
value = ob.path_resolve(self.data_path)
|
||||
except Exception:
|
||||
return {'CANCELLED'}
|
||||
|
||||
function = shape['function']
|
||||
if shape.get('variables'):
|
||||
variables=shape['variables'].to_dict()
|
||||
else:
|
||||
variables={}
|
||||
data = ob
|
||||
prop_name = self.data_path
|
||||
#if '.' in self.data_path:
|
||||
# data, prop = prop_name.rsplit('.', 1)
|
||||
|
||||
value = type(value)(not value)
|
||||
exec(f'{repr(ob)}.{self.data_path} = {value}')
|
||||
|
||||
variables['event']=event
|
||||
globals()[function](variables)
|
||||
#setattr(data, prop_name, value)
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
bpy.ops.object.mode_set(mode='POSE')
|
||||
|
||||
if context.scene.tool_settings.use_keyframe_insert_auto:
|
||||
bone_name, _ = split_path(self.data_path)
|
||||
ob.keyframe_insert(data_path=self.data_path, group=bone_name)
|
||||
|
||||
context.area.tag_redraw()
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self,context,event):
|
||||
self.event = event
|
||||
return self.execute(context)
|
||||
|
||||
class RP_OT_reload_picker(Operator):
|
||||
"""Reload the picker shapes"""
|
||||
|
||||
class RP_OT_reload_picker(bpy.types.Operator):
|
||||
bl_idname = "rigpicker.reload_picker"
|
||||
bl_label = 'Reload Picker'
|
||||
|
||||
|
@ -356,22 +391,37 @@ class RP_OT_reload_picker(bpy.types.Operator):
|
|||
# if not is_picker_space(context):
|
||||
# return
|
||||
|
||||
def execute(self,context):
|
||||
PICKERS.clear()
|
||||
def execute(self, context):
|
||||
#PICKERS.clear()
|
||||
if context.object.type == 'ARMATURE':
|
||||
rig = context.object
|
||||
else:
|
||||
collection = get_picker_collection(context.object)
|
||||
rig = collection.rig_picker.rig
|
||||
|
||||
for a in context.screen.areas:
|
||||
a.tag_redraw()
|
||||
load_picker_data(rig)
|
||||
|
||||
'''
|
||||
for area in context.screen.areas:
|
||||
#print(area.type, is_picker_space(area.spaces.active))
|
||||
if is_picker_space(area.spaces.active):
|
||||
|
||||
print('Tag Redraw Region', area.type)
|
||||
area.regions[0].tag_redraw()
|
||||
area.tag_redraw()
|
||||
'''
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class RP_OT_toogle_bone_layer(bpy.types.Operator):
|
||||
class RP_OT_toogle_bone_layer(Operator):
|
||||
"""Toogle bone layer visibility when double clicking on a bone"""
|
||||
bl_idname = "rigpicker.toogle_bone_layer"
|
||||
bl_label = 'Toogle Bone Layer'
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if not is_picker_space(context):
|
||||
if not is_picker_space(context.space_data):
|
||||
return
|
||||
|
||||
ob = context.object
|
||||
|
@ -396,13 +446,14 @@ class RP_OT_toogle_bone_layer(bpy.types.Operator):
|
|||
return {"FINISHED"}
|
||||
|
||||
|
||||
class RP_OT_context_menu_picker(bpy.types.Operator):
|
||||
class RP_OT_context_menu_picker(Operator):
|
||||
"""Display Menu with the custom properties of the hovered bone if any or the active bone"""
|
||||
bl_idname = "node.context_menu_picker"
|
||||
bl_label = 'Context Menu Picker'
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return is_picker_space(context)
|
||||
return is_picker_space(context.space_data)
|
||||
|
||||
def execute(self, context):
|
||||
bpy.ops.wm.call_menu(name='RP_MT_context_menu')
|
||||
|
@ -410,75 +461,91 @@ class RP_OT_context_menu_picker(bpy.types.Operator):
|
|||
return {"FINISHED"}
|
||||
|
||||
|
||||
class RP_OT_pack_picker(bpy.types.Operator):
|
||||
class RP_OT_pack_picker(Operator):
|
||||
"""Pack Unpack the picker on the rig"""
|
||||
bl_idname = "rigpicker.pack_picker"
|
||||
bl_label = 'Toogle Bone Layer'
|
||||
bl_label = 'Pack Picker'
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
ob = context.object
|
||||
return (ob and ob.type == 'ARMATURE' and ob.data.rig_picker.source)
|
||||
return (ob and ob.type == 'ARMATURE' and ob.data.rig_picker.sources)
|
||||
|
||||
def execute(self, context):
|
||||
print('Pack Picker')
|
||||
print('Pack Pickers...')
|
||||
|
||||
rig = context.object
|
||||
picker_path = get_picker_path(rig)
|
||||
|
||||
if 'picker' in rig.data.rig_picker.keys():
|
||||
if 'pickers' in rig.data.rig_picker.keys():
|
||||
unpack_picker(rig)
|
||||
self.report({"INFO"}, f'The picker is unpacked')
|
||||
return {"FINISHED"}
|
||||
|
||||
if not picker_path.exists():
|
||||
self.report({"ERROR"}, f'The path of the picker not exist: {picker_path}')
|
||||
return {"CANCELLED"}
|
||||
|
||||
pack_picker(rig)
|
||||
|
||||
if not rig.data.rig_picker['pickers']:
|
||||
self.report({"ERROR"}, f'No picker have been packed')
|
||||
return {"CANCELLED"}
|
||||
|
||||
elif len(rig.data.rig_picker['pickers']) < len(rig.data.rig_picker.sources):
|
||||
self.report({"WARNING"}, f'No all pickers have been packed')
|
||||
return {"FINISHED"}
|
||||
|
||||
self.report({"INFO"}, f'The picker is packed')
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class RP_OT_call_operator(bpy.types.Operator):
|
||||
class RP_OT_call_operator(Operator):
|
||||
bl_idname = "rigpicker.call_operator"
|
||||
bl_label = 'Call operator'
|
||||
|
||||
operator : bpy.props.StringProperty()
|
||||
view_3d : bpy.props.BoolProperty()
|
||||
operator : StringProperty()
|
||||
arguments : StringProperty()
|
||||
invoke: BoolProperty()
|
||||
view_3d : BoolProperty()
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return is_picker_space(context)
|
||||
return is_picker_space(context.space_data)
|
||||
|
||||
def execute(self, context):
|
||||
if not self.operator.startswith('bpy.ops'):
|
||||
print("Operator call_operator can only execute operator")
|
||||
return {"FINISHED"}
|
||||
|
||||
print('CAll Operator')
|
||||
@classmethod
|
||||
def description(cls, context, properties):
|
||||
try:
|
||||
op = eval_attr(bpy.ops, properties.operator).get_rna_type()
|
||||
return op.description
|
||||
except AttributeError:
|
||||
return 'Call operator'
|
||||
|
||||
context.area.tag_redraw()
|
||||
def execute(self, context):
|
||||
#context.area.tag_redraw()
|
||||
|
||||
override = {}
|
||||
if self.view_3d:
|
||||
override = get_view_3d_override()
|
||||
|
||||
arguments = json.loads(self.arguments or "{}")
|
||||
|
||||
with context.temp_override(**override):
|
||||
try:
|
||||
exec(self.operator)
|
||||
ops = eval_attr(bpy.ops, self.operator)
|
||||
if self.invoke:
|
||||
ops('INVOKE_DEFAULT', **arguments)
|
||||
else:
|
||||
ops(**arguments)
|
||||
|
||||
except Exception as e:
|
||||
self.report({"ERROR"}, e)
|
||||
print(e)
|
||||
self.report({"ERROR"}, f'The operator {self.operator} cannot be called')
|
||||
return {"CANCELLED"}
|
||||
|
||||
context.area.tag_redraw()
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class RP_MT_context_menu(bpy.types.Menu):
|
||||
class RP_MT_context_menu(Menu):
|
||||
bl_label = "Context Menu"
|
||||
#bl_idname = "RP_MT_context_menu"
|
||||
|
||||
# Set the menu operators and draw functions
|
||||
def draw(self, context):
|
||||
|
@ -491,7 +558,10 @@ class RP_MT_context_menu(bpy.types.Menu):
|
|||
|
||||
if picker.hover_shape and picker.hover_shape.type == 'bone':
|
||||
bone = picker.hover_shape.bone
|
||||
|
||||
else:
|
||||
bone = context.active_pose_bone
|
||||
|
||||
if bone:
|
||||
for key in bone.keys():
|
||||
layout.prop(bone,f'["{key}"]', slider=True)
|
||||
|
||||
|
@ -499,7 +569,51 @@ class RP_MT_context_menu(bpy.types.Menu):
|
|||
#layout.operator("rigidbody.objects_add", text="B Add Passive").type = 'PASSIVE'
|
||||
|
||||
|
||||
class RP_OT_add_picker_source(bpy.types.Operator):
|
||||
class RP_OT_add_picker_collection(Operator):
|
||||
bl_idname = "rigpicker.add_picker_collection"
|
||||
bl_label = "Add a Picker Collection"
|
||||
bl_description = "Add a Picker Collection"
|
||||
bl_options = {'UNDO', 'REGISTER'}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
ob = context.object
|
||||
return (ob and ob.type == 'ARMATURE')
|
||||
|
||||
def execute(self, context):
|
||||
scn = context.scene
|
||||
ob = context.object
|
||||
|
||||
name = ob.name
|
||||
if name.endswith('_rig'):
|
||||
name = name[:-4]
|
||||
|
||||
canvas_name = f'canevas_{name}'
|
||||
canvas_points = [(-0.5, 0.5, 0), (0.5, 0.5, 0), (0.5, -0.5, 0), (-0.5, -0.5, 0)]
|
||||
canvas_faces = [(0, 1, 2, 3)]
|
||||
|
||||
canvas_mesh = bpy.data.meshes.new(canvas_name)
|
||||
canvas_mesh.from_pydata(canvas_points, [], canvas_faces)
|
||||
canvas_mesh.update(calc_edges=True)
|
||||
|
||||
canvas_ob = bpy.data.objects.new(canvas_name, canvas_mesh)
|
||||
canvas_ob.rig_picker.shape_type = 'DISPLAY'
|
||||
|
||||
col = bpy.data.collections.new(f'Picker {name}')
|
||||
col.rig_picker.enabled = True
|
||||
col.rig_picker.rig = ob
|
||||
col.rig_picker.canvas = canvas_ob
|
||||
|
||||
col.objects.link(canvas_ob)
|
||||
|
||||
scn.collection.children.link(col)
|
||||
|
||||
self.report({"INFO"}, f"New Picker Collection {col.name} Created")
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class RP_OT_add_picker_source(Operator):
|
||||
bl_idname = "rigpicker.add_picker_source"
|
||||
bl_label = "Add a Picker source"
|
||||
bl_description = "Add a Picker source"
|
||||
|
@ -512,7 +626,7 @@ class RP_OT_add_picker_source(bpy.types.Operator):
|
|||
return {'FINISHED'}
|
||||
|
||||
|
||||
class RP_OT_remove_picker_source(bpy.types.Operator):
|
||||
class RP_OT_remove_picker_source(Operator):
|
||||
bl_idname = "rigpicker.remove_picker_source"
|
||||
bl_label = "Delete a Picker source"
|
||||
bl_description = "Delete a Picker source"
|
||||
|
@ -527,7 +641,7 @@ class RP_OT_remove_picker_source(bpy.types.Operator):
|
|||
return {'FINISHED'}
|
||||
|
||||
|
||||
class RP_OT_fit_picker(bpy.types.Operator):
|
||||
class RP_OT_fit_picker(Operator):
|
||||
bl_idname = "rigpicker.fit_picker"
|
||||
bl_label = "Fit Picker"
|
||||
bl_description = "Fit Picker in 2d view"
|
||||
|
@ -537,7 +651,7 @@ class RP_OT_fit_picker(bpy.types.Operator):
|
|||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return is_picker_space(context) and PICKERS.get(context.object)
|
||||
return is_picker_space(context.space_data) and PICKERS.get(context.object)
|
||||
|
||||
def execute(self, context):
|
||||
ob = context.object
|
||||
|
@ -585,43 +699,58 @@ def register_keymaps():
|
|||
keymaps.append((km, kmi))
|
||||
|
||||
kmi = km.keymap_items.new("rigpicker.call_operator", type="NUMPAD_PERIOD", value="PRESS")
|
||||
kmi.properties.operator = "bpy.ops.view3d.view_selected()"
|
||||
kmi.properties.operator = "view3d.view_selected"
|
||||
kmi.properties.view_3d = True
|
||||
keymaps.append((km, kmi))
|
||||
|
||||
kmi = km.keymap_items.new("rigpicker.call_operator", type="A", value="PRESS")
|
||||
kmi.properties.operator = "bpy.ops.pose.select_all(action='SELECT')"
|
||||
kmi.properties.operator = "pose.select_all"
|
||||
kmi.properties.arguments = '{"action": "SELECT"}'
|
||||
keymaps.append((km, kmi))
|
||||
|
||||
kmi = km.keymap_items.new("rigpicker.call_operator", type="A", value="PRESS", alt=True)
|
||||
kmi.properties.operator = "bpy.ops.pose.select_all(action='DESELECT')"
|
||||
kmi.properties.operator = "pose.select_all"
|
||||
kmi.properties.arguments = '{"action": "DESELECT"}'
|
||||
keymaps.append((km, kmi))
|
||||
|
||||
kmi = km.keymap_items.new("rigpicker.toogle_bone_layer", type="LEFTMOUSE", value="DOUBLE_CLICK")
|
||||
keymaps.append((km, kmi))
|
||||
|
||||
kmi = km.keymap_items.new("node.picker_transform", type="G", value="PRESS")
|
||||
kmi.properties.mode = 'TRANSLATE'
|
||||
kmi = km.keymap_items.new("rigpicker.call_operator", type="X", value="PRESS")
|
||||
kmi.properties.operator = "animtoolbox.reset_bone"
|
||||
keymaps.append((km, kmi))
|
||||
|
||||
kmi = km.keymap_items.new("node.picker_transform", type="G", value="PRESS", alt=True)
|
||||
kmi.properties.mode = 'TRANSLATE'
|
||||
kmi = km.keymap_items.new("rigpicker.call_operator", type="K", value="PRESS")
|
||||
kmi.properties.operator = "animtoolbox.insert_keyframe"
|
||||
keymaps.append((km, kmi))
|
||||
|
||||
kmi = km.keymap_items.new("anim.keyframe_delete_v3d", type="K", value="PRESS", alt=True)
|
||||
keymaps.append((km, kmi))
|
||||
|
||||
kmi = km.keymap_items.new("rigpicker.call_operator", type="G", value="PRESS")
|
||||
kmi.properties.operator = 'transform.translate'
|
||||
kmi.properties.view_3d = True
|
||||
kmi.properties.invoke = True
|
||||
keymaps.append((km, kmi))
|
||||
|
||||
kmi = km.keymap_items.new("rigpicker.call_operator", type="G", value="PRESS", alt=True)
|
||||
kmi.properties.operator = 'pose.loc_clear'
|
||||
keymaps.append((km, kmi))
|
||||
|
||||
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="R", value="PRESS", alt=True)
|
||||
kmi.properties.mode = 'ROTATE'
|
||||
kmi = km.keymap_items.new("rigpicker.call_operator", type="R", value="PRESS", alt=True)
|
||||
kmi.properties.operator = 'pose.rot_clear'
|
||||
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.picker_transform", type="S", value="PRESS", alt=True)
|
||||
kmi.properties.mode = 'SCALE'
|
||||
kmi = km.keymap_items.new("rigpicker.call_operator", type="S", value="PRESS", alt=True)
|
||||
kmi.properties.operator = 'pose.scale_clear'
|
||||
keymaps.append((km, kmi))
|
||||
|
||||
kmi = km.keymap_items.new("node.context_menu_picker", type="RIGHTMOUSE", value="PRESS")
|
||||
|
@ -641,9 +770,9 @@ def register_keymaps():
|
|||
keymaps.append((km, kmi))
|
||||
|
||||
#km = wm.keyconfigs.addon.keymaps.new(name="View2D")
|
||||
kmi = km.keymap_items.new("rigpicker.call_operator", type="MIDDLEMOUSE", value="PRESS")
|
||||
kmi.properties.operator = "bpy.ops.view2d.pan('INVOKE_DEFAULT')"
|
||||
keymaps.append((km, kmi))
|
||||
#kmi = km.keymap_items.new("rigpicker.call_operator", type="MIDDLEMOUSE", value="PRESS")
|
||||
#kmi.properties.operator = "bpy.ops.view2d.pan('INVOKE_DEFAULT')"
|
||||
#keymaps.append((km, kmi))
|
||||
|
||||
|
||||
def unregister_keymaps():
|
||||
|
@ -653,7 +782,7 @@ def unregister_keymaps():
|
|||
|
||||
classes = (
|
||||
RP_OT_box_select,
|
||||
RP_OT_function_execute,
|
||||
RP_OT_toogle_property,
|
||||
RP_OT_reload_picker,
|
||||
RP_OT_toogle_bone_layer,
|
||||
RP_OT_call_operator,
|
||||
|
@ -663,7 +792,8 @@ classes = (
|
|||
RP_OT_pack_picker,
|
||||
RP_OT_add_picker_source,
|
||||
RP_OT_remove_picker_source,
|
||||
RP_OT_fit_picker
|
||||
RP_OT_fit_picker,
|
||||
RP_OT_add_picker_collection
|
||||
#RP_OT_ui_draw
|
||||
)
|
||||
|
||||
|
@ -674,6 +804,7 @@ def register():
|
|||
|
||||
register_keymaps()
|
||||
|
||||
|
||||
def unregister():
|
||||
unregister_keymaps()
|
||||
for cls in reversed(classes):
|
||||
|
|
|
@ -8,7 +8,7 @@ from bpy.props import IntProperty
|
|||
from mathutils import Matrix
|
||||
|
||||
from ..core.shape import get_picker_data
|
||||
from ..core.addon_utils import is_shape
|
||||
from ..core.addon_utils import is_shape, get_picker_collection
|
||||
from ..core.bl_utils import link_mat_to_object, flip_name
|
||||
|
||||
|
||||
|
@ -77,7 +77,7 @@ class RP_OT_name_from_bone(Operator):
|
|||
class RP_OT_mirror_shape(Operator):
|
||||
bl_label = 'Mirror UI shape'
|
||||
bl_idname = 'rigpicker.mirror_shape'
|
||||
#bl_options = {'REGISTER', 'UNDO'}
|
||||
#bl_options = g
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
|
@ -86,7 +86,7 @@ class RP_OT_mirror_shape(Operator):
|
|||
def execute(self,context):
|
||||
scn = context.scene
|
||||
ob = context.object
|
||||
collection = next((c for c in ob.users_collection if c.rig_picker.enabled), None)
|
||||
collection = get_picker_collection(ob)
|
||||
objects = context.selected_objects
|
||||
|
||||
# Remove mirror object:
|
||||
|
@ -192,39 +192,33 @@ class RP_OT_save_picker(Operator):
|
|||
scn = context.scene
|
||||
ob = context.object
|
||||
|
||||
print('SAve Picker', self.index)
|
||||
|
||||
if self.index == -1:
|
||||
collection = next((c for c in ob.users_collection if c.rig_picker.enabled), None)
|
||||
collection = get_picker_collection(ob)
|
||||
else:
|
||||
source = context.active_object.data.rig_picker.sources[self.index].source
|
||||
source = Path(bpy.path.abspath(source, library=ob.data.library)).resolve()
|
||||
collection = next((c for c in set(scn.collection.children_recursive) if c.rig_picker.enabled
|
||||
and Path(bpy.path.abspath(c.rig_picker.destination)).resolve() == source), None)
|
||||
|
||||
print('source', source)
|
||||
print('colleciton', collection)
|
||||
if not collection:
|
||||
self.report({"ERROR"}, 'No Picker found')
|
||||
return {'CANCELLED'}
|
||||
|
||||
canvas = collection.rig_picker.canvas
|
||||
rig = collection.rig_picker.rig
|
||||
#rig = collection.rig_picker.rig
|
||||
shapes = [o for o in collection.all_objects if o != canvas and o.type in ('MESH', 'CURVE', 'FONT')]
|
||||
|
||||
if not rig:
|
||||
self.report({'ERROR'}, 'Choose a Rig')
|
||||
return {'CANCELLED'}
|
||||
|
||||
if not canvas:
|
||||
self.report({'ERROR'}, 'Choose a Canvas')
|
||||
return {'CANCELLED'}
|
||||
|
||||
|
||||
data = get_picker_data(shapes, canvas, rig)
|
||||
picker_data = get_picker_data(collection)
|
||||
|
||||
picker_path = Path(bpy.path.abspath(collection.rig_picker.destination))
|
||||
picker_path.write_text(json.dumps(data))
|
||||
|
||||
print(f'Save Picker to {picker_path}')
|
||||
picker_path.write_text(json.dumps(picker_data))
|
||||
|
||||
bpy.ops.rigpicker.reload_picker()
|
||||
|
||||
|
|
117
panels.py
117
panels.py
|
@ -1,117 +0,0 @@
|
|||
import bpy
|
||||
#import collections
|
||||
#import inspect
|
||||
from .core.addon_utils import get_operator_from_id
|
||||
from .core.bl_utils import get_mat
|
||||
|
||||
import re
|
||||
|
||||
|
||||
|
||||
|
||||
class RP_PT_picker_maker_panel(bpy.types.Panel):
|
||||
bl_label = 'Rig Picker'
|
||||
bl_category = 'Rigging'
|
||||
bl_space_type = 'VIEW_3D'
|
||||
bl_region_type = 'UI'
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.object
|
||||
|
||||
def draw(self, context):
|
||||
ob = context.object
|
||||
scn = context.scene
|
||||
|
||||
layout = self.layout
|
||||
col = layout.column(align=False)
|
||||
col.prop_search(scn.rig_picker, 'rig', scn, 'objects', text='Rig ')
|
||||
col.prop_search(scn.rig_picker, 'canvas', scn, 'objects', text='Canvas ')
|
||||
col.prop_search(scn.rig_picker, 'symmetry', scn, 'objects', text='Symmetry ')
|
||||
|
||||
if ob.type == 'ARMATURE':
|
||||
row = layout.row(align=True)
|
||||
is_packed = ('picker' in ob.data.rig_picker.keys())
|
||||
sub_row = row.row(align=True)
|
||||
sub_row.prop(ob.data.rig_picker, 'source', text='Picker')
|
||||
sub_row.enabled = not is_packed
|
||||
row.operator('rigpicker.pack_picker', icon='PACKAGE' if is_packed else 'UGLYPACKAGE', text='')
|
||||
|
||||
|
||||
col = layout.column(align=True)
|
||||
row = col.row(align=True)
|
||||
row.operator('rigpicker.create_shape', icon='MESH_DATA', text='Create shape')
|
||||
row.operator('rigpicker.mirror_shape', icon='ARROW_LEFTRIGHT', text='Mirror shape')
|
||||
col.operator('rigpicker.name_from_bone', icon='SORTALPHA' , text='Name from bones')
|
||||
col.prop(scn.rig_picker, 'use_pick_bone', icon='EYEDROPPER', text='Auto bone assign')
|
||||
|
||||
|
||||
if ob.type !='ARMATURE':
|
||||
box = layout.box()
|
||||
col = box.column(align=False)
|
||||
|
||||
material_row = col.row(align=True)
|
||||
material_row.operator('rigpicker.remove_mat', icon='REMOVE', text='')
|
||||
material_row.operator('rigpicker.add_mat', icon='ADD', text='')
|
||||
mat = False
|
||||
if ob.type in ('MESH', 'CURVE', 'FONT') and ob.data.materials:
|
||||
mat = get_mat(ob)
|
||||
if mat and mat.node_tree:
|
||||
emission_nodes = [n for n in mat.node_tree.nodes if n.type =='EMISSION']
|
||||
if emission_nodes:
|
||||
material_row.prop(emission_nodes[0].inputs[0], 'default_value', text='')
|
||||
mat = True
|
||||
if not mat:
|
||||
material_row.label(text='No Material')
|
||||
material_row.operator('rigpicker.eyedropper_mat', icon='EYEDROPPER', text='')
|
||||
|
||||
shape_type_row = col.row(align=True)
|
||||
shape_type_row.prop(ob.rig_picker,'shape_type',expand = True)
|
||||
shape_type_row.operator('rigpicker.select_shape_type', text='', icon='RESTRICT_SELECT_OFF')
|
||||
|
||||
|
||||
if ob.rig_picker.shape_type == 'OPERATOR':
|
||||
|
||||
op_row = col.row(align=True)
|
||||
op_row.prop(ob.rig_picker, 'operator', text='')
|
||||
op_row.operator('rigpicker.operator_selector', text='', icon='COLLAPSEMENU')
|
||||
|
||||
if ob.rig_picker.operator:
|
||||
col.prop(ob.rig_picker, 'name', text='Tooltip')
|
||||
col.prop(ob.rig_picker,'shortcut', text='Shortcut')
|
||||
|
||||
else:
|
||||
col.prop(ob.rig_picker, 'name', text='Tooltip')
|
||||
|
||||
'''
|
||||
if ob.rig_picker.operator:
|
||||
op = get_operator_from_id(ob.rig_picker.idname)
|
||||
if op:
|
||||
doc = re.findall(r'\(([^\)]+)\)', op.__doc__)[0]
|
||||
else:
|
||||
doc = 'Operator not found'
|
||||
|
||||
col.prop(ob.rig_picker,'name', text='Tooltip')
|
||||
if op:
|
||||
col.prop(ob.rig_picker,'shortcut', text='Shortcut')
|
||||
col.prop(ob.rig_picker,'arguments', text='')
|
||||
col.label(text=doc)
|
||||
else:
|
||||
col.label(text=doc)
|
||||
'''
|
||||
|
||||
|
||||
if ob.rig_picker.shape_type == 'BONE':
|
||||
if scn.rig_picker.rig:
|
||||
col.prop_search(ob.rig_picker, 'name', scn.rig_picker.rig.pose, 'bones', text='Bone')
|
||||
|
||||
#layout.separator()
|
||||
layout.prop(scn.rig_picker, 'destination', text='Filepath')
|
||||
layout.operator('rigpicker.save_picker', icon='PASTEDOWN', text='Save Picker')
|
||||
|
||||
|
||||
classes = (
|
||||
RP_PT_picker_maker_panel,
|
||||
)
|
||||
|
||||
register, unregister = bpy.utils.register_classes_factory(classes)
|
|
@ -0,0 +1,19 @@
|
|||
|
||||
flat in vec2 startPos;
|
||||
in vec2 vertPos;
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
uniform vec4 color;
|
||||
uniform float dashSize;
|
||||
uniform float gapSize;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec2 dir = (vertPos.xy - startPos.xy);
|
||||
float dist = length(dir);
|
||||
|
||||
if (fract(dist / (dashSize + gapSize)) > dashSize/(dashSize + gapSize))
|
||||
discard;
|
||||
fragColor = color;
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
|
||||
layout (location = 0) in vec2 pos;
|
||||
|
||||
flat out vec2 startPos;
|
||||
out vec2 vertPos;
|
||||
|
||||
uniform mat4 viewMatrix;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 outPos = viewMatrix * vec4(pos.x, pos.y, 0.0, 1.0);
|
||||
gl_Position = outPos;
|
||||
vertPos = pos.xy / outPos.w;
|
||||
startPos = vertPos;
|
||||
}
|
66
ui.py
66
ui.py
|
@ -2,7 +2,7 @@ import bpy
|
|||
from bpy.types import UIList
|
||||
#import collections
|
||||
#import inspect
|
||||
from .core.addon_utils import get_operator_from_id
|
||||
from .core.addon_utils import get_operator_from_id, get_picker_collection
|
||||
from .core.bl_utils import get_mat
|
||||
|
||||
import re
|
||||
|
@ -29,10 +29,13 @@ class RP_PT_picker_maker_panel(bpy.types.Panel):
|
|||
def poll(cls, context):
|
||||
return context.object
|
||||
|
||||
def draw_header_preset(self, context):
|
||||
self.layout.operator('rigpicker.add_picker_collection', text="", icon='ADD', emboss=False)
|
||||
|
||||
def draw(self, context):
|
||||
ob = context.object
|
||||
scn = context.scene
|
||||
collection = next((c for c in ob.users_collection if c.rig_picker.enabled), None)
|
||||
collection = get_picker_collection(ob)
|
||||
|
||||
layout = self.layout
|
||||
col = layout.column(align=False)
|
||||
|
@ -51,24 +54,45 @@ class RP_PT_picker_maker_panel(bpy.types.Panel):
|
|||
row.separator(factor=0.5)
|
||||
row.label(text="Sources")
|
||||
|
||||
is_packed = ('picker' in ob.data.rig_picker.keys())
|
||||
is_packed = ('pickers' in ob.data.rig_picker.keys())
|
||||
row.operator('rigpicker.pack_picker', icon='PACKAGE' if is_packed else 'UGLYPACKAGE', text='', emboss=False)
|
||||
row.operator("rigpicker.add_picker_source", icon ='ADD', text="", emboss=False)
|
||||
|
||||
for i, item in enumerate(ob.data.rig_picker.sources):
|
||||
row = col.row(align=True)
|
||||
is_packed = ('pickers' in ob.data.rig_picker.keys())
|
||||
row.enabled = not is_packed
|
||||
row.prop(item, 'source', text='')
|
||||
row.operator("rigpicker.remove_picker_source", icon ='PANEL_CLOSE', text="", emboss=False).index=i
|
||||
|
||||
if collection:
|
||||
#layout.separator()
|
||||
layout.prop(collection.rig_picker, 'destination', text='Filepath')
|
||||
layout.operator('rigpicker.save_picker', icon='PASTEDOWN', text='Save Picker')
|
||||
|
||||
|
||||
class RP_PT_shape(bpy.types.Panel):
|
||||
bl_label = 'Shape'
|
||||
bl_category = 'Rigging'
|
||||
bl_space_type = 'VIEW_3D'
|
||||
bl_region_type = 'UI'
|
||||
bl_parent_id = "RP_PT_picker_maker_panel"
|
||||
|
||||
def draw(self, context):
|
||||
ob = context.object
|
||||
scn = context.scene
|
||||
collection = get_picker_collection(ob)
|
||||
|
||||
layout = self.layout
|
||||
col = layout.column(align=False)
|
||||
|
||||
if collection:
|
||||
#if context.collection and context.collection.rig_picker.enabled:
|
||||
col = layout.column(align=True)
|
||||
row = col.row(align=True)
|
||||
row.operator('rigpicker.create_shape', icon='MESH_DATA', text='Create shape')
|
||||
row.operator('rigpicker.mirror_shape', icon='MOD_MIRROR', text='Mirror shape')
|
||||
col.operator('rigpicker.name_from_bone', icon='SORTALPHA' , text='Name from bones')
|
||||
col.prop(scn.rig_picker, 'use_pick_bone', icon='EYEDROPPER', text='Auto bone assign')
|
||||
row.operator('rigpicker.create_shape', icon='MESH_DATA', text='Create Shape')
|
||||
row.operator('rigpicker.mirror_shape', icon='MOD_MIRROR', text='Mirror Shape')
|
||||
col.operator('rigpicker.name_from_bone', icon='SORTALPHA' , text='Name From Bones')
|
||||
col.prop(scn.rig_picker, 'use_pick_bone', icon='EYEDROPPER', text='Auto Bone Assign')
|
||||
|
||||
if ob.type != 'ARMATURE':
|
||||
box = layout.box()
|
||||
|
@ -107,36 +131,14 @@ class RP_PT_picker_maker_panel(bpy.types.Panel):
|
|||
else:
|
||||
col.prop(ob.rig_picker, 'name', text='Tooltip')
|
||||
|
||||
'''
|
||||
if ob.rig_picker.operator:
|
||||
op = get_operator_from_id(ob.rig_picker.idname)
|
||||
if op:
|
||||
doc = re.findall(r'\(([^\)]+)\)', op.__doc__)[0]
|
||||
else:
|
||||
doc = 'Operator not found'
|
||||
|
||||
col.prop(ob.rig_picker,'name', text='Tooltip')
|
||||
if op:
|
||||
col.prop(ob.rig_picker,'shortcut', text='Shortcut')
|
||||
col.prop(ob.rig_picker,'arguments', text='')
|
||||
col.label(text=doc)
|
||||
else:
|
||||
col.label(text=doc)
|
||||
'''
|
||||
|
||||
|
||||
if ob.rig_picker.shape_type == 'BONE':
|
||||
elif ob.rig_picker.shape_type == 'BONE':
|
||||
if collection and collection.rig_picker.rig:
|
||||
col.prop_search(ob.rig_picker, 'name', collection.rig_picker.rig.pose, 'bones', text='Bone')
|
||||
|
||||
#layout.separator()
|
||||
layout.prop(collection.rig_picker, 'destination', text='Filepath')
|
||||
layout.operator('rigpicker.save_picker', icon='PASTEDOWN', text='Save Picker')
|
||||
|
||||
|
||||
classes = (
|
||||
#RP_UL_picker_source,
|
||||
RP_PT_picker_maker_panel,
|
||||
RP_PT_shape
|
||||
)
|
||||
|
||||
register, unregister = bpy.utils.register_classes_factory(classes)
|
Loading…
Reference in New Issue