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_selected_cols = [ col for col in bpy.data.collections if col.rig_picker.link_shape ] for col in picker_selected_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)