Before the patch, shapes were systematically linked to the scene's main collection, regardless of the collection selection. The patch changes this to link geometry only if no canvas collection is selected.
243 lines
7.6 KiB
Python
243 lines
7.6 KiB
Python
|
|
import json
|
|
from os.path import abspath
|
|
from pathlib import Path
|
|
import bpy
|
|
from bpy.types import Operator
|
|
from bpy.props import IntProperty
|
|
from mathutils import Matrix
|
|
|
|
from ..core.shape import get_picker_data
|
|
from ..core.addon_utils import is_shape, get_picker_collection
|
|
from ..core.bl_utils import link_mat_to_object, flip_name
|
|
|
|
|
|
class RP_OT_create_shape(Operator):
|
|
bl_label = 'Create UI shape'
|
|
bl_idname = 'rigpicker.create_shape'
|
|
#bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return (context.object and context.object.mode == 'POSE')
|
|
|
|
def invoke(self, context, event):
|
|
return context.window_manager.invoke_props_dialog(self)
|
|
|
|
def draw(self, context):
|
|
for col in bpy.data.collections:
|
|
if col.rig_picker.enabled:
|
|
self.layout.prop(col.rig_picker, 'link_shape', text=col.name)
|
|
|
|
def execute(self,context):
|
|
scn = context.scene
|
|
vl = context.view_layer
|
|
|
|
offset = 0
|
|
for bone in context.selected_pose_bones:
|
|
name = bone.name
|
|
|
|
mesh = bpy.data.meshes.new(name)
|
|
ob = bpy.data.objects.new(name, mesh)
|
|
|
|
ob.rig_picker.name = name
|
|
|
|
verts=[(0,0,0)]
|
|
edges = []
|
|
faces =[]
|
|
|
|
mesh.from_pydata(verts, edges, faces)
|
|
|
|
picker_cols = [col for col in bpy.data.collections if col.rig_picker.link_shape]
|
|
|
|
for col in picker_cols or [scn.collection]:
|
|
col.objects.link(ob)
|
|
|
|
ob.location.z = 0.05
|
|
ob.location.x = offset
|
|
|
|
for obj in scn.objects:
|
|
obj.select_set(False)
|
|
|
|
vl.objects.active = ob
|
|
ob.select_set(True)
|
|
|
|
offset += 0.05
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
class RP_OT_name_from_bone(Operator):
|
|
bl_label = 'Name Shape from selected bones'
|
|
bl_idname = 'rigpicker.name_from_bone'
|
|
bl_description = 'Rename all shapes from related bones name'
|
|
#bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
def execute(self,context):
|
|
|
|
col = get_picker_collection(context.object)
|
|
|
|
for ob in col.all_objects:
|
|
if ob.rig_picker.shape_type == 'BONE':
|
|
ob.name = ob.rig_picker.name
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
class RP_OT_mirror_shape(Operator):
|
|
bl_label = 'Mirror UI shape'
|
|
bl_idname = 'rigpicker.mirror_shape'
|
|
#bl_options = g
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return (context.object and context.object.type in ('MESH', 'CURVE', 'TEXT'))
|
|
|
|
def execute(self,context):
|
|
collection = get_picker_collection(context.object)
|
|
objects = context.selected_objects
|
|
|
|
# Remove mirror object:
|
|
for ob in list(collection.all_objects):
|
|
if ob in objects:
|
|
continue
|
|
|
|
for mod in ob.modifiers:
|
|
if (mod.type == 'NODES' and mod.node_group and mod.node_group.name == 'Symmetrize' and
|
|
mod.get('Socket_2') in objects):
|
|
|
|
bpy.data.objects.remove(ob)
|
|
|
|
if collection.rig_picker.symmetry:
|
|
x_axis = collection.rig_picker.symmetry.matrix_world.to_translation()[0]
|
|
else:
|
|
x_axis = 0
|
|
|
|
for ob in objects:
|
|
flipped_name = flip_name(ob.name)
|
|
if flipped_name == ob.name:
|
|
suffix = '.L'
|
|
flipped_suffix = '.R'
|
|
# Determine side of the object
|
|
if ob.matrix_world.to_translation()[0] < x_axis:
|
|
suffix, flipped_suffix = flipped_suffix, suffix
|
|
|
|
flipped_name = f'{ob.name}{flipped_suffix}'
|
|
ob.name = f'{ob.name}{suffix}'
|
|
|
|
flipped_object = ob.copy()
|
|
flipped_object.name = flipped_name
|
|
flipped_object.parent = None
|
|
flipped_object.matrix_world = Matrix()
|
|
for mod in list(flipped_object.modifiers):
|
|
flipped_object.modifiers.remove(mod)
|
|
|
|
# Add symmetrize modifier TODO add it into a resource
|
|
mod = flipped_object.modifiers.new(name='Symmetrize', type='NODES')
|
|
mod.node_group = bpy.data.node_groups['Symmetrize']
|
|
mod['Socket_2'] = ob
|
|
mod['Socket_3'] = collection.rig_picker.symmetry
|
|
|
|
for col in ob.users_collection:
|
|
col.objects.link(flipped_object)
|
|
|
|
if ob.rig_picker.shape_type == 'BONE':
|
|
flipped_object.rig_picker.name = flip_name(ob.rig_picker.name)
|
|
|
|
elif ob.rig_picker.shape_type == 'FUNCTION':
|
|
args = {}
|
|
for key,value in eval(ob.rig_picker.arguments).items():
|
|
if type(value) == list:
|
|
mirrored_value = []
|
|
for item in value:
|
|
mirrored_value.append(flip_name(item))
|
|
|
|
elif type(value) == str:
|
|
mirrored_value = flip_name(value)
|
|
args[key] = mirrored_value
|
|
|
|
flipped_object.rig_picker.arguments = str(args)
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
class RP_OT_select_shape_type(Operator):
|
|
bl_label = 'Select Shape by Type'
|
|
bl_idname = 'rigpicker.select_shape_type'
|
|
#bl_options = {'REGISTER', 'UNDO'}
|
|
|
|
shape_type = bpy.props.EnumProperty(items =[(i.upper(), i, '') for i in ('Bone', 'Display', 'Function')])
|
|
|
|
def draw(self,context):
|
|
layout = self.layout
|
|
|
|
col = layout.column()
|
|
col.prop(self,'shape_type', expand=True)
|
|
|
|
def execute(self,context):
|
|
scene = context.scene
|
|
#print(self.shape_type)
|
|
canvas = context.scene.rig_picker.canvas
|
|
if canvas:
|
|
for ob in [o for o in bpy.data.objects if o.layers==canvas.layers]:
|
|
if ob.type in ['MESH', 'CURVE', 'FONT'] and ob.rig_picker.shape_type == self.shape_type:
|
|
ob.select = True
|
|
|
|
return {'FINISHED'}
|
|
|
|
def invoke(self, context, event):
|
|
wm = context.window_manager
|
|
return wm.invoke_props_dialog(self, width=150)
|
|
|
|
|
|
class RP_OT_save_picker(Operator):
|
|
bl_label = 'Store UI Data'
|
|
bl_idname = 'rigpicker.save_picker'
|
|
|
|
index : IntProperty(default=-1)
|
|
|
|
def execute(self, context):
|
|
scn = context.scene
|
|
ob = context.object
|
|
|
|
if self.index == -1:
|
|
collection = get_picker_collection(ob)
|
|
else:
|
|
source = context.active_object.data.rig_picker.sources[self.index].source
|
|
source = Path(bpy.path.abspath(source, library=ob.data.library)).resolve()
|
|
collection = next((c for c in set(scn.collection.children_recursive) if c.rig_picker.enabled
|
|
and Path(bpy.path.abspath(c.rig_picker.destination)).resolve() == source), None)
|
|
|
|
if not collection:
|
|
self.report({"ERROR"}, 'No Picker found')
|
|
return {'CANCELLED'}
|
|
|
|
canvas = collection.rig_picker.canvas
|
|
#rig = collection.rig_picker.rig
|
|
shapes = [o for o in collection.all_objects if o != canvas and o.type in ('MESH', 'CURVE', 'FONT')]
|
|
|
|
if not canvas:
|
|
self.report({'ERROR'}, 'Choose a Canvas')
|
|
return {'CANCELLED'}
|
|
|
|
|
|
picker_data = get_picker_data(collection)
|
|
|
|
picker_path = Path(bpy.path.abspath(collection.rig_picker.destination))
|
|
|
|
print(f'Save Picker to {picker_path}')
|
|
picker_path.write_text(json.dumps(picker_data))
|
|
|
|
bpy.ops.rigpicker.reload_picker()
|
|
|
|
return {'FINISHED'}
|
|
|
|
classes = (
|
|
RP_OT_create_shape,
|
|
RP_OT_name_from_bone,
|
|
RP_OT_mirror_shape,
|
|
RP_OT_select_shape_type,
|
|
RP_OT_save_picker
|
|
)
|
|
|
|
register, unregister = bpy.utils.register_classes_factory(classes) |