[Bf-extensions-cvs] [36e8d00a] master: Rigify: add support for user-defined rig packages and related utilities.

Alexander Gavrilov noreply at git.blender.org
Thu Mar 14 12:40:41 CET 2019


Commit: 36e8d00aec705b06008a0bc334fe266448b4f2c2
Author: Alexander Gavrilov
Date:   Sat Feb 16 13:57:57 2019 +0300
Branches: master
https://developer.blender.org/rBA36e8d00aec705b06008a0bc334fe266448b4f2c2

Rigify: add support for user-defined rig packages and related utilities.

As suggested by @icappielo, and after discussion with @meta-androcto,
I start a public request to commit third-party contributions already
accepted to https://github.com/eigen-value/rigify/tree/rigify_0.6_beta

Specifically, this includes:

* User-defined rig package (feature set) support by @pioverfour.
  This allows users to install pre-packaged rig sets via zip
  files, which become accessible together with built-in rigs,
  as discussed in T52758.

  https://github.com/eigen-value/rigify/pull/1

* Modularization of python script generation, allowing rigs to
  add their own utility functions and operators to the generated
  script. This is critical to make custom rig support really
  useful.

  https://github.com/eigen-value/rigify/pull/5

* The utils.py file is split into multiple modules with a backward
  compatibility proxy for old functions.

* Automatic verification that different rigs don't try to create
  different rig settings with the same name to alleviate increased
  risk of namespace conflicts with custom rigs.

  https://github.com/eigen-value/rigify/pull/7

* New utility class that implements bone layer selection UI.

  https://github.com/eigen-value/rigify/pull/6

* New utilities to replace copy & pasted boilerplate code for
  creating custom properties, constraints and drivers.

  https://github.com/eigen-value/rigify/pull/11

Some other random changes by MAD have likely slipped through.

These changes have already been extensively discussed and accepted
into the branch by @luciorossi, so I see no reason not to commit
them to the official repository to be tested during 2.8 beta.

Reviewers: icappiello

Differential Revision: https://developer.blender.org/D4364

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

M	rigify/__init__.py
A	rigify/feature_sets.py
M	rigify/generate.py
M	rigify/legacy/rigs/biped/arm/__init__.py
M	rigify/legacy/rigs/biped/leg/__init__.py
M	rigify/metarig_menu.py
M	rigify/rig_lists.py
M	rigify/rig_ui_template.py
M	rigify/rigs/experimental/super_chain.py
M	rigify/rigs/faces/super_face.py
M	rigify/rigs/limbs/arm.py
M	rigify/rigs/limbs/leg.py
M	rigify/rigs/limbs/paw.py
M	rigify/rigs/limbs/simple_tentacle.py
M	rigify/rigs/limbs/super_limb.py
M	rigify/rigs/spines/super_spine.py
M	rigify/ui.py
D	rigify/utils.py
A	rigify/utils/__init__.py
A	rigify/utils/animation.py
A	rigify/utils/bones.py
A	rigify/utils/collections.py
A	rigify/utils/errors.py
A	rigify/utils/layers.py
A	rigify/utils/mechanism.py
A	rigify/utils/misc.py
A	rigify/utils/naming.py
A	rigify/utils/rig.py
A	rigify/utils/widgets.py
A	rigify/utils/widgets_basic.py
A	rigify/utils/widgets_special.py

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

