From 9a7afcbcae91978db8173e205f0ec73f1d6ad440 Mon Sep 17 00:00:00 2001 From: Demeter Dzadik Date: Wed, 18 Aug 2021 19:23:01 +0200 Subject: Rigify: Clean up "Rigify Buttons" panel UX The UX for this panel felt like it could use a facelift. It was extremely ugly to look at, nothing about it was done the correct way and it broke every possible modern Blender UI convention it could. Before/After: {F10135475} {F10159077} After generating a rig: {F10159078} - Removed the "overwrite/new" enum. - If there is a target rig object, we overwrite. If not, we create. I think that's intuitive behaviour without the extra UI element. - If a rig object with the desired name already existed, but wasn't selected as the target rig, the "overwrite" option still overwrote that rig. I don't agree with that because this meant messing with data without indicating that that data is going to be messed with. Unaware users could lose data/work. With these changes, the worst thing that can happen is that your rig ends up with a .001 suffix. - Removed the "rig name" text input field. Before this patch, this would always rename your rig object and your rig script text datablock, which I think is more frustrating than useful. Now you can simply rename them after generation yourself, and the names will be kept in subsequent generations. - Renamed the panel from "Rigify Buttons" to "Rigify Generation" in pose/object mode and "Rigify Samples" in edit mode. - Changed the "Advanced Options" into a sub-panel instead. - Single column layout. - Added an info message to show the name of the successfully generated rig: {F10159079} Feedback welcome. Differential Revision: https://developer.blender.org/D11356 --- rigify/__init__.py | 16 ---- rigify/generate.py | 59 ++++-------- rigify/rig_ui_template.py | 25 +---- rigify/ui.py | 228 +++++++++++++++++++++++----------------------- 4 files changed, 137 insertions(+), 191 deletions(-) diff --git a/rigify/__init__.py b/rigify/__init__.py index 1bb633f6..1eac52ae 100644 --- a/rigify/__init__.py +++ b/rigify/__init__.py @@ -507,20 +507,6 @@ 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) @@ -579,8 +565,6 @@ 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 aa9a9a84..bb3f1ac0 100644 --- a/rigify/generate.py +++ b/rigify/generate.py @@ -65,53 +65,28 @@ class Generator(base_generate.BaseGenerator): return rig_module.Rig - - def __create_rig_object(self): - scene = self.scene - id_store = self.id_store + + 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. + """ meta_data = self.metarig.data - # 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 + target_rig = meta_data.rigify_target_rig + if target_rig: + return target_rig - if not obj and name in scene.objects: - obj = scene.objects[name] + rig_new_name = meta_data.rigify_rig_basename or "rig" - if obj: - self.rig_old_name = obj.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) - 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' + meta_data.rigify_target_rig = target_rig + target_rig.data.pose_position = 'POSE' - self.obj = obj - return obj + return target_rig def __create_widget_group(self): @@ -383,7 +358,7 @@ class Generator(base_generate.BaseGenerator): #------------------------------------------ # Create/find the rig object and set it up - obj = self.__create_rig_object() + self.obj = 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 c83dd02b..8780461d 100644 --- a/rigify/rig_ui_template.py +++ b/rigify/rig_ui_template.py @@ -1167,27 +1167,10 @@ class ScriptGenerator(base_generate.GeneratorPlugin): layer_layout += [(l.name, l.row)] # Generate the 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 + script = metarig.data.rigify_rig_ui + if not script: + script = bpy.data.texts.new("rig_ui.py") + 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 6ba455da..2dda2485 100644 --- a/rigify/ui.py +++ b/rigify/ui.py @@ -59,140 +59,139 @@ 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" - @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 +class DATA_PT_rigify_generate(DATA_PT_rigify_generate_base): + bl_label = "Rigify Generation" def draw(self, context): - C = context layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False obj = context.object - id_store = C.window_manager + id_store = context.window_manager + + armature_id_store = context.object.data - if obj.mode in {'POSE', 'OBJECT'}: - armature_id_store = C.object.data + show_warning = False + show_update_metarig = False + show_not_updatable = False + show_upgrade_face = False - WARNING = "Warning: Some features may change after generation" - show_warning = False - show_update_metarig = False - show_not_updatable = False - show_upgrade_face = False + check_props = ['IK_follow', 'root/parent', 'FK_limb_follow', 'IK_Stretch'] - check_props = ['IK_follow', 'root/parent', 'FK_limb_follow', 'IK_Stretch'] + for bone in obj.pose.bones: + if bone.bone.layers[30] and (list(set(bone.keys()) & set(check_props))): + show_warning = True + break - for bone in obj.pose.bones: - if bone.bone.layers[30] and (list(set(bone.keys()) & set(check_props))): - show_warning = True + 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 - 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") + if show_warning: + layout.label(text="Warning: Some features may change after generation", icon='ERROR') - row = layout.row() - # Rig type field + 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.") - col = layout.column(align=True) - col.active = (not 'rig_id' in C.object.data) + 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") - col.separator() - row = col.row() - row.operator("pose.rigify_generate", text="Generate Rig", icon='POSE_HLT') + enable_generation = not (show_not_updatable or show_update_metarig) - row.enabled = enable_generate_and_advanced + col = layout.column() + col.enabled = enable_generation - if armature_id_store.rigify_advanced_generation: - icon = 'UNLOCKED' - else: - icon = 'LOCKED' + # 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') - col = layout.column() - col.enabled = enable_generate_and_advanced - row = col.row() - row.prop(armature_id_store, "rigify_advanced_generation", toggle=True, icon=icon) +class DATA_PT_rigify_generate_advanced(DATA_PT_rigify_generate_base): + bl_label = "Advanced" + bl_parent_id = 'DATA_PT_rigify_generate' - if armature_id_store.rigify_advanced_generation: + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False - 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') + 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') - 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_samples(bpy.types.Panel): + bl_label = "Rigify Samples" + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = "data" + + @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' + + 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 + + # 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: + 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') + + 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): @@ -788,8 +787,9 @@ class Generate(bpy.types.Operator): return is_metarig(context.object) def execute(self, context): + metarig = context.object try: - generate.generate_rig(context, context.object) + generate.generate_rig(context, metarig) except MetarigError as rig_exception: import traceback traceback.print_exc() @@ -800,6 +800,8 @@ 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') @@ -1378,7 +1380,9 @@ classes = ( DATA_MT_rigify_bone_groups_context_menu, DATA_PT_rigify_bone_groups, DATA_PT_rigify_layer_names, - DATA_PT_rigify_buttons, + DATA_PT_rigify_generate, + DATA_PT_rigify_generate_advanced, + DATA_PT_rigify_samples, BONE_PT_rigify_buttons, VIEW3D_PT_rigify_animation_tools, VIEW3D_PT_tools_rigify_dev, -- cgit v1.2.3