diff --git a/rigify/__init__.py b/rigify/__init__.py index 1eac52ae6d1459f699cca0cae9785ede11c4f6a9..1bb633f6c24cf7270c4c6107a7f40e24ab8e6827 100644 --- a/rigify/__init__.py +++ b/rigify/__init__.py @@ -507,6 +507,20 @@ def register(): IDStore.rigify_types = CollectionProperty(type=RigifyName) IDStore.rigify_active_type = IntProperty(name="Rigify Active Type", description="The selected rig type") + bpy.types.Armature.rigify_advanced_generation = BoolProperty(name="Advanced Options", + description="Enables/disables advanced options for Rigify rig generation", + default=False) + + def update_mode(self, context): + if self.rigify_generate_mode == 'new': + self.rigify_force_widget_update = False + + bpy.types.Armature.rigify_generate_mode = EnumProperty(name="Rigify Generate Rig Mode", + description="'Generate Rig' mode. In 'overwrite' mode the features of the target rig will be updated as defined by the metarig. In 'new' mode a new rig will be created as defined by the metarig. Current mode", + update=update_mode, + items=( ('overwrite', 'overwrite', ''), + ('new', 'new', ''))) + bpy.types.Armature.rigify_force_widget_update = BoolProperty(name="Force Widget Update", description="Forces Rigify to delete and rebuild all the rig widgets. if unset, only missing widgets will be created", default=False) @@ -565,6 +579,8 @@ def unregister(): del ArmStore.rigify_colors_index del ArmStore.rigify_colors_lock del ArmStore.rigify_theme_to_add + del ArmStore.rigify_advanced_generation + del ArmStore.rigify_generate_mode del ArmStore.rigify_force_widget_update del ArmStore.rigify_target_rig del ArmStore.rigify_rig_ui diff --git a/rigify/generate.py b/rigify/generate.py index bb3f1ac082853fa18bc54fc664f8934da18033a9..aa9a9a84b2565c0e8758a025b2e2c4180c36823e 100644 --- a/rigify/generate.py +++ b/rigify/generate.py @@ -65,28 +65,53 @@ class Generator(base_generate.BaseGenerator): return rig_module.Rig - - def __create_rig_object(self) -> bpy.types.Object: - """ Check if the generated rig already exists, so we can - regenerate in the same object. If not, create a new - object to generate the rig in. - """ + + def __create_rig_object(self): + scene = self.scene + id_store = self.id_store meta_data = self.metarig.data - target_rig = meta_data.rigify_target_rig - if target_rig: - return target_rig + # Check if the generated rig already exists, so we can + # regenerate in the same object. If not, create a new + # object to generate the rig in. + print("Fetch rig.") + + self.rig_new_name = name = meta_data.rigify_rig_basename or "rig" + + obj = None + + if meta_data.rigify_generate_mode == 'overwrite': + obj = meta_data.rigify_target_rig - rig_new_name = meta_data.rigify_rig_basename or "rig" + if not obj and name in scene.objects: + obj = scene.objects[name] - target_rig = bpy.data.objects.new(rig_new_name, bpy.data.armatures.new(rig_new_name)) - target_rig.display_type = 'WIRE' - self.collection.objects.link(target_rig) + if obj: + self.rig_old_name = obj.name - meta_data.rigify_target_rig = target_rig - target_rig.data.pose_position = 'POSE' + obj.name = name + obj.data.name = obj.name + + rig_collections = filter_layer_collections_by_object(self.usable_collections, obj) + self.layer_collection = (rig_collections + [self.layer_collection])[0] + self.collection = self.layer_collection.collection + + elif name in bpy.data.objects: + obj = bpy.data.objects[name] + + if not obj: + obj = bpy.data.objects.new(name, bpy.data.armatures.new(name)) + obj.display_type = 'WIRE' + self.collection.objects.link(obj) + + elif obj.name not in self.collection.objects: # rig exists but was deleted + self.collection.objects.link(obj) + + meta_data.rigify_target_rig = obj + obj.data.pose_position = 'POSE' - return target_rig + self.obj = obj + return obj def __create_widget_group(self): @@ -358,7 +383,7 @@ class Generator(base_generate.BaseGenerator): #------------------------------------------ # Create/find the rig object and set it up - self.obj = obj = self.__create_rig_object() + obj = self.__create_rig_object() # Get rid of anim data in case the rig already existed print("Clear rig animation data.") diff --git a/rigify/rig_ui_template.py b/rigify/rig_ui_template.py index 8780461de0162354e3033df13f306af29cddc410..c83dd02b9302c11f9ee7a596d1f349270c03e5f3 100644 --- a/rigify/rig_ui_template.py +++ b/rigify/rig_ui_template.py @@ -1167,10 +1167,27 @@ class ScriptGenerator(base_generate.GeneratorPlugin): layer_layout += [(l.name, l.row)] # Generate the UI script - script = metarig.data.rigify_rig_ui - if not script: - script = bpy.data.texts.new("rig_ui.py") - metarig.data.rigify_rig_ui = script + if metarig.data.rigify_rig_basename: + rig_ui_name = metarig.data.rigify_rig_basename + '_ui.py' + else: + rig_ui_name = 'rig_ui.py' + + script = None + + if metarig.data.rigify_generate_mode == 'overwrite': + script = metarig.data.rigify_rig_ui + + if not script and rig_ui_name in bpy.data.texts: + script = bpy.data.texts[rig_ui_name] + + if script: + script.clear() + script.name = rig_ui_name + + if script is None: + script = bpy.data.texts.new(rig_ui_name) + + metarig.data.rigify_rig_ui = script for s in OrderedDict.fromkeys(self.ui_imports): script.write(s + "\n") diff --git a/rigify/ui.py b/rigify/ui.py index 2dda2485d41d56439bbf8b45f9dd13700ee88f08..6ba455dafcf3e9b35a2beb6f7d5b867a0332668c 100644 --- a/rigify/ui.py +++ b/rigify/ui.py @@ -59,139 +59,140 @@ def build_type_list(context, rigify_types): a = rigify_types.add() a.name = r -class DATA_PT_rigify_generate_base(bpy.types.Panel): - @classmethod - def poll(cls, context): - obj = context.object - if not context.object: - return False - return obj.type == 'ARMATURE' \ - and obj.data.get("rig_id") is None \ - and obj.mode in {'POSE', 'OBJECT'} +class DATA_PT_rigify_buttons(bpy.types.Panel): + bl_label = "Rigify Buttons" bl_space_type = 'PROPERTIES' bl_region_type = 'WINDOW' bl_context = "data" -class DATA_PT_rigify_generate(DATA_PT_rigify_generate_base): - bl_label = "Rigify Generation" + @classmethod + def poll(cls, context): + if not context.object: + return False + return context.object.type == 'ARMATURE' and context.active_object.data.get("rig_id") is None def draw(self, context): + C = context layout = self.layout - layout.use_property_split = True - layout.use_property_decorate = False obj = context.object - id_store = context.window_manager - - armature_id_store = context.object.data + id_store = C.window_manager - show_warning = False - show_update_metarig = False - show_not_updatable = False - show_upgrade_face = False + if obj.mode in {'POSE', 'OBJECT'}: + armature_id_store = C.object.data - check_props = ['IK_follow', 'root/parent', 'FK_limb_follow', 'IK_Stretch'] + WARNING = "Warning: Some features may change after generation" + show_warning = False + show_update_metarig = False + show_not_updatable = False + show_upgrade_face = False - for bone in obj.pose.bones: - if bone.bone.layers[30] and (list(set(bone.keys()) & set(check_props))): - show_warning = True - break + check_props = ['IK_follow', 'root/parent', 'FK_limb_follow', 'IK_Stretch'] - for b in obj.pose.bones: - if b.rigify_type in outdated_types.keys(): - old_bone = b.name - old_rig = b.rigify_type - if outdated_types[b.rigify_type]: - show_update_metarig = True - else: - show_update_metarig = False - show_not_updatable = True + for bone in obj.pose.bones: + if bone.bone.layers[30] and (list(set(bone.keys()) & set(check_props))): + show_warning = True break - elif b.rigify_type == 'faces.super_face': - show_upgrade_face = True - - if show_warning: - layout.label(text="Warning: Some features may change after generation", icon='ERROR') - - if show_not_updatable: - layout.label(text="WARNING: This metarig contains deprecated rigify rig-types and cannot be upgraded automatically.", icon='ERROR') - layout.label(text="("+old_rig+" on bone "+old_bone+")") - layout.label(text="If you want to use it anyway try enabling the legacy mode before generating again.") - - layout.operator("pose.rigify_switch_to_legacy", text="Switch to Legacy") - elif show_update_metarig: - layout.label(text="This metarig contains old rig-types that can be automatically upgraded to benefit of rigify's new features.", icon='ERROR') - layout.label(text="("+old_rig+" on bone "+old_bone+")") - layout.label(text="To use it as-is you need to enable legacy mode.",) - layout.operator("pose.rigify_upgrade_types", text="Upgrade Metarig") - elif show_upgrade_face: - layout.label(text="This metarig uses the old face rig.", icon='INFO') - layout.operator("pose.rigify_upgrade_face") - enable_generation = not (show_not_updatable or show_update_metarig) + for b in obj.pose.bones: + if b.rigify_type in outdated_types.keys(): + old_bone = b.name + old_rig = b.rigify_type + if outdated_types[b.rigify_type]: + show_update_metarig = True + else: + show_update_metarig = False + show_not_updatable = True + break + elif b.rigify_type == 'faces.super_face': + show_upgrade_face = True + + if show_warning: + layout.label(text=WARNING, icon='ERROR') + + enable_generate_and_advanced = not (show_not_updatable or show_update_metarig) + + if show_not_updatable: + layout.label(text="WARNING: This metarig contains deprecated rigify rig-types and cannot be upgraded automatically.", icon='ERROR') + layout.label(text="("+old_rig+" on bone "+old_bone+")") + elif show_update_metarig: + layout.label(text="This metarig contains old rig-types that can be automatically upgraded to benefit of rigify's new features.", icon='ERROR') + layout.label(text="("+old_rig+" on bone "+old_bone+")") + layout.operator("pose.rigify_upgrade_types", text="Upgrade Metarig") + elif show_upgrade_face: + layout.label(text="This metarig uses the old face rig.", icon='INFO') + layout.operator("pose.rigify_upgrade_face") - col = layout.column() - col.enabled = enable_generation - - # Make it clear whether we are generating a new object or overwriting existing one. - text = "Generate New Rig" - if armature_id_store.rigify_target_rig: - text = f'Re-Generate Target Rig' - col.row().operator("pose.rigify_generate", text=text, icon='POSE_HLT') - -class DATA_PT_rigify_generate_advanced(DATA_PT_rigify_generate_base): - bl_label = "Advanced" - bl_parent_id = 'DATA_PT_rigify_generate' - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - layout.use_property_decorate = False - - armature_id_store = context.object.data - - col = layout.column() - col.row().prop(armature_id_store, 'rigify_target_rig', text="Target Rig") - col.row().prop(armature_id_store, 'rigify_rig_ui', text="Rig UI Script") - col.row().prop(armature_id_store, 'rigify_force_widget_update') + row = layout.row() + # Rig type field + col = layout.column(align=True) + col.active = (not 'rig_id' in C.object.data) -class DATA_PT_rigify_samples(bpy.types.Panel): - bl_label = "Rigify Samples" - bl_space_type = 'PROPERTIES' - bl_region_type = 'WINDOW' - bl_context = "data" + col.separator() + row = col.row() + row.operator("pose.rigify_generate", text="Generate Rig", icon='POSE_HLT') - @classmethod - def poll(cls, context): - obj = context.object - if not obj: - return False - return obj.type == 'ARMATURE' \ - and obj.data.get("rig_id") is None \ - and obj.mode == 'EDIT' + row.enabled = enable_generate_and_advanced - def draw(self, context): - layout = self.layout - layout.use_property_split = True - layout.use_property_decorate = False - obj = context.object - id_store = context.window_manager + if armature_id_store.rigify_advanced_generation: + icon = 'UNLOCKED' + else: + icon = 'LOCKED' - # Build types list - build_type_list(context, id_store.rigify_types) + col = layout.column() + col.enabled = enable_generate_and_advanced + row = col.row() + row.prop(armature_id_store, "rigify_advanced_generation", toggle=True, icon=icon) - if id_store.rigify_active_type > len(id_store.rigify_types): - id_store.rigify_active_type = 0 + if armature_id_store.rigify_advanced_generation: - # Rig type list - if len(feature_set_list.get_installed_list()) > 0: - layout.row().prop(obj.data, "active_feature_set") - row = layout.row() - row.template_list("UI_UL_list", "rigify_types", id_store, "rigify_types", id_store, 'rigify_active_type') + row = col.row(align=True) + row.prop(armature_id_store, "rigify_generate_mode", expand=True) + + main_row = col.row(align=True).split(factor=0.3) + col1 = main_row.column() + col2 = main_row.column() + col1.label(text="Rig Name") + row = col1.row() + row.label(text="Target Rig") + row.enabled = (armature_id_store.rigify_generate_mode == "overwrite") + row = col1.row() + row.label(text="Target UI") + row.enabled = (armature_id_store.rigify_generate_mode == "overwrite") + + row = col2.row(align=True) + row.prop(armature_id_store, "rigify_rig_basename", text="", icon="SORTALPHA") + + row = col2.row(align=True) + row.prop(armature_id_store, "rigify_target_rig", text="") + row.enabled = (armature_id_store.rigify_generate_mode == "overwrite") + + row = col2.row() + row.prop(armature_id_store, "rigify_rig_ui", text="", icon='TEXT') + row.enabled = (armature_id_store.rigify_generate_mode == "overwrite") + + row = col.row() + row.prop(armature_id_store, "rigify_force_widget_update") + if armature_id_store.rigify_generate_mode == 'new': + row.enabled = False + + elif obj.mode == 'EDIT': + # Build types list + build_type_list(context, id_store.rigify_types) + + if id_store.rigify_active_type > len(id_store.rigify_types): + id_store.rigify_active_type = 0 + + # Rig type list + if len(feature_set_list.get_installed_list()) > 0: + row = layout.row() + row.prop(context.object.data, "active_feature_set") + row = layout.row() + row.template_list("UI_UL_list", "rigify_types", id_store, "rigify_types", id_store, 'rigify_active_type') - props = layout.operator("armature.metarig_sample_add", text="Add sample") - props.metarig_type = id_store.rigify_types[id_store.rigify_active_type].name + props = layout.operator("armature.metarig_sample_add", text="Add sample") + props.metarig_type = id_store.rigify_types[id_store.rigify_active_type].name class DATA_PT_rigify_layer_names(bpy.types.Panel): @@ -787,9 +788,8 @@ class Generate(bpy.types.Operator): return is_metarig(context.object) def execute(self, context): - metarig = context.object try: - generate.generate_rig(context, metarig) + generate.generate_rig(context, context.object) except MetarigError as rig_exception: import traceback traceback.print_exc() @@ -800,8 +800,6 @@ class Generate(bpy.types.Operator): traceback.print_exc() self.report({'ERROR'}, 'Generation has thrown an exception: ' + str(rig_exception)) - else: - self.report({'INFO'}, 'Successfully generated: "' + metarig.data.rigify_target_rig.name + '"') finally: bpy.ops.object.mode_set(mode='OBJECT') @@ -1380,9 +1378,7 @@ classes = ( DATA_MT_rigify_bone_groups_context_menu, DATA_PT_rigify_bone_groups, DATA_PT_rigify_layer_names, - DATA_PT_rigify_generate, - DATA_PT_rigify_generate_advanced, - DATA_PT_rigify_samples, + DATA_PT_rigify_buttons, BONE_PT_rigify_buttons, VIEW3D_PT_rigify_animation_tools, VIEW3D_PT_tools_rigify_dev,