[Bf-extensions-cvs] [eed6d6cc] master: Rigify: support generating mirrored linked duplicate widgets.

Alexander Gavrilov noreply at git.blender.org
Mon Sep 20 21:34:20 CEST 2021


Commit: eed6d6cc132d194efe018996d43a36ebc8d91ad4
Author: Alexander Gavrilov
Date:   Mon Sep 20 22:16:41 2021 +0300
Branches: master
https://developer.blender.org/rBAeed6d6cc132d194efe018996d43a36ebc8d91ad4

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.

===================================================================

M	rigify/__init__.py
M	rigify/generate.py
M	rigify/rigs/widgets.py
M	rigify/ui.py
M	rigify/utils/widgets.py

===================================================================

diff --git a/rigify/__init__.py b/rigify/__init__.py
index dd55be04..4b926c48 100644
--- a/rigify/__init__.py
+++ b/rigify/__init__.py
@@ -530,6 +530,10 @@ def register():
         description="Forces Rigify to delete and rebuild all the rig widgets. if unset, only missing widgets will be created",
         default=False)
 
+    bpy.types.Armature.rigify_mirror_widgets = BoolProperty(name="Mirror Widgets",
+        description="Make widgets for left and right side bones linked duplicates with negative X scale for the right side, based on bone name symmetry",
+        default=True)
+
     bpy.types.Armature.rigify_target_rig = PointerProperty(type=bpy.types.Object,
         name="Rigify Target Rig",
         description="Defines which rig to overwrite. If unset, a new one called 'rig' will be created",
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]
 
