Add menu and some cleanup
This commit is contained in:
+18
-1
@@ -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
|
||||
|
||||
+42
-19
@@ -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']
|
||||
+97
-77
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user