diff --git a/rigify/__init__.py b/rigify/__init__.py
index 9fded5ef..2b0a553f 100644
--- a/rigify/__init__.py
+++ b/rigify/__init__.py
@@ -20,7 +20,7 @@
 
 bl_info = {
     "name": "Rigify",
-    "version": (0, 5),
+    "version": (0, 5, 1),
     "author": "Nathan Vegdahl, Lucio Rossi, Ivan Cappiello",
     "blender": (2, 80, 0),
     "description": "Automatic rigging from building-block components",
@@ -37,8 +37,9 @@ if "bpy" in locals():
     importlib.reload(utils)
     importlib.reload(metarig_menu)
     importlib.reload(rig_lists)
+    importlib.reload(feature_sets)
 else:
-    from . import utils, rig_lists, generate, ui, metarig_menu
+    from . import (utils, rig_lists, generate, ui, metarig_menu, feature_sets)
 
 import bpy
 import sys
@@ -126,14 +127,35 @@ class RigifyPreferences(AddonPreferences):
 
             register()
 
+    def update_external_rigs(self):
+        """Get external feature sets"""
+        if self.legacy_mode:
+            return
+
+        feature_sets_path = os.path.join(bpy.utils.script_path_user(), 'rigify')
+
+        if os.path.exists(feature_sets_path):
+            if feature_sets_path not in sys.path:
+                sys.path.append(feature_sets_path)
+            # Reload rigs
+            print('Reloading external rigs...')
+            rig_lists.get_external_rigs(feature_sets_path)
+
+            # Reload metarigs
+            print('Reloading external metarigs...')
+            metarig_menu.get_external_metarigs(feature_sets_path)
+
     legacy_mode: BoolProperty(
         name='Rigify Legacy Mode',
         description='Select if you want to use Rigify in legacy mode',
         default=False,
         update=update_legacy
     )
+
     show_expanded: BoolProperty()
 
+    show_rigs_folder_expanded: BoolProperty()
+
     def draw(self, context):
         layout = self.layout
         column = layout.column()
@@ -160,6 +182,33 @@ class RigifyPreferences(AddonPreferences):
             split.label(text='Description:')
             split.label(text='When enabled the add-on will run in legacy mode using the old 2.76b feature set.')
 
+        box = column.box()
+        rigs_expand = getattr(self, 'show_rigs_folder_expanded')
+        icon = 'TRIA_DOWN' if rigs_expand else 'TRIA_RIGHT'
+        col = box.column()
+        row = col.row()
+        sub = row.row()
+        sub.context_pointer_set('addon_prefs', self)
+        sub.alignment = 'LEFT'
+        op = sub.operator('wm.context_toggle', text='', icon=icon,
+                          emboss=False)
+        op.data_path = 'addon_prefs.show_rigs_folder_expanded'
+        sub.label(text='{}: {}'.format('Rigify', 'External feature sets'))
+        if rigs_expand:
+            if os.path.exists(os.path.join(bpy.utils.script_path_user(), 'rigify')):
+                feature_sets_path = os.path.join(bpy.utils.script_path_user(), 'rigify')
+                for fs in os.listdir(feature_sets_path):
+                    row = col.row()
+                    row.label(text=fs)
+                    op = row.operator("wm.rigify_remove_feature_set", text="Remove", icon='CANCEL')
+                    op.featureset = fs
+            row = col.row(align=True)
+            row.operator("wm.rigify_add_feature_set", text="Install Feature Set from File...", icon='FILEBROWSER')
+
+            split = col.row().split(factor=0.15)
+            split.label(text='Description:')
+            split.label(text='External feature sets (rigs, metarigs, ui layouts)')
+
         row = layout.row()
         row.label(text="End of Rigify Preferences")
 
@@ -220,10 +269,65 @@ class RigifyParameters(bpy.types.PropertyGroup):
 # Remember the initial property set
 RIGIFY_PARAMETERS_BASE_DIR = set(dir(RigifyParameters))
 
+RIGIFY_PARAMETER_TABLE = {'name': ('DEFAULT', StringProperty())}
+
 def clear_rigify_parameters():
     for name in list(dir(RigifyParameters)):
         if name not in RIGIFY_PARAMETERS_BASE_DIR:
             delattr(RigifyParameters, name)
+            if name in RIGIFY_PARAMETER_TABLE:
+                del RIGIFY_PARAMETER_TABLE[name]
+
+
+def format_property_spec(spec):
+    """Turns the return value of bpy.props.SomeProperty(...) into a readable string."""
+    callback, params = spec
+    param_str = ["%s=%r" % (k, v) for k, v in params.items()]
+    return "%s(%s)" % (callback.__name__, ', '.join(param_str))
+
+
+class RigifyParameterValidator(object):
+    """
+    A wrapper around RigifyParameters that verifies properties
+    defined from rigs for incompatible redefinitions using a table.
+
+    Relies on the implementation details of bpy.props return values:
+    specifically, they just return a tuple containing the real define
+    function, and a dictionary with parameters. This allows comparing
+    parameters before the property is actually defined.
+    """
+    __params = None
+    __rig_name = ''
+    __prop_table = {}
+
+    def __init__(self, params, rig_name, prop_table):
+        self.__params = params
+        self.__rig_name = rig_name
+        self.__prop_table = prop_table
+
+    def __getattr__(self, name):
+        return getattr(self.__params, name)
+
+    def __setattr__(self, name, val):
+        # allow __init__ to work correctly
+        if hasattr(RigifyParameterValidator, name):
+            return object.__setattr__(self, name, val)
+
+        if not (isinstance(val, tuple) and callable(val[0]) and isinstance(val[1], dict)):
+            print("!!! RIGIFY RIG %s: INVALID DEFINITION FOR RIG PARAMETER %s: %r\n" % (self.__rig_name, name, val))
+            return
+
+        if name in self.__prop_table:
+            cur_rig, cur_info = self.__prop_table[name]
+            if val != cur_info:
+                print("!!! RIGIFY RIG %s: REDEFINING PARAMETER %s AS:\n\n    %s\n" % (self.__rig_name, name, format_property_spec(val)))
+                print("!!! PREVIOUS DEFINITION BY %s:\n\n    %s\n" % (cur_rig, format_property_spec(cur_info)))
+
+        # actually defining the property modifies the dictionary with new parameters, so copy it now
+        new_def = (val[0], val[1].copy())
+
+        setattr(self.__params, name, val)
+        self.__prop_table[name] = (self.__rig_name, new_def)
 
 
 class RigifyArmatureLayer(bpy.types.PropertyGroup):
@@ -265,6 +369,7 @@ def register():
 
     # Sub-modules.
     ui.register()
+    feature_sets.register()
     metarig_menu.register()
 
     # Classes.
@@ -274,6 +379,12 @@ def register():
     # Properties.
     bpy.types.Armature.rigify_layers = CollectionProperty(type=RigifyArmatureLayer)
 
+    bpy.types.Armature.active_feature_set = EnumProperty(
+        items=feature_sets.feature_set_items,
+        name="Feature Set",
+        description="Restrict the rig list to a specific custom feature set"
+        )
+
     bpy.types.PoseBone.rigify_type = StringProperty(name="Rigify Type", description="Rig type for this bone")
     bpy.types.PoseBone.rigify_parameters = PointerProperty(type=RigifyParameters)
 
@@ -307,7 +418,7 @@ def register():
         ), name='Theme')
 
     IDStore = bpy.types.WindowManager
