diff options
author | Alexander Gavrilov <angavrilov@gmail.com> | 2021-09-20 22:16:41 +0300 |
---|---|---|
committer | Alexander Gavrilov <angavrilov@gmail.com> | 2021-09-20 22:34:09 +0300 |
commit | eed6d6cc132d194efe018996d43a36ebc8d91ad4 (patch) | |
tree | 0817ea53fe929cb2959e219d1076d998d9d22b61 /rigify/generate.py | |
parent | ecf30de46c368ffddad259c125402a38e6093382 (diff) |
Rigify: support generating mirrored linked duplicate widgets.
After the custom shape scale property was split into an XYZ vector,
it is possible to generate truly mirrored widgets using the same
mesh for the left and right side. The widgets Rigify generates are
originally symmetrical, but in practice they usually need to be
tweaked to fit the character better, and proper mirroring matters.
This commit implements widget mirroring and enables it by default.
When reusing widgets left from a previous generation the code tries
to detect whether they were actually originally mirrored by checking
object scale to avoid flipping in pre-existing rigs.
As an aside, reusing pre-existing widgets is made more robust
to random name changes by building a table via scanning the old
generated rig before overwriting it.
Diffstat (limited to 'rigify/generate.py')
-rw-r--r-- | rigify/generate.py | 58 |
1 files changed, 40 insertions, 18 deletions
diff --git a/rigify/generate.py b/rigify/generate.py index ad8f43b5..a2d9a5d1 100644 --- a/rigify/generate.py +++ b/rigify/generate.py @@ -25,7 +25,7 @@ import time from .utils.errors import MetarigError from .utils.bones import new_bone from .utils.layers import ORG_LAYER, MCH_LAYER, DEF_LAYER, ROOT_LAYER -from .utils.naming import ORG_PREFIX, MCH_PREFIX, DEF_PREFIX, ROOT_NAME, make_original_name +from .utils.naming import ORG_PREFIX, MCH_PREFIX, DEF_PREFIX, ROOT_NAME, make_original_name, change_name_side, get_name_side, Side from .utils.widgets import WGT_PREFIX from .utils.widgets_special import create_root_widget from .utils.mechanism import refresh_all_drivers @@ -165,27 +165,44 @@ class Generator(base_generate.BaseGenerator): for obj in list(old_collection.objects): bpy.data.objects.remove(obj) - # Rename widgets and collection if renaming - if self.rig_old_name: - old_prefix = WGT_PREFIX + self.rig_old_name + "_" - new_prefix = WGT_PREFIX + self.obj.name + "_" + # Rename the collection + old_collection.name = new_group_name - for obj in list(old_collection.objects): - if obj.name.startswith(old_prefix): - new_name = new_prefix + obj.name[len(old_prefix):] - elif obj.name == wgts_group_name: - new_name = new_group_name - else: - continue + # Create/find widget collection + self.widget_collection = ensure_widget_collection(self.context, new_group_name) + self.use_mirror_widgets = self.metarig.data.rigify_mirror_widgets - obj.data.name = new_name - obj.name = new_name + # Build tables for existing widgets + self.old_widget_table = {} + self.new_widget_table = {} + self.widget_mirror_mesh = {} - old_collection.name = new_group_name + if not self.metarig.data.rigify_force_widget_update and self.obj.pose: + # Find all widgets from the collection referenced by the old rig + known_widgets = set(obj.name for obj in self.widget_collection.objects) - # Create/find widget collection - self.widget_collection = ensure_widget_collection(self.context, new_group_name) - self.wgts_group_name = new_group_name + for bone in self.obj.pose.bones: + if bone.custom_shape and bone.custom_shape.name in known_widgets: + self.old_widget_table[bone.name] = bone.custom_shape + + # Rename widgets in case the rig was renamed + name_prefix = WGT_PREFIX + self.obj.name + "_" + + for bone_name, widget in self.old_widget_table.items(): + old_data_name = change_name_side(widget.name, get_name_side(widget.data.name)) + + widget.name = name_prefix + bone_name + + # If the mesh name is the same as the object, rename it too + if widget.data.name == old_data_name: + widget.data.name = change_name_side(widget.name, get_name_side(widget.data.name)) + + # Find meshes for mirroring + if self.use_mirror_widgets: + for bone_name, widget in self.old_widget_table.items(): + mid_name = change_name_side(bone_name, Side.MIDDLE) + if bone_name != mid_name: + self.widget_mirror_mesh[mid_name] = widget.data def __duplicate_rig(self): @@ -373,6 +390,11 @@ class Generator(base_generate.BaseGenerator): # Assign shapes to bones # Object's with name WGT-<bone_name> get used as that bone's shape. for bone in self.obj.pose.bones: + # First check the table built by create_widget + if bone.name in self.new_widget_table: + bone.custom_shape = self.new_widget_table[bone.name] + continue + # Object names are limited to 63 characters... arg wgt_name = (WGT_PREFIX + self.obj.name + '_' + bone.name)[:63] |