bl_info = { "name": "Mabulig Rigger Plugin", "author": "Fritz Villahermosa", "version": (1, 3), "blender": (5, 2, 0), "location": "View3D > Sidebar > Mabulig Rigger", "description": "Frog rigging made simple!", "category": "3D View", } import bpy import os from bpy.props import FloatProperty, PointerProperty, BoolProperty from . import rigger_functionality from . import controlrig_generate # ========================================================================= # GLOBAL VARIABLES # ========================================================================= # # current_dir = os.path.dirname(bpy.data.filepath) # filepath = os.path.join(current_dir, source_blend_name) # ========================================================================= # PROPERTIES # ========================================================================= # SVL Properties class SpecimenProperties(bpy.types.PropertyGroup): svl: FloatProperty( name="Specimen SVL", description="Snout-Vent Length of the specimen in meters", default=0.1, # 10 cm default min=0.001, soft_max=10.0, unit='LENGTH' ) # pyright: ignore[reportInvalidTypeForm] # Model Picker target_model: PointerProperty( type=bpy.types.Object, name="Target Model", description="Points to the specimen's 3d model" ) # pyright: ignore[reportInvalidTypeForm] class PanelProperties(bpy.types.PropertyGroup): panel1_g2: BoolProperty( name="Panel 1, Group 2", description="Toggle panel/group visibility", default=True ) # pyright: ignore[reportInvalidTypeForm] panel2_g1: BoolProperty( name="Panel 2, Group 1", description="Toggle panel/group visibility", default=True ) # pyright: ignore[reportInvalidTypeForm] panel3_g1: BoolProperty( name="Panel 3, Group 1", description="Toggle panel/group visibility", default=False ) # pyright: ignore[reportInvalidTypeForm] # ========================================================================= # Functions # ========================================================================= def hide_all_coll(): mabulig_rig_coll = bpy.data.collections['Mabulig-Rig'].children for collection in mabulig_rig_coll: collection.hide_viewport = True mabulig_rig_objects = bpy.data.collections['Mabulig-Rig'].objects for obj in mabulig_rig_objects: obj.hide_viewport = True def show_all_helpers(): mabulig_rig_coll = bpy.data.collections['Mabulig-Rig'].children for collection in mabulig_rig_coll: collection.hide_viewport = False mabulig_rig_objects = bpy.data.collections['Mabulig-Rig'].objects for obj in mabulig_rig_objects: obj.hide_viewport = False def inspect_scene(): coll_name = "Mabulig-Rig" if bpy.data.collections.get(coll_name) is not None: bpy.context.scene.panel_propert.panel2_g1 = False bpy.context.scene.panel_propert.panel3_g1 = True else: bpy.context.scene.panel_propert.panel3_g1 = False bpy.context.scene.panel_propert.panel2_g1 = True # ========================================================================= # OPERATORS # ========================================================================= # SETUP SCENE BUTTON class SceneSetup(bpy.types.Operator): bl_idname = "scene_setup.operator" bl_label = "Setup Scene" bl_description = "Imports all the necessary rigging setup components" def execute(self, context): source_blend_name = "rig_setup.blend" collection_name = "Mabulig-Rig" script_dir = os.path.dirname(os.path.abspath(__file__)) filepath = os.path.join(script_dir, source_blend_name) with bpy.data.libraries.load(filepath) as (data_from, data_to): if collection_name in data_from.collections: data_to.collections.append(collection_name) else: print(f"Collection '{collection_name}' not found in {source_blend_name}") appended_collection = bpy.data.collections.get(collection_name) if appended_collection: # Link it to the scene's master collection bpy.context.scene.collection.children.link(appended_collection) print(f"Successfully appended and linked collection: '{collection_name}'") # initially hide all collections hide_all_coll(); #make certain layers unselectable bpy.data.collections['gular'].hide_select = True bpy.data.collections['belly_l'].hide_select = True bpy.data.collections['belly_r'].hide_select = True bpy.data.collections['nostril_l'].hide_select = True bpy.data.collections['nostril_r'].hide_select = True #Disable self panel group bpy.context.scene.panel_propert.panel2_g1 = False bpy.context.scene.panel_propert.panel3_g1 = True return {'FINISHED'} # SETUP SKELETAL BONES class SkelSetup(bpy.types.Operator): bl_idname = "skel_setup.operator" bl_label = "Skeleton" bl_description = "Enables the empties for easier skeletal bone placement" def execute(self, context): hide_all_coll(); bpy.data.collections['helpers'].hide_viewport = False return {'FINISHED'} # SETUP GULAR BONES class GularSetup(bpy.types.Operator): bl_idname = "gular_setup.operator" bl_label = "Gular" bl_description = "Enables the gular mesh" def execute(self, context): hide_all_coll(); bpy.data.objects['gular_mesh'].hide_viewport = False # bpy.data.collections['gular'].hide_viewport = False return {'FINISHED'} # SETUP BELLY BONES class BellySetup(bpy.types.Operator): bl_idname = "belly_setup.operator" bl_label = "Belly" bl_description = "Enables the belly mesh" def execute(self, context): hide_all_coll(); bpy.data.objects['belly_mesh'].hide_viewport = False # bpy.data.collections['belly_l'].hide_viewport = False # bpy.data.collections['belly_r'].hide_viewport = False return {'FINISHED'} # SETUP NOSTRIL BONES class NostrilsSetup(bpy.types.Operator): bl_idname = "nostrils_setup.operator" bl_label = "Nostrils" bl_description = "Enables the nostrils mesh" def execute(self, context): hide_all_coll(); bpy.data.objects['nostril_mesh'].hide_viewport = False # bpy.data.collections['nostril_l'].hide_viewport = False # bpy.data.collections['nostril_r'].hide_viewport = False return {'FINISHED'} # SETUP FINGER BONES class FingersSetup(bpy.types.Operator): bl_idname = "fingers_setup.operator" bl_label = "Fingers" bl_description = "Enables the empties for finger bones placement" def execute(self, context): hide_all_coll(); bpy.data.collections['fingers'].hide_viewport = False return {'FINISHED'} # SETUP TOES BONES class ToesSetup(bpy.types.Operator): bl_idname = "toes_setup.operator" bl_label = "Toes" bl_description = "Enables the empties for toe bones placement" def execute(self, context): hide_all_coll(); bpy.data.collections['toes'].hide_viewport = False return {'FINISHED'} # SPAWN RULER PLANE class RulerPlane(bpy.types.Operator): bl_idname = "ruler_plane.operator" bl_label = "Spawn Ruler Plane" bl_description = "Spawns a plane with the SVL length" def execute(self, context): props = context.scene.specimen_props svl_val = props.svl # Directly reset the scene cursor location and rotation context.scene.cursor.location = (0.0, 0.0, 0.0) context.scene.cursor.rotation_euler = (0.0, 0.0, 0.0) # Spawn plane mesh, set size to SVL, diplaymode to wireframe, then show in front bpy.ops.mesh.primitive_plane_add(size=2, enter_editmode=False, align='WORLD', scale=(1, 1, 1)) rulerplane_obj = context.active_object rulerplane_obj.scale = (svl_val, svl_val, 1.0) bpy.ops.object.transform_apply(location=False, rotation=False, scale=True) rulerplane_obj.name = f"Specimen_Plane_{svl_val:.2f}m" rulerplane_obj.display_type = 'WIRE' rulerplane_obj.show_in_front = True # Enable Panel 2, Group 2 bpy.context.scene.panel_propert.panel1_g2 = True return {'FINISHED'} # APPLY SCALE class ApplyScale(bpy.types.Operator): bl_idname = "apply_scale.operator" bl_label = "Apply Scale and Adjust" bl_description = "Applies the scale and adjusts the mesh for the 3d viewer scale" def execute(self, context): props = context.scene.specimen_props bpy.ops.object.select_all(action='DESELECT') target_obj = props.target_model if target_obj: target_obj.select_set(True) target_obj.scale = (10, 10, 10) bpy.ops.object.transform_apply(location=False, rotation=True, scale=True) # Enable Panel 2, Group 2 bpy.context.scene.panel_propert.panel2_g1 = True else: print("No model selected") return {'FINISHED'} # SHOW ALL class ShowAllComponents(bpy.types.Operator): bl_idname = "show_all_components.operator" bl_label = "Show All" bl_description = "Show all rigging components" def execute(self, context): show_all_helpers() bpy.data.objects['OG_Armature'].hide_viewport = True bpy.data.collections['rig_shapes'].hide_viewport = True bpy.data.collections['gular'].hide_viewport = True bpy.data.collections['belly_l'].hide_viewport = True bpy.data.collections['belly_r'].hide_viewport = True bpy.data.collections['nostril_l'].hide_viewport = True bpy.data.collections['nostril_r'].hide_viewport = True return {'FINISHED'} # GENERATE CONTROL RIG class GenerateControlRig(bpy.types.Operator): bl_idname = "generate_control_rig.operator" bl_label = "Generate Control Rig" bl_description = "Builds the control rig according to helper placement" def execute(self, context): controlrig_generate.generatecontrolrig(); return {'FINISHED'} # REFRESH BUTTON class RefreshScene(bpy.types.Operator): bl_idname = "refresh_inspect.operator" bl_label = "Refresh" bl_description = "Forces re-check to see if the Mabulig-Rig setup exists" def execute(self, context): inspect_scene() return {'FINISHED'} # ADJUST ARMATURE class AdjustArmature(bpy.types.Operator): bl_idname = "adjust_armature.operator" bl_label = "Adjust Armature" bl_description = "Auto setup armature to follow helpers" def execute(self, context): rigger_functionality.startAutoRig() return {'FINISHED'} # ========================================================================= # PANELS # ========================================================================= class ModelPrepPanel(bpy.types.Panel): bl_label = "1. Model Preparation" bl_idname = "model_prep.panel" bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_category = "Mabulig Rigger" def draw(self, context): layout = self.layout props = context.scene.specimen_props mod_prep_col1 = layout.column() mod_prep_col1.prop(props, "svl") mod_prep_col1.operator("ruler_plane.operator", icon='MESH_PLANE') # layout.separator() # mod_prep_col2 = layout.column() # mod_prep_col2.prop(props, "target_model") # mod_prep_col2.operator("apply_scale.operator", icon='FIXED_SIZE') # mod_prep_col2.enabled = bpy.context.scene.panel_propert.panel1_g2 class MabuligRiggerSceneSetupPanel(bpy.types.Panel): bl_label = "2. Scene Setup" bl_idname = "scene_setup.panel" bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_category = "Mabulig Rigger" def draw_header_preset(self, context): layout = self.layout layout.operator("refresh_inspect.operator", icon='FILE_REFRESH') def draw(self, context): layout = self.layout #Add Scene Setup button scene_prep_col1 = layout.column() scene_prep_col1.operator("scene_setup.operator", icon='OUTLINER_COLLECTION') scene_prep_col1.enabled = bpy.context.scene.panel_propert.panel2_g1 class RiggingProcessPanel(bpy.types.Panel): bl_label = "3. Rigging Process" bl_idname = "rigging_process.panel" bl_space_type = 'VIEW_3D' bl_region_type = 'UI' bl_category = "Mabulig Rigger" def draw(self, context): layout = self.layout rig_proc_row = layout.row() rig_proc_row.alignment = 'CENTER' rig_proc_row.label(text="Setup Helpers") rig_proc_grid = layout.grid_flow(columns=3, align=True) #Add Buttons rig_proc_grid.operator("skel_setup.operator") rig_proc_grid.operator("gular_setup.operator") rig_proc_grid.operator("fingers_setup.operator") rig_proc_grid.operator("belly_setup.operator") rig_proc_grid.operator("toes_setup.operator") rig_proc_grid.operator("nostrils_setup.operator") rig_proc_row2 = layout.column() rig_proc_row2.alignment = 'CENTER' rig_proc_row2.operator("show_all_components.operator") layout.separator() rig_proc_row3 = layout.column() rig_proc_row3.alignment = 'CENTER' rig_proc_row3.operator("adjust_armature.operator") rig_proc_row3.operator("generate_control_rig.operator") rig_proc_grid.enabled = bpy.context.scene.panel_propert.panel3_g1 rig_proc_row2.enabled = bpy.context.scene.panel_propert.panel3_g1 rig_proc_row3.enabled = bpy.context.scene.panel_propert.panel3_g1 classes = (SceneSetup, SkelSetup, GularSetup, BellySetup, NostrilsSetup, FingersSetup, ToesSetup, RulerPlane, ApplyScale, ModelPrepPanel, MabuligRiggerSceneSetupPanel, RiggingProcessPanel, SpecimenProperties, PanelProperties, ShowAllComponents, GenerateControlRig, RefreshScene, AdjustArmature) def register(): for i in classes: bpy.utils.register_class(i) bpy.types.Scene.specimen_props = bpy.props.PointerProperty(type=SpecimenProperties) bpy.types.Scene.panel_propert = bpy.props.PointerProperty(type=PanelProperties) def unregister(): for i in classes: bpy.utils.unregister_class(i) del bpy.types.Scene.specimen_props del bpy.types.Scene.panel_propert if __name__ == "__main__": register() inspect_scene()