-    IDStore.rigify_collection = EnumProperty(items=rig_lists.col_enum_list, default="All",
+    IDStore.rigify_collection = EnumProperty(items=(("All", "All", "All"),), default="All",
         name="Rigify Active Collection",
         description="The selected rig collection")
 
@@ -360,22 +471,41 @@ def register():
     if (ui and 'legacy' in str(ui)) or bpy.context.preferences.addons['rigify'].preferences.legacy_mode:
         bpy.context.preferences.addons['rigify'].preferences.legacy_mode = True
 
+    bpy.context.preferences.addons['rigify'].preferences.update_external_rigs()
+
     # Add rig parameters
-    for rig in rig_lists.rig_list:
-        r = utils.get_rig_type(rig)
-        try:
-            r.add_parameters(RigifyParameters)
-        except AttributeError:
-            pass
+    if bpy.context.preferences.addons['rigify'].preferences.legacy_mode:
+        for rig in rig_lists.rig_list:
+            r = utils.get_rig_type(rig)
+            try:
+                r.add_parameters(RigifyParameterValidator(RigifyParameters, rig, RIGIFY_PARAMETER_TABLE))
+            except AttributeError:
+                pass
+    else:
+        for rig in rig_lists.rigs:
+            r = rig_lists.rigs[rig]['module']
+            try:
+                r.add_parameters(RigifyParameterValidator(RigifyParameters, rig, RIGIFY_PARAMETER_TABLE))
+            except AttributeError:
+                pass
 
 
 def unregister():
     from bpy.utils import unregister_class
 
-    # Properties.
+    # Properties on PoseBones and Armature.
     del bpy.types.PoseBone.rigify_type
     del bpy.types.PoseBone.rigify_parameters
 
+    ArmStore = bpy.types.Armature
+    del ArmStore.rigify_layers
+    del ArmStore.active_feature_set
+    del ArmStore.rigify_colors
+    del ArmStore.rigify_selection_colors
+    del ArmStore.rigify_colors_index
+    del ArmStore.rigify_colors_lock
+    del ArmStore.rigify_theme_to_add
+
     IDStore = bpy.types.WindowManager
     del IDStore.rigify_collection
     del IDStore.rigify_types
@@ -401,3 +531,4 @@ def unregister():
     # Sub-modules.
     metarig_menu.unregister()
     ui.unregister()
+    feature_sets.unregister()
diff --git a/rigify/feature_sets.py b/rigify/feature_sets.py
new file mode 100644
index 00000000..9ee6821a
--- /dev/null
+++ b/rigify/feature_sets.py
@@ -0,0 +1,99 @@
+#====================== BEGIN GPL LICENSE BLOCK ======================
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software Foundation,
+#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+#

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-extensions-cvs mailing list