diff --git a/rigify/rigs/widgets.py b/rigify/rigs/widgets.py
index ef0bb544..0c434593 100644
--- a/rigify/rigs/widgets.py
+++ b/rigify/rigs/widgets.py
@@ -97,7 +97,7 @@ def create_ikarrow_widget(rig, bone_name, size=1.0, bone_transform_name=None, ro
 
 def create_hand_widget(rig, bone_name, size=1.0, bone_transform_name=None):
     # Create hand widget
-    obj = create_widget(rig, bone_name, bone_transform_name)
+    obj = create_widget(rig, bone_name, bone_transform_name, subsurf=2)
     if obj is not None:
         verts = [(0.0*size, 1.5*size, -0.7000000476837158*size), (1.1920928955078125e-07*size, -0.25*size, -0.6999999284744263*size), (0.0*size, -0.25*size, 0.7000000476837158*size), (-1.1920928955078125e-07*size, 1.5*size, 0.6999999284744263*size), (5.960464477539063e-08*size, 0.7229999899864197*size, -0.699999988079071*size), (-5.960464477539063e-08*size, 0.7229999899864197*size, 0.699999988079071*size), (1.1920928955078125e-07*size, -2.9802322387695312e-08*size, -0.699999988079071*siz [...]
         edges = [(1, 2), (0, 3), (0, 4), (3, 5), (4, 6), (1, 6), (5, 7), (2, 7)]
@@ -107,8 +107,6 @@ def create_hand_widget(rig, bone_name, size=1.0, bone_transform_name=None):
         mesh.from_pydata(verts, edges, faces)
         mesh.update()
 
-        mod = obj.modifiers.new("subsurf", 'SUBSURF')
-        mod.levels = 2
         return obj
     else:
         return None
@@ -116,7 +114,7 @@ def create_hand_widget(rig, bone_name, size=1.0, bone_transform_name=None):
 
 def create_foot_widget(rig, bone_name, size=1.0, bone_transform_name=None):
     # Create hand widget
-    obj = create_widget(rig, bone_name, bone_transform_name)
+    obj = create_widget(rig, bone_name, bone_transform_name, subsurf=2)
     if obj is not None:
         verts = [(-0.6999998688697815*size, -0.5242648720741272*size, 0.0*size), (-0.7000001072883606*size, 1.2257349491119385*size, 0.0*size), (0.6999998688697815*size, 1.2257351875305176*size, 0.0*size), (0.7000001072883606*size, -0.5242648720741272*size, 0.0*size), (-0.6999998688697815*size, 0.2527350187301636*size, 0.0*size), (0.7000001072883606*size, 0.2527352571487427*size, 0.0*size), (-0.7000001072883606*size, 0.975735068321228*size, 0.0*size), (0.6999998688697815*size, 0.97573524 [...]
         edges = [(1, 2), (0, 3), (0, 4), (3, 5), (4, 6), (1, 6), (5, 7), (2, 7), ]
@@ -126,8 +124,6 @@ def create_foot_widget(rig, bone_name, size=1.0, bone_transform_name=None):
         mesh.from_pydata(verts, edges, faces)
         mesh.update()
 
-        mod = obj.modifiers.new("subsurf", 'SUBSURF')
-        mod.levels = 2
         return obj
     else:
         return None
diff --git a/rigify/ui.py b/rigify/ui.py
index c9592677..c801ac25 100644
--- a/rigify/ui.py
+++ b/rigify/ui.py
@@ -177,6 +177,7 @@ class DATA_PT_rigify_buttons(bpy.types.Panel):
                 if armature_id_store.rigify_generate_mode == 'new':
                     row.enabled = False
 
+                col.prop(armature_id_store, "rigify_mirror_widgets")
                 col.prop(armature_id_store, "rigify_finalize_script", text="Run Script")
 
         elif obj.mode == 'EDIT':
diff --git a/rigify/utils/widgets.py b/rigify/utils/widgets.py
index a8691349..3f0bf252 100644
--- a/rigify/utils/widgets.py
+++ b/rigify/utils/widgets.py
@@ -28,6 +28,7 @@ from itertools import count
 
 from .errors import MetarigError
 from .collections import ensure_widget_collection
+from .naming import change_name_side, get_name_side, Side
 
 WGT_PREFIX = "WGT-"  # Prefix for widget objects
 
@@ -56,47 +57,113 @@ def obj_to_bone(obj, rig, bone_name, bone_transform_name=None):
     elif bone.custom_shape_transform:
         bone = bone.custom_shape_transform
 
-    shape_mat = Matrix.Translation(loc) @ (Euler(rot).to_matrix() @ Matrix.Diagonal(scale)).to_4x4()
+    shape_mat = Matrix.LocRotScale(loc, Euler(rot), scale)
 
     obj.rotation_mode = 'XYZ'
     obj.matrix_basis = rig.matrix_world @ bone.bone.matrix_local @ shape_mat
 
 
-def create_widget(rig, bone_name, bone_transform_name=None, *, widget_name=None, widget_force_new=False):
+def create_widget(rig, bone_name, bone_transform_name=None, *, widget_name=None, widget_force_new=False, subsurf=0):
     """ Creates an empty widget object for a bone, and returns the object.
     """
     assert rig.mode != 'EDIT'
 
-    obj_name = widget_name or WGT_PREFIX + rig.name + '_' + bone_name
+    from ..base_generate import BaseGenerator
+
     scene = bpy.context.scene
-    collection = ensure_widget_collection(bpy.context, 'WGTS_' + rig.name)
+    bone = rig.pose.bones[bone_name]
+
+    # Access the current generator instance when generating (ugh, globals)
+    generator = BaseGenerator.instance
+
+    if generator:
+        collection = generator.widget_collection
+    else:
+        collection = ensure_widget_collection(bpy.context, 'WGTS_' + rig.name)
+
+    use_mirror = generator and generator.use_mirror_widgets
+
+    if use_mirror:
+        bone_mid_name = change_name_side(bone_name, Side.MIDDLE)
+
+    obj_name = widget_name or WGT_PREFIX + rig.name + '_' + bone_name
     reuse_mesh = None
 
     # Check if it already exists i

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-extensions-cvs mailing list