Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/rigify
diff options
context:
space:
mode:
authorAlexander Gavrilov <angavrilov@gmail.com>2019-02-16 13:57:57 +0300
committerAlexander Gavrilov <angavrilov@gmail.com>2019-03-14 14:39:16 +0300
commit36e8d00aec705b06008a0bc334fe266448b4f2c2 (patch)
tree02bf0290ec6423c611e3cd4ad49c69cb77acb67e /rigify
parenteabb5cddf79e5fae3ca429242cf2c6f5a272920e (diff)
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
Diffstat (limited to 'rigify')
-rw-r--r--rigify/__init__.py151
-rw-r--r--rigify/feature_sets.py99
-rw-r--r--rigify/generate.py87
-rw-r--r--rigify/legacy/rigs/biped/arm/__init__.py2
-rw-r--r--rigify/legacy/rigs/biped/leg/__init__.py2
-rw-r--r--rigify/metarig_menu.py165
-rw-r--r--rigify/rig_lists.py89
-rw-r--r--rigify/rig_ui_template.py136
-rw-r--r--rigify/rigs/experimental/super_chain.py64
-rw-r--r--rigify/rigs/faces/super_face.py4
-rw-r--r--rigify/rigs/limbs/arm.py102
-rw-r--r--rigify/rigs/limbs/leg.py102
-rw-r--r--rigify/rigs/limbs/paw.py103
-rw-r--r--rigify/rigs/limbs/simple_tentacle.py62
-rw-r--r--rigify/rigs/limbs/super_limb.py69
-rw-r--r--rigify/rigs/spines/super_spine.py62
-rw-r--r--rigify/ui.py100
-rw-r--r--rigify/utils.py1302
-rw-r--r--rigify/utils/__init__.py31
-rw-r--r--rigify/utils/animation.py84
-rw-r--r--rigify/utils/bones.py417
-rw-r--r--rigify/utils/collections.py68
-rw-r--r--rigify/utils/errors.py34
-rw-r--r--rigify/utils/layers.py140
-rw-r--r--rigify/utils/mechanism.py264
-rw-r--r--rigify/utils/misc.py100
-rw-r--r--rigify/utils/naming.py136
-rw-r--r--rigify/utils/rig.py294
-rw-r--r--rigify/utils/widgets.py168
-rw-r--r--rigify/utils/widgets_basic.py123
-rw-r--r--rigify/utils/widgets_special.py195
31 files changed, 2713 insertions, 2042 deletions
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.
+#
+#======================= END GPL LICENSE BLOCK ========================
+
+import bpy
+from bpy.props import StringProperty
+import os
+from zipfile import ZipFile
+from shutil import rmtree
+
+
+def feature_set_items(scene, context):
+ """Get items for the Feature Set EnumProperty"""
+ feature_sets_path = os.path.join(
+ bpy.utils.script_path_user(), 'rigify')
+ items = [('all',)*3, ('rigify',)*3, ]
+ if os.path.exists(feature_sets_path):
+ for fs in os.listdir(feature_sets_path):
+ items.append((fs,)*3)
+
+ return items
+
+class DATA_OT_rigify_add_feature_set(bpy.types.Operator):
+ bl_idname = "wm.rigify_add_feature_set"
+ bl_label = "Add External Feature Set"
+ bl_description = "Add external feature set (rigs, metarigs, ui templates)"
+ bl_options = {"REGISTER", "UNDO"}
+
+ filter_glob: StringProperty(default="*.zip", options={'HIDDEN'})
+ filepath: StringProperty(maxlen=1024, subtype='FILE_PATH', options={'HIDDEN', 'SKIP_SAVE'})
+
+ @classmethod
+ def poll(cls, context):
+ return True
+
+ def invoke(self, context, event):
+ context.window_manager.fileselect_add(self)
+ return {'RUNNING_MODAL'}
+
+ def execute(self, context):
+ addon_prefs = context.preferences.addons[__package__].preferences
+
+ rigify_config_path = os.path.join(bpy.utils.script_path_user(), 'rigify')
+ os.makedirs(rigify_config_path, exist_ok=True)
+ with ZipFile(bpy.path.abspath(self.filepath), 'r') as zip_archive:
+ zip_archive.extractall(rigify_config_path)
+
+ addon_prefs.machin = bpy.props.EnumProperty(items=(('a',)*3, ('b',)*3, ('c',)*3),)
+
+ addon_prefs.update_external_rigs()
+ return {'FINISHED'}
+
+
+class DATA_OT_rigify_remove_feature_set(bpy.types.Operator):
+ bl_idname = "wm.rigify_remove_feature_set"
+ bl_label = "Remove External Feature Set"
+ bl_description = "Remove external feature set (rigs, metarigs, ui templates)"
+ bl_options = {"REGISTER", "UNDO"}
+
+ featureset: StringProperty(maxlen=1024, options={'HIDDEN', 'SKIP_SAVE'})
+
+ @classmethod
+ def poll(cls, context):
+ return True
+
+ def invoke(self, context, event):
+ return context.window_manager.invoke_confirm(self, event)
+
+ def execute(self, context):
+ addon_prefs = context.preferences.addons[__package__].preferences
+
+ rigify_config_path = os.path.join(bpy.utils.script_path_user(), 'rigify')
+ if os.path.exists(os.path.join(rigify_config_path, self.featureset)):
+ rmtree(os.path.join(rigify_config_path, self.featureset))
+
+ addon_prefs.update_external_rigs()
+ return {'FINISHED'}
+
+def register():
+ bpy.utils.register_class(DATA_OT_rigify_add_feature_set)
+ bpy.utils.register_class(DATA_OT_rigify_remove_feature_set)
+
+def unregister():
+ bpy.utils.unregister_class(DATA_OT_rigify_add_feature_set)
+ bpy.utils.unregister_class(DATA_OT_rigify_remove_feature_set)
diff --git a/rigify/generate.py b/rigify/generate.py
index 5b5f0e98..7eff06f3 100644
--- a/rigify/generate.py
+++ b/rigify/generate.py
@@ -24,17 +24,17 @@ import time
import traceback
import sys
from rna_prop_ui import rna_idprop_ui_prop_get
+from collections import OrderedDict
-from .utils import MetarigError, new_bone, get_rig_type
-from .utils import ORG_PREFIX, MCH_PREFIX, DEF_PREFIX, WGT_PREFIX, ROOT_NAME, make_original_name
-from .utils import RIG_DIR
+from .utils import MetarigError, new_bone
+from .utils import MCH_PREFIX, DEF_PREFIX, WGT_PREFIX, ROOT_NAME, make_original_name
from .utils import create_root_widget
-from .utils import ensure_widget_collection
+from .utils.collections import ensure_widget_collection
from .utils import random_id
from .utils import copy_attributes
from .utils import gamma_correct
-from .rig_ui_template import UI_SLIDERS, layers_ui, UI_REGISTER
-
+from . import rig_lists
+from . import rig_ui_template
RIG_MODULE = "rigs"
ORG_LAYER = [n == 31 for n in range(0, 32)] # Armature layer that original bones should be moved to.
@@ -123,6 +123,7 @@ def generate_rig(context, metarig):
# Remove wgts if force update is set
wgts_group_name = "WGTS_" + (rig_old_name or obj.name)
if wgts_group_name in scene.objects and id_store.rigify_force_widget_update:
+ bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='DESELECT')
for wgt in bpy.data.objects[wgts_group_name].children:
wgt.select_set(True)
@@ -301,6 +302,8 @@ def generate_rig(context, metarig):
rna_idprop_ui_prop_get(obj.data, "rig_id", create=True)
obj.data["rig_id"] = rig_id
+ t.tick("Create root bone: ")
+
# Create/find widge collection
widget_collection = ensure_widget_collection(context)
@@ -332,6 +335,10 @@ def generate_rig(context, metarig):
# Generate all the rigs.
ui_scripts = []
+ ui_imports = rig_ui_template.UI_IMPORTS.copy()
+ ui_utilities = rig_ui_template.UI_UTILITIES.copy()
+ ui_register = rig_ui_template.UI_REGISTER.copy()
+ noparent_bones = []
for rig in rigs:
# Go into editmode in the rig armature
bpy.ops.object.mode_set(mode='OBJECT')
@@ -339,9 +346,21 @@ def generate_rig(context, metarig):
obj.select_set(True)
bpy.ops.object.mode_set(mode='EDIT')
scripts = rig.generate()
- if scripts is not None:
+ if isinstance(scripts, dict):
+ if 'script' in scripts:
+ ui_scripts += scripts['script']
+ if 'imports' in scripts:
+ ui_imports += scripts['imports']
+ if 'utilities' in scripts:
+ ui_utilities += scripts['utilities']
+ if 'register' in scripts:
+ ui_register += scripts['register']
+ if 'noparent_bones' in scripts:
+ noparent_bones += scripts['noparent_bones']
+ elif scripts is not None:
ui_scripts += [scripts[0]]
t.tick("Generate rigs: ")
+
except Exception as e:
# Cleanup if something goes wrong
print("Rigify: failed to generate rig.")
@@ -358,25 +377,8 @@ def generate_rig(context, metarig):
# Get a list of all the bones in the armature
bones = [bone.name for bone in obj.data.bones]
- # Parent any free-floating bones to the root excluding bones with child of constraint.
- pbones = obj.pose.bones
-
-
- ik_follow_drivers = []
-
- if obj.animation_data:
- for drv in obj.animation_data.drivers:
- for var in drv.driver.variables:
- if 'IK_follow' == var.name:
- ik_follow_drivers.append(drv.data_path)
-
- noparent_bones = []
- for bone in bones:
- # if 'IK_follow' in pbones[bone].keys():
- # noparent_bones += [bone]
- for d in ik_follow_drivers:
- if bone in d:
- noparent_bones += [bone]
+ # Parent any free-floating bones to the root excluding noparent_bones
+ noparent_bones = dict.fromkeys(noparent_bones)
bpy.ops.object.mode_set(mode='EDIT')
for bone in bones:
@@ -486,11 +488,23 @@ def generate_rig(context, metarig):
id_store.rigify_rig_ui = script.name
- script.write(UI_SLIDERS % rig_id)
+ for s in OrderedDict.fromkeys(ui_imports):
+ script.write(s + "\n")
+ script.write(rig_ui_template.UI_BASE_UTILITIES % rig_id)
+ for s in OrderedDict.fromkeys(ui_utilities):
+ script.write(s + "\n")
+ script.write(rig_ui_template.UI_SLIDERS)
for s in ui_scripts:
script.write("\n " + s.replace("\n", "\n ") + "\n")
- script.write(layers_ui(vis_layers, layer_layout))
- script.write(UI_REGISTER)
+ script.write(rig_ui_template.layers_ui(vis_layers, layer_layout))
+ script.write("\ndef register():\n")
+ ui_register = OrderedDict.fromkeys(ui_register)
+ for s in ui_register:
+ script.write(" bpy.utils.register_class("+s+");\n")
+ script.write("\ndef unregister():\n")
+ for s in ui_register:
+ script.write(" bpy.utils.unregister_class("+s+");\n")
+ script.write("\nregister()\n")
script.use_module = True
# Run UI script
@@ -505,6 +519,16 @@ def generate_rig(context, metarig):
# Add rig_ui to logic
create_persistent_rig_ui(obj, script)
+ # Do final gluing
+ for rig in rigs:
+ if hasattr(rig, "glue"):
+ # update glue_bone rigs
+ bpy.ops.object.mode_set(mode='EDIT')
+ rig = rig.__class__(rig.obj, rig.base_bone, rig.params)
+
+ rig.glue()
+ t.tick("Glue pass")
+
t.tick("The rest: ")
#----------------------------------
# Deconfigure
@@ -636,8 +660,9 @@ def get_bone_rigs(obj, bone_name, halt_on_missing=False):
# Get the rig
try:
- rig = get_rig_type(rig_type).Rig(obj, bone_name, params)
- except ImportError:
+ rig = rig_lists.rigs[rig_type]["module"]
+ rig = rig.Rig(obj, bone_name, params)
+ except (KeyError, ImportError):
message = "Rig Type Missing: python module for type '%s' not found (bone: %s)" % (rig_type, bone_name)
if halt_on_missing:
raise MetarigError(message)
diff --git a/rigify/legacy/rigs/biped/arm/__init__.py b/rigify/legacy/rigs/biped/arm/__init__.py
index e418a45a..e30b58cc 100644
--- a/rigify/legacy/rigs/biped/arm/__init__.py
+++ b/rigify/legacy/rigs/biped/arm/__init__.py
@@ -117,7 +117,7 @@ def add_parameters(params):
"""
params.use_complex_arm = bpy.props.BoolProperty(name="Complex Arm Rig", default=True, description="Generate the full, complex arm rig with twist bones and rubber-hose controls")
- params.bend_hint = bpy.props.BoolProperty(name="Bend Hint", default=True, description="Give IK chain a hint about which way to bend. Useful for perfectly straight chains")
+ params.bend_hint = bpy.props.BoolProperty(name="Bend Hint", default=True, description="Give IK chain a hint about which way to bend. Useful for perfectly straight chains")
items = [('X', 'X', ''), ('Y', 'Y', ''), ('Z', 'Z', ''), ('-X', '-X', ''), ('-Y', '-Y', ''), ('-Z', '-Z', '')]
params.primary_rotation_axis = bpy.props.EnumProperty(items=items, name="Primary Rotation Axis", default='X')
diff --git a/rigify/legacy/rigs/biped/leg/__init__.py b/rigify/legacy/rigs/biped/leg/__init__.py
index 97241ead..bfa9f535 100644
--- a/rigify/legacy/rigs/biped/leg/__init__.py
+++ b/rigify/legacy/rigs/biped/leg/__init__.py
@@ -121,7 +121,7 @@ def add_parameters(params):
"""
params.use_complex_leg = bpy.props.BoolProperty(name="Complex Leg Rig", default=True, description="Generate the full, complex leg rig with twist bones and rubber-hose controls")
- params.bend_hint = bpy.props.BoolProperty(name="Bend Hint", default=True, description="Give IK chain a hint about which way to bend (useful for perfectly straight chains)")
+ params.bend_hint = bpy.props.BoolProperty(name="Bend Hint", default=True, description="Give IK chain a hint about which way to bend. Useful for perfectly straight chains")
items = [('X', 'X', ''), ('Y', 'Y', ''), ('Z', 'Z', ''), ('-X', '-X', ''), ('-Y', '-Y', ''), ('-Z', '-Z', '')]
params.primary_rotation_axis = bpy.props.EnumProperty(items=items, name="Primary Rotation Axis", default='X')
diff --git a/rigify/metarig_menu.py b/rigify/metarig_menu.py
index 102d366d..18d8e550 100644
--- a/rigify/metarig_menu.py
+++ b/rigify/metarig_menu.py
@@ -37,45 +37,40 @@ class ArmatureSubMenu(bpy.types.Menu):
layout.operator(op, icon='OUTLINER_OB_ARMATURE', text=text)
-def get_metarig_list(path, depth=0):
+def get_metarigs(base_path, path, depth=0):
""" Searches for metarig modules, and returns a list of the
imported modules.
"""
- metarigs = []
- metarigs_dict = dict()
- MODULE_DIR = os.path.dirname(__file__)
- METARIG_DIR_ABS = os.path.join(MODULE_DIR, utils.METARIG_DIR)
- SEARCH_DIR_ABS = os.path.join(METARIG_DIR_ABS, path)
- files = os.listdir(SEARCH_DIR_ABS)
+
+ metarigs = {}
+
+ files = os.listdir(os.path.join(base_path, path))
files.sort()
for f in files:
- # Is it a directory?
- complete_path = os.path.join(SEARCH_DIR_ABS, f)
- if os.path.isdir(complete_path) and depth == 0:
- if f[0] != '_':
- metarigs_dict[f] = get_metarig_list(f, depth=1)
- else:
- continue
- elif not f.endswith(".py"):
+ is_dir = os.path.isdir(os.path.join(base_path, path, f)) # Whether the file is a directory
+
+ # Stop cases
+ if f[0] in [".", "_"]:
continue
- elif f == "__init__.py":
+ if f.count(".") >= 2 or (is_dir and "." in f):
+ print("Warning: %r, filename contains a '.', skipping" % os.path.join(path, f))
continue
- else:
- module_name = f[:-3]
- try:
- if depth == 1:
- metarigs += [utils.get_metarig_module(module_name, utils.METARIG_DIR + '.' + path)]
- else:
- metarigs += [utils.get_metarig_module(module_name, utils.METARIG_DIR)]
- except (ImportError):
- pass
- if depth == 1:
- return metarigs
+ if is_dir: # Check directories
+ # Check for sub-metarigs
+ metarigs[f] = get_metarigs(base_path, os.path.join(path, f, ""), depth=1) # "" adds a final slash
+ elif f.endswith(".py"):
+ # Check straight-up python files
+ f = f[:-3]
+ module_name = os.path.join(path, f).replace(os.sep, ".")
+ metarig_module = utils.get_resource(module_name, base_path=base_path)
+ if depth == 1:
+ metarigs[f] = metarig_module
+ else:
+ metarigs[utils.METARIG_DIR] = {f: metarig_module}
- metarigs_dict[utils.METARIG_DIR] = metarigs
- return metarigs_dict
+ return metarigs
def make_metarig_add_execute(m):
@@ -117,47 +112,69 @@ def make_submenu_func(bl_idname, text):
# Get the metarig modules
-metarigs_dict = get_metarig_list("")
-armature_submenus = []
+def get_internal_metarigs():
+ MODULE_DIR = os.path.dirname(os.path.dirname(__file__))
-# Create metarig add Operators
-metarig_ops = {}
-for metarig_class in metarigs_dict:
- metarig_ops[metarig_class] = []
- for m in metarigs_dict[metarig_class]:
- name = m.__name__.rsplit('.', 1)[1]
-
- # Dynamically construct an Operator
- T = type("Add_" + name + "_Metarig", (bpy.types.Operator,), {})
- T.bl_idname = "object.armature_" + name + "_metarig_add"
- T.bl_label = "Add " + name.replace("_", " ").capitalize() + " (metarig)"
- T.bl_options = {'REGISTER', 'UNDO'}
- T.execute = make_metarig_add_execute(m)
-
- metarig_ops[metarig_class].append((T, name))
+ metarigs.update(get_metarigs(MODULE_DIR, os.path.join(os.path.basename(os.path.dirname(__file__)), utils.METARIG_DIR, '')))
+metarigs = {}
+metarig_ops = {}
+armature_submenus = []
menu_funcs = []
-for mop, name in metarig_ops[utils.METARIG_DIR]:
- text = capwords(name.replace("_", " ")) + " (Meta-Rig)"
- menu_funcs += [make_metarig_menu_func(mop.bl_idname, text)]
+get_internal_metarigs()
-metarigs_dict.pop(utils.METARIG_DIR)
+def create_metarig_ops(dic=metarigs):
+ """Create metarig add Operators"""
+ for metarig_category in dic:
+ if metarig_category == "external":
+ create_metarig_ops(dic[metarig_category])
+ continue
+ if not metarig_category in metarig_ops:
+ metarig_ops[metarig_category] = []
+ for m in dic[metarig_category].values():
+ name = m.__name__.rsplit('.', 1)[1]
+
+ # Dynamically construct an Operator
+ T = type("Add_" + name + "_Metarig", (bpy.types.Operator,), {})
+ T.bl_idname = "object.armature_" + name + "_metarig_add"
+ T.bl_label = "Add " + name.replace("_", " ").capitalize() + " (metarig)"
+ T.bl_options = {'REGISTER', 'UNDO'}
+ T.execute = make_metarig_add_execute(m)
+
+ metarig_ops[metarig_category].append((T, name))
+
+def create_menu_funcs():
+ global menu_funcs
+ for mop, name in metarig_ops[utils.METARIG_DIR]:
+ text = capwords(name.replace("_", " ")) + " (Meta-Rig)"
+ menu_funcs += [make_metarig_menu_func(mop.bl_idname, text)]
+
+def create_armature_submenus(dic=metarigs):
+ global menu_funcs
+ metarig_categories = list(dic.keys())
+ metarig_categories.sort()
+ for metarig_category in metarig_categories:
+ # Create menu functions
+ if metarig_category == "external":
+ create_armature_submenus(dic=metarigs["external"])
+ continue
+ if metarig_category == utils.METARIG_DIR:
+ continue
-metarig_classes = list(metarigs_dict.keys())
-metarig_classes.sort()
-for metarig_class in metarig_classes:
- # Create menu functions
+ armature_submenus.append(type('Class_' + metarig_category + '_submenu', (ArmatureSubMenu,), {}))
+ armature_submenus[-1].bl_label = metarig_category + ' (submenu)'
+ armature_submenus[-1].bl_idname = 'ARMATURE_MT_%s_class' % metarig_category
+ armature_submenus[-1].operators = []
+ menu_funcs += [make_submenu_func(armature_submenus[-1].bl_idname, metarig_category)]
- armature_submenus.append(type('Class_' + metarig_class + '_submenu', (ArmatureSubMenu,), {}))
- armature_submenus[-1].bl_label = metarig_class + ' (submenu)'
- armature_submenus[-1].bl_idname = 'ARMATURE_MT_%s_class' % metarig_class
- armature_submenus[-1].operators = []
- menu_funcs += [make_submenu_func(armature_submenus[-1].bl_idname, metarig_class)]
+ for mop, name in metarig_ops[metarig_category]:
+ arm_sub = next((e for e in armature_submenus if e.bl_label == metarig_category + ' (submenu)'), '')
+ arm_sub.operators.append((mop.bl_idname, name,))
- for mop, name in metarig_ops[metarig_class]:
- arm_sub = next((e for e in armature_submenus if e.bl_label == metarig_class + ' (submenu)'), '')
- arm_sub.operators.append((mop.bl_idname, name,))
+create_metarig_ops()
+create_menu_funcs()
+create_armature_submenus()
### Registering ###
@@ -176,7 +193,6 @@ def register():
for mf in menu_funcs:
bpy.types.VIEW3D_MT_armature_add.append(mf)
-
def unregister():
from bpy.utils import unregister_class
@@ -189,3 +205,26 @@ def unregister():
for mf in menu_funcs:
bpy.types.VIEW3D_MT_armature_add.remove(mf)
+
+def get_external_metarigs(feature_sets_path):
+ unregister()
+
+ # Clear and fill metarigs public variables
+ metarigs.clear()
+ get_internal_metarigs()
+ metarigs['external'] = {}
+
+ for feature_set in os.listdir(feature_sets_path):
+ if feature_set:
+ utils.get_resource(os.path.join(feature_set, '__init__'), base_path=feature_sets_path)
+
+ metarigs['external'].update(get_metarigs(feature_sets_path, os.path.join(feature_set, utils.METARIG_DIR)))
+
+ metarig_ops.clear()
+ armature_submenus.clear()
+ menu_funcs.clear()
+
+ create_metarig_ops()
+ create_menu_funcs()
+ create_armature_submenus()
+ register()
diff --git a/rigify/rig_lists.py b/rigify/rig_lists.py
index edca1090..c1846b99 100644
--- a/rigify/rig_lists.py
+++ b/rigify/rig_lists.py
@@ -21,69 +21,70 @@ import os
from . import utils
-def get_rig_list(path):
+def get_rigs(base_path, path, feature_set='rigify'):
""" Recursively searches for rig types, and returns a list.
+
+ :param base_path: base dir where rigs are stored
+ :type path:str
+ :param path: rig path inside the base dir
+ :type path:str
"""
- rigs_dict = dict()
- rigs = []
- implementation_rigs = []
- MODULE_DIR = os.path.dirname(__file__)
- RIG_DIR_ABS = os.path.join(MODULE_DIR, utils.RIG_DIR)
- SEARCH_DIR_ABS = os.path.join(RIG_DIR_ABS, path)
- files = os.listdir(SEARCH_DIR_ABS)
+
+ rigs = {}
+ impl_rigs = {}
+
+ files = os.listdir(os.path.join(base_path, path))
files.sort()
for f in files:
- is_dir = os.path.isdir(os.path.join(SEARCH_DIR_ABS, f)) # Whether the file is a directory
+ is_dir = os.path.isdir(os.path.join(base_path, path, f)) # Whether the file is a directory
# Stop cases
if f[0] in [".", "_"]:
continue
if f.count(".") >= 2 or (is_dir and "." in f):
- print("Warning: %r, filename contains a '.', skipping" % os.path.join(SEARCH_DIR_ABS, f))
+ print("Warning: %r, filename contains a '.', skipping" % os.path.join(path, f))
continue
if is_dir:
# Check directories
- module_name = os.path.join(path, f).replace(os.sep, ".")
- rig = utils.get_rig_type(module_name)
- # Check if it's a rig itself
- if hasattr(rig, "Rig"):
- rigs += [f]
- else:
- # Check for sub-rigs
- sub_dict = get_rig_list(os.path.join(path, f, "")) # "" adds a final slash
- rigs.extend(["%s.%s" % (f, l) for l in sub_dict['rig_list']])
- implementation_rigs.extend(["%s.%s" % (f, l) for l in sub_dict['implementation_rigs']])
+ module_name = os.path.join(path, "__init__").replace(os.sep, ".")
+ # Check for sub-rigs
+ sub_rigs, sub_impls = get_rigs(base_path, os.path.join(path, f, ""), feature_set) # "" adds a final slash
+ rigs.update({"%s.%s" % (f, l): sub_rigs[l] for l in sub_rigs})
+ impl_rigs.update({"%s.%s" % (f, l): sub_rigs[l] for l in sub_impls})
elif f.endswith(".py"):
# Check straight-up python files
- t = f[:-3]
- module_name = os.path.join(path, t).replace(os.sep, ".")
- rig = utils.get_rig_type(module_name)
- if hasattr(rig, "Rig"):
- rigs += [t]
- if hasattr(rig, 'IMPLEMENTATION') and rig.IMPLEMENTATION:
- implementation_rigs += [t]
- rigs.sort()
+ f = f[:-3]
+ module_name = os.path.join(path, f).replace(os.sep, ".")
+ rig_module = utils.get_resource(module_name, base_path=base_path)
+ if hasattr(rig_module, "Rig"):
+ rigs[f] = {"module": rig_module,
+ "feature_set": feature_set}
+ if hasattr(rig_module, 'IMPLEMENTATION') and rig_module.IMPLEMENTATION:
+ impl_rigs[f] = rig_module
+
+ return rigs, impl_rigs
- rigs_dict['rig_list'] = rigs
- rigs_dict['implementation_rigs'] = implementation_rigs
- return rigs_dict
+# Public variables
+MODULE_DIR = os.path.dirname(os.path.dirname(__file__))
+rigs, implementation_rigs = get_rigs(MODULE_DIR, os.path.join(os.path.basename(os.path.dirname(__file__)), utils.RIG_DIR, ''))
-def get_collection_list(rig_list):
- collection_list = []
- for r in rig_list:
- a = r.split(".")
- if len(a) >= 2 and a[0] not in collection_list:
- collection_list += [a[0]]
- return collection_list
+def get_external_rigs(feature_sets_path):
+ # Clear and fill rigify rigs and implementation rigs public variables
+ for rig in list(rigs.keys()):
+ if rigs[rig]["feature_set"] != "rigify":
+ rigs.pop(rig)
+ if rig in implementation_rigs:
+ implementation_rigs.pop(rig)
-# Public variables
-rigs_dict = get_rig_list("")
-rig_list = rigs_dict['rig_list']
-implementation_rigs = rigs_dict['implementation_rigs']
-collection_list = get_collection_list(rig_list)
-col_enum_list = [("All", "All", ""), ("None", "None", "")] + [(c, c, "") for c in collection_list]
+ # Get external rigs
+ for feature_set in os.listdir(feature_sets_path):
+ if feature_set:
+ utils.get_resource(os.path.join(feature_set, '__init__'), feature_sets_path)
+ external_rigs, external_impl_rigs = get_rigs(feature_sets_path, os.path.join(feature_set, utils.RIG_DIR), feature_set)
+ rigs.update(external_rigs)
+ implementation_rigs.update(external_impl_rigs)
diff --git a/rigify/rig_ui_template.py b/rigify/rig_ui_template.py
index 0a2234e1..7265999b 100644
--- a/rigify/rig_ui_template.py
+++ b/rigify/rig_ui_template.py
@@ -18,12 +18,15 @@
# <pep8 compliant>
-UI_SLIDERS = '''
-import bpy
-from bpy.props import StringProperty
-from mathutils import Matrix, Vector
-from math import acos, pi, radians
-
+UI_IMPORTS = [
+ 'import bpy',
+ 'from bpy.props import StringProperty',
+ 'import math',
+ 'from math import pi',
+ 'from mathutils import Euler, Matrix, Quaternion, Vector',
+]
+
+UI_BASE_UTILITIES = '''
rig_id = "%s"
@@ -55,7 +58,7 @@ def rotation_difference(mat1, mat2):
"""
q1 = mat1.to_quaternion()
q2 = mat2.to_quaternion()
- angle = acos(min(1,max(-1,q1.dot(q2)))) * 2
+ angle = math.acos(min(1,max(-1,q1.dot(q2)))) * 2
if angle > pi:
angle = -angle + (2*pi)
return angle
@@ -296,6 +299,22 @@ def match_pole_target(ik_first, ik_last, pole, match_bone, length):
if ang1 < ang2:
set_pole(pv1)
+##########
+## Misc ##
+##########
+
+def parse_bone_names(names_string):
+ if names_string[0] == '[' and names_string[-1] == ']':
+ return eval(names_string)
+ else:
+ return names_string
+
+'''
+
+UTILITIES_FUNC_ARM_FKIK = ['''
+######################
+## IK Arm functions ##
+######################
def fk2ik_arm(obj, fk, ik):
""" Matches the fk bones in an arm rig to the ik bones.
@@ -391,6 +410,12 @@ def ik2fk_arm(obj, fk, ik):
match_pose_scale(uarmi, uarm)
# Rotation Correction
correct_rotation(uarmi, uarm)
+''']
+
+UTILITIES_FUNC_LEG_FKIK = ['''
+######################
+## IK Leg functions ##
+######################
def fk2ik_leg(obj, fk, ik):
""" Matches the fk bones in a leg rig to the ik bones.
@@ -519,18 +544,13 @@ def ik2fk_leg(obj, fk, ik):
# Pole target position
match_pole_target(thighi, shini, pole, thigh, (thighi.length + shini.length))
+''']
-
+UTILITIES_FUNC_POLE = ['''
################################
## IK Rotation-Pole functions ##
################################
-def parse_bone_names(names_string):
- if names_string[0] == '[' and names_string[-1] == ']':
- return eval(names_string)
- else:
- return names_string
-
def rotPoleToggle(rig, limb_type, controls, ik_ctrl, fk_ctrl, parent, pole):
rig_id = rig.data['rig_id']
@@ -589,10 +609,14 @@ def rotPoleToggle(rig, limb_type, controls, ik_ctrl, fk_ctrl, parent, pole):
func2(**kwargs2)
bpy.ops.pose.select_all(action='DESELECT')
+''']
-##############################
-## IK/FK snapping operators ##
-##############################
+REGISTER_OP_ARM_FKIK = ['Rigify_Arm_FK2IK', 'Rigify_Arm_IK2FK']
+
+UTILITIES_OP_ARM_FKIK = ['''
+##################################
+## IK/FK Arm snapping operators ##
+##################################
class Rigify_Arm_FK2IK(bpy.types.Operator):
""" Snaps an FK arm to an IK arm.
@@ -653,7 +677,14 @@ class Rigify_Arm_IK2FK(bpy.types.Operator):
finally:
context.preferences.edit.use_global_undo = use_global_undo
return {'FINISHED'}
+''']
+
+REGISTER_OP_LEG_FKIK = ['Rigify_Leg_FK2IK', 'Rigify_Leg_IK2FK']
+UTILITIES_OP_LEG_FKIK = ['''
+##################################
+## IK/FK Leg snapping operators ##
+##################################
class Rigify_Leg_FK2IK(bpy.types.Operator):
""" Snaps an FK leg to an IK leg.
@@ -718,7 +749,11 @@ class Rigify_Leg_IK2FK(bpy.types.Operator):
finally:
context.preferences.edit.use_global_undo = use_global_undo
return {'FINISHED'}
+''']
+
+REGISTER_OP_POLE = ['Rigify_Rot2PoleSwitch']
+UTILITIES_OP_POLE = ['''
###########################
## IK Rotation Pole Snap ##
###########################
@@ -745,7 +780,47 @@ class Rigify_Rot2PoleSwitch(bpy.types.Operator):
rotPoleToggle(rig, self.limb_type, self.controls, self.ik_ctrl, self.fk_ctrl, self.parent, self.pole)
return {'FINISHED'}
+''']
+
+REGISTER_RIG_ARM = REGISTER_OP_ARM_FKIK + REGISTER_OP_POLE
+
+UTILITIES_RIG_ARM = [
+ *UTILITIES_FUNC_ARM_FKIK,
+ *UTILITIES_FUNC_POLE,
+ *UTILITIES_OP_ARM_FKIK,
+ *UTILITIES_OP_POLE,
+]
+
+REGISTER_RIG_LEG = REGISTER_OP_LEG_FKIK + REGISTER_OP_POLE
+
+UTILITIES_RIG_LEG = [
+ *UTILITIES_FUNC_LEG_FKIK,
+ *UTILITIES_FUNC_POLE,
+ *UTILITIES_OP_LEG_FKIK,
+ *UTILITIES_OP_POLE,
+]
+
+##############################
+## Default set of utilities ##
+##############################
+UI_REGISTER = [
+ 'RigUI',
+ 'RigLayers',
+ *REGISTER_OP_ARM_FKIK,
+ *REGISTER_OP_LEG_FKIK,
+]
+
+# Include arm and leg utilities for now in case somebody wants to use
+# legacy limb rigs, which expect these to be available by default.
+UI_UTILITIES = [
+ *UTILITIES_FUNC_ARM_FKIK,
+ *UTILITIES_FUNC_LEG_FKIK,
+ *UTILITIES_OP_ARM_FKIK,
+ *UTILITIES_OP_LEG_FKIK,
+]
+
+UI_SLIDERS = '''
###################
## Rig UI Panels ##
###################
@@ -841,30 +916,3 @@ class RigLayers(bpy.types.Panel):
code += " row.prop(context.active_object.data, 'layers', index=28, toggle=True, text='Root')\n"
return code
-
-
-UI_REGISTER = '''
-
-classes = (
- Rigify_Arm_FK2IK,
- Rigify_Arm_IK2FK,
- Rigify_Leg_FK2IK,
- Rigify_Leg_IK2FK,
- Rigify_Rot2PoleSwitch,
- RigUI,
- RigLayers,
-)
-
-def register():
- from bpy.utils import register_class
- for cls in classes:
- register_class(cls)
-
-
-def unregister():
- from bpy.utils import unregister_class
- for cls in classes:
- unregister_class(cls)
-
-register()
-'''
diff --git a/rigify/rigs/experimental/super_chain.py b/rigify/rigs/experimental/super_chain.py
index 565e5bdc..e833d256 100644
--- a/rigify/rigs/experimental/super_chain.py
+++ b/rigify/rigs/experimental/super_chain.py
@@ -4,6 +4,7 @@ from ...utils import copy_bone, put_bone, org, align_bone_y_axis, align_bone_x_a
from ...utils import strip_org, make_deformer_name, connected_children_names
from ...utils import create_chain_widget
from ...utils import make_mechanism_name, create_cube_widget
+from ...utils import ControlLayersOption
from rna_prop_ui import rna_idprop_ui_prop_get
from ..limbs.limb_utils import get_bone_name
@@ -22,12 +23,6 @@ class Rig:
self.bbones = params.bbones
self.SINGLE_BONE = (len(self.org_bones) == 1)
- # Assign values to tweak layers props if opted by user
- if params.tweak_extra_layers:
- self.tweak_layers = list(params.tweak_layers)
- else:
- self.tweak_layers = None
-
def orient_bone(self, eb, axis, scale, reverse=False):
v = Vector((0, 0, 0))
@@ -563,9 +558,7 @@ class Rig:
)
# Assigning layers to tweaks and ctrls
- for bone in bones['chain']['tweak']:
- if self.tweak_layers:
- pb[bone].bone.layers = self.tweak_layers
+ ControlLayersOption.TWEAK.assign(self.params, pb, bones['chain']['tweak'])
return
@@ -626,18 +619,7 @@ def add_parameters(params):
description='Number of segments'
)
- # Setting up extra layers for the FK and tweak
- params.tweak_extra_layers = bpy.props.BoolProperty(
- name="tweak_extra_layers",
- default=True,
- description=""
- )
-
- params.tweak_layers = bpy.props.BoolVectorProperty(
- size=32,
- description="Layers for the tweak controls to be on",
- default=tuple([i == 1 for i in range(0, 32)])
- )
+ ControlLayersOption.TWEAK.add_parameters(params)
def parameters_ui(layout, params):
@@ -656,45 +638,7 @@ def parameters_ui(layout, params):
r = layout.row()
r.prop_search(params, 'conv_bone', pb, "bones", text="Convergence Bone")
- r = layout.row()
- r.prop(params, "tweak_extra_layers")
- r.active = params.tweak_extra_layers
-
- col = r.column(align=True)
- row = col.row(align=True)
-
- bone_layers = bpy.context.active_pose_bone.bone.layers[:]
-
- for i in range(8):
- icon = "NONE"
- if bone_layers[i]:
- icon = "LAYER_ACTIVE"
- row.prop(params, "tweak_layers", index=i, toggle=True, text="", icon=icon)
-
- row = col.row(align=True)
-
- for i in range(16, 24):
- icon = "NONE"
- if bone_layers[i]:
- icon = "LAYER_ACTIVE"
- row.prop(params, "tweak_layers", index=i, toggle=True, text="", icon=icon)
-
- col = r.column(align=True)
- row = col.row(align=True)
-
- for i in range(8, 16):
- icon = "NONE"
- if bone_layers[i]:
- icon = "LAYER_ACTIVE"
- row.prop(params, "tweak_layers", index=i, toggle=True, text="", icon=icon)
-
- row = col.row(align=True)
-
- for i in range(24, 32):
- icon = "NONE"
- if bone_layers[i]:
- icon = "LAYER_ACTIVE"
- row.prop(params, "tweak_layers", index=i, toggle=True, text="", icon=icon)
+ ControlLayersOption.TWEAK.parameters_ui(layout, params)
def create_sample(obj):
diff --git a/rigify/rigs/faces/super_face.py b/rigify/rigs/faces/super_face.py
index c097cbb9..8248c38b 100644
--- a/rigify/rigs/faces/super_face.py
+++ b/rigify/rigs/faces/super_face.py
@@ -1042,7 +1042,7 @@ def add_parameters(params):
)
params.primary_layers = bpy.props.BoolVectorProperty(
size=32,
- description="Layers for the 1st tweak controls to be on",
+ description="Layers for the primary controls to be on",
default=tuple([i == 1 for i in range(0, 32)])
)
params.secondary_layers_extra = bpy.props.BoolProperty(
@@ -1052,7 +1052,7 @@ def add_parameters(params):
)
params.secondary_layers = bpy.props.BoolVectorProperty(
size=32,
- description="Layers for the 2nd tweak controls to be on",
+ description="Layers for the secondary controls to be on",
default=tuple([i == 1 for i in range(0, 32)])
)
diff --git a/rigify/rigs/limbs/arm.py b/rigify/rigs/limbs/arm.py
index f9174e92..1f964dc0 100644
--- a/rigify/rigs/limbs/arm.py
+++ b/rigify/rigs/limbs/arm.py
@@ -3,12 +3,14 @@ from ..widgets import create_hand_widget, create_gear_widget
from .ui import create_script
from .limb_utils import *
from mathutils import Vector
-from ...utils import copy_bone, flip_bone, put_bone, create_cube_widget
-from ...utils import strip_org, strip_mch, make_deformer_name, create_widget
+from ...utils import copy_bone, put_bone
+from ...utils import strip_org, strip_mch
from ...utils import create_circle_widget, create_sphere_widget, create_line_widget
-from ...utils import MetarigError, make_mechanism_name, org
+from ...utils import make_mechanism_name
from ...utils import create_limb_widget, connected_children_names
-from ...utils import align_bone_y_axis, align_bone_x_axis, align_bone_z_axis
+from ...utils import align_bone_x_axis, align_bone_z_axis
+from ...rig_ui_template import UTILITIES_RIG_ARM, REGISTER_RIG_ARM
+from ...utils import ControlLayersOption
from rna_prop_ui import rna_idprop_ui_prop_get
from ..widgets import create_ikarrow_widget
from math import trunc, pi
@@ -46,16 +48,6 @@ class Rig:
self.rot_axis = params.rotation_axis
self.auto_align_extremity = params.auto_align_extremity
- # Assign values to tweak/FK layers props if opted by user
- if params.tweak_extra_layers:
- self.tweak_layers = list(params.tweak_layers)
- else:
- self.tweak_layers = None
-
- if params.fk_extra_layers:
- self.fk_layers = list(params.fk_layers)
- else:
- self.fk_layers = None
def orient_org_bones(self):
@@ -266,8 +258,7 @@ class Rig:
create_sphere_widget(self.obj, t, bone_transform_name=None)
- if self.tweak_layers:
- pb[t].bone.layers = self.tweak_layers
+ ControlLayersOption.TWEAK.assign(self.params, pb, tweaks['ctrl'])
return tweaks
@@ -547,9 +538,7 @@ class Rig:
create_circle_widget(self.obj, ctrls[2], radius=0.4, head_tail=0.0)
- for c in ctrls:
- if self.fk_layers:
- pb[c].bone.layers = self.fk_layers
+ ControlLayersOption.FK.assign(self.params, pb, ctrls)
return {'ctrl': ctrls, 'mch': mch}
@@ -1067,7 +1056,12 @@ class Rig:
script += extra_script % (controls_string, bones['main_parent'], 'IK_follow',
'pole_follow', 'pole_follow', 'root/parent', 'root/parent')
- return [script]
+ return {
+ 'script': [script],
+ 'utilities': UTILITIES_RIG_ARM,
+ 'register': REGISTER_RIG_ARM,
+ 'noparent_bones': [bones['ik']['mch_hand'][i] for i in [0,1]],
+ }
def add_parameters(params):
@@ -1108,30 +1102,8 @@ def add_parameters(params):
)
# Setting up extra layers for the FK and tweak
- params.tweak_extra_layers = bpy.props.BoolProperty(
- name = "tweak_extra_layers",
- default = True,
- description = ""
- )
-
- params.tweak_layers = bpy.props.BoolVectorProperty(
- size = 32,
- description = "Layers for the tweak controls to be on",
- default = tuple( [ i == 1 for i in range(0, 32) ] )
- )
-
- # Setting up extra layers for the FK and tweak
- params.fk_extra_layers = bpy.props.BoolProperty(
- name = "fk_extra_layers",
- default = True,
- description = ""
- )
-
- params.fk_layers = bpy.props.BoolVectorProperty(
- size = 32,
- description = "Layers for the FK controls to be on",
- default = tuple( [ i == 1 for i in range(0, 32) ] )
- )
+ ControlLayersOption.FK.add_parameters(params)
+ ControlLayersOption.TWEAK.add_parameters(params)
def parameters_ui(layout, params):
@@ -1151,46 +1123,8 @@ def parameters_ui(layout, params):
r = layout.row()
r.prop(params, "bbones")
- bone_layers = bpy.context.active_pose_bone.bone.layers[:]
-
- for layer in ['fk', 'tweak']:
- r = layout.row()
- r.prop(params, layer + "_extra_layers")
- r.active = params.tweak_extra_layers
-
- col = r.column(align=True)
- row = col.row(align=True)
-
- for i in range(8):
- icon = "NONE"
- if bone_layers[i]:
- icon = "LAYER_ACTIVE"
- row.prop(params, layer + "_layers", index=i, toggle=True, text="", icon=icon)
-
- row = col.row(align=True)
-
- for i in range(16, 24):
- icon = "NONE"
- if bone_layers[i]:
- icon = "LAYER_ACTIVE"
- row.prop(params, layer + "_layers", index=i, toggle=True, text="", icon=icon)
-
- col = r.column(align=True)
- row = col.row(align=True)
-
- for i in range(8, 16):
- icon = "NONE"
- if bone_layers[i]:
- icon = "LAYER_ACTIVE"
- row.prop(params, layer + "_layers", index=i, toggle=True, text="", icon=icon)
-
- row = col.row(align=True)
-
- for i in range(24, 32):
- icon = "NONE"
- if bone_layers[i]:
- icon = "LAYER_ACTIVE"
- row.prop(params, layer + "_layers", index=i, toggle=True, text="", icon=icon)
+ ControlLayersOption.FK.parameters_ui(layout, params)
+ ControlLayersOption.TWEAK.parameters_ui(layout, params)
def create_sample(obj):
diff --git a/rigify/rigs/limbs/leg.py b/rigify/rigs/limbs/leg.py
index 4f53fb89..743e5703 100644
--- a/rigify/rigs/limbs/leg.py
+++ b/rigify/rigs/limbs/leg.py
@@ -1,15 +1,17 @@
-import bpy, re, math
+import bpy, math
from ..widgets import create_foot_widget, create_ballsocket_widget, create_gear_widget
from .ui import create_script
from .limb_utils import *
from mathutils import Vector
-from ...utils import copy_bone, flip_bone, put_bone, create_cube_widget
-from ...utils import strip_org, strip_mch, make_deformer_name, create_widget
+from ...utils import copy_bone, flip_bone, put_bone
+from ...utils import strip_org, strip_mch
from ...utils import create_circle_widget, create_sphere_widget, create_line_widget
-from ...utils import MetarigError, make_mechanism_name, org
+from ...utils import MetarigError, make_mechanism_name
from ...utils import create_limb_widget, connected_children_names
from ...utils import align_bone_y_axis, align_bone_x_axis, align_bone_z_axis
+from ...rig_ui_template import UTILITIES_RIG_LEG, REGISTER_RIG_LEG
+from ...utils import ControlLayersOption
from rna_prop_ui import rna_idprop_ui_prop_get
from ..widgets import create_ikarrow_widget
from math import trunc, pi
@@ -47,16 +49,6 @@ class Rig:
self.rot_axis = params.rotation_axis
self.auto_align_extremity = params.auto_align_extremity
- # Assign values to tweak/FK layers props if opted by user
- if params.tweak_extra_layers:
- self.tweak_layers = list(params.tweak_layers)
- else:
- self.tweak_layers = None
-
- if params.fk_extra_layers:
- self.fk_layers = list(params.fk_layers)
- else:
- self.fk_layers = None
def orient_org_bones(self):
@@ -293,8 +285,7 @@ class Rig:
create_sphere_widget(self.obj, t, bone_transform_name=None)
- if self.tweak_layers:
- pb[t].bone.layers = self.tweak_layers
+ ControlLayersOption.TWEAK.assign(self.params, pb, tweaks['ctrl'])
return tweaks
@@ -574,9 +565,7 @@ class Rig:
create_circle_widget(self.obj, ctrls[2], radius=0.4, head_tail=0.0)
- for c in ctrls:
- if self.fk_layers:
- pb[c].bone.layers = self.fk_layers
+ ControlLayersOption.FK.assign(self.params, pb, ctrls)
return {'ctrl': ctrls, 'mch': mch}
@@ -1390,7 +1379,12 @@ class Rig:
script += extra_script % (controls_string, bones['main_parent'], 'IK_follow',
'pole_follow', 'pole_follow', 'root/parent', 'root/parent')
- return [script]
+ return {
+ 'script': [script],
+ 'utilities': UTILITIES_RIG_LEG,
+ 'register': REGISTER_RIG_LEG,
+ 'noparent_bones': [bones['ik']['mch_foot'][i] for i in [0,1]],
+ }
def add_parameters(params):
@@ -1431,30 +1425,8 @@ def add_parameters(params):
)
# Setting up extra layers for the FK and tweak
- params.tweak_extra_layers = bpy.props.BoolProperty(
- name = "tweak_extra_layers",
- default = True,
- description = ""
- )
-
- params.tweak_layers = bpy.props.BoolVectorProperty(
- size = 32,
- description = "Layers for the tweak controls to be on",
- default = tuple( [ i == 1 for i in range(0, 32) ] )
- )
-
- # Setting up extra layers for the FK and tweak
- params.fk_extra_layers = bpy.props.BoolProperty(
- name = "fk_extra_layers",
- default = True,
- description = ""
- )
-
- params.fk_layers = bpy.props.BoolVectorProperty(
- size = 32,
- description = "Layers for the FK controls to be on",
- default = tuple( [ i == 1 for i in range(0, 32) ] )
- )
+ ControlLayersOption.FK.add_parameters(params)
+ ControlLayersOption.TWEAK.add_parameters(params)
def parameters_ui(layout, params):
@@ -1474,46 +1446,8 @@ def parameters_ui(layout, params):
r = layout.row()
r.prop(params, "bbones")
- bone_layers = bpy.context.active_pose_bone.bone.layers[:]
-
- for layer in ['fk', 'tweak']:
- r = layout.row()
- r.prop(params, layer + "_extra_layers")
- r.active = params.tweak_extra_layers
-
- col = r.column(align=True)
- row = col.row(align=True)
-
- for i in range(8):
- icon = "NONE"
- if bone_layers[i]:
- icon = "LAYER_ACTIVE"
- row.prop(params, layer + "_layers", index=i, toggle=True, text="", icon=icon)
-
- row = col.row(align=True)
-
- for i in range(16, 24):
- icon = "NONE"
- if bone_layers[i]:
- icon = "LAYER_ACTIVE"
- row.prop(params, layer + "_layers", index=i, toggle=True, text="", icon=icon)
-
- col = r.column(align=True)
- row = col.row(align=True)
-
- for i in range(8, 16):
- icon = "NONE"
- if bone_layers[i]:
- icon = "LAYER_ACTIVE"
- row.prop(params, layer + "_layers", index=i, toggle=True, text="", icon=icon)
-
- row = col.row(align=True)
-
- for i in range(24, 32):
- icon = "NONE"
- if bone_layers[i]:
- icon = "LAYER_ACTIVE"
- row.prop(params, layer + "_layers", index=i, toggle=True, text="", icon=icon)
+ ControlLayersOption.FK.parameters_ui(layout, params)
+ ControlLayersOption.TWEAK.parameters_ui(layout, params)
def create_sample(obj):
diff --git a/rigify/rigs/limbs/paw.py b/rigify/rigs/limbs/paw.py
index cb1438bf..44851e3e 100644
--- a/rigify/rigs/limbs/paw.py
+++ b/rigify/rigs/limbs/paw.py
@@ -2,12 +2,14 @@ import bpy
from .ui import create_script
from .limb_utils import *
from mathutils import Vector
-from ...utils import copy_bone, flip_bone, put_bone, create_cube_widget
-from ...utils import strip_org, strip_mch, make_deformer_name, create_widget
+from ...utils import copy_bone, flip_bone, put_bone
+from ...utils import strip_org, strip_mch
from ...utils import create_circle_widget, create_sphere_widget, create_line_widget
-from ...utils import MetarigError, make_mechanism_name, org
+from ...utils import MetarigError, make_mechanism_name
from ...utils import create_limb_widget, connected_children_names
-from ...utils import align_bone_y_axis, align_bone_x_axis, align_bone_z_axis
+from ...utils import align_bone_x_axis, align_bone_z_axis
+from ...rig_ui_template import UTILITIES_RIG_LEG, REGISTER_RIG_LEG
+from ...utils import ControlLayersOption
from rna_prop_ui import rna_idprop_ui_prop_get
from ..widgets import create_ikarrow_widget, create_gear_widget
from ..widgets import create_foot_widget, create_ballsocket_widget
@@ -46,17 +48,6 @@ class Rig:
self.rot_axis = params.rotation_axis
self.auto_align_extremity = params.auto_align_extremity
- # Assign values to tweak/FK layers props if opted by user
- if params.tweak_extra_layers:
- self.tweak_layers = list(params.tweak_layers)
- else:
- self.tweak_layers = None
-
- if params.fk_extra_layers:
- self.fk_layers = list(params.fk_layers)
- else:
- self.fk_layers = None
-
def orient_org_bones(self):
bpy.ops.object.mode_set(mode='EDIT')
@@ -285,8 +276,7 @@ class Rig:
create_sphere_widget(self.obj, t, bone_transform_name=None)
- if self.tweak_layers:
- pb[t].bone.layers = self.tweak_layers
+ ControlLayersOption.TWEAK.assign(self.params, pb, tweaks['ctrl'])
return tweaks
@@ -569,9 +559,7 @@ class Rig:
create_circle_widget(self.obj, ctrls[2], radius=0.4, head_tail=0.0)
- for c in ctrls:
- if self.fk_layers:
- pb[c].bone.layers = self.fk_layers
+ ControlLayersOption.FK.assign(self.params, pb, ctrls)
return {'ctrl': ctrls, 'mch': mch}
@@ -1218,7 +1206,12 @@ class Rig:
script += extra_script % (controls_string, bones['main_parent'], 'IK_follow',
'pole_follow', 'pole_follow', 'root/parent', 'root/parent')
- return [script]
+ return {
+ 'script': [script],
+ 'utilities': UTILITIES_RIG_LEG,
+ 'register': REGISTER_RIG_LEG,
+ 'noparent_bones': [bones['ik']['mch_foot'][i] for i in [0,1]],
+ }
def add_parameters(params):
@@ -1259,30 +1252,8 @@ def add_parameters(params):
)
# Setting up extra layers for the FK and tweak
- params.tweak_extra_layers = bpy.props.BoolProperty(
- name = "tweak_extra_layers",
- default = True,
- description = ""
- )
-
- params.tweak_layers = bpy.props.BoolVectorProperty(
- size = 32,
- description = "Layers for the tweak controls to be on",
- default = tuple( [ i == 1 for i in range(0, 32) ] )
- )
-
- # Setting up extra layers for the FK and tweak
- params.fk_extra_layers = bpy.props.BoolProperty(
- name = "fk_extra_layers",
- default = True,
- description = ""
- )
-
- params.fk_layers = bpy.props.BoolVectorProperty(
- size = 32,
- description = "Layers for the FK controls to be on",
- default = tuple( [ i == 1 for i in range(0, 32) ] )
- )
+ ControlLayersOption.FK.add_parameters(params)
+ ControlLayersOption.TWEAK.add_parameters(params)
def parameters_ui(layout, params):
@@ -1302,46 +1273,8 @@ def parameters_ui(layout, params):
r = layout.row()
r.prop(params, "bbones")
- bone_layers = bpy.context.active_pose_bone.bone.layers[:]
-
- for layer in ['fk', 'tweak']:
- r = layout.row()
- r.prop(params, layer + "_extra_layers")
- r.active = params.tweak_extra_layers
-
- col = r.column(align=True)
- row = col.row(align=True)
-
- for i in range(8):
- icon = "NONE"
- if bone_layers[i]:
- icon = "LAYER_ACTIVE"
- row.prop(params, layer + "_layers", index=i, toggle=True, text="", icon=icon)
-
- row = col.row(align=True)
-
- for i in range(16, 24):
- icon = "NONE"
- if bone_layers[i]:
- icon = "LAYER_ACTIVE"
- row.prop(params, layer + "_layers", index=i, toggle=True, text="", icon=icon)
-
- col = r.column(align=True)
- row = col.row(align=True)
-
- for i in range(8, 16):
- icon = "NONE"
- if bone_layers[i]:
- icon = "LAYER_ACTIVE"
- row.prop(params, layer + "_layers", index=i, toggle=True, text="", icon=icon)
-
- row = col.row(align=True)
-
- for i in range(24, 32):
- icon = "NONE"
- if bone_layers[i]:
- icon = "LAYER_ACTIVE"
- row.prop(params, layer + "_layers", index=i, toggle=True, text="", icon=icon)
+ ControlLayersOption.FK.parameters_ui(layout, params)
+ ControlLayersOption.TWEAK.parameters_ui(layout, params)
def create_sample(obj):
diff --git a/rigify/rigs/limbs/simple_tentacle.py b/rigify/rigs/limbs/simple_tentacle.py
index 71a9eaa9..82fcafe8 100644
--- a/rigify/rigs/limbs/simple_tentacle.py
+++ b/rigify/rigs/limbs/simple_tentacle.py
@@ -4,6 +4,7 @@ from ...utils import strip_org, make_deformer_name, connected_children_names
from ...utils import put_bone, create_sphere_widget
from ...utils import create_circle_widget, align_bone_x_axis
from ...utils import MetarigError
+from ...utils import ControlLayersOption
class Rig:
@@ -15,11 +16,6 @@ class Rig:
self.copy_rotation_axes = params.copy_rotation_axes
- if params.tweak_extra_layers:
- self.tweak_layers = list(params.tweak_layers)
- else:
- self.tweak_layers = None
-
if len(self.org_bones) <= 1:
raise MetarigError(
"RIGIFY ERROR: invalid rig structure on bone: %s" % (strip_org(bone_name))
@@ -118,9 +114,7 @@ class Rig:
tweak_pb.lock_rotation = (True, True, True)
tweak_pb.lock_scale = (True, True, True)
- # Set up tweak bone layers
- if self.tweak_layers:
- tweak_pb.bone.layers = self.tweak_layers
+ ControlLayersOption.TWEAK.assign(self.params, self.obj.pose.bones, tweak_chain)
return tweak_chain
@@ -250,17 +244,7 @@ def add_parameters(params):
)
# Setting up extra tweak layers
- params.tweak_extra_layers = bpy.props.BoolProperty(
- name="tweak_extra_layers",
- default=True,
- description=""
- )
-
- params.tweak_layers = bpy.props.BoolVectorProperty(
- size=32,
- description="Layers for the tweak controls to be on",
- default=tuple([i == 1 for i in range(0, 32)])
- )
+ ControlLayersOption.TWEAK.add_parameters(params)
items = [('automatic', 'Automatic', ''), ('manual', 'Manual', '')]
params.roll_alignment = bpy.props.EnumProperty(items=items, name="Bone roll alignment", default='automatic')
@@ -277,45 +261,7 @@ def parameters_ui(layout, params):
for i, axis in enumerate(['x', 'y', 'z']):
row.prop(params, "copy_rotation_axes", index=i, toggle=True, text=axis)
- r = layout.row()
- r.prop(params, "tweak_extra_layers")
- r.active = params.tweak_extra_layers
-
- col = r.column(align=True)
- row = col.row(align=True)
-
- bone_layers = bpy.context.active_pose_bone.bone.layers[:]
-
- for i in range(8): # Layers 0-7
- icon = "NONE"
- if bone_layers[i]:
- icon = "LAYER_ACTIVE"
- row.prop(params, "tweak_layers", index=i, toggle=True, text="", icon=icon)
-
- row = col.row(align=True)
-
- for i in range(16, 24): # Layers 16-23
- icon = "NONE"
- if bone_layers[i]:
- icon = "LAYER_ACTIVE"
- row.prop(params, "tweak_layers", index=i, toggle=True, text="", icon=icon)
-
- col = r.column(align=True)
- row = col.row(align=True)
-
- for i in range(8, 16): # Layers 8-15
- icon = "NONE"
- if bone_layers[i]:
- icon = "LAYER_ACTIVE"
- row.prop(params, "tweak_layers", index=i, toggle=True, text="", icon=icon)
-
- row = col.row(align=True)
-
- for i in range(24, 32): # Layers 24-31
- icon = "NONE"
- if bone_layers[i]:
- icon = "LAYER_ACTIVE"
- row.prop(params, "tweak_layers", index=i, toggle=True, text="", icon=icon)
+ ControlLayersOption.TWEAK.parameters_ui(layout, params)
def create_sample(obj):
diff --git a/rigify/rigs/limbs/super_limb.py b/rigify/rigs/limbs/super_limb.py
index e5670829..3d2bb8e2 100644
--- a/rigify/rigs/limbs/super_limb.py
+++ b/rigify/rigs/limbs/super_limb.py
@@ -4,6 +4,7 @@ from .arm import Rig as armRig
from .leg import Rig as legRig
from .paw import Rig as pawRig
+from ...utils import ControlLayersOption
class Rig:
@@ -83,30 +84,8 @@ def add_parameters(params):
)
# Setting up extra layers for the FK and tweak
- params.tweak_extra_layers = bpy.props.BoolProperty(
- name = "tweak_extra_layers",
- default = True,
- description = ""
- )
-
- params.tweak_layers = bpy.props.BoolVectorProperty(
- size = 32,
- description = "Layers for the tweak controls to be on",
- default = tuple( [ i == 1 for i in range(0, 32) ] )
- )
-
- # Setting up extra layers for the FK and tweak
- params.fk_extra_layers = bpy.props.BoolProperty(
- name = "fk_extra_layers",
- default = True,
- description = ""
- )
-
- params.fk_layers = bpy.props.BoolVectorProperty(
- size = 32,
- description = "Layers for the FK controls to be on",
- default = tuple( [ i == 1 for i in range(0, 32) ] )
- )
+ ControlLayersOption.FK.add_parameters(params)
+ ControlLayersOption.TWEAK.add_parameters(params)
def parameters_ui(layout, params):
@@ -130,46 +109,8 @@ def parameters_ui(layout, params):
r = layout.row()
r.prop(params, "bbones")
- bone_layers = bpy.context.active_pose_bone.bone.layers[:]
-
- for layer in ['fk', 'tweak']:
- r = layout.row()
- r.prop(params, layer + "_extra_layers")
- r.active = params.tweak_extra_layers
-
- col = r.column(align=True)
- row = col.row(align=True)
-
- for i in range(8):
- icon = "NONE"
- if bone_layers[i]:
- icon = "LAYER_ACTIVE"
- row.prop(params, layer + "_layers", index=i, toggle=True, text="", icon=icon)
-
- row = col.row(align=True)
-
- for i in range(16,24):
- icon = "NONE"
- if bone_layers[i]:
- icon = "LAYER_ACTIVE"
- row.prop(params, layer + "_layers", index=i, toggle=True, text="", icon=icon)
-
- col = r.column(align=True)
- row = col.row(align=True)
-
- for i in range(8,16):
- icon = "NONE"
- if bone_layers[i]:
- icon = "LAYER_ACTIVE"
- row.prop(params, layer + "_layers", index=i, toggle=True, text="", icon=icon)
-
- row = col.row(align=True)
-
- for i in range(24,32):
- icon = "NONE"
- if bone_layers[i]:
- icon = "LAYER_ACTIVE"
- row.prop(params, layer + "_layers", index=i, toggle=True, text="", icon=icon)
+ ControlLayersOption.FK.parameters_ui(layout, params)
+ ControlLayersOption.TWEAK.parameters_ui(layout, params)
def create_sample(obj):
diff --git a/rigify/rigs/spines/super_spine.py b/rigify/rigs/spines/super_spine.py
index 6e46aa5c..d1b7593f 100644
--- a/rigify/rigs/spines/super_spine.py
+++ b/rigify/rigs/spines/super_spine.py
@@ -5,6 +5,7 @@ from ...utils import strip_org, make_deformer_name, connected_children_names
from ...utils import create_circle_widget, create_sphere_widget, create_neck_bend_widget, create_neck_tweak_widget
from ..widgets import create_ballsocket_widget
from ...utils import MetarigError, make_mechanism_name, create_cube_widget
+from ...utils import ControlLayersOption
from rna_prop_ui import rna_idprop_ui_prop_get
script = """
@@ -58,12 +59,6 @@ class Rig:
if self.use_tail and self.pivot_pos - 2 > 0:
self.tail_pos = params.tail_pos
- # Assign values to tweak layers props if opted by user
- if params.tweak_extra_layers:
- self.tweak_layers = list(params.tweak_layers)
- else:
- self.tweak_layers = None
-
# Report error of user created less than the minimum of bones for rig
min_bone_number = 3
if self.use_head:
@@ -941,8 +936,7 @@ class Rig:
continue
create_sphere_widget(self.obj, bone, bone_transform_name=None)
- if self.tweak_layers:
- pb[bone].bone.layers = self.tweak_layers
+ ControlLayersOption.TWEAK.assign(self.params, pb, tweaks)
def generate(self):
# Torso Rig Anatomy:
@@ -1058,17 +1052,7 @@ def add_parameters(params):
)
# Setting up extra layers for the FK and tweak
- params.tweak_extra_layers = bpy.props.BoolProperty(
- name="tweak_extra_layers",
- default=True,
- description=""
- )
-
- params.tweak_layers = bpy.props.BoolVectorProperty(
- size = 32,
- description = "Layers for the tweak controls to be on",
- default = tuple( [ i == 1 for i in range(0, 32) ] )
- )
+ ControlLayersOption.TWEAK.add_parameters(params)
def parameters_ui(layout, params):
@@ -1096,45 +1080,7 @@ def parameters_ui(layout, params):
row.prop(params, "copy_rotation_axes", index=i, toggle=True, text=axis)
r.enabled = params.use_tail
- r = layout.row()
- r.prop(params, "tweak_extra_layers")
- r.active = params.tweak_extra_layers
-
- col = r.column(align=True)
- row = col.row(align=True)
-
- bone_layers = bpy.context.active_pose_bone.bone.layers[:]
-
- for i in range(8):
- icon = "NONE"
- if bone_layers[i]:
- icon = "LAYER_ACTIVE"
- row.prop(params, "tweak_layers", index=i, toggle=True, text="", icon=icon)
-
- row = col.row(align=True)
-
- for i in range(16, 24):
- icon = "NONE"
- if bone_layers[i]:
- icon = "LAYER_ACTIVE"
- row.prop(params, "tweak_layers", index=i, toggle=True, text="", icon=icon)
-
- col = r.column(align=True)
- row = col.row(align=True)
-
- for i in range(8, 16):
- icon = "NONE"
- if bone_layers[i]:
- icon = "LAYER_ACTIVE"
- row.prop(params, "tweak_layers", index=i, toggle=True, text="", icon=icon)
-
- row = col.row(align=True)
-
- for i in range(24, 32):
- icon = "NONE"
- if bone_layers[i]:
- icon = "LAYER_ACTIVE"
- row.prop(params, "tweak_layers", index=i, toggle=True, text="", icon=icon)
+ ControlLayersOption.TWEAK.parameters_ui(layout, params)
def create_sample(obj):
diff --git a/rigify/ui.py b/rigify/ui.py
index 3d2a1efa..c3c9f07d 100644
--- a/rigify/ui.py
+++ b/rigify/ui.py
@@ -28,7 +28,7 @@ from bpy.props import (
from mathutils import Color
-from .utils import get_rig_type, MetarigError
+from .utils import MetarigError
from .utils import write_metarig, write_widget
from .utils import unique_name
from .utils import upgradeMetarigTypes, outdated_types
@@ -38,6 +38,18 @@ from .rigs.utils import get_limb_generated_names
from . import rig_lists
from . import generate
from . import rot_mode
+from . import feature_sets
+
+
+def build_type_list(context, rigify_types):
+ rigify_types.clear()
+
+ for r in sorted(rig_lists.rigs):
+ if (context.object.data.active_feature_set in ('all', rig_lists.rigs[r]['feature_set'])
+ or len(feature_sets.feature_set_items(context.scene, context)) == 2
+ ):
+ a = rigify_types.add()
+ a.name = r
class DATA_PT_rigify_buttons(bpy.types.Panel):
@@ -65,21 +77,18 @@ class DATA_PT_rigify_buttons(bpy.types.Panel):
check_props = ['IK_follow', 'root/parent', 'FK_limb_follow', 'IK_Stretch']
- for obj in bpy.data.objects:
- if type(obj.data) != bpy.types.Armature:
- continue
- for bone in obj.pose.bones:
- if bone.bone.layers[30] and (list(set(bone.keys()) & set(check_props))):
- show_warning = True
+ for bone in obj.pose.bones:
+ if bone.bone.layers[30] and (list(set(bone.keys()) & set(check_props))):
+ show_warning = True
+ break
+ for b in obj.pose.bones:
+ if b.rigify_type in outdated_types.keys():
+ if outdated_types[b.rigify_type]:
+ show_update_metarig = True
+ else:
+ show_update_metarig = False
+ show_not_updatable = True
break
- for b in obj.pose.bones:
- if b.rigify_type in outdated_types.keys():
- if outdated_types[b.rigify_type]:
- show_update_metarig = True
- else:
- show_update_metarig = False
- show_not_updatable = True
- break
if show_warning:
layout.label(text=WARNING, icon='ERROR')
@@ -99,7 +108,15 @@ class DATA_PT_rigify_buttons(bpy.types.Panel):
layout.operator("pose.rigify_upgrade_types", text="Upgrade Metarig")
row = layout.row()
+ # Rig type field
+
+ col = layout.column(align=True)
+ col.active = (not 'rig_id' in C.object.data)
+
+ col.separator()
+ row = col.row()
row.operator("pose.rigify_generate", text="Generate Rig", icon='POSE_HLT')
+
row.enabled = enable_generate_and_advanced
if id_store.rigify_advanced_generation:
@@ -162,24 +179,15 @@ class DATA_PT_rigify_buttons(bpy.types.Panel):
elif obj.mode == 'EDIT':
# Build types list
- collection_name = str(id_store.rigify_collection).replace(" ", "")
-
- for i in range(0, len(id_store.rigify_types)):
- id_store.rigify_types.remove(0)
-
- for r in rig_lists.rig_list:
+ build_type_list(context, id_store.rigify_types)
- if collection_name == "All":
- a = id_store.rigify_types.add()
- a.name = r
- elif r.startswith(collection_name + '.'):
- a = id_store.rigify_types.add()
- a.name = r
- elif (collection_name == "None") and ("." not in r):
- a = id_store.rigify_types.add()
- a.name = r
+ if id_store.rigify_active_type > len(id_store.rigify_types):
+ id_store.rigify_active_type = 0
# Rig type list
+ if len(feature_sets.feature_set_items(context.scene, context)) > 2:
+ 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')
@@ -582,38 +590,24 @@ class BONE_PT_rigify_buttons(bpy.types.Panel):
C = context
id_store = C.window_manager
bone = context.active_pose_bone
- collection_name = str(id_store.rigify_collection).replace(" ", "")
rig_name = str(context.active_pose_bone.rigify_type).replace(" ", "")
layout = self.layout
# Build types list
- for i in range(0, len(id_store.rigify_types)):
- id_store.rigify_types.remove(0)
-
- for r in rig_lists.rig_list:
- if r in rig_lists.implementation_rigs:
- continue
- # collection = r.split('.')[0] # UNUSED
- if collection_name == "All":
- a = id_store.rigify_types.add()
- a.name = r
- elif r.startswith(collection_name + '.'):
- a = id_store.rigify_types.add()
- a.name = r
- elif collection_name == "None" and len(r.split('.')) == 1:
- a = id_store.rigify_types.add()
- a.name = r
+ build_type_list(context, id_store.rigify_types)
# Rig type field
+ if len(feature_sets.feature_set_items(context.scene, context)) > 2:
+ row = layout.row()
+ row.prop(context.object.data, "active_feature_set")
row = layout.row()
- row.prop_search(bone, "rigify_type", id_store, "rigify_types", text="Rig type:")
+ row.prop_search(bone, "rigify_type", id_store, "rigify_types", text="Rig type")
# Rig type parameters / Rig type non-exist alert
if rig_name != "":
try:
- rig = get_rig_type(rig_name)
- rig.Rig
+ rig = rig_lists.rigs[rig_name]['module']
except (ImportError, AttributeError):
row = layout.row()
box = row.box()
@@ -641,6 +635,10 @@ class VIEW3D_PT_tools_rigify_dev(bpy.types.Panel):
def poll(cls, context):
return context.mode in ['EDIT_ARMATURE', 'EDIT_MESH']
+ @classmethod
+ def poll(cls, context):
+ return context.mode in ['EDIT_ARMATURE', 'EDIT_MESH']
+
def draw(self, context):
obj = context.active_object
if obj is not None:
@@ -818,7 +816,7 @@ class Sample(bpy.types.Operator):
use_global_undo = context.preferences.edit.use_global_undo
context.preferences.edit.use_global_undo = False
try:
- rig = get_rig_type(self.metarig_type)
+ rig = rig_lists.rigs[self.metarig_type]["module"]
create_sample = rig.create_sample
except (ImportError, AttributeError):
raise Exception("rig type '" + self.metarig_type + "' has no sample.")
diff --git a/rigify/utils.py b/rigify/utils.py
deleted file mode 100644
index 73b64112..00000000
--- a/rigify/utils.py
+++ /dev/null
@@ -1,1302 +0,0 @@
-#====================== 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.
-#
-#======================= END GPL LICENSE BLOCK ========================
-
-# <pep8 compliant>
-
-import bpy
-import imp
-import importlib
-import math
-import random
-import time
-import re
-import os
-from mathutils import Vector, Matrix, Color
-from rna_prop_ui import rna_idprop_ui_prop_get
-
-RIG_DIR = "rigs" # Name of the directory where rig types are kept
-METARIG_DIR = "metarigs" # Name of the directory where metarigs are kept
-
-ORG_PREFIX = "ORG-" # Prefix of original bones.
-MCH_PREFIX = "MCH-" # Prefix of mechanism bones.
-DEF_PREFIX = "DEF-" # Prefix of deformation bones.
-WGT_PREFIX = "WGT-" # Prefix for widget objects
-ROOT_NAME = "root" # Name of the root bone.
-
-MODULE_NAME = "rigify" # Windows/Mac blender is weird, so __package__ doesn't work
-
-outdated_types = {"pitchipoy.limbs.super_limb": "limbs.super_limb",
- "pitchipoy.limbs.super_arm": "limbs.super_limb",
- "pitchipoy.limbs.super_leg": "limbs.super_limb",
- "pitchipoy.limbs.super_front_paw": "limbs.super_limb",
- "pitchipoy.limbs.super_rear_paw": "limbs.super_limb",
- "pitchipoy.limbs.super_finger": "limbs.super_finger",
- "pitchipoy.super_torso_turbo": "spines.super_spine",
- "pitchipoy.simple_tentacle": "limbs.simple_tentacle",
- "pitchipoy.super_face": "faces.super_face",
- "pitchipoy.super_palm": "limbs.super_palm",
- "pitchipoy.super_copy": "basic.super_copy",
- "pitchipoy.tentacle": "",
- "palm": "limbs.super_palm",
- "basic.copy": "basic.super_copy",
- "biped.arm": "",
- "biped.leg": "",
- "finger": "",
- "neck_short": "",
- "misc.delta": "",
- "spine": ""
- }
-
-#=======================================================================
-# Error handling
-#=======================================================================
-class MetarigError(Exception):
- """ Exception raised for errors.
- """
- def __init__(self, message):
- self.message = message
-
- def __str__(self):
- return repr(self.message)
-
-
-#=======================================================================
-# Name manipulation
-#=======================================================================
-
-def strip_trailing_number(s):
- m = re.search(r'\.(\d{3})$', s)
- return s[0:-4] if m else s
-
-
-def unique_name(collection, base_name):
- base_name = strip_trailing_number(base_name)
- count = 1
- name = base_name
-
- while collection.get(name):
- name = "%s.%03d" % (base_name, count)
- count += 1
- return name
-
-
-def org_name(name):
- """ Returns the name with ORG_PREFIX stripped from it.
- """
- if name.startswith(ORG_PREFIX):
- return name[len(ORG_PREFIX):]
- else:
- return name
-
-
-def strip_org(name):
- """ Returns the name with ORG_PREFIX stripped from it.
- """
- if name.startswith(ORG_PREFIX):
- return name[len(ORG_PREFIX):]
- else:
- return name
-org_name = strip_org
-
-
-def strip_mch(name):
- """ Returns the name with ORG_PREFIX stripped from it.
- """
- if name.startswith(MCH_PREFIX):
- return name[len(MCH_PREFIX):]
- else:
- return name
-
-def org(name):
- """ Prepends the ORG_PREFIX to a name if it doesn't already have
- it, and returns it.
- """
- if name.startswith(ORG_PREFIX):
- return name
- else:
- return ORG_PREFIX + name
-make_original_name = org
-
-
-def mch(name):
- """ Prepends the MCH_PREFIX to a name if it doesn't already have
- it, and returns it.
- """
- if name.startswith(MCH_PREFIX):
- return name
- else:
- return MCH_PREFIX + name
-make_mechanism_name = mch
-
-
-def deformer(name):
- """ Prepends the DEF_PREFIX to a name if it doesn't already have
- it, and returns it.
- """
- if name.startswith(DEF_PREFIX):
- return name
- else:
- return DEF_PREFIX + name
-make_deformer_name = deformer
-
-
-def insert_before_lr(name, text):
- if name[-1] in ['l', 'L', 'r', 'R'] and name[-2] in ['.', '-', '_']:
- return name[:-2] + text + name[-2:]
- else:
- return name + text
-
-
-def upgradeMetarigTypes(metarig, revert=False):
- """Replaces rigify_type properties from old versions with their current names
-
- :param revert: revert types to previous version (if old type available)
- """
-
- if revert:
- vals = list(outdated_types.values())
- rig_defs = {v: k for k, v in outdated_types.items() if vals.count(v) == 1}
- else:
- rig_defs = outdated_types
-
- for bone in metarig.pose.bones:
- rig_type = bone.rigify_type
- if rig_type in rig_defs:
- bone.rigify_type = rig_defs[rig_type]
- if 'leg' in rig_type:
- bone.rigfy_parameters.limb_type = 'leg'
- if 'arm' in rig_type:
- bone.rigfy_parameters.limb_type = 'arm'
- if 'paw' in rig_type:
- bone.rigfy_parameters.limb_type = 'paw'
- if rig_type == "basic.copy":
- bone.rigify_parameters.make_widget = False
-
-
-
-#=======================
-# Bone manipulation
-#=======================
-
-def new_bone(obj, bone_name):
- """ Adds a new bone to the given armature object.
- Returns the resulting bone's name.
- """
- if obj == bpy.context.active_object and bpy.context.mode == 'EDIT_ARMATURE':
- edit_bone = obj.data.edit_bones.new(bone_name)
- name = edit_bone.name
- edit_bone.head = (0, 0, 0)
- edit_bone.tail = (0, 1, 0)
- edit_bone.roll = 0
- bpy.ops.object.mode_set(mode='OBJECT')
- bpy.ops.object.mode_set(mode='EDIT')
- return name
- else:
- raise MetarigError("Can't add new bone '%s' outside of edit mode" % bone_name)
-
-
-def copy_bone_simple(obj, bone_name, assign_name=''):
- """ Makes a copy of the given bone in the given armature object.
- but only copies head, tail positions and roll. Does not
- address parenting either.
- """
- #if bone_name not in obj.data.bones:
- if bone_name not in obj.data.edit_bones:
- raise MetarigError("copy_bone(): bone '%s' not found, cannot copy it" % bone_name)
-
- if obj == bpy.context.active_object and bpy.context.mode == 'EDIT_ARMATURE':
- if assign_name == '':
- assign_name = bone_name
- # Copy the edit bone
- edit_bone_1 = obj.data.edit_bones[bone_name]
- edit_bone_2 = obj.data.edit_bones.new(assign_name)
- bone_name_1 = bone_name
- bone_name_2 = edit_bone_2.name
-
- # Copy edit bone attributes
- edit_bone_2.layers = list(edit_bone_1.layers)
-
- edit_bone_2.head = Vector(edit_bone_1.head)
- edit_bone_2.tail = Vector(edit_bone_1.tail)
- edit_bone_2.roll = edit_bone_1.roll
-
- return bone_name_2
- else:
- raise MetarigError("Cannot copy bones outside of edit mode")
-
-
-def copy_bone(obj, bone_name, assign_name=''):
- """ Makes a copy of the given bone in the given armature object.
- Returns the resulting bone's name.
- """
- #if bone_name not in obj.data.bones:
- if bone_name not in obj.data.edit_bones:
- raise MetarigError("copy_bone(): bone '%s' not found, cannot copy it" % bone_name)
-
- if obj == bpy.context.active_object and bpy.context.mode == 'EDIT_ARMATURE':
- if assign_name == '':
- assign_name = bone_name
- # Copy the edit bone
- edit_bone_1 = obj.data.edit_bones[bone_name]
- edit_bone_2 = obj.data.edit_bones.new(assign_name)
- bone_name_1 = bone_name
- bone_name_2 = edit_bone_2.name
-
- edit_bone_2.parent = edit_bone_1.parent
- edit_bone_2.use_connect = edit_bone_1.use_connect
-
- # Copy edit bone attributes
- edit_bone_2.layers = list(edit_bone_1.layers)
-
- edit_bone_2.head = Vector(edit_bone_1.head)
- edit_bone_2.tail = Vector(edit_bone_1.tail)
- edit_bone_2.roll = edit_bone_1.roll
-
- edit_bone_2.use_inherit_rotation = edit_bone_1.use_inherit_rotation
- edit_bone_2.use_inherit_scale = edit_bone_1.use_inherit_scale
- edit_bone_2.use_local_location = edit_bone_1.use_local_location
-
- edit_bone_2.use_deform = edit_bone_1.use_deform
- edit_bone_2.bbone_segments = edit_bone_1.bbone_segments
- edit_bone_2.bbone_easein = edit_bone_1.bbone_easein
- edit_bone_2.bbone_easeout = edit_bone_1.bbone_easeout
-
- bpy.ops.object.mode_set(mode='OBJECT')
-
- # Get the pose bones
- pose_bone_1 = obj.pose.bones[bone_name_1]
- pose_bone_2 = obj.pose.bones[bone_name_2]
-
- # Copy pose bone attributes
- pose_bone_2.rotation_mode = pose_bone_1.rotation_mode
- pose_bone_2.rotation_axis_angle = tuple(pose_bone_1.rotation_axis_angle)
- pose_bone_2.rotation_euler = tuple(pose_bone_1.rotation_euler)
- pose_bone_2.rotation_quaternion = tuple(pose_bone_1.rotation_quaternion)
-
- pose_bone_2.lock_location = tuple(pose_bone_1.lock_location)
- pose_bone_2.lock_scale = tuple(pose_bone_1.lock_scale)
- pose_bone_2.lock_rotation = tuple(pose_bone_1.lock_rotation)
- pose_bone_2.lock_rotation_w = pose_bone_1.lock_rotation_w
- pose_bone_2.lock_rotations_4d = pose_bone_1.lock_rotations_4d
-
- # Copy custom properties
- for key in pose_bone_1.keys():
- if key != "_RNA_UI" \
- and key != "rigify_parameters" \
- and key != "rigify_type":
- prop1 = rna_idprop_ui_prop_get(pose_bone_1, key, create=False)
- prop2 = rna_idprop_ui_prop_get(pose_bone_2, key, create=True)
- pose_bone_2[key] = pose_bone_1[key]
- for key in prop1.keys():
- prop2[key] = prop1[key]
-
- bpy.ops.object.mode_set(mode='EDIT')
-
- return bone_name_2
- else:
- raise MetarigError("Cannot copy bones outside of edit mode")
-
-
-def flip_bone(obj, bone_name):
- """ Flips an edit bone.
- """
- if bone_name not in obj.data.bones:
- raise MetarigError("flip_bone(): bone '%s' not found, cannot copy it" % bone_name)
-
- if obj == bpy.context.active_object and bpy.context.mode == 'EDIT_ARMATURE':
- bone = obj.data.edit_bones[bone_name]
- head = Vector(bone.head)
- tail = Vector(bone.tail)
- bone.tail = head + tail
- bone.head = tail
- bone.tail = head
- else:
- raise MetarigError("Cannot flip bones outside of edit mode")
-
-
-def put_bone(obj, bone_name, pos):
- """ Places a bone at the given position.
- """
- if bone_name not in obj.data.bones:
- raise MetarigError("put_bone(): bone '%s' not found, cannot move it" % bone_name)
-
- if obj == bpy.context.active_object and bpy.context.mode == 'EDIT_ARMATURE':
- bone = obj.data.edit_bones[bone_name]
-
- delta = pos - bone.head
- bone.translate(delta)
- else:
- raise MetarigError("Cannot 'put' bones outside of edit mode")
-
-
-def make_nonscaling_child(obj, bone_name, location, child_name_postfix=""):
- """ Takes the named bone and creates a non-scaling child of it at
- the given location. The returned bone (returned by name) is not
- a true child, but behaves like one sans inheriting scaling.
-
- It is intended as an intermediate construction to prevent rig types
- from scaling with their parents. The named bone is assumed to be
- an ORG bone.
- """
- if bone_name not in obj.data.bones:
- raise MetarigError("make_nonscaling_child(): bone '%s' not found, cannot copy it" % bone_name)
-
- if obj == bpy.context.active_object and bpy.context.mode == 'EDIT_ARMATURE':
- # Create desired names for bones
- name1 = make_mechanism_name(strip_org(insert_before_lr(bone_name, child_name_postfix + "_ns_ch")))
- name2 = make_mechanism_name(strip_org(insert_before_lr(bone_name, child_name_postfix + "_ns_intr")))
-
- # Create bones
- child = copy_bone(obj, bone_name, name1)
- intermediate_parent = copy_bone(obj, bone_name, name2)
-
- # Get edit bones
- eb = obj.data.edit_bones
- child_e = eb[child]
- intrpar_e = eb[intermediate_parent]
-
- # Parenting
- child_e.use_connect = False
- child_e.parent = None
-
- intrpar_e.use_connect = False
- intrpar_e.parent = eb[bone_name]
-
- # Positioning
- child_e.length *= 0.5
- intrpar_e.length *= 0.25
-
- put_bone(obj, child, location)
- put_bone(obj, intermediate_parent, location)
-
- # Object mode
- bpy.ops.object.mode_set(mode='OBJECT')
- pb = obj.pose.bones
-
- # Add constraints
- con = pb[child].constraints.new('COPY_LOCATION')
- con.name = "parent_loc"
- con.target = obj
- con.subtarget = intermediate_parent
-
- con = pb[child].constraints.new('COPY_ROTATION')
- con.name = "parent_loc"
- con.target = obj
- con.subtarget = intermediate_parent
-
- bpy.ops.object.mode_set(mode='EDIT')
-
- return child
- else:
- raise MetarigError("Cannot make nonscaling child outside of edit mode")
-
-
-#=============================================
-# Widget creation
-#=============================================
-
-def obj_to_bone(obj, rig, bone_name):
- """ Places an object at the location/rotation/scale of the given bone.
- """
- if bpy.context.mode == 'EDIT_ARMATURE':
- raise MetarigError("obj_to_bone(): does not work while in edit mode")
-
- bone = rig.data.bones[bone_name]
-
- mat = rig.matrix_world @ bone.matrix_local
-
- obj.location = mat.to_translation()
-
- obj.rotation_mode = 'XYZ'
- obj.rotation_euler = mat.to_euler()
-
- scl = mat.to_scale()
- scl_avg = (scl[0] + scl[1] + scl[2]) / 3
- obj.scale = (bone.length * scl_avg), (bone.length * scl_avg), (bone.length * scl_avg)
-
-
-def create_widget(rig, bone_name, bone_transform_name=None):
- """ Creates an empty widget object for a bone, and returns the object.
- """
- if bone_transform_name is None:
- bone_transform_name = bone_name
-
- obj_name = WGT_PREFIX + rig.name + '_' + bone_name
- scene = bpy.context.scene
- collection = bpy.context.collection
- id_store = bpy.context.window_manager
-
- # Check if it already exists in the scene
- if obj_name in scene.objects:
- # Move object to bone position, in case it changed
- obj = scene.objects[obj_name]
- obj_to_bone(obj, rig, bone_transform_name)
-
- return None
- else:
- # Delete object if it exists in blend data but not scene data.
- # This is necessary so we can then create the object without
- # name conflicts.
- if obj_name in bpy.data.objects:
- bpy.data.objects[obj_name].user_clear()
- bpy.data.objects.remove(bpy.data.objects[obj_name])
-
- # Create mesh object
- mesh = bpy.data.meshes.new(obj_name)
- obj = bpy.data.objects.new(obj_name, mesh)
- collection.objects.link(obj)
-
- # Move object to bone position and set layers
- obj_to_bone(obj, rig, bone_transform_name)
- wgts_group_name = 'WGTS_' + rig.name
- if wgts_group_name in bpy.data.objects.keys():
- obj.parent = bpy.data.objects[wgts_group_name]
-
- return obj
-
-
-# Common Widgets
-
-def create_line_widget(rig, bone_name, bone_transform_name=None):
- """ Creates a basic line widget, a line that spans the length of the bone.
- """
- obj = create_widget(rig, bone_name, bone_transform_name)
- if obj is not None:
- mesh = obj.data
- mesh.from_pydata([(0, 0, 0), (0, 1, 0)], [(0, 1)], [])
- mesh.update()
-
-
-def create_circle_widget(rig, bone_name, radius=1.0, head_tail=0.0, with_line=False, bone_transform_name=None):
- """ Creates a basic circle widget, a circle around the y-axis.
- radius: the radius of the circle
- head_tail: where along the length of the bone the circle is (0.0=head, 1.0=tail)
- """
- obj = create_widget(rig, bone_name, bone_transform_name)
- if obj != None:
- v = [(0.7071068286895752, 2.980232238769531e-07, -0.7071065306663513), (0.8314696550369263, 2.980232238769531e-07, -0.5555699467658997), (0.9238795042037964, 2.682209014892578e-07, -0.3826831877231598), (0.9807852506637573, 2.5331974029541016e-07, -0.19509011507034302), (1.0, 2.365559055306221e-07, 1.6105803979371558e-07), (0.9807853698730469, 2.2351741790771484e-07, 0.19509044289588928), (0.9238796234130859, 2.086162567138672e-07, 0.38268351554870605), (0.8314696550369263, 1.7881393432617188e-07, 0.5555704236030579), (0.7071068286895752, 1.7881393432617188e-07, 0.7071070075035095), (0.5555702447891235, 1.7881393432617188e-07, 0.8314698934555054), (0.38268327713012695, 1.7881393432617188e-07, 0.923879861831665), (0.19509008526802063, 1.7881393432617188e-07, 0.9807855486869812), (-3.2584136988589307e-07, 1.1920928955078125e-07, 1.000000238418579), (-0.19509072601795197, 1.7881393432617188e-07, 0.9807854294776917), (-0.3826838731765747, 1.7881393432617188e-07, 0.9238795638084412), (-0.5555707216262817, 1.7881393432617188e-07, 0.8314695358276367), (-0.7071071863174438, 1.7881393432617188e-07, 0.7071065902709961), (-0.8314700126647949, 1.7881393432617188e-07, 0.5555698871612549), (-0.923879861831665, 2.086162567138672e-07, 0.3826829195022583), (-0.9807853698730469, 2.2351741790771484e-07, 0.1950896978378296), (-1.0, 2.365559907957504e-07, -7.290432222362142e-07), (-0.9807850122451782, 2.5331974029541016e-07, -0.195091113448143), (-0.9238790273666382, 2.682209014892578e-07, -0.38268423080444336), (-0.831468939781189, 2.980232238769531e-07, -0.5555710196495056), (-0.7071058750152588, 2.980232238769531e-07, -0.707107424736023), (-0.555569052696228, 2.980232238769531e-07, -0.8314701318740845), (-0.38268208503723145, 2.980232238769531e-07, -0.923879861831665), (-0.19508881866931915, 2.980232238769531e-07, -0.9807853102684021), (1.6053570561780361e-06, 2.980232238769531e-07, -0.9999997615814209), (0.19509197771549225, 2.980232238769531e-07, -0.9807847142219543), (0.3826850652694702, 2.980232238769531e-07, -0.9238786101341248), (0.5555717945098877, 2.980232238769531e-07, -0.8314683437347412)]
- verts = [(a[0] * radius, head_tail, a[2] * radius) for a in v]
- if with_line:
- edges = [(28, 12), (0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7), (7, 8), (8, 9), (9, 10), (10, 11), (11, 12), (12, 13), (13, 14), (14, 15), (15, 16), (16, 17), (17, 18), (18, 19), (19, 20), (20, 21), (21, 22), (22, 23), (23, 24), (24, 25), (25, 26), (26, 27), (27, 28), (28, 29), (29, 30), (30, 31), (0, 31)]
- else:
- edges = [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7), (7, 8), (8, 9), (9, 10), (10, 11), (11, 12), (12, 13), (13, 14), (14, 15), (15, 16), (16, 17), (17, 18), (18, 19), (19, 20), (20, 21), (21, 22), (22, 23), (23, 24), (24, 25), (25, 26), (26, 27), (27, 28), (28, 29), (29, 30), (30, 31), (0, 31)]
- mesh = obj.data
- mesh.from_pydata(verts, edges, [])
- mesh.update()
- return obj
- else:
- return None
-
-
-def create_cube_widget(rig, bone_name, radius=0.5, bone_transform_name=None):
- """ Creates a basic cube widget.
- """
- obj = create_widget(rig, bone_name, bone_transform_name)
- if obj is not None:
- r = radius
- verts = [(r, r, r), (r, -r, r), (-r, -r, r), (-r, r, r), (r, r, -r), (r, -r, -r), (-r, -r, -r), (-r, r, -r)]
- edges = [(0, 1), (1, 2), (2, 3), (3, 0), (4, 5), (5, 6), (6, 7), (7, 4), (0, 4), (1, 5), (2, 6), (3, 7)]
- mesh = obj.data
- mesh.from_pydata(verts, edges, [])
- mesh.update()
-
-
-def create_chain_widget(rig, bone_name, radius=0.5, invert=False, bone_transform_name=None):
- """Creates a basic chain widget
- """
- obj = create_widget(rig, bone_name, bone_transform_name)
- if obj != None:
- r = radius
- rh = radius/2
- if invert:
- verts = [(rh, rh, rh), (r, -r, r), (-r, -r, r), (-rh, rh, rh), (rh, rh, -rh), (r, -r, -r), (-r, -r, -r), (-rh, rh, -rh)]
- else:
- verts = [(r, r, r), (rh, -rh, rh), (-rh, -rh, rh), (-r, r, r), (r, r, -r), (rh, -rh, -rh), (-rh, -rh, -rh), (-r, r, -r)]
- edges = [(0, 1), (1, 2), (2, 3), (3, 0), (4, 5), (5, 6), (6, 7), (7, 4), (0, 4), (1, 5), (2, 6), (3, 7)]
- mesh = obj.data
- mesh.from_pydata(verts, edges, [])
- mesh.update()
-
-
-def create_sphere_widget(rig, bone_name, bone_transform_name=None):
- """ Creates a basic sphere widget, three pependicular overlapping circles.
- """
- obj = create_widget(rig, bone_name, bone_transform_name)
- if obj != None:
- verts = [(0.3535533845424652, 0.3535533845424652, 0.0), (0.4619397521018982, 0.19134171307086945, 0.0), (0.5, -2.1855694143368964e-08, 0.0), (0.4619397521018982, -0.19134175777435303, 0.0), (0.3535533845424652, -0.3535533845424652, 0.0), (0.19134174287319183, -0.4619397521018982, 0.0), (7.549790126404332e-08, -0.5, 0.0), (-0.1913416087627411, -0.46193981170654297, 0.0), (-0.35355329513549805, -0.35355350375175476, 0.0), (-0.4619397521018982, -0.19134178757667542, 0.0), (-0.5, 5.962440319251527e-09, 0.0), (-0.4619397222995758, 0.1913418024778366, 0.0), (-0.35355326533317566, 0.35355350375175476, 0.0), (-0.19134148955345154, 0.46193987131118774, 0.0), (3.2584136988589307e-07, 0.5, 0.0), (0.1913420855998993, 0.46193960309028625, 0.0), (7.450580596923828e-08, 0.46193960309028625, 0.19134199619293213), (5.9254205098113744e-08, 0.5, 2.323586443253589e-07), (4.470348358154297e-08, 0.46193987131118774, -0.1913415789604187), (2.9802322387695312e-08, 0.35355350375175476, -0.3535533547401428), (2.9802322387695312e-08, 0.19134178757667542, -0.46193981170654297), (5.960464477539063e-08, -1.1151834122813398e-08, -0.5000000596046448), (5.960464477539063e-08, -0.1913418024778366, -0.46193984150886536), (5.960464477539063e-08, -0.35355350375175476, -0.3535533845424652), (7.450580596923828e-08, -0.46193981170654297, -0.19134166836738586), (9.348272556053416e-08, -0.5, 1.624372103492533e-08), (1.043081283569336e-07, -0.4619397521018982, 0.19134168326854706), (1.1920928955078125e-07, -0.3535533845424652, 0.35355329513549805), (1.1920928955078125e-07, -0.19134174287319183, 0.46193966269493103), (1.1920928955078125e-07, -4.7414250303745575e-09, 0.49999991059303284), (1.1920928955078125e-07, 0.19134172797203064, 0.46193966269493103), (8.940696716308594e-08, 0.3535533845424652, 0.35355329513549805), (0.3535534739494324, 0.0, 0.35355329513549805), (0.1913418173789978, -2.9802322387695312e-08, 0.46193966269493103), (8.303572940349113e-08, -5.005858838558197e-08, 0.49999991059303284), (-0.19134165346622467, -5.960464477539063e-08, 0.46193966269493103), (-0.35355329513549805, -8.940696716308594e-08, 0.35355329513549805), (-0.46193963289260864, -5.960464477539063e-08, 0.19134168326854706), (-0.49999991059303284, -5.960464477539063e-08, 1.624372103492533e-08), (-0.4619397521018982, -2.9802322387695312e-08, -0.19134166836738586), (-0.3535534143447876, -2.9802322387695312e-08, -0.3535533845424652), (-0.19134171307086945, 0.0, -0.46193984150886536), (7.662531942287387e-08, 9.546055501630235e-09, -0.5000000596046448), (0.19134187698364258, 5.960464477539063e-08, -0.46193981170654297), (0.3535535931587219, 5.960464477539063e-08, -0.3535533547401428), (0.4619399905204773, 5.960464477539063e-08, -0.1913415789604187), (0.5000000596046448, 5.960464477539063e-08, 2.323586443253589e-07), (0.4619396924972534, 2.9802322387695312e-08, 0.19134199619293213)]
- edges = [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7), (7, 8), (8, 9), (9, 10), (10, 11), (11, 12), (12, 13), (13, 14), (14, 15), (0, 15), (16, 31), (16, 17), (17, 18), (18, 19), (19, 20), (20, 21), (21, 22), (22, 23), (23, 24), (24, 25), (25, 26), (26, 27), (27, 28), (28, 29), (29, 30), (30, 31), (32, 33), (33, 34), (34, 35), (35, 36), (36, 37), (37, 38), (38, 39), (39, 40), (40, 41), (41, 42), (42, 43), (43, 44), (44, 45), (45, 46), (46, 47), (32, 47)]
- mesh = obj.data
- mesh.from_pydata(verts, edges, [])
- mesh.update()
-
-
-def create_limb_widget(rig, bone_name, bone_transform_name=None):
- """ Creates a basic limb widget, a line that spans the length of the
- bone, with a circle around the center.
- """
- obj = create_widget(rig, bone_name, bone_transform_name)
- if obj != None:
- verts = [(-1.1920928955078125e-07, 1.7881393432617188e-07, 0.0), (3.5762786865234375e-07, 1.0000004768371582, 0.0), (0.1767769455909729, 0.5000001192092896, 0.17677664756774902), (0.20786768198013306, 0.5000001192092896, 0.1388925313949585), (0.23097014427185059, 0.5000001192092896, 0.09567084908485413), (0.24519658088684082, 0.5000001192092896, 0.048772573471069336), (0.2500002384185791, 0.5000001192092896, -2.545945676502015e-09), (0.24519658088684082, 0.5000001192092896, -0.048772573471069336), (0.23097014427185059, 0.5000001192092896, -0.09567084908485413), (0.20786768198013306, 0.5000001192092896, -0.13889259099960327), (0.1767769455909729, 0.5000001192092896, -0.1767767071723938), (0.13889282941818237, 0.5000001192092896, -0.20786744356155396), (0.09567105770111084, 0.5000001192092896, -0.23096990585327148), (0.04877278208732605, 0.5000001192092896, -0.24519634246826172), (1.7279069197684294e-07, 0.5000000596046448, -0.25), (-0.0487724244594574, 0.5000001192092896, -0.24519634246826172), (-0.09567070007324219, 0.5000001192092896, -0.2309698462486267), (-0.13889241218566895, 0.5000001192092896, -0.20786738395690918), (-0.17677652835845947, 0.5000001192092896, -0.17677664756774902), (-0.20786726474761963, 0.5000001192092896, -0.13889244198799133), (-0.23096972703933716, 0.5000001192092896, -0.09567070007324219), (-0.24519610404968262, 0.5000001192092896, -0.04877239465713501), (-0.2499997615814209, 0.5000001192092896, 2.1997936983098043e-07), (-0.24519598484039307, 0.5000001192092896, 0.04877282679080963), (-0.23096948862075806, 0.5000001192092896, 0.09567108750343323), (-0.20786696672439575, 0.5000001192092896, 0.1388927698135376), (-0.1767762303352356, 0.5000001192092896, 0.17677688598632812), (-0.13889199495315552, 0.5000001192092896, 0.2078675627708435), (-0.09567028284072876, 0.5000001192092896, 0.23097002506256104), (-0.048771947622299194, 0.5000001192092896, 0.24519634246826172), (6.555903269145347e-07, 0.5000001192092896, 0.25), (0.04877324402332306, 0.5000001192092896, 0.24519622325897217), (0.09567153453826904, 0.5000001192092896, 0.23096966743469238), (0.13889318704605103, 0.5000001192092896, 0.20786714553833008)]
- edges = [(0, 1), (2, 3), (4, 3), (5, 4), (5, 6), (6, 7), (8, 7), (8, 9), (10, 9), (10, 11), (11, 12), (13, 12), (14, 13), (14, 15), (16, 15), (16, 17), (17, 18), (19, 18), (19, 20), (21, 20), (21, 22), (22, 23), (24, 23), (25, 24), (25, 26), (27, 26), (27, 28), (29, 28), (29, 30), (30, 31), (32, 31), (32, 33), (2, 33)]
- mesh = obj.data
- mesh.from_pydata(verts, edges, [])
- mesh.update()
-
-
-def create_bone_widget(rig, bone_name, bone_transform_name=None):
- """ Creates a basic bone widget, a simple obolisk-esk shape.
- """
- obj = create_widget(rig, bone_name, bone_transform_name)
- if obj != None:
- verts = [(0.04, 1.0, -0.04), (0.1, 0.0, -0.1), (-0.1, 0.0, -0.1), (-0.04, 1.0, -0.04), (0.04, 1.0, 0.04), (0.1, 0.0, 0.1), (-0.1, 0.0, 0.1), (-0.04, 1.0, 0.04)]
- edges = [(1, 2), (0, 1), (0, 3), (2, 3), (4, 5), (5, 6), (6, 7), (4, 7), (1, 5), (0, 4), (2, 6), (3, 7)]
- mesh = obj.data
- mesh.from_pydata(verts, edges, [])
- mesh.update()
-
-
-def create_compass_widget(rig, bone_name, bone_transform_name=None):
- """ Creates a compass-shaped widget.
- """
- obj = create_widget(rig, bone_name, bone_transform_name)
- if obj != None:
- verts = [(0.0, 1.2000000476837158, 0.0), (0.19509032368659973, 0.9807852506637573, 0.0), (0.3826834559440613, 0.9238795042037964, 0.0), (0.5555702447891235, 0.8314695954322815, 0.0), (0.7071067690849304, 0.7071067690849304, 0.0), (0.8314696550369263, 0.5555701851844788, 0.0), (0.9238795042037964, 0.3826834261417389, 0.0), (0.9807852506637573, 0.19509035348892212, 0.0), (1.2000000476837158, 7.549790126404332e-08, 0.0), (0.9807853102684021, -0.19509020447731018, 0.0), (0.9238795638084412, -0.38268327713012695, 0.0), (0.8314696550369263, -0.5555701851844788, 0.0), (0.7071067690849304, -0.7071067690849304, 0.0), (0.5555701851844788, -0.8314696550369263, 0.0), (0.38268327713012695, -0.9238796234130859, 0.0), (0.19509008526802063, -0.9807853102684021, 0.0), (-3.2584136988589307e-07, -1.2999999523162842, 0.0), (-0.19509072601795197, -0.9807851910591125, 0.0), (-0.3826838731765747, -0.9238793253898621, 0.0), (-0.5555707216262817, -0.8314692974090576, 0.0), (-0.7071072459220886, -0.707106351852417, 0.0), (-0.8314700126647949, -0.5555696487426758, 0.0), (-0.923879861831665, -0.3826826810836792, 0.0), (-0.9807854294776917, -0.1950894594192505, 0.0), (-1.2000000476837158, 9.655991561885457e-07, 0.0), (-0.980785071849823, 0.1950913518667221, 0.0), (-0.923879086971283, 0.38268446922302246, 0.0), (-0.831468939781189, 0.5555712580680847, 0.0), (-0.7071058750152588, 0.707107663154602, 0.0), (-0.5555691123008728, 0.8314703702926636, 0.0), (-0.38268208503723145, 0.9238801002502441, 0.0), (-0.19508881866931915, 0.9807855486869812, 0.0)]
- edges = [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7), (7, 8), (8, 9), (9, 10), (10, 11), (11, 12), (12, 13), (13, 14), (14, 15), (15, 16), (16, 17), (17, 18), (18, 19), (19, 20), (20, 21), (21, 22), (22, 23), (23, 24), (24, 25), (25, 26), (26, 27), (27, 28), (28, 29), (29, 30), (30, 31), (0, 31)]
- mesh = obj.data
- mesh.from_pydata(verts, edges, [])
- mesh.update()
-
-
-def create_root_widget(rig, bone_name, bone_transform_name=None):
- """ Creates a widget for the root bone.
- """
- obj = create_widget(rig, bone_name, bone_transform_name)
- if obj != None:
- verts = [(0.7071067690849304, 0.7071067690849304, 0.0), (0.7071067690849304, -0.7071067690849304, 0.0), (-0.7071067690849304, 0.7071067690849304, 0.0), (-0.7071067690849304, -0.7071067690849304, 0.0), (0.8314696550369263, 0.5555701851844788, 0.0), (0.8314696550369263, -0.5555701851844788, 0.0), (-0.8314696550369263, 0.5555701851844788, 0.0), (-0.8314696550369263, -0.5555701851844788, 0.0), (0.9238795042037964, 0.3826834261417389, 0.0), (0.9238795042037964, -0.3826834261417389, 0.0), (-0.9238795042037964, 0.3826834261417389, 0.0), (-0.9238795042037964, -0.3826834261417389, 0.0), (0.9807852506637573, 0.19509035348892212, 0.0), (0.9807852506637573, -0.19509035348892212, 0.0), (-0.9807852506637573, 0.19509035348892212, 0.0), (-0.9807852506637573, -0.19509035348892212, 0.0), (0.19509197771549225, 0.9807849526405334, 0.0), (0.19509197771549225, -0.9807849526405334, 0.0), (-0.19509197771549225, 0.9807849526405334, 0.0), (-0.19509197771549225, -0.9807849526405334, 0.0), (0.3826850652694702, 0.9238788485527039, 0.0), (0.3826850652694702, -0.9238788485527039, 0.0), (-0.3826850652694702, 0.9238788485527039, 0.0), (-0.3826850652694702, -0.9238788485527039, 0.0), (0.5555717945098877, 0.8314685821533203, 0.0), (0.5555717945098877, -0.8314685821533203, 0.0), (-0.5555717945098877, 0.8314685821533203, 0.0), (-0.5555717945098877, -0.8314685821533203, 0.0), (0.19509197771549225, 1.2807848453521729, 0.0), (0.19509197771549225, -1.2807848453521729, 0.0), (-0.19509197771549225, 1.2807848453521729, 0.0), (-0.19509197771549225, -1.2807848453521729, 0.0), (1.280785322189331, 0.19509035348892212, 0.0), (1.280785322189331, -0.19509035348892212, 0.0), (-1.280785322189331, 0.19509035348892212, 0.0), (-1.280785322189331, -0.19509035348892212, 0.0), (0.3950919806957245, 1.2807848453521729, 0.0), (0.3950919806957245, -1.2807848453521729, 0.0), (-0.3950919806957245, 1.2807848453521729, 0.0), (-0.3950919806957245, -1.2807848453521729, 0.0), (1.280785322189331, 0.39509034156799316, 0.0), (1.280785322189331, -0.39509034156799316, 0.0), (-1.280785322189331, 0.39509034156799316, 0.0), (-1.280785322189331, -0.39509034156799316, 0.0), (0.0, 1.5807849168777466, 0.0), (0.0, -1.5807849168777466, 0.0), (1.5807852745056152, 0.0, 0.0), (-1.5807852745056152, 0.0, 0.0)]
- edges = [(0, 4), (1, 5), (2, 6), (3, 7), (4, 8), (5, 9), (6, 10), (7, 11), (8, 12), (9, 13), (10, 14), (11, 15), (16, 20), (17, 21), (18, 22), (19, 23), (20, 24), (21, 25), (22, 26), (23, 27), (0, 24), (1, 25), (2, 26), (3, 27), (16, 28), (17, 29), (18, 30), (19, 31), (12, 32), (13, 33), (14, 34), (15, 35), (28, 36), (29, 37), (30, 38), (31, 39), (32, 40), (33, 41), (34, 42), (35, 43), (36, 44), (37, 45), (38, 44), (39, 45), (40, 46), (41, 46), (42, 47), (43, 47)]
- mesh = obj.data
- mesh.from_pydata(verts, edges, [])
- mesh.update()
-
-
-def create_neck_bend_widget(rig, bone_name, radius=1.0, head_tail=0.0, bone_transform_name=None):
- obj = create_widget(rig, bone_name, bone_transform_name)
- size = 2.0
- if obj != None:
- v = [(-0.08855080604553223 * size, 0.7388765811920166 * size, -0.3940150737762451 * size),
- (0.08855044841766357 * size, 0.7388765811920166 * size, -0.3940150737762451 * size),
- (0.17710095643997192 * size, 0.5611097812652588 * size, -0.6478927135467529 * size),
- (-4.0892032870942785e-07 * size, 0.4087378978729248 * size, -0.865501880645752 * size),
- (-0.17710143327713013 * size, 0.5611097812652588 * size, -0.6478922367095947 * size),
- (0.08855026960372925 * size, 0.5611097812652588 * size, -0.6478924751281738 * size),
- (-0.08855092525482178 * size, 0.5611097812652588 * size, -0.6478927135467529 * size),
- (-0.6478927135467529 * size, 0.5611097812652588 * size, 0.08855098485946655 * size),
- (-0.6478927135467529 * size, 0.5611097812652588 * size, -0.08855020999908447 * size),
- (-0.6478924751281738 * size, 0.5611097812652588 * size, 0.17710155248641968 * size),
- (-0.865501880645752 * size, 0.4087378978729248 * size, 4.6876743908796925e-07 * size),
- (-0.647892951965332 * size, 0.5611097812652588 * size, -0.17710083723068237 * size),
- (-0.39401543140411377 * size, 0.7388765811920166 * size, -0.08855029940605164 * size),
- (-0.39401543140411377 * size, 0.7388765811920166 * size, 0.08855095505714417 * size),
- (0.6478927135467529 * size, 0.5611097812652588 * size, -0.08855059742927551 * size),
- (0.6478927135467529 * size, 0.5611097812652588 * size, 0.08855065703392029 * size),
- (0.6478924751281738 * size, 0.5611097812652588 * size, -0.17710113525390625 * size),
- (0.865501880645752 * size, 0.4087378978729248 * size, -3.264514703005261e-08 * size),
- (0.647892951965332 * size, 0.5611097812652588 * size, 0.1771012544631958 * size),
- (0.08855065703392029 * size, 0.7388765811920166 * size, 0.3940155506134033 * size),
- (-0.08855056762695312 * size, 0.7388765811920166 * size, 0.3940155506134033 * size),
- (-0.17710107564926147 * size, 0.5611097812652588 * size, 0.647892951965332 * size),
- (2.244429140318971e-07 * size, 0.4087378978729248 * size, 0.865502119064331 * size),
- (0.17710131406784058 * size, 0.5611097812652588 * size, 0.6478927135467529 * size),
- (-0.08855044841766357 * size, 0.5611097812652588 * size, 0.647892951965332 * size),
- (0.08855074644088745 * size, 0.5611097812652588 * size, 0.647892951965332 * size),
- (0.3940153121948242 * size, 0.7388765811920166 * size, 0.08855071663856506 * size),
- (0.39401519298553467 * size, 0.7388765811920166 * size, -0.08855047821998596 * size),
- (-8.416645869147032e-08 * size, 0.8255770206451416 * size, -0.2656517028808594 * size),
- (-0.06875583529472351 * size, 0.8255770206451416 * size, -0.2565997838973999 * size),
- (-0.13282597064971924 * size, 0.8255770206451416 * size, -0.2300611138343811 * size),
- (-0.18784427642822266 * size, 0.8255770206451416 * size, -0.18784409761428833 * size),
- (-0.2300613522529602 * size, 0.8255770206451416 * size, -0.1328257918357849 * size),
- (-0.256600022315979 * size, 0.8255770206451416 * size, -0.06875564157962799 * size),
- (-0.2656519412994385 * size, 0.8255770206451416 * size, 9.328307726264029e-08 * size),
- (-0.25660014152526855 * size, 0.8255770206451416 * size, 0.06875583529472351 * size),
- (-0.2300613522529602 * size, 0.8255770206451416 * size, 0.13282597064971924 * size),
- (-0.18784433603286743 * size, 0.8255770206451416 * size, 0.18784421682357788 * size),
- (-0.1328260898590088 * size, 0.8255770206451416 * size, 0.23006129264831543 * size),
- (-0.06875592470169067 * size, 0.8255770206451416 * size, 0.256600022315979 * size),
- (-1.8761508613351907e-07 * size, 0.8255770206451416 * size, 0.2656519412994385 * size),
- (0.06875556707382202 * size, 0.8255770206451416 * size, 0.2566000819206238 * size),
- (0.13282573223114014 * size, 0.8255770206451416 * size, 0.23006141185760498 * size),
- (0.18784403800964355 * size, 0.8255770206451416 * size, 0.1878443956375122 * size),
- (0.23006105422973633 * size, 0.8255770206451416 * size, 0.1328260898590088 * size),
- (0.25659990310668945 * size, 0.8255770206451416 * size, 0.06875596940517426 * size),
- (0.2656517028808594 * size, 0.8255770206451416 * size, 2.3684407324253698e-07 * size),
- (0.25659990310668945 * size, 0.8255770206451416 * size, -0.06875550746917725 * size),
- (0.23006117343902588 * size, 0.8255770206451416 * size, -0.13282567262649536 * size),
- (0.18784427642822266 * size, 0.8255770206451416 * size, -0.18784397840499878 * size),
- (0.13282597064971924 * size, 0.8255770206451416 * size, -0.23006099462509155 * size),
- (0.0687558501958847 * size, 0.8255770206451416 * size, -0.2565997838973999 * size), ]
- edges = [(1, 0), (3, 2), (5, 2), (4, 3), (6, 4), (1, 5), (0, 6), (13, 7), (12, 8), (7, 9), (9, 10), (8, 11),
- (27, 14), (26, 15), (14, 16), (16, 17), (15, 18), (17, 18), (10, 11), (12, 13), (20, 19), (22, 21),
- (24, 21), (23, 22), (29, 28), (30, 29), (31, 30), (32, 31), (33, 32), (34, 33), (35, 34), (36, 35),
- (37, 36), (38, 37), (39, 38), (40, 39), (41, 40), (42, 41), (43, 42), (44, 43), (45, 44), (46, 45),
- (47, 46), (48, 47), (49, 48), (50, 49), (51, 50), (28, 51), (26, 27), (25, 23), (20, 24),
- (19, 25), ]
-
- verts = [(a[0] * radius, head_tail, a[2] * radius) for a in v]
- mesh = obj.data
- mesh.from_pydata(verts, edges, [])
- mesh.update()
-
-
-def create_neck_tweak_widget(rig, bone_name, size=1.0, bone_transform_name=None):
- obj = create_widget(rig, bone_name, bone_transform_name)
-
- if obj != None:
- verts = [(0.3535533845424652 * size, 0.3535533845424652 * size, 0.0 * size),
- (0.4619397521018982 * size, 0.19134171307086945 * size, 0.0 * size),
- (0.5 * size, -2.1855694143368964e-08 * size, 0.0 * size),
- (0.4619397521018982 * size, -0.19134175777435303 * size, 0.0 * size),
- (0.3535533845424652 * size, -0.3535533845424652 * size, 0.0 * size),
- (0.19134174287319183 * size, -0.4619397521018982 * size, 0.0 * size),
- (7.549790126404332e-08 * size, -0.5 * size, 0.0 * size),
- (-0.1913416087627411 * size, -0.46193981170654297 * size, 0.0 * size),
- (-0.35355329513549805 * size, -0.35355350375175476 * size, 0.0 * size),
- (-0.4619397521018982 * size, -0.19134178757667542 * size, 0.0 * size),
- (-0.5 * size, 5.962440319251527e-09 * size, 0.0 * size),
- (-0.4619397222995758 * size, 0.1913418024778366 * size, 0.0 * size),
- (-0.35355326533317566 * size, 0.35355350375175476 * size, 0.0 * size),
- (-0.19134148955345154 * size, 0.46193987131118774 * size, 0.0 * size),
- (3.2584136988589307e-07 * size, 0.5 * size, 0.0 * size),
- (0.1913420855998993 * size, 0.46193960309028625 * size, 0.0 * size),
- (7.450580596923828e-08 * size, 0.46193960309028625 * size, 0.19134199619293213 * size),
- (5.9254205098113744e-08 * size, 0.5 * size, 2.323586443253589e-07 * size),
- (4.470348358154297e-08 * size, 0.46193987131118774 * size, -0.1913415789604187 * size),
- (2.9802322387695312e-08 * size, 0.35355350375175476 * size, -0.3535533547401428 * size),
- (2.9802322387695312e-08 * size, 0.19134178757667542 * size, -0.46193981170654297 * size),
- (5.960464477539063e-08 * size, -1.1151834122813398e-08 * size, -0.5000000596046448 * size),
- (5.960464477539063e-08 * size, -0.1913418024778366 * size, -0.46193984150886536 * size),
- (5.960464477539063e-08 * size, -0.35355350375175476 * size, -0.3535533845424652 * size),
- (7.450580596923828e-08 * size, -0.46193981170654297 * size, -0.19134166836738586 * size),
- (9.348272556053416e-08 * size, -0.5 * size, 1.624372103492533e-08 * size),
- (1.043081283569336e-07 * size, -0.4619397521018982 * size, 0.19134168326854706 * size),
- (1.1920928955078125e-07 * size, -0.3535533845424652 * size, 0.35355329513549805 * size),
- (1.1920928955078125e-07 * size, -0.19134174287319183 * size, 0.46193966269493103 * size),
- (1.1920928955078125e-07 * size, -4.7414250303745575e-09 * size, 0.49999991059303284 * size),
- (1.1920928955078125e-07 * size, 0.19134172797203064 * size, 0.46193966269493103 * size),
- (8.940696716308594e-08 * size, 0.3535533845424652 * size, 0.35355329513549805 * size),
- (0.3535534739494324 * size, 0.0 * size, 0.35355329513549805 * size),
- (0.1913418173789978 * size, -2.9802322387695312e-08 * size, 0.46193966269493103 * size),
- (8.303572940349113e-08 * size, -5.005858838558197e-08 * size, 0.49999991059303284 * size),
- (-0.19134165346622467 * size, -5.960464477539063e-08 * size, 0.46193966269493103 * size),
- (-0.35355329513549805 * size, -8.940696716308594e-08 * size, 0.35355329513549805 * size),
- (-0.46193963289260864 * size, -5.960464477539063e-08 * size, 0.19134168326854706 * size),
- (-0.49999991059303284 * size, -5.960464477539063e-08 * size, 1.624372103492533e-08 * size),
- (-0.4619397521018982 * size, -2.9802322387695312e-08 * size, -0.19134166836738586 * size),
- (-0.3535534143447876 * size, -2.9802322387695312e-08 * size, -0.3535533845424652 * size),
- (-0.19134171307086945 * size, 0.0 * size, -0.46193984150886536 * size),
- (7.662531942287387e-08 * size, 9.546055501630235e-09 * size, -0.5000000596046448 * size),
- (0.19134187698364258 * size, 5.960464477539063e-08 * size, -0.46193981170654297 * size),
- (0.3535535931587219 * size, 5.960464477539063e-08 * size, -0.3535533547401428 * size),
- (0.4619399905204773 * size, 5.960464477539063e-08 * size, -0.1913415789604187 * size),
- (0.5000000596046448 * size, 5.960464477539063e-08 * size, 2.323586443253589e-07 * size),
- (0.4619396924972534 * size, 2.9802322387695312e-08 * size, 0.19134199619293213 * size),
- (1.563460111618042 * size, 2.778762819843905e-08 * size, 1.5634593963623047 * size),
- (0.8461387157440186 * size, -1.0400220418205208e-07 * size, 2.0427582263946533 * size),
- (7.321979467178608e-08 * size, -1.9357810288056498e-07 * size, 2.2110657691955566 * size),
- (-0.8461385369300842 * size, -2.3579201524626114e-07 * size, 2.0427582263946533 * size),
- (-1.5634597539901733 * size, -3.67581861837607e-07 * size, 1.5634593963623047 * size),
- (-2.0427584648132324 * size, -2.3579204366797057e-07 * size, 0.8461383581161499 * size),
- (-2.211066246032715 * size, -2.3579204366797057e-07 * size, 9.972505665700737e-08 * size),
- (-2.0427589416503906 * size, -1.0400223260376151e-07 * size, -0.8461381196975708 * size),
- (-1.5634604692459106 * size, -1.040022183929068e-07 * size, -1.563459873199463 * size),
- (-0.8461387753486633 * size, 2.77876033294433e-08 * size, -2.042759418487549 * size),
- (4.4872678017782164e-08 * size, 7.00015263532805e-08 * size, -2.211066484451294 * size),
- (0.8461388349533081 * size, 2.913672290105751e-07 * size, -2.0427591800689697 * size),
- (1.5634608268737793 * size, 2.9136725743228453e-07 * size, -1.563459873199463 * size),
- (2.042759895324707 * size, 2.9136725743228453e-07 * size, -0.8461377024650574 * size),
- (2.211066722869873 * size, 2.9136725743228453e-07 * size, 1.0554133496043505e-06 * size),
- (2.0427587032318115 * size, 1.5957746768435754e-07 * size, 0.8461397886276245 * size), ]
- edges = [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7), (7, 8), (8, 9), (9, 10), (10, 11),
- (11, 12), (12, 13), (13, 14), (14, 15), (0, 15), (16, 31), (16, 17), (17, 18), (18, 19), (19, 20),
- (20, 21), (21, 22), (22, 23), (23, 24), (24, 25), (25, 26), (26, 27), (27, 28), (28, 29), (29, 30),
- (30, 31), (32, 33), (33, 34), (34, 35), (35, 36), (36, 37), (37, 38), (38, 39), (39, 40), (40, 41),
- (41, 42), (42, 43), (43, 44), (44, 45), (45, 46), (46, 47), (32, 47), (48, 49), (49, 50), (50, 51),
- (51, 52), (52, 53), (53, 54), (54, 55), (55, 56), (56, 57), (57, 58), (58, 59), (59, 60), (60, 61),
- (61, 62), (62, 63), (48, 63), (21, 58), (10, 54), (29, 50), (2, 62), ]
-
- mesh = obj.data
- mesh.from_pydata(verts, edges, [])
- mesh.update()
-
-
-#=============================================
-# Math
-#=============================================
-
-def angle_on_plane(plane, vec1, vec2):
- """ Return the angle between two vectors projected onto a plane.
- """
- plane.normalize()
- vec1 = vec1 - (plane * (vec1.dot(plane)))
- vec2 = vec2 - (plane * (vec2.dot(plane)))
- vec1.normalize()
- vec2.normalize()
-
- # Determine the angle
- angle = math.acos(max(-1.0, min(1.0, vec1.dot(vec2))))
-
- if angle < 0.00001: # close enough to zero that sign doesn't matter
- return angle
-
- # Determine the sign of the angle
- vec3 = vec2.cross(vec1)
- vec3.normalize()
- sign = vec3.dot(plane)
- if sign >= 0:
- sign = 1
- else:
- sign = -1
-
- return angle * sign
-
-
-def align_bone_roll(obj, bone1, bone2):
- """ Aligns the roll of two bones.
- """
- bone1_e = obj.data.edit_bones[bone1]
- bone2_e = obj.data.edit_bones[bone2]
-
- bone1_e.roll = 0.0
-
- # Get the directions the bones are pointing in, as vectors
- y1 = bone1_e.y_axis
- x1 = bone1_e.x_axis
- y2 = bone2_e.y_axis
- x2 = bone2_e.x_axis
-
- # Get the shortest axis to rotate bone1 on to point in the same direction as bone2
- axis = y1.cross(y2)
- axis.normalize()
-
- # Angle to rotate on that shortest axis
- angle = y1.angle(y2)
-
- # Create rotation matrix to make bone1 point in the same direction as bone2
- rot_mat = Matrix.Rotation(angle, 3, axis)
-
- # Roll factor
- x3 = rot_mat @ x1
- dot = x2 @ x3
- if dot > 1.0:
- dot = 1.0
- elif dot < -1.0:
- dot = -1.0
- roll = math.acos(dot)
-
- # Set the roll
- bone1_e.roll = roll
-
- # Check if we rolled in the right direction
- x3 = rot_mat @ bone1_e.x_axis
- check = x2 @ x3
-
- # If not, reverse
- if check < 0.9999:
- bone1_e.roll = -roll
-
-
-def align_bone_x_axis(obj, bone, vec):
- """ Rolls the bone to align its x-axis as closely as possible to
- the given vector.
- Must be in edit mode.
- """
- bone_e = obj.data.edit_bones[bone]
-
- vec = vec.cross(bone_e.y_axis)
- vec.normalize()
-
- dot = max(-1.0, min(1.0, bone_e.z_axis.dot(vec)))
- angle = math.acos(dot)
-
- bone_e.roll += angle
-
- dot1 = bone_e.z_axis.dot(vec)
-
- bone_e.roll -= angle * 2
-
- dot2 = bone_e.z_axis.dot(vec)
-
- if dot1 > dot2:
- bone_e.roll += angle * 2
-
-
-def align_bone_z_axis(obj, bone, vec):
- """ Rolls the bone to align its z-axis as closely as possible to
- the given vector.
- Must be in edit mode.
- """
- bone_e = obj.data.edit_bones[bone]
-
- vec = bone_e.y_axis.cross(vec)
- vec.normalize()
-
- dot = max(-1.0, min(1.0, bone_e.x_axis.dot(vec)))
- angle = math.acos(dot)
-
- bone_e.roll += angle
-
- dot1 = bone_e.x_axis.dot(vec)
-
- bone_e.roll -= angle * 2
-
- dot2 = bone_e.x_axis.dot(vec)
-
- if dot1 > dot2:
- bone_e.roll += angle * 2
-
-
-def align_bone_y_axis(obj, bone, vec):
- """ Matches the bone y-axis to
- the given vector.
- Must be in edit mode.
- """
-
- bone_e = obj.data.edit_bones[bone]
- vec.normalize()
- vec = vec * bone_e.length
-
- bone_e.tail = bone_e.head + vec
-
-
-#=============================================
-# Misc
-#=============================================
-
-def copy_attributes(a, b):
- keys = dir(a)
- for key in keys:
- if not key.startswith("_") \
- and not key.startswith("error_") \
- and key != "group" \
- and key != "is_valid" \
- and key != "rna_type" \
- and key != "bl_rna":
- try:
- setattr(b, key, getattr(a, key))
- except AttributeError:
- pass
-
-
-def get_rig_type(rig_type):
- """ Fetches a rig module by name, and returns it.
- """
- name = ".%s.%s" % (RIG_DIR, rig_type)
- submod = importlib.import_module(name, package=MODULE_NAME)
- importlib.reload(submod)
- return submod
-
-
-def get_metarig_module(metarig_name, path=METARIG_DIR):
- """ Fetches a rig module by name, and returns it.
- """
-
- name = ".%s.%s" % (path, metarig_name)
- submod = importlib.import_module(name, package=MODULE_NAME)
- importlib.reload(submod)
- return submod
-
-
-def connected_children_names(obj, bone_name):
- """ Returns a list of bone names (in order) of the bones that form a single
- connected chain starting with the given bone as a parent.
- If there is a connected branch, the list stops there.
- """
- bone = obj.data.bones[bone_name]
- names = []
-
- while True:
- connects = 0
- con_name = ""
-
- for child in bone.children:
- if child.use_connect:
- connects += 1
- con_name = child.name
-
- if connects == 1:
- names += [con_name]
- bone = obj.data.bones[con_name]
- else:
- break
-
- return names
-
-
-def has_connected_children(bone):
- """ Returns true/false whether a bone has connected children or not.
- """
- t = False
- for b in bone.children:
- t = t or b.use_connect
- return t
-
-
-def get_layers(layers):
- """ Does it's best to extract a set of layers from any data thrown at it.
- """
- if type(layers) == int:
- return [x == layers for x in range(0, 32)]
- elif type(layers) == str:
- s = layers.split(",")
- l = []
- for i in s:
- try:
- l += [int(float(i))]
- except ValueError:
- pass
- return [x in l for x in range(0, 32)]
- elif type(layers) == tuple or type(layers) == list:
- return [x in layers for x in range(0, 32)]
- else:
- try:
- list(layers)
- except TypeError:
- pass
- else:
- return [x in layers for x in range(0, 32)]
-
-
-def write_metarig(obj, layers=False, func_name="create", groups=False):
- """
- Write a metarig as a python script, this rig is to have all info needed for
- generating the real rig with rigify.
- """
- code = []
-
- code.append("import bpy\n\n")
- code.append("from mathutils import Color\n\n")
-
- code.append("def %s(obj):" % func_name)
- code.append(" # generated by rigify.utils.write_metarig")
- bpy.ops.object.mode_set(mode='EDIT')
- code.append(" bpy.ops.object.mode_set(mode='EDIT')")
- code.append(" arm = obj.data")
-
- arm = obj.data
-
- # Rigify bone group colors info
- if groups and len(arm.rigify_colors) > 0:
- code.append("\n for i in range(" + str(len(arm.rigify_colors)) + "):")
- code.append(" arm.rigify_colors.add()\n")
-
- for i in range(len(arm.rigify_colors)):
- name = arm.rigify_colors[i].name
- active = arm.rigify_colors[i].active
- normal = arm.rigify_colors[i].normal
- select = arm.rigify_colors[i].select
- standard_colors_lock = arm.rigify_colors[i].standard_colors_lock
- code.append(' arm.rigify_colors[' + str(i) + '].name = "' + name + '"')
- code.append(' arm.rigify_colors[' + str(i) + '].active = Color(' + str(active[:]) + ')')
- code.append(' arm.rigify_colors[' + str(i) + '].normal = Color(' + str(normal[:]) + ')')
- code.append(' arm.rigify_colors[' + str(i) + '].select = Color(' + str(select[:]) + ')')
- code.append(' arm.rigify_colors[' + str(i) + '].standard_colors_lock = ' + str(standard_colors_lock))
-
- # Rigify layer layout info
- if layers and len(arm.rigify_layers) > 0:
- code.append("\n for i in range(" + str(len(arm.rigify_layers)) + "):")
- code.append(" arm.rigify_layers.add()\n")
-
- for i in range(len(arm.rigify_layers)):
- name = arm.rigify_layers[i].name
- row = arm.rigify_layers[i].row
- selset = arm.rigify_layers[i].selset
- group = arm.rigify_layers[i].group
- code.append(' arm.rigify_layers[' + str(i) + '].name = "' + name + '"')
- code.append(' arm.rigify_layers[' + str(i) + '].row = ' + str(row))
- code.append(' arm.rigify_layers[' + str(i) + '].selset = ' + str(selset))
- code.append(' arm.rigify_layers[' + str(i) + '].group = ' + str(group))
-
- # write parents first
- bones = [(len(bone.parent_recursive), bone.name) for bone in arm.edit_bones]
- bones.sort(key=lambda item: item[0])
- bones = [item[1] for item in bones]
-
- code.append("\n bones = {}\n")
-
- for bone_name in bones:
- bone = arm.edit_bones[bone_name]
- code.append(" bone = arm.edit_bones.new(%r)" % bone.name)
- code.append(" bone.head[:] = %.4f, %.4f, %.4f" % bone.head.to_tuple(4))
- code.append(" bone.tail[:] = %.4f, %.4f, %.4f" % bone.tail.to_tuple(4))
- code.append(" bone.roll = %.4f" % bone.roll)
- code.append(" bone.use_connect = %s" % str(bone.use_connect))
- if bone.parent:
- code.append(" bone.parent = arm.edit_bones[bones[%r]]" % bone.parent.name)
- code.append(" bones[%r] = bone.name" % bone.name)
-
- bpy.ops.object.mode_set(mode='OBJECT')
- code.append("")
- code.append(" bpy.ops.object.mode_set(mode='OBJECT')")
-
- # Rig type and other pose properties
- for bone_name in bones:
- pbone = obj.pose.bones[bone_name]
-
- code.append(" pbone = obj.pose.bones[bones[%r]]" % bone_name)
- code.append(" pbone.rigify_type = %r" % pbone.rigify_type)
- code.append(" pbone.lock_location = %s" % str(tuple(pbone.lock_location)))
- code.append(" pbone.lock_rotation = %s" % str(tuple(pbone.lock_rotation)))
- code.append(" pbone.lock_rotation_w = %s" % str(pbone.lock_rotation_w))
- code.append(" pbone.lock_scale = %s" % str(tuple(pbone.lock_scale)))
- code.append(" pbone.rotation_mode = %r" % pbone.rotation_mode)
- if layers:
- code.append(" pbone.bone.layers = %s" % str(list(pbone.bone.layers)))
- # Rig type parameters
- for param_name in pbone.rigify_parameters.keys():
- param = getattr(pbone.rigify_parameters, param_name, '')
- if str(type(param)) == "<class 'bpy_prop_array'>":
- param = list(param)
- if type(param) == str:
- param = '"' + param + '"'
- code.append(" try:")
- code.append(" pbone.rigify_parameters.%s = %s" % (param_name, str(param)))
- code.append(" except AttributeError:")
- code.append(" pass")
-
- code.append("\n bpy.ops.object.mode_set(mode='EDIT')")
- code.append(" for bone in arm.edit_bones:")
- code.append(" bone.select = False")
- code.append(" bone.select_head = False")
- code.append(" bone.select_tail = False")
-
- code.append(" for b in bones:")
- code.append(" bone = arm.edit_bones[bones[b]]")
- code.append(" bone.select = True")
- code.append(" bone.select_head = True")
- code.append(" bone.select_tail = True")
- code.append(" arm.edit_bones.active = bone")
-
- # Set appropriate layers visible
- if layers:
- # Find what layers have bones on them
- active_layers = []
- for bone_name in bones:
- bone = obj.data.bones[bone_name]
- for i in range(len(bone.layers)):
- if bone.layers[i]:
- if i not in active_layers:
- active_layers.append(i)
- active_layers.sort()
-
- code.append("\n arm.layers = [(x in " + str(active_layers) + ") for x in range(" + str(len(arm.layers)) + ")]")
-
- code.append('\nif __name__ == "__main__":')
- code.append(" " + func_name + "(bpy.context.active_object)")
-
- return "\n".join(code)
-
-
-def write_widget(obj):
- """ Write a mesh object as a python script for widget use.
- """
- script = ""
- script += "def create_thing_widget(rig, bone_name, size=1.0, bone_transform_name=None):\n"
- script += " obj = create_widget(rig, bone_name, bone_transform_name)\n"
- script += " if obj != None:\n"
-
- # Vertices
- if len(obj.data.vertices) > 0:
- script += " verts = ["
- for v in obj.data.vertices:
- script += "(" + str(v.co[0]) + "*size, " + str(v.co[1]) + "*size, " + str(v.co[2]) + "*size), "
- script += "]\n"
-
- # Edges
- if len(obj.data.edges) > 0:
- script += " edges = ["
- for e in obj.data.edges:
- script += "(" + str(e.vertices[0]) + ", " + str(e.vertices[1]) + "), "
- script += "]\n"
-
- # Faces
- if len(obj.data.polygons) > 0:
- script += " faces = ["
- for f in obj.data.polygons:
- script += "("
- for v in f.vertices:
- script += str(v) + ", "
- script += "), "
- script += "]\n"
-
- # Build mesh
- script += "\n mesh = obj.data\n"
- script += " mesh.from_pydata(verts, edges, faces)\n"
- script += " mesh.update()\n"
- script += " mesh.update()\n"
- script += " return obj\n"
- script += " else:\n"
- script += " return None\n"
-
- return script
-
-
-def random_id(length=8):
- """ Generates a random alphanumeric id string.
- """
- tlength = int(length / 2)
- rlength = int(length / 2) + int(length % 2)
-
- chars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']
- text = ""
- for i in range(0, rlength):
- text += random.choice(chars)
- text += str(hex(int(time.time())))[2:][-tlength:].rjust(tlength, '0')[::-1]
- return text
-
-
-#=============================================
-# Color correction functions
-#=============================================
-
-def linsrgb_to_srgb (linsrgb):
- """Convert physically linear RGB values into sRGB ones. The transform is
- uniform in the components, so *linsrgb* can be of any shape.
-
- *linsrgb* values should range between 0 and 1, inclusively.
-
- """
- # From Wikipedia, but easy analogue to the above.
- gamma = 1.055 * linsrgb**(1./2.4) - 0.055
- scale = linsrgb * 12.92
- # return np.where (linsrgb > 0.0031308, gamma, scale)
- if linsrgb > 0.0031308:
- return gamma
- return scale
-
-
-def gamma_correct(color):
-
- corrected_color = Color()
- for i, component in enumerate(color):
- corrected_color[i] = linsrgb_to_srgb(color[i])
- return corrected_color
-
-
-#=============================================
-# Keyframing functions
-#=============================================
-
-
-def get_keyed_frames(rig):
- frames = []
- if rig.animation_data:
- if rig.animation_data.action:
- fcus = rig.animation_data.action.fcurves
- for fc in fcus:
- for kp in fc.keyframe_points:
- if kp.co[0] not in frames:
- frames.append(kp.co[0])
-
- frames.sort()
-
- return frames
-
-
-def bones_in_frame(f, rig, *args):
- """
- True if one of the bones listed in args is animated at frame f
- :param f: the frame
- :param rig: the rig
- :param args: bone names
- :return:
- """
-
- if rig.animation_data and rig.animation_data.action:
- fcus = rig.animation_data.action.fcurves
- else:
- return False
-
- for fc in fcus:
- animated_frames = [kp.co[0] for kp in fc.keyframe_points]
- for bone in args:
- if bone in fc.data_path.split('"') and f in animated_frames:
- return True
-
- return False
-
-
-def overwrite_prop_animation(rig, bone, prop_name, value, frames):
- act = rig.animation_data.action
- if not act:
- return
-
- bone_name = bone.name
- curve = None
-
- for fcu in act.fcurves:
- words = fcu.data_path.split('"')
- if words[0] == "pose.bones[" and words[1] == bone_name and words[-2] == prop_name:
- curve = fcu
- break
-
- if not curve:
- return
-
- for kp in curve.keyframe_points:
- if kp.co[0] in frames:
- kp.co[1] = value
-
-
-def find_layer_collection_by_collection(layer_collection, collection):
- if collection == layer_collection.collection:
- return layer_collection
-
- # go recursive
- for child in layer_collection.children:
- layer_collection = find_layer_collection_by_collection(child, collection)
- if layer_collection:
- return layer_collection
-
-
-def ensure_widget_collection(context):
- wgts_collection_name = "Widgets"
-
- view_layer = context.view_layer
- layer_collection = bpy.context.layer_collection
- collection = layer_collection.collection
-
- widget_collection = bpy.data.collections.get(wgts_collection_name)
- if not widget_collection:
- # ------------------------------------------
- # Create the widget collection
- widget_collection = bpy.data.collections.new(wgts_collection_name)
- widget_collection.hide_viewport = True
- widget_collection.hide_render = True
-
- widget_layer_collection = None
- else:
- widget_layer_collection = find_layer_collection_by_collection(view_layer.layer_collection, widget_collection)
-
- if not widget_layer_collection:
- # Add the widget collection to the tree
- collection.children.link(widget_collection)
- widget_layer_collection = [c for c in layer_collection.children if c.collection == widget_collection][0]
-
- # Make the widget the active collection for the upcoming added (widget) objects
- view_layer.active_layer_collection = widget_layer_collection
- return widget_collection
diff --git a/rigify/utils/__init__.py b/rigify/utils/__init__.py
new file mode 100644
index 00000000..da4689a9
--- /dev/null
+++ b/rigify/utils/__init__.py
@@ -0,0 +1,31 @@
+# These forwarding imports are for backwards compatibility with legacy code
+# that expects a single utils.py file. New code should import directly from
+# the modules that contain the utilities. Also, don't add more imports here.
+
+from .errors import MetarigError
+
+from .misc import angle_on_plane, linsrgb_to_srgb, gamma_correct, copy_attributes
+
+from .naming import ORG_PREFIX, MCH_PREFIX, DEF_PREFIX, ROOT_NAME
+from .naming import strip_trailing_number, unique_name, org_name, strip_org, strip_mch, strip_def
+from .naming import org, make_original_name, mch, make_mechanism_name, deformer, make_deformer_name
+from .naming import insert_before_lr, random_id
+
+from .bones import new_bone, copy_bone_simple, copy_bone, flip_bone, put_bone, make_nonscaling_child
+from .bones import align_bone_roll, align_bone_x_axis, align_bone_z_axis, align_bone_y_axis
+
+from .widgets import WGT_PREFIX, obj_to_bone, create_widget, write_widget, create_circle_polygon
+
+from .widgets_basic import create_line_widget, create_circle_widget, create_cube_widget, create_chain_widget
+from .widgets_basic import create_sphere_widget, create_limb_widget, create_bone_widget
+
+from .widgets_special import create_compass_widget, create_root_widget
+from .widgets_special import create_neck_bend_widget, create_neck_tweak_widget
+
+from .animation import get_keyed_frames, bones_in_frame, overwrite_prop_animation
+
+from .rig import RIG_DIR, METARIG_DIR, MODULE_NAME, TEMPLATE_DIR, outdated_types, upgradeMetarigTypes
+from .rig import get_rig_type, get_metarig_module, write_metarig, get_resource
+from .rig import connected_children_names, has_connected_children
+
+from .layers import get_layers, ControlLayersOption
diff --git a/rigify/utils/animation.py b/rigify/utils/animation.py
new file mode 100644
index 00000000..ab99282f
--- /dev/null
+++ b/rigify/utils/animation.py
@@ -0,0 +1,84 @@
+#====================== 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.
+#
+#======================= END GPL LICENSE BLOCK ========================
+
+# <pep8 compliant>
+
+
+#=============================================
+# Keyframing functions
+#=============================================
+
+
+def get_keyed_frames(rig):
+ frames = []
+ if rig.animation_data:
+ if rig.animation_data.action:
+ fcus = rig.animation_data.action.fcurves
+ for fc in fcus:
+ for kp in fc.keyframe_points:
+ if kp.co[0] not in frames:
+ frames.append(kp.co[0])
+
+ frames.sort()
+
+ return frames
+
+
+def bones_in_frame(f, rig, *args):
+ """
+ True if one of the bones listed in args is animated at frame f
+ :param f: the frame
+ :param rig: the rig
+ :param args: bone names
+ :return:
+ """
+
+ if rig.animation_data and rig.animation_data.action:
+ fcus = rig.animation_data.action.fcurves
+ else:
+ return False
+
+ for fc in fcus:
+ animated_frames = [kp.co[0] for kp in fc.keyframe_points]
+ for bone in args:
+ if bone in fc.data_path.split('"') and f in animated_frames:
+ return True
+
+ return False
+
+
+def overwrite_prop_animation(rig, bone, prop_name, value, frames):
+ act = rig.animation_data.action
+ if not act:
+ return
+
+ bone_name = bone.name
+ curve = None
+
+ for fcu in act.fcurves:
+ words = fcu.data_path.split('"')
+ if words[0] == "pose.bones[" and words[1] == bone_name and words[-2] == prop_name:
+ curve = fcu
+ break
+
+ if not curve:
+ return
+
+ for kp in curve.keyframe_points:
+ if kp.co[0] in frames:
+ kp.co[1] = value
diff --git a/rigify/utils/bones.py b/rigify/utils/bones.py
new file mode 100644
index 00000000..9002d083
--- /dev/null
+++ b/rigify/utils/bones.py
@@ -0,0 +1,417 @@
+#====================== 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.
+#
+#======================= END GPL LICENSE BLOCK ========================
+
+# <pep8 compliant>
+
+import bpy
+import math
+from mathutils import Vector, Matrix, Color
+from rna_prop_ui import rna_idprop_ui_prop_get
+
+from .errors import MetarigError
+from .naming import strip_org, make_mechanism_name, insert_before_lr
+
+#=======================
+# Bone collection
+#=======================
+
+class BoneDict(dict):
+ """
+ Special dictionary for holding bone names in a structured way.
+
+ Allows access to contained items as attributes, and only
+ accepts certain types of values.
+ """
+
+ @staticmethod
+ def __sanitize_attr(key, value):
+ if hasattr(BoneDict, key):
+ raise KeyError("Invalid BoneDict key: %s" % (key))
+
+ if (value is None or
+ isinstance(value, str) or
+ isinstance(value, list) or
+ isinstance(value, BoneDict)):
+ return value
+
+ if isinstance(value, dict):
+ return BoneDict(value)
+
+ raise ValueError("Invalid BoneDict value: %r" % (value))
+
+ def __init__(self, *args, **kwargs):
+ super(BoneDict, self).__init__()
+
+ for key, value in dict(*args, **kwargs).items():
+ dict.__setitem__(self, key, BoneDict.__sanitize_attr(key, value))
+
+ self.__dict__ = self
+
+ def __repr__(self):
+ return "BoneDict(%s)" % (dict.__repr__(self))
+
+ def __setitem__(self, key, value):
+ dict.__setitem__(self, key, BoneDict.__sanitize_attr(key, value))
+
+ def update(self, *args, **kwargs):
+ for key, value in dict(*args, **kwargs).items():
+ dict.__setitem__(self, key, BoneDict.__sanitize_attr(key, value))
+
+ def flatten(self):
+ """Return all contained bones as a list."""
+
+ all_bones = []
+
+ for item in self.values():
+ if isinstance(item, BoneDict):
+ all_bones.extend(item.flatten())
+ elif isinstance(item, list):
+ all_bones.extend(item)
+ elif item is not None:
+ all_bones.append(item)
+
+ return all_bones
+
+#=======================
+# Bone manipulation
+#=======================
+
+def new_bone(obj, bone_name):
+ """ Adds a new bone to the given armature object.
+ Returns the resulting bone's name.
+ """
+ if obj == bpy.context.active_object and bpy.context.mode == 'EDIT_ARMATURE':
+ edit_bone = obj.data.edit_bones.new(bone_name)
+ name = edit_bone.name
+ edit_bone.head = (0, 0, 0)
+ edit_bone.tail = (0, 1, 0)
+ edit_bone.roll = 0
+ bpy.ops.object.mode_set(mode='OBJECT')
+ bpy.ops.object.mode_set(mode='EDIT')
+ return name
+ else:
+ raise MetarigError("Can't add new bone '%s' outside of edit mode" % bone_name)
+
+
+def copy_bone_simple(obj, bone_name, assign_name=''):
+ """ Makes a copy of the given bone in the given armature object.
+ but only copies head, tail positions and roll. Does not
+ address parenting either.
+ """
+ #if bone_name not in obj.data.bones:
+ if bone_name not in obj.data.edit_bones:
+ raise MetarigError("copy_bone(): bone '%s' not found, cannot copy it" % bone_name)
+
+ if obj == bpy.context.active_object and bpy.context.mode == 'EDIT_ARMATURE':
+ if assign_name == '':
+ assign_name = bone_name
+ # Copy the edit bone
+ edit_bone_1 = obj.data.edit_bones[bone_name]
+ edit_bone_2 = obj.data.edit_bones.new(assign_name)
+ bone_name_1 = bone_name
+ bone_name_2 = edit_bone_2.name
+
+ # Copy edit bone attributes
+ edit_bone_2.layers = list(edit_bone_1.layers)
+
+ edit_bone_2.head = Vector(edit_bone_1.head)
+ edit_bone_2.tail = Vector(edit_bone_1.tail)
+ edit_bone_2.roll = edit_bone_1.roll
+
+ return bone_name_2
+ else:
+ raise MetarigError("Cannot copy bones outside of edit mode")
+
+
+def copy_bone(obj, bone_name, assign_name=''):
+ """ Makes a copy of the given bone in the given armature object.
+ Returns the resulting bone's name.
+ """
+ #if bone_name not in obj.data.bones:
+ if bone_name not in obj.data.edit_bones:
+ raise MetarigError("copy_bone(): bone '%s' not found, cannot copy it" % bone_name)
+
+ if obj == bpy.context.active_object and bpy.context.mode == 'EDIT_ARMATURE':
+ if assign_name == '':
+ assign_name = bone_name
+ # Copy the edit bone
+ edit_bone_1 = obj.data.edit_bones[bone_name]
+ edit_bone_2 = obj.data.edit_bones.new(assign_name)
+ bone_name_1 = bone_name
+ bone_name_2 = edit_bone_2.name
+
+ edit_bone_2.parent = edit_bone_1.parent
+ edit_bone_2.use_connect = edit_bone_1.use_connect
+
+ # Copy edit bone attributes
+ edit_bone_2.layers = list(edit_bone_1.layers)
+
+ edit_bone_2.head = Vector(edit_bone_1.head)
+ edit_bone_2.tail = Vector(edit_bone_1.tail)
+ edit_bone_2.roll = edit_bone_1.roll
+
+ edit_bone_2.use_inherit_rotation = edit_bone_1.use_inherit_rotation
+ edit_bone_2.use_inherit_scale = edit_bone_1.use_inherit_scale
+ edit_bone_2.use_local_location = edit_bone_1.use_local_location
+
+ edit_bone_2.use_deform = edit_bone_1.use_deform
+ edit_bone_2.bbone_segments = edit_bone_1.bbone_segments
+ edit_bone_2.bbone_easein = edit_bone_1.bbone_easein
+ edit_bone_2.bbone_easeout = edit_bone_1.bbone_easeout
+
+ bpy.ops.object.mode_set(mode='OBJECT')
+
+ # Get the pose bones
+ pose_bone_1 = obj.pose.bones[bone_name_1]
+ pose_bone_2 = obj.pose.bones[bone_name_2]
+
+ # Copy pose bone attributes
+ pose_bone_2.rotation_mode = pose_bone_1.rotation_mode
+ pose_bone_2.rotation_axis_angle = tuple(pose_bone_1.rotation_axis_angle)
+ pose_bone_2.rotation_euler = tuple(pose_bone_1.rotation_euler)
+ pose_bone_2.rotation_quaternion = tuple(pose_bone_1.rotation_quaternion)
+
+ pose_bone_2.lock_location = tuple(pose_bone_1.lock_location)
+ pose_bone_2.lock_scale = tuple(pose_bone_1.lock_scale)
+ pose_bone_2.lock_rotation = tuple(pose_bone_1.lock_rotation)
+ pose_bone_2.lock_rotation_w = pose_bone_1.lock_rotation_w
+ pose_bone_2.lock_rotations_4d = pose_bone_1.lock_rotations_4d
+
+ # Copy custom properties
+ for key in pose_bone_1.keys():
+ if key != "_RNA_UI" \
+ and key != "rigify_parameters" \
+ and key != "rigify_type":
+ prop1 = rna_idprop_ui_prop_get(pose_bone_1, key, create=False)
+ prop2 = rna_idprop_ui_prop_get(pose_bone_2, key, create=True)
+ pose_bone_2[key] = pose_bone_1[key]
+ for key in prop1.keys():
+ prop2[key] = prop1[key]
+
+ bpy.ops.object.mode_set(mode='EDIT')
+
+ return bone_name_2
+ else:
+ raise MetarigError("Cannot copy bones outside of edit mode")
+
+
+def flip_bone(obj, bone_name):
+ """ Flips an edit bone.
+ """
+ if bone_name not in obj.data.bones:
+ raise MetarigError("flip_bone(): bone '%s' not found, cannot copy it" % bone_name)
+
+ if obj == bpy.context.active_object and bpy.context.mode == 'EDIT_ARMATURE':
+ bone = obj.data.edit_bones[bone_name]
+ head = Vector(bone.head)
+ tail = Vector(bone.tail)
+ bone.tail = head + tail
+ bone.head = tail
+ bone.tail = head
+ else:
+ raise MetarigError("Cannot flip bones outside of edit mode")
+
+
+def put_bone(obj, bone_name, pos):
+ """ Places a bone at the given position.
+ """
+ if bone_name not in obj.data.bones:
+ raise MetarigError("put_bone(): bone '%s' not found, cannot move it" % bone_name)
+
+ if obj == bpy.context.active_object and bpy.context.mode == 'EDIT_ARMATURE':
+ bone = obj.data.edit_bones[bone_name]
+
+ delta = pos - bone.head
+ bone.translate(delta)
+ else:
+ raise MetarigError("Cannot 'put' bones outside of edit mode")
+
+
+def make_nonscaling_child(obj, bone_name, location, child_name_postfix=""):
+ """ Takes the named bone and creates a non-scaling child of it at
+ the given location. The returned bone (returned by name) is not
+ a true child, but behaves like one sans inheriting scaling.
+
+ It is intended as an intermediate construction to prevent rig types
+ from scaling with their parents. The named bone is assumed to be
+ an ORG bone.
+ """
+ if bone_name not in obj.data.bones:
+ raise MetarigError("make_nonscaling_child(): bone '%s' not found, cannot copy it" % bone_name)
+
+ if obj == bpy.context.active_object and bpy.context.mode == 'EDIT_ARMATURE':
+ # Create desired names for bones
+ name1 = make_mechanism_name(strip_org(insert_before_lr(bone_name, child_name_postfix + "_ns_ch")))
+ name2 = make_mechanism_name(strip_org(insert_before_lr(bone_name, child_name_postfix + "_ns_intr")))
+
+ # Create bones
+ child = copy_bone(obj, bone_name, name1)
+ intermediate_parent = copy_bone(obj, bone_name, name2)
+
+ # Get edit bones
+ eb = obj.data.edit_bones
+ child_e = eb[child]
+ intrpar_e = eb[intermediate_parent]
+
+ # Parenting
+ child_e.use_connect = False
+ child_e.parent = None
+
+ intrpar_e.use_connect = False
+ intrpar_e.parent = eb[bone_name]
+
+ # Positioning
+ child_e.length *= 0.5
+ intrpar_e.length *= 0.25
+
+ put_bone(obj, child, location)
+ put_bone(obj, intermediate_parent, location)
+
+ # Object mode
+ bpy.ops.object.mode_set(mode='OBJECT')
+ pb = obj.pose.bones
+
+ # Add constraints
+ con = pb[child].constraints.new('COPY_LOCATION')
+ con.name = "parent_loc"
+ con.target = obj
+ con.subtarget = intermediate_parent
+
+ con = pb[child].constraints.new('COPY_ROTATION')
+ con.name = "parent_loc"
+ con.target = obj
+ con.subtarget = intermediate_parent
+
+ bpy.ops.object.mode_set(mode='EDIT')
+
+ return child
+ else:
+ raise MetarigError("Cannot make nonscaling child outside of edit mode")
+
+
+#=============================================
+# Math
+#=============================================
+
+
+def align_bone_roll(obj, bone1, bone2):
+ """ Aligns the roll of two bones.
+ """
+ bone1_e = obj.data.edit_bones[bone1]
+ bone2_e = obj.data.edit_bones[bone2]
+
+ bone1_e.roll = 0.0
+
+ # Get the directions the bones are pointing in, as vectors
+ y1 = bone1_e.y_axis
+ x1 = bone1_e.x_axis
+ y2 = bone2_e.y_axis
+ x2 = bone2_e.x_axis
+
+ # Get the shortest axis to rotate bone1 on to point in the same direction as bone2
+ axis = y1.cross(y2)
+ axis.normalize()
+
+ # Angle to rotate on that shortest axis
+ angle = y1.angle(y2)
+
+ # Create rotation matrix to make bone1 point in the same direction as bone2
+ rot_mat = Matrix.Rotation(angle, 3, axis)
+
+ # Roll factor
+ x3 = rot_mat @ x1
+ dot = x2 @ x3
+ if dot > 1.0:
+ dot = 1.0
+ elif dot < -1.0:
+ dot = -1.0
+ roll = math.acos(dot)
+
+ # Set the roll
+ bone1_e.roll = roll
+
+ # Check if we rolled in the right direction
+ x3 = rot_mat @ bone1_e.x_axis
+ check = x2 @ x3
+
+ # If not, reverse
+ if check < 0.9999:
+ bone1_e.roll = -roll
+
+
+def align_bone_x_axis(obj, bone, vec):
+ """ Rolls the bone to align its x-axis as closely as possible to
+ the given vector.
+ Must be in edit mode.
+ """
+ bone_e = obj.data.edit_bones[bone]
+
+ vec = vec.cross(bone_e.y_axis)
+ vec.normalize()
+
+ dot = max(-1.0, min(1.0, bone_e.z_axis.dot(vec)))
+ angle = math.acos(dot)
+
+ bone_e.roll += angle
+
+ dot1 = bone_e.z_axis.dot(vec)
+
+ bone_e.roll -= angle * 2
+
+ dot2 = bone_e.z_axis.dot(vec)
+
+ if dot1 > dot2:
+ bone_e.roll += angle * 2
+
+
+def align_bone_z_axis(obj, bone, vec):
+ """ Rolls the bone to align its z-axis as closely as possible to
+ the given vector.
+ Must be in edit mode.
+ """
+ bone_e = obj.data.edit_bones[bone]
+
+ vec = bone_e.y_axis.cross(vec)
+ vec.normalize()
+
+ dot = max(-1.0, min(1.0, bone_e.x_axis.dot(vec)))
+ angle = math.acos(dot)
+
+ bone_e.roll += angle
+
+ dot1 = bone_e.x_axis.dot(vec)
+
+ bone_e.roll -= angle * 2
+
+ dot2 = bone_e.x_axis.dot(vec)
+
+ if dot1 > dot2:
+ bone_e.roll += angle * 2
+
+
+def align_bone_y_axis(obj, bone, vec):
+ """ Matches the bone y-axis to
+ the given vector.
+ Must be in edit mode.
+ """
+
+ bone_e = obj.data.edit_bones[bone]
+ vec.normalize()
+ vec = vec * bone_e.length
+
+ bone_e.tail = bone_e.head + vec
diff --git a/rigify/utils/collections.py b/rigify/utils/collections.py
new file mode 100644
index 00000000..25596905
--- /dev/null
+++ b/rigify/utils/collections.py
@@ -0,0 +1,68 @@
+#====================== 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.
+#
+#======================= END GPL LICENSE BLOCK ========================
+
+# <pep8 compliant>
+
+import bpy
+import math
+
+from .errors import MetarigError
+
+
+#=============================================
+# Collection management
+#=============================================
+
+def find_layer_collection_by_collection(layer_collection, collection):
+ if collection == layer_collection.collection:
+ return layer_collection
+
+ # go recursive
+ for child in layer_collection.children:
+ layer_collection = find_layer_collection_by_collection(child, collection)
+ if layer_collection:
+ return layer_collection
+
+
+def ensure_widget_collection(context):
+ wgts_collection_name = "Widgets"
+
+ view_layer = context.view_layer
+ layer_collection = bpy.context.layer_collection
+ collection = layer_collection.collection
+
+ widget_collection = bpy.data.collections.get(wgts_collection_name)
+ if not widget_collection:
+ # ------------------------------------------
+ # Create the widget collection
+ widget_collection = bpy.data.collections.new(wgts_collection_name)
+ widget_collection.hide_viewport = True
+ widget_collection.hide_render = True
+
+ widget_layer_collection = None
+ else:
+ widget_layer_collection = find_layer_collection_by_collection(view_layer.layer_collection, widget_collection)
+
+ if not widget_layer_collection:
+ # Add the widget collection to the tree
+ collection.children.link(widget_collection)
+ widget_layer_collection = [c for c in layer_collection.children if c.collection == widget_collection][0]
+
+ # Make the widget the active collection for the upcoming added (widget) objects
+ view_layer.active_layer_collection = widget_layer_collection
+ return widget_collection
diff --git a/rigify/utils/errors.py b/rigify/utils/errors.py
new file mode 100644
index 00000000..71295057
--- /dev/null
+++ b/rigify/utils/errors.py
@@ -0,0 +1,34 @@
+#====================== 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.
+#
+#======================= END GPL LICENSE BLOCK ========================
+
+# <pep8 compliant>
+
+#=======================================================================
+# Error handling
+#=======================================================================
+
+
+class MetarigError(Exception):
+ """ Exception raised for errors.
+ """
+ def __init__(self, message):
+ self.message = message
+
+ def __str__(self):
+ return repr(self.message)
+
diff --git a/rigify/utils/layers.py b/rigify/utils/layers.py
new file mode 100644
index 00000000..1045e493
--- /dev/null
+++ b/rigify/utils/layers.py
@@ -0,0 +1,140 @@
+#====================== 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.
+#
+#======================= END GPL LICENSE BLOCK ========================
+
+# <pep8 compliant>
+
+import bpy
+
+
+def get_layers(layers):
+ """ Does its best to extract a set of layers from any data thrown at it.
+ """
+ if type(layers) == int:
+ return [x == layers for x in range(0, 32)]
+ elif type(layers) == str:
+ s = layers.split(",")
+ l = []
+ for i in s:
+ try:
+ l += [int(float(i))]
+ except ValueError:
+ pass
+ return [x in l for x in range(0, 32)]
+ elif type(layers) == tuple or type(layers) == list:
+ return [x in layers for x in range(0, 32)]
+ else:
+ try:
+ list(layers)
+ except TypeError:
+ pass
+ else:
+ return [x in layers for x in range(0, 32)]
+
+
+#=============================================
+# UI utilities
+#=============================================
+
+class ControlLayersOption:
+ def __init__(self, name, toggle_name=None, toggle_default=True, description="Set of control layers"):
+ self.name = name
+ self.toggle_default = toggle_default
+ self.description = description
+
+ self.toggle_option = self.name+'_layers_extra'
+ self.layers_option = self.name+'_layers'
+ self.toggle_name = toggle_name if toggle_name else self.toggle_option
+
+ def get(self, params):
+ if getattr(params, self.toggle_option):
+ return list(getattr(params, self.layers_option))
+ else:
+ return None
+
+ def assign(self, params, bone_set, bone_list):
+ layers = self.get(params)
+
+ if layers:
+ for name in bone_list:
+ bone = bone_set[name]
+ if isinstance(bone, bpy.types.PoseBone):
+ bone = bone.bone
+
+ bone.layers = layers
+
+ def add_parameters(self, params):
+ prop_toggle = bpy.props.BoolProperty(
+ name=self.toggle_name,
+ default=self.toggle_default,
+ description=""
+ )
+
+ setattr(params, self.toggle_option, prop_toggle)
+
+ prop_layers = bpy.props.BoolVectorProperty(
+ size=32,
+ description=self.description,
+ default=tuple([i == 1 for i in range(0, 32)])
+ )
+
+ setattr(params, self.layers_option, prop_layers)
+
+ def parameters_ui(self, layout, params):
+ r = layout.row()
+ r.prop(params, self.toggle_option)
+ r.active = getattr(params, self.toggle_option)
+
+ col = r.column(align=True)
+ row = col.row(align=True)
+
+ bone_layers = bpy.context.active_pose_bone.bone.layers[:]
+
+ for i in range(8): # Layers 0-7
+ icon = "NONE"
+ if bone_layers[i]:
+ icon = "LAYER_ACTIVE"
+ row.prop(params, self.layers_option, index=i, toggle=True, text="", icon=icon)
+
+ row = col.row(align=True)
+
+ for i in range(16, 24): # Layers 16-23
+ icon = "NONE"
+ if bone_layers[i]:
+ icon = "LAYER_ACTIVE"
+ row.prop(params, self.layers_option, index=i, toggle=True, text="", icon=icon)
+
+ col = r.column(align=True)
+ row = col.row(align=True)
+
+ for i in range(8, 16): # Layers 8-15
+ icon = "NONE"
+ if bone_layers[i]:
+ icon = "LAYER_ACTIVE"
+ row.prop(params, self.layers_option, index=i, toggle=True, text="", icon=icon)
+
+ row = col.row(align=True)
+
+ for i in range(24, 32): # Layers 24-31
+ icon = "NONE"
+ if bone_layers[i]:
+ icon = "LAYER_ACTIVE"
+ row.prop(params, self.layers_option, index=i, toggle=True, text="", icon=icon)
+
+
+ControlLayersOption.FK = ControlLayersOption('fk', description="Layers for the FK controls to be on")
+ControlLayersOption.TWEAK = ControlLayersOption('tweak', description="Layers for the tweak controls to be on")
diff --git a/rigify/utils/mechanism.py b/rigify/utils/mechanism.py
new file mode 100644
index 00000000..90fedd75
--- /dev/null
+++ b/rigify/utils/mechanism.py
@@ -0,0 +1,264 @@
+#====================== 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.
+#
+#======================= END GPL LICENSE BLOCK ========================
+
+# <pep8 compliant>
+
+import bpy
+
+from rna_prop_ui import rna_idprop_ui_prop_get
+
+#=============================================
+# Constraint creation utilities
+#=============================================
+
+_TRACK_AXIS_MAP = {
+ 'X': 'TRACK_X', '-X': 'TRACK_NEGATIVE_X',
+ 'Y': 'TRACK_Y', '-Y': 'TRACK_NEGATIVE_Y',
+ 'Z': 'TRACK_Z', '-Z': 'TRACK_NEGATIVE_Z',
+}
+
+def make_constraint(
+ owner, type, target=None, subtarget=None,
+ space=None, track_axis=None, use_xyz=None,
+ **options):
+ """
+ Creates and initializes constraint of the specified type for the owner bone.
+
+ Specially handled keyword arguments:
+
+ target, subtarget: if both not None, passed through to the constraint
+ space : assigned to both owner_space and target_space
+ track_axis : allows shorter X, Y, Z, -X, -Y, -Z notation
+ use_xyz : list of 3 items is assigned to use_x, use_y and use_z options
+ min/max_x/y/z : a corresponding use_min/max_x/y/z option is set to True
+
+ Other keyword arguments are directly assigned to the constraint options.
+ Returns the newly created constraint.
+ """
+ con = owner.constraints.new(type)
+
+ if target is not None and subtarget is not None:
+ con.target = target
+ con.subtarget = subtarget
+
+ if space is not None:
+ con.owner_space = con.target_space = space
+
+ if track_axis is not None:
+ con.track_axis = _TRACK_AXIS_MAP.get(track_axis, track_axis)
+
+ if use_xyz is not None:
+ con.use_x, con.use_y, con.use_z = use_xyz[0:3]
+
+ for key in ['min_x', 'max_x', 'min_y', 'max_y', 'min_z', 'max_z']:
+ if key in options and 'use_'+key not in options:
+ options['use_'+key] = True
+
+ for p, v in options.items():
+ setattr(con, p, v)
+
+ return con
+
+#=============================================
+# Custom property creation utilities
+#=============================================
+
+def make_property(owner, name, default=0.0, min=0.0, max=1.0, soft_min=None, soft_max=None, description=None):
+ """
+ Creates and initializes a custom property of owner.
+
+ The soft_min and soft_max parameters default to min and max.
+ """
+ owner[name] = default
+
+ prop = rna_idprop_ui_prop_get(owner, name, create=True)
+ prop["min"] = min
+ prop["soft_min"] = soft_min if soft_min is not None else min
+ prop["max"] = max
+ prop["soft_max"] = soft_max if soft_max is not None else max
+ if description:
+ prop["description"] = description
+
+ return prop
+
+#=============================================
+# Driver creation utilities
+#=============================================
+
+def _init_driver_target(drv_target, var_info, target_id):
+ """Initialize a driver variable target from a specification."""
+
+ # Parse the simple list format for the common case.
+ if isinstance(var_info, tuple):
+ # [ (target_id,) subtarget, ...path ]
+
+ # If target_id is supplied as parameter, allow omitting it
+ if target_id is None or isinstance(var_info[0], bpy.types.ID):
+ target_id,subtarget,*refs = var_info
+ else:
+ subtarget,*refs = var_info
+
+ # Simple path string case.
+ if len(refs) == 0:
+ # [ (target_id,) path_str ]
+ path = subtarget
+ else:
+ # If subtarget is a string, look up a bone in the target
+ if isinstance(subtarget, str):
+ subtarget = target_id.pose.bones[subtarget]
+
+ # Use ".foo" type path items verbatim, otherwise quote
+ path = subtarget.path_from_id()
+ for item in refs:
+ path += item if item[0] == '.' else '["'+item+'"]'
+
+ drv_target.id = target_id
+ drv_target.data_path = path
+
+ else:
+ # { 'id': ..., ... }
+ if target_id is not None:
+ drv_target.id = target_id
+
+ for tp, tv in tdata.items():
+ setattr(drv_target, tp, tv)
+
+
+def _add_driver_variable(drv, var_name, var_info, target_id):
+ """Add and initialize a driver variable."""
+
+ var = drv.variables.new()
+ var.name = var_name
+
+ # Parse the simple list format for the common case.
+ if isinstance(var_info, tuple):
+ # [ (target_id,) subtarget, ...path ]
+ var.type = "SINGLE_PROP"
+
+ _init_driver_target(var.targets[0], var_info, target_id)
+
+ else:
+ # Variable info as generic dictionary - assign properties.
+ # { 'type': 'SINGLE_PROP', 'targets':[...] }
+ var.type = var_info['type']
+
+ for p, v in var_info.items():
+ if p == 'targets':
+ for i, tdata in enumerate(v):
+ _init_driver_target(var.targets[i], tdata, target_id)
+ elif p != 'type':
+ setattr(var, p, v)
+
+def make_driver(owner, prop, index=-1, type='SUM', expression=None, variables={}, polynomial=None, target_id=None):
+ """
+ Creates and initializes a driver for the 'prop' property of owner.
+
+ Arguments:
+ index : item index for vector properties
+ type, expression: mutually exclusive options to set core driver mode.
+ variables : either a list or dictionary of variable specifications.
+ polynomial : coefficients of the POLYNOMIAL driver modifier
+ target_id : specifies the target ID of variables implicitly
+
+ Specification format:
+ If the variables argument is a dictionary, keys specify variable names.
+ Otherwise names are set to var0, var1... etc:
+
+ variables = [ ..., ..., ... ]
+ variables = { 'var0': ..., 'var1': ..., 'var2': ... }
+
+ Variable specifications are constructed as nested dictionaries and lists that
+ follow the property structure of the original Blender objects, but the most
+ common case can be abbreviated as a simple tuple.
+
+ The following specifications are equivalent:
+
+ ( target, subtarget, '.foo', 'bar' )
+
+ { 'type': 'SINGLE_PROP', 'targets':[( target, subtarget, '.foo', 'bar' )] }
+
+ { 'type': 'SINGLE_PROP',
+ 'targets':[{ 'id': target, 'data_path': subtarget.path_from_id() + '.foo["bar"]' }] }
+
+ If subtarget is as string, it is automatically looked up within target as a bone.
+
+ It is possible to specify path directly as a simple string without following items:
+
+ ( target, 'path' )
+
+ { 'type': 'SINGLE_PROP', 'targets':[{ 'id': target, 'data_path': 'path' }] }
+
+ If the target_id parameter is not None, it is possible to omit target:
+
+ ( subtarget, '.foo', 'bar' )
+
+ { 'type': 'SINGLE_PROP',
+ 'targets':[{ 'id': target_id, 'data_path': subtarget.path_from_id() + '.foo["bar"]' }] }
+
+ Returns the newly created driver FCurve.
+ """
+ fcu = owner.driver_add(prop, index)
+ drv = fcu.driver
+
+ if expression is not None:
+ drv.type = 'SCRIPTED'
+ drv.expression = expression
+ else:
+ drv.type = type
+
+ if isinstance(variables, list):
+ # variables = [ info, ... ]
+ for i, var_info in enumerate(variables):
+ _add_driver_variable(drv, 'var'+str(i), var_info, target_id)
+ else:
+ # variables = { 'varname': info, ... }
+ for var_name, var_info in variables.items():
+ _add_driver_variable(drv, var_name, var_info, target_id)
+
+ if polynomial is not None:
+ drv_modifier = fcu.modifiers[0]
+ drv_modifier.mode = 'POLYNOMIAL'
+ drv_modifier.poly_order = len(polynomial)-1
+ for i,v in enumerate(polynomial):
+ drv_modifier.coefficients[i] = v
+
+ return fcu
+
+#=============================================
+# Utility mixin
+#=============================================
+
+class MechanismUtilityMixin(object):
+ """
+ Provides methods for more convenient creation of constraints, properties
+ and drivers within an armature (by implicitly providing context).
+
+ Requires self.obj to be the armature object being worked on.
+ """
+
+ def make_constraint(self, bone, type, subtarget=None, **args):
+ assert(self.obj.mode == 'OBJECT')
+ return make_constraint(self.obj.pose.bones[bone], type, self.obj, subtarget, **args)
+
+ def make_property(self, bone, name, **args):
+ assert(self.obj.mode == 'OBJECT')
+ return make_property(self.obj.pose.bones[bone], name, **args)
+
+ def make_driver(self, owner, prop, **args):
+ assert(self.obj.mode == 'OBJECT')
+ return make_driver(owner, prop, target_id=self.obj, **args)
diff --git a/rigify/utils/misc.py b/rigify/utils/misc.py
new file mode 100644
index 00000000..2ca7b016
--- /dev/null
+++ b/rigify/utils/misc.py
@@ -0,0 +1,100 @@
+#====================== 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.
+#
+#======================= END GPL LICENSE BLOCK ========================
+
+# <pep8 compliant>
+
+import math
+from mathutils import Vector, Matrix, Color
+
+#=============================================
+# Math
+#=============================================
+
+
+def angle_on_plane(plane, vec1, vec2):
+ """ Return the angle between two vectors projected onto a plane.
+ """
+ plane.normalize()
+ vec1 = vec1 - (plane * (vec1.dot(plane)))
+ vec2 = vec2 - (plane * (vec2.dot(plane)))
+ vec1.normalize()
+ vec2.normalize()
+
+ # Determine the angle
+ angle = math.acos(max(-1.0, min(1.0, vec1.dot(vec2))))
+
+ if angle < 0.00001: # close enough to zero that sign doesn't matter
+ return angle
+
+ # Determine the sign of the angle
+ vec3 = vec2.cross(vec1)
+ vec3.normalize()
+ sign = vec3.dot(plane)
+ if sign >= 0:
+ sign = 1
+ else:
+ sign = -1
+
+ return angle * sign
+
+#=============================================
+# Color correction functions
+#=============================================
+
+
+def linsrgb_to_srgb (linsrgb):
+ """Convert physically linear RGB values into sRGB ones. The transform is
+ uniform in the components, so *linsrgb* can be of any shape.
+
+ *linsrgb* values should range between 0 and 1, inclusively.
+
+ """
+ # From Wikipedia, but easy analogue to the above.
+ gamma = 1.055 * linsrgb**(1./2.4) - 0.055
+ scale = linsrgb * 12.92
+ # return np.where (linsrgb > 0.0031308, gamma, scale)
+ if linsrgb > 0.0031308:
+ return gamma
+ return scale
+
+
+def gamma_correct(color):
+
+ corrected_color = Color()
+ for i, component in enumerate(color):
+ corrected_color[i] = linsrgb_to_srgb(color[i])
+ return corrected_color
+
+
+#=============================================
+# Misc
+#=============================================
+
+def copy_attributes(a, b):
+ keys = dir(a)
+ for key in keys:
+ if not key.startswith("_") \
+ and not key.startswith("error_") \
+ and key != "group" \
+ and key != "is_valid" \
+ and key != "rna_type" \
+ and key != "bl_rna":
+ try:
+ setattr(b, key, getattr(a, key))
+ except AttributeError:
+ pass
diff --git a/rigify/utils/naming.py b/rigify/utils/naming.py
new file mode 100644
index 00000000..3af3d851
--- /dev/null
+++ b/rigify/utils/naming.py
@@ -0,0 +1,136 @@
+#====================== 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.
+#
+#======================= END GPL LICENSE BLOCK ========================
+
+# <pep8 compliant>
+
+import random
+import time
+import re
+
+ORG_PREFIX = "ORG-" # Prefix of original bones.
+MCH_PREFIX = "MCH-" # Prefix of mechanism bones.
+DEF_PREFIX = "DEF-" # Prefix of deformation bones.
+ROOT_NAME = "root" # Name of the root bone.
+
+#=======================================================================
+# Name manipulation
+#=======================================================================
+
+
+def strip_trailing_number(s):
+ m = re.search(r'\.(\d{3})$', s)
+ return s[0:-4] if m else s
+
+
+def unique_name(collection, base_name):
+ base_name = strip_trailing_number(base_name)
+ count = 1
+ name = base_name
+
+ while collection.get(name):
+ name = "%s.%03d" % (base_name, count)
+ count += 1
+ return name
+
+
+def org_name(name):
+ """ Returns the name with ORG_PREFIX stripped from it.
+ """
+ if name.startswith(ORG_PREFIX):
+ return name[len(ORG_PREFIX):]
+ else:
+ return name
+
+
+def strip_org(name):
+ """ Returns the name with ORG_PREFIX stripped from it.
+ """
+ if name.startswith(ORG_PREFIX):
+ return name[len(ORG_PREFIX):]
+ else:
+ return name
+org_name = strip_org
+
+
+def strip_mch(name):
+ """ Returns the name with ORG_PREFIX stripped from it.
+ """
+ if name.startswith(MCH_PREFIX):
+ return name[len(MCH_PREFIX):]
+ else:
+ return name
+
+def strip_def(name):
+ """ Returns the name with DEF_PREFIX stripped from it.
+ """
+ if name.startswith(DEF_PREFIX):
+ return name[len(DEF_PREFIX):]
+ else:
+ return name
+
+def org(name):
+ """ Prepends the ORG_PREFIX to a name if it doesn't already have
+ it, and returns it.
+ """
+ if name.startswith(ORG_PREFIX):
+ return name
+ else:
+ return ORG_PREFIX + name
+make_original_name = org
+
+
+def mch(name):
+ """ Prepends the MCH_PREFIX to a name if it doesn't already have
+ it, and returns it.
+ """
+ if name.startswith(MCH_PREFIX):
+ return name
+ else:
+ return MCH_PREFIX + name
+make_mechanism_name = mch
+
+
+def deformer(name):
+ """ Prepends the DEF_PREFIX to a name if it doesn't already have
+ it, and returns it.
+ """
+ if name.startswith(DEF_PREFIX):
+ return name
+ else:
+ return DEF_PREFIX + name
+make_deformer_name = deformer
+
+
+def insert_before_lr(name, text):
+ if name[-1] in ['l', 'L', 'r', 'R'] and name[-2] in ['.', '-', '_']:
+ return name[:-2] + text + name[-2:]
+ else:
+ return name + text
+
+def random_id(length=8):
+ """ Generates a random alphanumeric id string.
+ """
+ tlength = int(length / 2)
+ rlength = int(length / 2) + int(length % 2)
+
+ chars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']
+ text = ""
+ for i in range(0, rlength):
+ text += random.choice(chars)
+ text += str(hex(int(time.time())))[2:][-tlength:].rjust(tlength, '0')[::-1]
+ return text
diff --git a/rigify/utils/rig.py b/rigify/utils/rig.py
new file mode 100644
index 00000000..638194e7
--- /dev/null
+++ b/rigify/utils/rig.py
@@ -0,0 +1,294 @@
+#====================== 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.
+#
+#======================= END GPL LICENSE BLOCK ========================
+
+# <pep8 compliant>
+
+import bpy
+import importlib
+import importlib.util
+import os
+
+RIG_DIR = "rigs" # Name of the directory where rig types are kept
+METARIG_DIR = "metarigs" # Name of the directory where metarigs are kept
+TEMPLATE_DIR = "ui_templates" # Name of the directory where ui templates are kept
+
+MODULE_NAME = "rigify" # Windows/Mac blender is weird, so __package__ doesn't work
+
+outdated_types = {"pitchipoy.limbs.super_limb": "limbs.super_limb",
+ "pitchipoy.limbs.super_arm": "limbs.super_limb",
+ "pitchipoy.limbs.super_leg": "limbs.super_limb",
+ "pitchipoy.limbs.super_front_paw": "limbs.super_limb",
+ "pitchipoy.limbs.super_rear_paw": "limbs.super_limb",
+ "pitchipoy.limbs.super_finger": "limbs.super_finger",
+ "pitchipoy.super_torso_turbo": "spines.super_spine",
+ "pitchipoy.simple_tentacle": "limbs.simple_tentacle",
+ "pitchipoy.super_face": "faces.super_face",
+ "pitchipoy.super_palm": "limbs.super_palm",
+ "pitchipoy.super_copy": "basic.super_copy",
+ "pitchipoy.tentacle": "",
+ "palm": "limbs.super_palm",
+ "basic.copy": "basic.super_copy",
+ "biped.arm": "",
+ "biped.leg": "",
+ "finger": "",
+ "neck_short": "",
+ "misc.delta": "",
+ "spine": ""
+ }
+
+def upgradeMetarigTypes(metarig, revert=False):
+ """Replaces rigify_type properties from old versions with their current names
+
+ :param revert: revert types to previous version (if old type available)
+ """
+
+ if revert:
+ vals = list(outdated_types.values())
+ rig_defs = {v: k for k, v in outdated_types.items() if vals.count(v) == 1}
+ else:
+ rig_defs = outdated_types
+
+ for bone in metarig.pose.bones:
+ rig_type = bone.rigify_type
+ if rig_type in rig_defs:
+ bone.rigify_type = rig_defs[rig_type]
+ if 'leg' in rig_type:
+ bone.rigfy_parameters.limb_type = 'leg'
+ if 'arm' in rig_type:
+ bone.rigfy_parameters.limb_type = 'arm'
+ if 'paw' in rig_type:
+ bone.rigfy_parameters.limb_type = 'paw'
+ if rig_type == "basic.copy":
+ bone.rigify_parameters.make_widget = False
+
+
+#=============================================
+# Misc
+#=============================================
+
+def get_rig_type(rig_type, base_path=''):
+ return get_resource(rig_type, base_path=base_path)
+
+def get_resource(resource_name, base_path=''):
+ """ Fetches a rig module by name, and returns it.
+ """
+
+ if '.' in resource_name:
+ module_subpath = str.join(os.sep, resource_name.split('.'))
+ package = resource_name.split('.')[0]
+ for sub in resource_name.split('.')[1:]:
+ package = '.'.join([package, sub])
+ submod = importlib.import_module(package)
+ else:
+ module_subpath = resource_name
+
+ spec = importlib.util.spec_from_file_location(resource_name, os.path.join(base_path, module_subpath + '.py'))
+ submod = importlib.util.module_from_spec(spec)
+ spec.loader.exec_module(submod)
+ return submod
+
+
+def get_metarig_module(metarig_name, path=METARIG_DIR):
+ """ Fetches a rig module by name, and returns it.
+ """
+
+ name = ".%s.%s" % (path, metarig_name)
+ submod = importlib.import_module(name, package=MODULE_NAME)
+ importlib.reload(submod)
+ return submod
+
+
+def connected_children_names(obj, bone_name):
+ """ Returns a list of bone names (in order) of the bones that form a single
+ connected chain starting with the given bone as a parent.
+ If there is a connected branch, the list stops there.
+ """
+ bone = obj.data.bones[bone_name]
+ names = []
+
+ while True:
+ connects = 0
+ con_name = ""
+
+ for child in bone.children:
+ if child.use_connect:
+ connects += 1
+ con_name = child.name
+
+ if connects == 1:
+ names += [con_name]
+ bone = obj.data.bones[con_name]
+ else:
+ break
+
+ return names
+
+
+def has_connected_children(bone):
+ """ Returns true/false whether a bone has connected children or not.
+ """
+ t = False
+ for b in bone.children:
+ t = t or b.use_connect
+ return t
+
+
+def write_metarig(obj, layers=False, func_name="create", groups=False):
+ """
+ Write a metarig as a python script, this rig is to have all info needed for
+ generating the real rig with rigify.
+ """
+ code = []
+
+ code.append("import bpy\n\n")
+ code.append("from mathutils import Color\n\n")
+
+ code.append("def %s(obj):" % func_name)
+ code.append(" # generated by rigify.utils.write_metarig")
+ bpy.ops.object.mode_set(mode='EDIT')
+ code.append(" bpy.ops.object.mode_set(mode='EDIT')")
+ code.append(" arm = obj.data")
+
+ arm = obj.data
+
+ # Rigify bone group colors info
+ if groups and len(arm.rigify_colors) > 0:
+ code.append("\n for i in range(" + str(len(arm.rigify_colors)) + "):")
+ code.append(" arm.rigify_colors.add()\n")
+
+ for i in range(len(arm.rigify_colors)):
+ name = arm.rigify_colors[i].name
+ active = arm.rigify_colors[i].active
+ normal = arm.rigify_colors[i].normal
+ select = arm.rigify_colors[i].select
+ standard_colors_lock = arm.rigify_colors[i].standard_colors_lock
+ code.append(' arm.rigify_colors[' + str(i) + '].name = "' + name + '"')
+ code.append(' arm.rigify_colors[' + str(i) + '].active = Color(' + str(active[:]) + ')')
+ code.append(' arm.rigify_colors[' + str(i) + '].normal = Color(' + str(normal[:]) + ')')
+ code.append(' arm.rigify_colors[' + str(i) + '].select = Color(' + str(select[:]) + ')')
+ code.append(' arm.rigify_colors[' + str(i) + '].standard_colors_lock = ' + str(standard_colors_lock))
+
+ # Rigify layer layout info
+ if layers and len(arm.rigify_layers) > 0:
+ code.append("\n for i in range(" + str(len(arm.rigify_layers)) + "):")
+ code.append(" arm.rigify_layers.add()\n")
+
+ for i in range(len(arm.rigify_layers)):
+ name = arm.rigify_layers[i].name
+ row = arm.rigify_layers[i].row
+ selset = arm.rigify_layers[i].selset
+ group = arm.rigify_layers[i].group
+ code.append(' arm.rigify_layers[' + str(i) + '].name = "' + name + '"')
+ code.append(' arm.rigify_layers[' + str(i) + '].row = ' + str(row))
+ code.append(' arm.rigify_layers[' + str(i) + '].selset = ' + str(selset))
+ code.append(' arm.rigify_layers[' + str(i) + '].group = ' + str(group))
+
+ # write parents first
+ bones = [(len(bone.parent_recursive), bone.name) for bone in arm.edit_bones]
+ bones.sort(key=lambda item: item[0])
+ bones = [item[1] for item in bones]
+
+ code.append("\n bones = {}\n")
+
+ for bone_name in bones:
+ bone = arm.edit_bones[bone_name]
+ code.append(" bone = arm.edit_bones.new(%r)" % bone.name)
+ code.append(" bone.head[:] = %.4f, %.4f, %.4f" % bone.head.to_tuple(4))
+ code.append(" bone.tail[:] = %.4f, %.4f, %.4f" % bone.tail.to_tuple(4))
+ code.append(" bone.roll = %.4f" % bone.roll)
+ code.append(" bone.use_connect = %s" % str(bone.use_connect))
+ if bone.parent:
+ code.append(" bone.parent = arm.edit_bones[bones[%r]]" % bone.parent.name)
+ code.append(" bones[%r] = bone.name" % bone.name)
+
+ bpy.ops.object.mode_set(mode='OBJECT')
+ code.append("")
+ code.append(" bpy.ops.object.mode_set(mode='OBJECT')")
+
+ # Rig type and other pose properties
+ for bone_name in bones:
+ pbone = obj.pose.bones[bone_name]
+
+ code.append(" pbone = obj.pose.bones[bones[%r]]" % bone_name)
+ code.append(" pbone.rigify_type = %r" % pbone.rigify_type)
+ code.append(" pbone.lock_location = %s" % str(tuple(pbone.lock_location)))
+ code.append(" pbone.lock_rotation = %s" % str(tuple(pbone.lock_rotation)))
+ code.append(" pbone.lock_rotation_w = %s" % str(pbone.lock_rotation_w))
+ code.append(" pbone.lock_scale = %s" % str(tuple(pbone.lock_scale)))
+ code.append(" pbone.rotation_mode = %r" % pbone.rotation_mode)
+ if layers:
+ code.append(" pbone.bone.layers = %s" % str(list(pbone.bone.layers)))
+ # Rig type parameters
+ for param_name in pbone.rigify_parameters.keys():
+ param = getattr(pbone.rigify_parameters, param_name, '')
+ if str(type(param)) == "<class 'bpy_prop_array'>":
+ param = list(param)
+ if type(param) == str:
+ param = '"' + param + '"'
+ code.append(" try:")
+ code.append(" pbone.rigify_parameters.%s = %s" % (param_name, str(param)))
+ code.append(" except AttributeError:")
+ code.append(" pass")
+
+ code.append("\n bpy.ops.object.mode_set(mode='EDIT')")
+ code.append(" for bone in arm.edit_bones:")
+ code.append(" bone.select = False")
+ code.append(" bone.select_head = False")
+ code.append(" bone.select_tail = False")
+
+ code.append(" for b in bones:")
+ code.append(" bone = arm.edit_bones[bones[b]]")
+ code.append(" bone.select = True")
+ code.append(" bone.select_head = True")
+ code.append(" bone.select_tail = True")
+ code.append(" arm.edit_bones.active = bone")
+
+ # Set appropriate layers visible
+ if layers:
+ # Find what layers have bones on them
+ active_layers = []
+ for bone_name in bones:
+ bone = obj.data.bones[bone_name]
+ for i in range(len(bone.layers)):
+ if bone.layers[i]:
+ if i not in active_layers:
+ active_layers.append(i)
+ active_layers.sort()
+
+ code.append("\n arm.layers = [(x in " + str(active_layers) + ") for x in range(" + str(len(arm.layers)) + ")]")
+
+ if func_name == "create":
+ active_template = arm.rigify_active_template
+ template_name = arm.rigify_templates[active_template].name
+ code.append("\n # Select proper UI template")
+ code.append(" template_name = '{}'".format(template_name))
+ code.append(" arm_templates = arm.rigify_templates.items()")
+ code.append(" template_index = None")
+ code.append(" for i, template in enumerate(arm_templates):")
+ code.append(" if template[0] == template_name:")
+ code.append(" template_index = i")
+ code.append(" break")
+ code.append(" if template_index is None:")
+ code.append(" template_index = 0 # Default to something...")
+ code.append(" else:")
+ code.append(" arm.rigify_active_template = template_index")
+
+ code.append('\nif __name__ == "__main__":')
+ code.append(" " + func_name + "(bpy.context.active_object)\n")
+
+ return "\n".join(code)
diff --git a/rigify/utils/widgets.py b/rigify/utils/widgets.py
new file mode 100644
index 00000000..184429a3
--- /dev/null
+++ b/rigify/utils/widgets.py
@@ -0,0 +1,168 @@
+#====================== 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.
+#
+#======================= END GPL LICENSE BLOCK ========================
+
+# <pep8 compliant>
+
+import bpy
+import math
+
+from .errors import MetarigError
+
+WGT_PREFIX = "WGT-" # Prefix for widget objects
+
+#=============================================
+# Widget creation
+#=============================================
+
+
+def obj_to_bone(obj, rig, bone_name):
+ """ Places an object at the location/rotation/scale of the given bone.
+ """
+ if bpy.context.mode == 'EDIT_ARMATURE':
+ raise MetarigError("obj_to_bone(): does not work while in edit mode")
+
+ bone = rig.data.bones[bone_name]
+
+ mat = rig.matrix_world @ bone.matrix_local
+
+ obj.location = mat.to_translation()
+
+ obj.rotation_mode = 'XYZ'
+ obj.rotation_euler = mat.to_euler()
+
+ scl = mat.to_scale()
+ scl_avg = (scl[0] + scl[1] + scl[2]) / 3
+ obj.scale = (bone.length * scl_avg), (bone.length * scl_avg), (bone.length * scl_avg)
+
+
+def create_widget(rig, bone_name, bone_transform_name=None):
+ """ Creates an empty widget object for a bone, and returns the object.
+ """
+ if bone_transform_name is None:
+ bone_transform_name = bone_name
+
+ obj_name = WGT_PREFIX + rig.name + '_' + bone_name
+ scene = bpy.context.scene
+ collection = bpy.context.collection
+
+ # Check if it already exists in the scene
+ if obj_name in scene.objects:
+ # Move object to bone position, in case it changed
+ obj = scene.objects[obj_name]
+ obj_to_bone(obj, rig, bone_transform_name)
+
+ return None
+ else:
+ # Delete object if it exists in blend data but not scene data.
+ # This is necessary so we can then create the object without
+ # name conflicts.
+ if obj_name in bpy.data.objects:
+ bpy.data.objects[obj_name].user_clear()
+ bpy.data.objects.remove(bpy.data.objects[obj_name])
+
+ # Create mesh object
+ mesh = bpy.data.meshes.new(obj_name)
+ obj = bpy.data.objects.new(obj_name, mesh)
+ collection.objects.link(obj)
+
+ # Move object to bone position and set layers
+ obj_to_bone(obj, rig, bone_transform_name)
+ wgts_group_name = 'WGTS_' + rig.name
+ if wgts_group_name in bpy.data.objects.keys():
+ obj.parent = bpy.data.objects[wgts_group_name]
+
+ return obj
+
+
+def create_circle_polygon(number_verts, axis, radius=1.0, head_tail=0.0):
+ """ Creates a basic circle around of an axis selected.
+ number_verts: number of vertices of the poligon
+ axis: axis normal to the circle
+ radius: the radius of the circle
+ head_tail: where along the length of the bone the circle is (0.0=head, 1.0=tail)
+ """
+ verts = []
+ edges = []
+ angle = 2 * math.pi / number_verts
+ i = 0
+
+ assert(axis in 'XYZ')
+
+ while i < (number_verts):
+ a = math.cos(i * angle)
+ b = math.sin(i * angle)
+
+ if axis == 'X':
+ verts.append((head_tail, a * radius, b * radius))
+ elif axis == 'Y':
+ verts.append((a * radius, head_tail, b * radius))
+ elif axis == 'Z':
+ verts.append((a * radius, b * radius, head_tail))
+
+ if i < (number_verts - 1):
+ edges.append((i , i + 1))
+
+ i += 1
+
+ edges.append((0, number_verts - 1))
+
+ return verts, edges
+
+
+def write_widget(obj):
+ """ Write a mesh object as a python script for widget use.
+ """
+ script = ""
+ script += "def create_thing_widget(rig, bone_name, size=1.0, bone_transform_name=None):\n"
+ script += " obj = create_widget(rig, bone_name, bone_transform_name)\n"
+ script += " if obj != None:\n"
+
+ # Vertices
+ if len(obj.data.vertices) > 0:
+ script += " verts = ["
+ for v in obj.data.vertices:
+ script += "(" + str(v.co[0]) + "*size, " + str(v.co[1]) + "*size, " + str(v.co[2]) + "*size), "
+ script += "]\n"
+
+ # Edges
+ if len(obj.data.edges) > 0:
+ script += " edges = ["
+ for e in obj.data.edges:
+ script += "(" + str(e.vertices[0]) + ", " + str(e.vertices[1]) + "), "
+ script += "]\n"
+
+ # Faces
+ if len(obj.data.polygons) > 0:
+ script += " faces = ["
+ for f in obj.data.polygons:
+ script += "("
+ for v in f.vertices:
+ script += str(v) + ", "
+ script += "), "
+ script += "]\n"
+
+ # Build mesh
+ script += "\n mesh = obj.data\n"
+ script += " mesh.from_pydata(verts, edges, faces)\n"
+ script += " mesh.update()\n"
+ script += " mesh.update()\n"
+ script += " return obj\n"
+ script += " else:\n"
+ script += " return None\n"
+
+ return script
diff --git a/rigify/utils/widgets_basic.py b/rigify/utils/widgets_basic.py
new file mode 100644
index 00000000..aae8f6bb
--- /dev/null
+++ b/rigify/utils/widgets_basic.py
@@ -0,0 +1,123 @@
+#====================== 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.
+#
+#======================= END GPL LICENSE BLOCK ========================
+
+# <pep8 compliant>
+
+from .widgets import create_widget
+
+# Common Widgets
+
+
+def create_line_widget(rig, bone_name, bone_transform_name=None):
+ """ Creates a basic line widget, a line that spans the length of the bone.
+ """
+ obj = create_widget(rig, bone_name, bone_transform_name)
+ if obj is not None:
+ mesh = obj.data
+ mesh.from_pydata([(0, 0, 0), (0, 1, 0)], [(0, 1)], [])
+ mesh.update()
+
+
+def create_circle_widget(rig, bone_name, radius=1.0, head_tail=0.0, with_line=False, bone_transform_name=None):
+ """ Creates a basic circle widget, a circle around the y-axis.
+ radius: the radius of the circle
+ head_tail: where along the length of the bone the circle is (0.0=head, 1.0=tail)
+ """
+ obj = create_widget(rig, bone_name, bone_transform_name)
+ if obj != None:
+ v = [(0.7071068286895752, 2.980232238769531e-07, -0.7071065306663513), (0.8314696550369263, 2.980232238769531e-07, -0.5555699467658997), (0.9238795042037964, 2.682209014892578e-07, -0.3826831877231598), (0.9807852506637573, 2.5331974029541016e-07, -0.19509011507034302), (1.0, 2.365559055306221e-07, 1.6105803979371558e-07), (0.9807853698730469, 2.2351741790771484e-07, 0.19509044289588928), (0.9238796234130859, 2.086162567138672e-07, 0.38268351554870605), (0.8314696550369263, 1.7881393432617188e-07, 0.5555704236030579), (0.7071068286895752, 1.7881393432617188e-07, 0.7071070075035095), (0.5555702447891235, 1.7881393432617188e-07, 0.8314698934555054), (0.38268327713012695, 1.7881393432617188e-07, 0.923879861831665), (0.19509008526802063, 1.7881393432617188e-07, 0.9807855486869812), (-3.2584136988589307e-07, 1.1920928955078125e-07, 1.000000238418579), (-0.19509072601795197, 1.7881393432617188e-07, 0.9807854294776917), (-0.3826838731765747, 1.7881393432617188e-07, 0.9238795638084412), (-0.5555707216262817, 1.7881393432617188e-07, 0.8314695358276367), (-0.7071071863174438, 1.7881393432617188e-07, 0.7071065902709961), (-0.8314700126647949, 1.7881393432617188e-07, 0.5555698871612549), (-0.923879861831665, 2.086162567138672e-07, 0.3826829195022583), (-0.9807853698730469, 2.2351741790771484e-07, 0.1950896978378296), (-1.0, 2.365559907957504e-07, -7.290432222362142e-07), (-0.9807850122451782, 2.5331974029541016e-07, -0.195091113448143), (-0.9238790273666382, 2.682209014892578e-07, -0.38268423080444336), (-0.831468939781189, 2.980232238769531e-07, -0.5555710196495056), (-0.7071058750152588, 2.980232238769531e-07, -0.707107424736023), (-0.555569052696228, 2.980232238769531e-07, -0.8314701318740845), (-0.38268208503723145, 2.980232238769531e-07, -0.923879861831665), (-0.19508881866931915, 2.980232238769531e-07, -0.9807853102684021), (1.6053570561780361e-06, 2.980232238769531e-07, -0.9999997615814209), (0.19509197771549225, 2.980232238769531e-07, -0.9807847142219543), (0.3826850652694702, 2.980232238769531e-07, -0.9238786101341248), (0.5555717945098877, 2.980232238769531e-07, -0.8314683437347412)]
+ verts = [(a[0] * radius, head_tail, a[2] * radius) for a in v]
+ if with_line:
+ edges = [(28, 12), (0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7), (7, 8), (8, 9), (9, 10), (10, 11), (11, 12), (12, 13), (13, 14), (14, 15), (15, 16), (16, 17), (17, 18), (18, 19), (19, 20), (20, 21), (21, 22), (22, 23), (23, 24), (24, 25), (25, 26), (26, 27), (27, 28), (28, 29), (29, 30), (30, 31), (0, 31)]
+ else:
+ edges = [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7), (7, 8), (8, 9), (9, 10), (10, 11), (11, 12), (12, 13), (13, 14), (14, 15), (15, 16), (16, 17), (17, 18), (18, 19), (19, 20), (20, 21), (21, 22), (22, 23), (23, 24), (24, 25), (25, 26), (26, 27), (27, 28), (28, 29), (29, 30), (30, 31), (0, 31)]
+ mesh = obj.data
+ mesh.from_pydata(verts, edges, [])
+ mesh.update()
+ return obj
+ else:
+ return None
+
+
+def create_cube_widget(rig, bone_name, radius=0.5, bone_transform_name=None):
+ """ Creates a basic cube widget.
+ """
+ obj = create_widget(rig, bone_name, bone_transform_name)
+ if obj is not None:
+ r = radius
+ verts = [(r, r, r), (r, -r, r), (-r, -r, r), (-r, r, r), (r, r, -r), (r, -r, -r), (-r, -r, -r), (-r, r, -r)]
+ edges = [(0, 1), (1, 2), (2, 3), (3, 0), (4, 5), (5, 6), (6, 7), (7, 4), (0, 4), (1, 5), (2, 6), (3, 7)]
+ mesh = obj.data
+ mesh.from_pydata(verts, edges, [])
+ mesh.update()
+
+
+def create_chain_widget(rig, bone_name, radius=0.5, invert=False, bone_transform_name=None):
+ """Creates a basic chain widget
+ """
+ obj = create_widget(rig, bone_name, bone_transform_name)
+ if obj != None:
+ r = radius
+ rh = radius/2
+ if invert:
+ verts = [(rh, rh, rh), (r, -r, r), (-r, -r, r), (-rh, rh, rh), (rh, rh, -rh), (r, -r, -r), (-r, -r, -r), (-rh, rh, -rh)]
+ else:
+ verts = [(r, r, r), (rh, -rh, rh), (-rh, -rh, rh), (-r, r, r), (r, r, -r), (rh, -rh, -rh), (-rh, -rh, -rh), (-r, r, -r)]
+ edges = [(0, 1), (1, 2), (2, 3), (3, 0), (4, 5), (5, 6), (6, 7), (7, 4), (0, 4), (1, 5), (2, 6), (3, 7)]
+ mesh = obj.data
+ mesh.from_pydata(verts, edges, [])
+ mesh.update()
+
+
+def create_sphere_widget(rig, bone_name, bone_transform_name=None):
+ """ Creates a basic sphere widget, three pependicular overlapping circles.
+ """
+ obj = create_widget(rig, bone_name, bone_transform_name)
+ if obj != None:
+ verts = [(0.3535533845424652, 0.3535533845424652, 0.0), (0.4619397521018982, 0.19134171307086945, 0.0), (0.5, -2.1855694143368964e-08, 0.0), (0.4619397521018982, -0.19134175777435303, 0.0), (0.3535533845424652, -0.3535533845424652, 0.0), (0.19134174287319183, -0.4619397521018982, 0.0), (7.549790126404332e-08, -0.5, 0.0), (-0.1913416087627411, -0.46193981170654297, 0.0), (-0.35355329513549805, -0.35355350375175476, 0.0), (-0.4619397521018982, -0.19134178757667542, 0.0), (-0.5, 5.962440319251527e-09, 0.0), (-0.4619397222995758, 0.1913418024778366, 0.0), (-0.35355326533317566, 0.35355350375175476, 0.0), (-0.19134148955345154, 0.46193987131118774, 0.0), (3.2584136988589307e-07, 0.5, 0.0), (0.1913420855998993, 0.46193960309028625, 0.0), (7.450580596923828e-08, 0.46193960309028625, 0.19134199619293213), (5.9254205098113744e-08, 0.5, 2.323586443253589e-07), (4.470348358154297e-08, 0.46193987131118774, -0.1913415789604187), (2.9802322387695312e-08, 0.35355350375175476, -0.3535533547401428), (2.9802322387695312e-08, 0.19134178757667542, -0.46193981170654297), (5.960464477539063e-08, -1.1151834122813398e-08, -0.5000000596046448), (5.960464477539063e-08, -0.1913418024778366, -0.46193984150886536), (5.960464477539063e-08, -0.35355350375175476, -0.3535533845424652), (7.450580596923828e-08, -0.46193981170654297, -0.19134166836738586), (9.348272556053416e-08, -0.5, 1.624372103492533e-08), (1.043081283569336e-07, -0.4619397521018982, 0.19134168326854706), (1.1920928955078125e-07, -0.3535533845424652, 0.35355329513549805), (1.1920928955078125e-07, -0.19134174287319183, 0.46193966269493103), (1.1920928955078125e-07, -4.7414250303745575e-09, 0.49999991059303284), (1.1920928955078125e-07, 0.19134172797203064, 0.46193966269493103), (8.940696716308594e-08, 0.3535533845424652, 0.35355329513549805), (0.3535534739494324, 0.0, 0.35355329513549805), (0.1913418173789978, -2.9802322387695312e-08, 0.46193966269493103), (8.303572940349113e-08, -5.005858838558197e-08, 0.49999991059303284), (-0.19134165346622467, -5.960464477539063e-08, 0.46193966269493103), (-0.35355329513549805, -8.940696716308594e-08, 0.35355329513549805), (-0.46193963289260864, -5.960464477539063e-08, 0.19134168326854706), (-0.49999991059303284, -5.960464477539063e-08, 1.624372103492533e-08), (-0.4619397521018982, -2.9802322387695312e-08, -0.19134166836738586), (-0.3535534143447876, -2.9802322387695312e-08, -0.3535533845424652), (-0.19134171307086945, 0.0, -0.46193984150886536), (7.662531942287387e-08, 9.546055501630235e-09, -0.5000000596046448), (0.19134187698364258, 5.960464477539063e-08, -0.46193981170654297), (0.3535535931587219, 5.960464477539063e-08, -0.3535533547401428), (0.4619399905204773, 5.960464477539063e-08, -0.1913415789604187), (0.5000000596046448, 5.960464477539063e-08, 2.323586443253589e-07), (0.4619396924972534, 2.9802322387695312e-08, 0.19134199619293213)]
+ edges = [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7), (7, 8), (8, 9), (9, 10), (10, 11), (11, 12), (12, 13), (13, 14), (14, 15), (0, 15), (16, 31), (16, 17), (17, 18), (18, 19), (19, 20), (20, 21), (21, 22), (22, 23), (23, 24), (24, 25), (25, 26), (26, 27), (27, 28), (28, 29), (29, 30), (30, 31), (32, 33), (33, 34), (34, 35), (35, 36), (36, 37), (37, 38), (38, 39), (39, 40), (40, 41), (41, 42), (42, 43), (43, 44), (44, 45), (45, 46), (46, 47), (32, 47)]
+ mesh = obj.data
+ mesh.from_pydata(verts, edges, [])
+ mesh.update()
+
+
+def create_limb_widget(rig, bone_name, bone_transform_name=None):
+ """ Creates a basic limb widget, a line that spans the length of the
+ bone, with a circle around the center.
+ """
+ obj = create_widget(rig, bone_name, bone_transform_name)
+ if obj != None:
+ verts = [(-1.1920928955078125e-07, 1.7881393432617188e-07, 0.0), (3.5762786865234375e-07, 1.0000004768371582, 0.0), (0.1767769455909729, 0.5000001192092896, 0.17677664756774902), (0.20786768198013306, 0.5000001192092896, 0.1388925313949585), (0.23097014427185059, 0.5000001192092896, 0.09567084908485413), (0.24519658088684082, 0.5000001192092896, 0.048772573471069336), (0.2500002384185791, 0.5000001192092896, -2.545945676502015e-09), (0.24519658088684082, 0.5000001192092896, -0.048772573471069336), (0.23097014427185059, 0.5000001192092896, -0.09567084908485413), (0.20786768198013306, 0.5000001192092896, -0.13889259099960327), (0.1767769455909729, 0.5000001192092896, -0.1767767071723938), (0.13889282941818237, 0.5000001192092896, -0.20786744356155396), (0.09567105770111084, 0.5000001192092896, -0.23096990585327148), (0.04877278208732605, 0.5000001192092896, -0.24519634246826172), (1.7279069197684294e-07, 0.5000000596046448, -0.25), (-0.0487724244594574, 0.5000001192092896, -0.24519634246826172), (-0.09567070007324219, 0.5000001192092896, -0.2309698462486267), (-0.13889241218566895, 0.5000001192092896, -0.20786738395690918), (-0.17677652835845947, 0.5000001192092896, -0.17677664756774902), (-0.20786726474761963, 0.5000001192092896, -0.13889244198799133), (-0.23096972703933716, 0.5000001192092896, -0.09567070007324219), (-0.24519610404968262, 0.5000001192092896, -0.04877239465713501), (-0.2499997615814209, 0.5000001192092896, 2.1997936983098043e-07), (-0.24519598484039307, 0.5000001192092896, 0.04877282679080963), (-0.23096948862075806, 0.5000001192092896, 0.09567108750343323), (-0.20786696672439575, 0.5000001192092896, 0.1388927698135376), (-0.1767762303352356, 0.5000001192092896, 0.17677688598632812), (-0.13889199495315552, 0.5000001192092896, 0.2078675627708435), (-0.09567028284072876, 0.5000001192092896, 0.23097002506256104), (-0.048771947622299194, 0.5000001192092896, 0.24519634246826172), (6.555903269145347e-07, 0.5000001192092896, 0.25), (0.04877324402332306, 0.5000001192092896, 0.24519622325897217), (0.09567153453826904, 0.5000001192092896, 0.23096966743469238), (0.13889318704605103, 0.5000001192092896, 0.20786714553833008)]
+ edges = [(0, 1), (2, 3), (4, 3), (5, 4), (5, 6), (6, 7), (8, 7), (8, 9), (10, 9), (10, 11), (11, 12), (13, 12), (14, 13), (14, 15), (16, 15), (16, 17), (17, 18), (19, 18), (19, 20), (21, 20), (21, 22), (22, 23), (24, 23), (25, 24), (25, 26), (27, 26), (27, 28), (29, 28), (29, 30), (30, 31), (32, 31), (32, 33), (2, 33)]
+ mesh = obj.data
+ mesh.from_pydata(verts, edges, [])
+ mesh.update()
+
+
+def create_bone_widget(rig, bone_name, bone_transform_name=None):
+ """ Creates a basic bone widget, a simple obolisk-esk shape.
+ """
+ obj = create_widget(rig, bone_name, bone_transform_name)
+ if obj != None:
+ verts = [(0.04, 1.0, -0.04), (0.1, 0.0, -0.1), (-0.1, 0.0, -0.1), (-0.04, 1.0, -0.04), (0.04, 1.0, 0.04), (0.1, 0.0, 0.1), (-0.1, 0.0, 0.1), (-0.04, 1.0, 0.04)]
+ edges = [(1, 2), (0, 1), (0, 3), (2, 3), (4, 5), (5, 6), (6, 7), (4, 7), (1, 5), (0, 4), (2, 6), (3, 7)]
+ mesh = obj.data
+ mesh.from_pydata(verts, edges, [])
+ mesh.update()
+
+
diff --git a/rigify/utils/widgets_special.py b/rigify/utils/widgets_special.py
new file mode 100644
index 00000000..8e2d7a27
--- /dev/null
+++ b/rigify/utils/widgets_special.py
@@ -0,0 +1,195 @@
+#====================== 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.
+#
+#======================= END GPL LICENSE BLOCK ========================
+
+# <pep8 compliant>
+
+from .widgets import create_widget
+
+
+def create_compass_widget(rig, bone_name, bone_transform_name=None):
+ """ Creates a compass-shaped widget.
+ """
+ obj = create_widget(rig, bone_name, bone_transform_name)
+ if obj != None:
+ verts = [(0.0, 1.2000000476837158, 0.0), (0.19509032368659973, 0.9807852506637573, 0.0), (0.3826834559440613, 0.9238795042037964, 0.0), (0.5555702447891235, 0.8314695954322815, 0.0), (0.7071067690849304, 0.7071067690849304, 0.0), (0.8314696550369263, 0.5555701851844788, 0.0), (0.9238795042037964, 0.3826834261417389, 0.0), (0.9807852506637573, 0.19509035348892212, 0.0), (1.2000000476837158, 7.549790126404332e-08, 0.0), (0.9807853102684021, -0.19509020447731018, 0.0), (0.9238795638084412, -0.38268327713012695, 0.0), (0.8314696550369263, -0.5555701851844788, 0.0), (0.7071067690849304, -0.7071067690849304, 0.0), (0.5555701851844788, -0.8314696550369263, 0.0), (0.38268327713012695, -0.9238796234130859, 0.0), (0.19509008526802063, -0.9807853102684021, 0.0), (-3.2584136988589307e-07, -1.2999999523162842, 0.0), (-0.19509072601795197, -0.9807851910591125, 0.0), (-0.3826838731765747, -0.9238793253898621, 0.0), (-0.5555707216262817, -0.8314692974090576, 0.0), (-0.7071072459220886, -0.707106351852417, 0.0), (-0.8314700126647949, -0.5555696487426758, 0.0), (-0.923879861831665, -0.3826826810836792, 0.0), (-0.9807854294776917, -0.1950894594192505, 0.0), (-1.2000000476837158, 9.655991561885457e-07, 0.0), (-0.980785071849823, 0.1950913518667221, 0.0), (-0.923879086971283, 0.38268446922302246, 0.0), (-0.831468939781189, 0.5555712580680847, 0.0), (-0.7071058750152588, 0.707107663154602, 0.0), (-0.5555691123008728, 0.8314703702926636, 0.0), (-0.38268208503723145, 0.9238801002502441, 0.0), (-0.19508881866931915, 0.9807855486869812, 0.0)]
+ edges = [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7), (7, 8), (8, 9), (9, 10), (10, 11), (11, 12), (12, 13), (13, 14), (14, 15), (15, 16), (16, 17), (17, 18), (18, 19), (19, 20), (20, 21), (21, 22), (22, 23), (23, 24), (24, 25), (25, 26), (26, 27), (27, 28), (28, 29), (29, 30), (30, 31), (0, 31)]
+ mesh = obj.data
+ mesh.from_pydata(verts, edges, [])
+ mesh.update()
+
+
+def create_root_widget(rig, bone_name, bone_transform_name=None):
+ """ Creates a widget for the root bone.
+ """
+ obj = create_widget(rig, bone_name, bone_transform_name)
+ if obj != None:
+ verts = [(0.7071067690849304, 0.7071067690849304, 0.0), (0.7071067690849304, -0.7071067690849304, 0.0), (-0.7071067690849304, 0.7071067690849304, 0.0), (-0.7071067690849304, -0.7071067690849304, 0.0), (0.8314696550369263, 0.5555701851844788, 0.0), (0.8314696550369263, -0.5555701851844788, 0.0), (-0.8314696550369263, 0.5555701851844788, 0.0), (-0.8314696550369263, -0.5555701851844788, 0.0), (0.9238795042037964, 0.3826834261417389, 0.0), (0.9238795042037964, -0.3826834261417389, 0.0), (-0.9238795042037964, 0.3826834261417389, 0.0), (-0.9238795042037964, -0.3826834261417389, 0.0), (0.9807852506637573, 0.19509035348892212, 0.0), (0.9807852506637573, -0.19509035348892212, 0.0), (-0.9807852506637573, 0.19509035348892212, 0.0), (-0.9807852506637573, -0.19509035348892212, 0.0), (0.19509197771549225, 0.9807849526405334, 0.0), (0.19509197771549225, -0.9807849526405334, 0.0), (-0.19509197771549225, 0.9807849526405334, 0.0), (-0.19509197771549225, -0.9807849526405334, 0.0), (0.3826850652694702, 0.9238788485527039, 0.0), (0.3826850652694702, -0.9238788485527039, 0.0), (-0.3826850652694702, 0.9238788485527039, 0.0), (-0.3826850652694702, -0.9238788485527039, 0.0), (0.5555717945098877, 0.8314685821533203, 0.0), (0.5555717945098877, -0.8314685821533203, 0.0), (-0.5555717945098877, 0.8314685821533203, 0.0), (-0.5555717945098877, -0.8314685821533203, 0.0), (0.19509197771549225, 1.2807848453521729, 0.0), (0.19509197771549225, -1.2807848453521729, 0.0), (-0.19509197771549225, 1.2807848453521729, 0.0), (-0.19509197771549225, -1.2807848453521729, 0.0), (1.280785322189331, 0.19509035348892212, 0.0), (1.280785322189331, -0.19509035348892212, 0.0), (-1.280785322189331, 0.19509035348892212, 0.0), (-1.280785322189331, -0.19509035348892212, 0.0), (0.3950919806957245, 1.2807848453521729, 0.0), (0.3950919806957245, -1.2807848453521729, 0.0), (-0.3950919806957245, 1.2807848453521729, 0.0), (-0.3950919806957245, -1.2807848453521729, 0.0), (1.280785322189331, 0.39509034156799316, 0.0), (1.280785322189331, -0.39509034156799316, 0.0), (-1.280785322189331, 0.39509034156799316, 0.0), (-1.280785322189331, -0.39509034156799316, 0.0), (0.0, 1.5807849168777466, 0.0), (0.0, -1.5807849168777466, 0.0), (1.5807852745056152, 0.0, 0.0), (-1.5807852745056152, 0.0, 0.0)]
+ edges = [(0, 4), (1, 5), (2, 6), (3, 7), (4, 8), (5, 9), (6, 10), (7, 11), (8, 12), (9, 13), (10, 14), (11, 15), (16, 20), (17, 21), (18, 22), (19, 23), (20, 24), (21, 25), (22, 26), (23, 27), (0, 24), (1, 25), (2, 26), (3, 27), (16, 28), (17, 29), (18, 30), (19, 31), (12, 32), (13, 33), (14, 34), (15, 35), (28, 36), (29, 37), (30, 38), (31, 39), (32, 40), (33, 41), (34, 42), (35, 43), (36, 44), (37, 45), (38, 44), (39, 45), (40, 46), (41, 46), (42, 47), (43, 47)]
+ mesh = obj.data
+ mesh.from_pydata(verts, edges, [])
+ mesh.update()
+
+
+def create_neck_bend_widget(rig, bone_name, radius=1.0, head_tail=0.0, bone_transform_name=None):
+ obj = create_widget(rig, bone_name, bone_transform_name)
+ size = 2.0
+ if obj != None:
+ v = [(-0.08855080604553223 * size, 0.7388765811920166 * size, -0.3940150737762451 * size),
+ (0.08855044841766357 * size, 0.7388765811920166 * size, -0.3940150737762451 * size),
+ (0.17710095643997192 * size, 0.5611097812652588 * size, -0.6478927135467529 * size),
+ (-4.0892032870942785e-07 * size, 0.4087378978729248 * size, -0.865501880645752 * size),
+ (-0.17710143327713013 * size, 0.5611097812652588 * size, -0.6478922367095947 * size),
+ (0.08855026960372925 * size, 0.5611097812652588 * size, -0.6478924751281738 * size),
+ (-0.08855092525482178 * size, 0.5611097812652588 * size, -0.6478927135467529 * size),
+ (-0.6478927135467529 * size, 0.5611097812652588 * size, 0.08855098485946655 * size),
+ (-0.6478927135467529 * size, 0.5611097812652588 * size, -0.08855020999908447 * size),
+ (-0.6478924751281738 * size, 0.5611097812652588 * size, 0.17710155248641968 * size),
+ (-0.865501880645752 * size, 0.4087378978729248 * size, 4.6876743908796925e-07 * size),
+ (-0.647892951965332 * size, 0.5611097812652588 * size, -0.17710083723068237 * size),
+ (-0.39401543140411377 * size, 0.7388765811920166 * size, -0.08855029940605164 * size),
+ (-0.39401543140411377 * size, 0.7388765811920166 * size, 0.08855095505714417 * size),
+ (0.6478927135467529 * size, 0.5611097812652588 * size, -0.08855059742927551 * size),
+ (0.6478927135467529 * size, 0.5611097812652588 * size, 0.08855065703392029 * size),
+ (0.6478924751281738 * size, 0.5611097812652588 * size, -0.17710113525390625 * size),
+ (0.865501880645752 * size, 0.4087378978729248 * size, -3.264514703005261e-08 * size),
+ (0.647892951965332 * size, 0.5611097812652588 * size, 0.1771012544631958 * size),
+ (0.08855065703392029 * size, 0.7388765811920166 * size, 0.3940155506134033 * size),
+ (-0.08855056762695312 * size, 0.7388765811920166 * size, 0.3940155506134033 * size),
+ (-0.17710107564926147 * size, 0.5611097812652588 * size, 0.647892951965332 * size),
+ (2.244429140318971e-07 * size, 0.4087378978729248 * size, 0.865502119064331 * size),
+ (0.17710131406784058 * size, 0.5611097812652588 * size, 0.6478927135467529 * size),
+ (-0.08855044841766357 * size, 0.5611097812652588 * size, 0.647892951965332 * size),
+ (0.08855074644088745 * size, 0.5611097812652588 * size, 0.647892951965332 * size),
+ (0.3940153121948242 * size, 0.7388765811920166 * size, 0.08855071663856506 * size),
+ (0.39401519298553467 * size, 0.7388765811920166 * size, -0.08855047821998596 * size),
+ (-8.416645869147032e-08 * size, 0.8255770206451416 * size, -0.2656517028808594 * size),
+ (-0.06875583529472351 * size, 0.8255770206451416 * size, -0.2565997838973999 * size),
+ (-0.13282597064971924 * size, 0.8255770206451416 * size, -0.2300611138343811 * size),
+ (-0.18784427642822266 * size, 0.8255770206451416 * size, -0.18784409761428833 * size),
+ (-0.2300613522529602 * size, 0.8255770206451416 * size, -0.1328257918357849 * size),
+ (-0.256600022315979 * size, 0.8255770206451416 * size, -0.06875564157962799 * size),
+ (-0.2656519412994385 * size, 0.8255770206451416 * size, 9.328307726264029e-08 * size),
+ (-0.25660014152526855 * size, 0.8255770206451416 * size, 0.06875583529472351 * size),
+ (-0.2300613522529602 * size, 0.8255770206451416 * size, 0.13282597064971924 * size),
+ (-0.18784433603286743 * size, 0.8255770206451416 * size, 0.18784421682357788 * size),
+ (-0.1328260898590088 * size, 0.8255770206451416 * size, 0.23006129264831543 * size),
+ (-0.06875592470169067 * size, 0.8255770206451416 * size, 0.256600022315979 * size),
+ (-1.8761508613351907e-07 * size, 0.8255770206451416 * size, 0.2656519412994385 * size),
+ (0.06875556707382202 * size, 0.8255770206451416 * size, 0.2566000819206238 * size),
+ (0.13282573223114014 * size, 0.8255770206451416 * size, 0.23006141185760498 * size),
+ (0.18784403800964355 * size, 0.8255770206451416 * size, 0.1878443956375122 * size),
+ (0.23006105422973633 * size, 0.8255770206451416 * size, 0.1328260898590088 * size),
+ (0.25659990310668945 * size, 0.8255770206451416 * size, 0.06875596940517426 * size),
+ (0.2656517028808594 * size, 0.8255770206451416 * size, 2.3684407324253698e-07 * size),
+ (0.25659990310668945 * size, 0.8255770206451416 * size, -0.06875550746917725 * size),
+ (0.23006117343902588 * size, 0.8255770206451416 * size, -0.13282567262649536 * size),
+ (0.18784427642822266 * size, 0.8255770206451416 * size, -0.18784397840499878 * size),
+ (0.13282597064971924 * size, 0.8255770206451416 * size, -0.23006099462509155 * size),
+ (0.0687558501958847 * size, 0.8255770206451416 * size, -0.2565997838973999 * size), ]
+ edges = [(1, 0), (3, 2), (5, 2), (4, 3), (6, 4), (1, 5), (0, 6), (13, 7), (12, 8), (7, 9), (9, 10), (8, 11),
+ (27, 14), (26, 15), (14, 16), (16, 17), (15, 18), (17, 18), (10, 11), (12, 13), (20, 19), (22, 21),
+ (24, 21), (23, 22), (29, 28), (30, 29), (31, 30), (32, 31), (33, 32), (34, 33), (35, 34), (36, 35),
+ (37, 36), (38, 37), (39, 38), (40, 39), (41, 40), (42, 41), (43, 42), (44, 43), (45, 44), (46, 45),
+ (47, 46), (48, 47), (49, 48), (50, 49), (51, 50), (28, 51), (26, 27), (25, 23), (20, 24),
+ (19, 25), ]
+
+ verts = [(a[0] * radius, head_tail, a[2] * radius) for a in v]
+ mesh = obj.data
+ mesh.from_pydata(verts, edges, [])
+ mesh.update()
+
+
+def create_neck_tweak_widget(rig, bone_name, size=1.0, bone_transform_name=None):
+ obj = create_widget(rig, bone_name, bone_transform_name)
+
+ if obj != None:
+ verts = [(0.3535533845424652 * size, 0.3535533845424652 * size, 0.0 * size),
+ (0.4619397521018982 * size, 0.19134171307086945 * size, 0.0 * size),
+ (0.5 * size, -2.1855694143368964e-08 * size, 0.0 * size),
+ (0.4619397521018982 * size, -0.19134175777435303 * size, 0.0 * size),
+ (0.3535533845424652 * size, -0.3535533845424652 * size, 0.0 * size),
+ (0.19134174287319183 * size, -0.4619397521018982 * size, 0.0 * size),
+ (7.549790126404332e-08 * size, -0.5 * size, 0.0 * size),
+ (-0.1913416087627411 * size, -0.46193981170654297 * size, 0.0 * size),
+ (-0.35355329513549805 * size, -0.35355350375175476 * size, 0.0 * size),
+ (-0.4619397521018982 * size, -0.19134178757667542 * size, 0.0 * size),
+ (-0.5 * size, 5.962440319251527e-09 * size, 0.0 * size),
+ (-0.4619397222995758 * size, 0.1913418024778366 * size, 0.0 * size),
+ (-0.35355326533317566 * size, 0.35355350375175476 * size, 0.0 * size),
+ (-0.19134148955345154 * size, 0.46193987131118774 * size, 0.0 * size),
+ (3.2584136988589307e-07 * size, 0.5 * size, 0.0 * size),
+ (0.1913420855998993 * size, 0.46193960309028625 * size, 0.0 * size),
+ (7.450580596923828e-08 * size, 0.46193960309028625 * size, 0.19134199619293213 * size),
+ (5.9254205098113744e-08 * size, 0.5 * size, 2.323586443253589e-07 * size),
+ (4.470348358154297e-08 * size, 0.46193987131118774 * size, -0.1913415789604187 * size),
+ (2.9802322387695312e-08 * size, 0.35355350375175476 * size, -0.3535533547401428 * size),
+ (2.9802322387695312e-08 * size, 0.19134178757667542 * size, -0.46193981170654297 * size),
+ (5.960464477539063e-08 * size, -1.1151834122813398e-08 * size, -0.5000000596046448 * size),
+ (5.960464477539063e-08 * size, -0.1913418024778366 * size, -0.46193984150886536 * size),
+ (5.960464477539063e-08 * size, -0.35355350375175476 * size, -0.3535533845424652 * size),
+ (7.450580596923828e-08 * size, -0.46193981170654297 * size, -0.19134166836738586 * size),
+ (9.348272556053416e-08 * size, -0.5 * size, 1.624372103492533e-08 * size),
+ (1.043081283569336e-07 * size, -0.4619397521018982 * size, 0.19134168326854706 * size),
+ (1.1920928955078125e-07 * size, -0.3535533845424652 * size, 0.35355329513549805 * size),
+ (1.1920928955078125e-07 * size, -0.19134174287319183 * size, 0.46193966269493103 * size),
+ (1.1920928955078125e-07 * size, -4.7414250303745575e-09 * size, 0.49999991059303284 * size),
+ (1.1920928955078125e-07 * size, 0.19134172797203064 * size, 0.46193966269493103 * size),
+ (8.940696716308594e-08 * size, 0.3535533845424652 * size, 0.35355329513549805 * size),
+ (0.3535534739494324 * size, 0.0 * size, 0.35355329513549805 * size),
+ (0.1913418173789978 * size, -2.9802322387695312e-08 * size, 0.46193966269493103 * size),
+ (8.303572940349113e-08 * size, -5.005858838558197e-08 * size, 0.49999991059303284 * size),
+ (-0.19134165346622467 * size, -5.960464477539063e-08 * size, 0.46193966269493103 * size),
+ (-0.35355329513549805 * size, -8.940696716308594e-08 * size, 0.35355329513549805 * size),
+ (-0.46193963289260864 * size, -5.960464477539063e-08 * size, 0.19134168326854706 * size),
+ (-0.49999991059303284 * size, -5.960464477539063e-08 * size, 1.624372103492533e-08 * size),
+ (-0.4619397521018982 * size, -2.9802322387695312e-08 * size, -0.19134166836738586 * size),
+ (-0.3535534143447876 * size, -2.9802322387695312e-08 * size, -0.3535533845424652 * size),
+ (-0.19134171307086945 * size, 0.0 * size, -0.46193984150886536 * size),
+ (7.662531942287387e-08 * size, 9.546055501630235e-09 * size, -0.5000000596046448 * size),
+ (0.19134187698364258 * size, 5.960464477539063e-08 * size, -0.46193981170654297 * size),
+ (0.3535535931587219 * size, 5.960464477539063e-08 * size, -0.3535533547401428 * size),
+ (0.4619399905204773 * size, 5.960464477539063e-08 * size, -0.1913415789604187 * size),
+ (0.5000000596046448 * size, 5.960464477539063e-08 * size, 2.323586443253589e-07 * size),
+ (0.4619396924972534 * size, 2.9802322387695312e-08 * size, 0.19134199619293213 * size),
+ (1.563460111618042 * size, 2.778762819843905e-08 * size, 1.5634593963623047 * size),
+ (0.8461387157440186 * size, -1.0400220418205208e-07 * size, 2.0427582263946533 * size),
+ (7.321979467178608e-08 * size, -1.9357810288056498e-07 * size, 2.2110657691955566 * size),
+ (-0.8461385369300842 * size, -2.3579201524626114e-07 * size, 2.0427582263946533 * size),
+ (-1.5634597539901733 * size, -3.67581861837607e-07 * size, 1.5634593963623047 * size),
+ (-2.0427584648132324 * size, -2.3579204366797057e-07 * size, 0.8461383581161499 * size),
+ (-2.211066246032715 * size, -2.3579204366797057e-07 * size, 9.972505665700737e-08 * size),
+ (-2.0427589416503906 * size, -1.0400223260376151e-07 * size, -0.8461381196975708 * size),
+ (-1.5634604692459106 * size, -1.040022183929068e-07 * size, -1.563459873199463 * size),
+ (-0.8461387753486633 * size, 2.77876033294433e-08 * size, -2.042759418487549 * size),
+ (4.4872678017782164e-08 * size, 7.00015263532805e-08 * size, -2.211066484451294 * size),
+ (0.8461388349533081 * size, 2.913672290105751e-07 * size, -2.0427591800689697 * size),
+ (1.5634608268737793 * size, 2.9136725743228453e-07 * size, -1.563459873199463 * size),
+ (2.042759895324707 * size, 2.9136725743228453e-07 * size, -0.8461377024650574 * size),
+ (2.211066722869873 * size, 2.9136725743228453e-07 * size, 1.0554133496043505e-06 * size),
+ (2.0427587032318115 * size, 1.5957746768435754e-07 * size, 0.8461397886276245 * size), ]
+ edges = [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7), (7, 8), (8, 9), (9, 10), (10, 11),
+ (11, 12), (12, 13), (13, 14), (14, 15), (0, 15), (16, 31), (16, 17), (17, 18), (18, 19), (19, 20),
+ (20, 21), (21, 22), (22, 23), (23, 24), (24, 25), (25, 26), (26, 27), (27, 28), (28, 29), (29, 30),
+ (30, 31), (32, 33), (33, 34), (34, 35), (35, 36), (36, 37), (37, 38), (38, 39), (39, 40), (40, 41),
+ (41, 42), (42, 43), (43, 44), (44, 45), (45, 46), (46, 47), (32, 47), (48, 49), (49, 50), (50, 51),
+ (51, 52), (52, 53), (53, 54), (54, 55), (55, 56), (56, 57), (57, 58), (58, 59), (59, 60), (60, 61),
+ (61, 62), (62, 63), (48, 63), (21, 58), (10, 54), (29, 50), (2, 62), ]
+
+ mesh = obj.data
+ mesh.from_pydata(verts, edges, [])
+ mesh.update()