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
diff options
context:
space:
mode:
authorAlexander Gavrilov <angavrilov@gmail.com>2019-03-30 22:00:55 +0300
committerAlexander Gavrilov <angavrilov@gmail.com>2019-09-14 09:29:26 +0300
commit3423174b37a0784dc12035ff3f2fb536835099e1 (patch)
tree3a54580902cdebdef5ebacd6099e86cc79ba75b3 /rigify/rigs
parent12af8a28c14b608e9b9b08568d981273c86590c1 (diff)
Rigify: redesign generate.py and introduce a base rig class.
The main goals are to provide an official way for rigs to interact in a structured way, and to remove mode switching within rigs. This involves introducing a base class for rigs that holds rig-to-rig and rig-to-bone references, converting the main generator into a class and passing it to rigs, and splitting the single generate method into multiple passes. For backward compatibility, old rigs are automatically handled via a wrapper that translates between old and new API. In addition, a way to create objects that receive the generate callbacks that aren't rigs is introduced via the GeneratorPlugin class. The UI script generation code is converted into a plugin. Making generic rig 'template' classes that are intended to be subclassed in specific rigs involves splitting operations done in each stage into multiple methods that can be overridden separately. The main callback thus ends up simply calling a sequence of other methods. To make such code cleaner it's better to allow registering those methods as new callbacks that would be automatically called by the system. This can be done via decorators. A new metaclass used for all rig and generate plugin classes builds and validates a table of all decorated methods, and allows calling them all together with the main callback. A new way to switch parents for IK bones based on the new features is introduced, and used in the existing limb rigs. Reviewers: icappiello campbellbarton Differential Revision: https://developer.blender.org/D4624
Diffstat (limited to 'rigify/rigs')
-rw-r--r--rigify/rigs/basic/copy_chain.py176
-rw-r--r--rigify/rigs/basic/super_copy.py151
-rw-r--r--rigify/rigs/chain_rigs.py387
-rw-r--r--rigify/rigs/experimental/super_chain.py2
-rw-r--r--rigify/rigs/faces/super_face.py2
-rw-r--r--rigify/rigs/limbs/arm.py122
-rw-r--r--rigify/rigs/limbs/leg.py130
-rw-r--r--rigify/rigs/limbs/super_limb.py2
-rw-r--r--rigify/rigs/spines/super_spine.py10
9 files changed, 598 insertions, 384 deletions
diff --git a/rigify/rigs/basic/copy_chain.py b/rigify/rigs/basic/copy_chain.py
index 4e426284..5145d735 100644
--- a/rigify/rigs/basic/copy_chain.py
+++ b/rigify/rigs/basic/copy_chain.py
@@ -20,124 +20,96 @@
import bpy
-from ...utils import MetarigError
-from ...utils import copy_bone
-from ...utils import connected_children_names
-from ...utils import strip_org, make_deformer_name
-from ...utils import create_bone_widget
+from ..chain_rigs import SimpleChainRig
+from ...utils.errors import MetarigError
+from ...utils.rig import connected_children_names
+from ...utils.naming import strip_org, make_deformer_name
+from ...utils.widgets_basic import create_bone_widget
-class Rig:
+from ...base_rig import BaseRig, stage
+
+
+class Rig(SimpleChainRig):
""" A "copy_chain" rig. All it does is duplicate the original bone chain
and constrain it.
This is a control and deformation rig.
-
"""
- def __init__(self, obj, bone_name, params):
+ def initialize(self):
+ super().initialize()
+
""" Gather and validate data about the rig.
"""
- self.obj = obj
- self.org_bones = [bone_name] + connected_children_names(obj, bone_name)
- self.params = params
- self.make_controls = params.make_controls
- self.make_deforms = params.make_deforms
+ self.make_controls = self.params.make_controls
+ self.make_deforms = self.params.make_deforms
- if len(self.org_bones) <= 1:
- raise MetarigError("RIGIFY ERROR: Bone '%s': input to rig type must be a chain of 2 or more bones" % (strip_org(bone_name)))
+ ##############################
+ # Control chain
- def generate(self):
- """ Generate the rig.
- Do NOT modify any of the original bones, except for adding constraints.
- The main armature should be selected and active before this is called.
+ @stage.generate_bones
+ def make_control_chain(self):
+ if self.make_controls:
+ super().make_control_chain()
- """
- bpy.ops.object.mode_set(mode='EDIT')
-
- # Create the deformation and control bone chains.
- # Just copies of the original chain.
- def_chain = []
- ctrl_chain = []
- for i in range(len(self.org_bones)):
- name = self.org_bones[i]
-
- # Control bone
- if self.make_controls:
- # Copy
- ctrl_bone = copy_bone(self.obj, name)
- eb = self.obj.data.edit_bones
- ctrl_bone_e = eb[ctrl_bone]
- # Name
- ctrl_bone_e.name = strip_org(name)
- # Parenting
- if i == 0:
- # First bone
- ctrl_bone_e.parent = eb[self.org_bones[0]].parent
- else:
- # The rest
- ctrl_bone_e.parent = eb[ctrl_chain[-1]]
- # Add to list
- ctrl_chain += [ctrl_bone_e.name]
- else:
- ctrl_chain += [None]
-
- # Deformation bone
- if self.make_deforms:
- # Copy
- def_bone = copy_bone(self.obj, name)
- eb = self.obj.data.edit_bones
- def_bone_e = eb[def_bone]
- # Name
- def_bone_e.name = make_deformer_name(strip_org(name))
- # Parenting
- if i == 0:
- # First bone
- def_bone_e.parent = eb[self.org_bones[0]].parent
- else:
- # The rest
- def_bone_e.parent = eb[def_chain[-1]]
- # Add to list
- def_chain += [def_bone_e.name]
- else:
- def_chain += [None]
-
- bpy.ops.object.mode_set(mode='OBJECT')
- pb = self.obj.pose.bones
-
- # Constraints for org and def
- for org, ctrl, defrm in zip(self.org_bones, ctrl_chain, def_chain):
- if self.make_controls:
- con = pb[org].constraints.new('COPY_TRANSFORMS')
- con.name = "copy_transforms"
- con.target = self.obj
- con.subtarget = ctrl
-
- if self.make_deforms:
- con = pb[defrm].constraints.new('COPY_TRANSFORMS')
- con.name = "copy_transforms"
- con.target = self.obj
- con.subtarget = org
-
- # Create control widgets
+ @stage.parent_bones
+ def parent_control_chain(self):
if self.make_controls:
- for bone in ctrl_chain:
- create_bone_widget(self.obj, bone)
+ super().parent_control_chain()
+ @stage.configure_bones
+ def configure_control_chain(self):
+ if self.make_controls:
+ super().configure_control_chain()
-def add_parameters(params):
- """ Add the parameters of this rig type to the
- RigifyParameters PropertyGroup
- """
- params.make_controls = bpy.props.BoolProperty(name="Controls", default=True, description="Create control bones for the copy")
- params.make_deforms = bpy.props.BoolProperty(name="Deform", default=True, description="Create deform bones for the copy")
+ @stage.generate_widgets
+ def make_control_widgets(self):
+ if self.make_controls:
+ super().make_control_widgets()
+ ##############################
+ # ORG chain
-def parameters_ui(layout, params):
- """ Create the ui for the rig parameters.
- """
- r = layout.row()
- r.prop(params, "make_controls")
- r = layout.row()
- r.prop(params, "make_deforms")
+ @stage.rig_bones
+ def rig_org_chain(self):
+ if self.make_controls:
+ super().rig_org_chain()
+
+ ##############################
+ # Deform chain
+
+ @stage.generate_bones
+ def make_deform_chain(self):
+ if self.make_deforms:
+ super().make_deform_chain()
+
+ @stage.parent_bones
+ def parent_deform_chain(self):
+ if self.make_deforms:
+ super().parent_deform_chain()
+
+ @stage.rig_bones
+ def rig_deform_chain(self):
+ if self.make_deforms:
+ super().rig_deform_chain()
+
+
+ @classmethod
+ def add_parameters(self, params):
+ """ Add the parameters of this rig type to the
+ RigifyParameters PropertyGroup
+ """
+ params.make_controls = bpy.props.BoolProperty(name="Controls", default=True, description="Create control bones for the copy")
+ params.make_deforms = bpy.props.BoolProperty(name="Deform", default=True, description="Create deform bones for the copy")
+
+
+ @classmethod
+ def parameters_ui(self, layout, params):
+ """ Create the ui for the rig parameters.
+ """
+ r = layout.row()
+ r.prop(params, "make_controls")
+ r = layout.row()
+ r.prop(params, "make_deforms")
def create_sample(obj):
diff --git a/rigify/rigs/basic/super_copy.py b/rigify/rigs/basic/super_copy.py
index b2045346..5abbf22e 100644
--- a/rigify/rigs/basic/super_copy.py
+++ b/rigify/rigs/basic/super_copy.py
@@ -20,107 +20,112 @@
import bpy
-from ...utils import copy_bone
-from ...utils import strip_org, make_deformer_name
-from ...utils import create_bone_widget, create_circle_widget
+from ...base_rig import BaseRig
+from ...utils.naming import strip_org, make_deformer_name
+from ...utils.widgets_basic import create_bone_widget, create_circle_widget
-class Rig:
+
+class Rig(BaseRig):
""" A "copy" rig. All it does is duplicate the original bone and
constrain it.
This is a control and deformation rig.
"""
- def __init__(self, obj, bone, params):
+ def find_org_bones(self, pose_bone):
+ return pose_bone.name
+
+
+ def initialize(self):
""" Gather and validate data about the rig.
"""
- self.obj = obj
- self.org_bone = bone
- self.org_name = strip_org(bone)
- self.params = params
- self.make_control = params.make_control
- self.make_widget = params.make_widget
- self.make_deform = params.make_deform
-
- def generate(self):
- """ Generate the rig.
- Do NOT modify any of the original bones, except for adding constraints.
- The main armature should be selected and active before this is called.
+ self.org_name = strip_org(self.bones.org)
- """
- bpy.ops.object.mode_set(mode='EDIT')
+ self.make_control = self.params.make_control
+ self.make_widget = self.params.make_widget
+ self.make_deform = self.params.make_deform
+
+
+ def generate_bones(self):
+ bones = self.bones
# Make a control bone (copy of original).
if self.make_control:
- bone = copy_bone(self.obj, self.org_bone, self.org_name)
+ bones.ctrl = self.copy_bone(bones.org, self.org_name, parent=True)
# Make a deformation bone (copy of original, child of original).
if self.make_deform:
- def_bone = copy_bone(self.obj, self.org_bone, make_deformer_name(self.org_name))
+ bones.deform = self.copy_bone(bones.org, make_deformer_name(self.org_name), bbone=True)
- # Get edit bones
- eb = self.obj.data.edit_bones
- # UNUSED
- # if self.make_control:
- # bone_e = eb[bone]
- if self.make_deform:
- def_bone_e = eb[def_bone]
- # Parent
+ def parent_bones(self):
+ bones = self.bones
+
if self.make_deform:
- def_bone_e.use_connect = False
- def_bone_e.parent = eb[self.org_bone]
+ self.set_bone_parent(bones.deform, bones.org, use_connect=False)
+
- bpy.ops.object.mode_set(mode='OBJECT')
- pb = self.obj.pose.bones
+ def configure_bones(self):
+ bones = self.bones
+
+ if self.make_control:
+ self.copy_bone_properties(bones.org, bones.ctrl)
+
+
+ def rig_bones(self):
+ bones = self.bones
if self.make_control:
# Constrain the original bone.
- con = pb[self.org_bone].constraints.new('COPY_TRANSFORMS')
- con.name = "copy_transforms"
- con.target = self.obj
- con.subtarget = bone
+ self.make_constraint(bones.org, 'COPY_TRANSFORMS', bones.ctrl)
+
+ def generate_widgets(self):
+ bones = self.bones
+
+ if self.make_control:
# Create control widget
if self.make_widget:
- create_circle_widget(self.obj, bone, radius=0.5)
+ create_circle_widget(self.obj, bones.ctrl, radius=0.5)
else:
- create_bone_widget(self.obj, bone)
+ create_bone_widget(self.obj, bones.ctrl)
-def add_parameters(params):
- """ Add the parameters of this rig type to the
- RigifyParameters PropertyGroup
- """
- params.make_control = bpy.props.BoolProperty(
- name = "Control",
- default = True,
- description = "Create a control bone for the copy"
- )
-
- params.make_widget = bpy.props.BoolProperty(
- name = "Widget",
- default = True,
- description = "Choose a widget for the bone control"
- )
-
- params.make_deform = bpy.props.BoolProperty(
- name = "Deform",
- default = True,
- description = "Create a deform bone for the copy"
- )
-
-
-def parameters_ui(layout, params):
- """ Create the ui for the rig parameters.
- """
- r = layout.row()
- r.prop(params, "make_control")
- r = layout.row()
- r.prop(params, "make_widget")
- r.enabled = params.make_control
- r = layout.row()
- r.prop(params, "make_deform")
+ @classmethod
+ def add_parameters(self, params):
+ """ Add the parameters of this rig type to the
+ RigifyParameters PropertyGroup
+ """
+ params.make_control = bpy.props.BoolProperty(
+ name = "Control",
+ default = True,
+ description = "Create a control bone for the copy"
+ )
+
+ params.make_widget = bpy.props.BoolProperty(
+ name = "Widget",
+ default = True,
+ description = "Choose a widget for the bone control"
+ )
+
+ params.make_deform = bpy.props.BoolProperty(
+ name = "Deform",
+ default = True,
+ description = "Create a deform bone for the copy"
+ )
+
+
+ @classmethod
+ def parameters_ui(self, layout, params):
+ """ Create the ui for the rig parameters.
+ """
+ r = layout.row()
+ r.prop(params, "make_control")
+ r = layout.row()
+ r.prop(params, "make_widget")
+ r.enabled = params.make_control
+ r = layout.row()
+ r.prop(params, "make_deform")
def create_sample(obj):
@@ -159,3 +164,5 @@ def create_sample(obj):
bone.select_head = True
bone.select_tail = True
arm.edit_bones.active = bone
+
+ return bones
diff --git a/rigify/rigs/chain_rigs.py b/rigify/rigs/chain_rigs.py
new file mode 100644
index 00000000..3f53cd69
--- /dev/null
+++ b/rigify/rigs/chain_rigs.py
@@ -0,0 +1,387 @@
+#====================== 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 itertools import count
+
+from ..utils.rig import connected_children_names
+from ..utils.naming import strip_org, make_derived_name
+from ..utils.bones import put_bone, flip_bone, flip_bone_chain, is_same_position, is_connected_position
+from ..utils.bones import copy_bone_position, connect_bbone_chain_handles
+from ..utils.widgets_basic import create_bone_widget, create_sphere_widget
+from ..utils.misc import map_list
+
+from ..base_rig import BaseRig, stage
+
+
+class SimpleChainRig(BaseRig):
+ """A rig that consists of 3 connected chains of control, org and deform bones."""
+ def find_org_bones(self, bone):
+ return [bone.name] + connected_children_names(self.obj, bone.name)
+
+ def initialize(self):
+ if len(self.bones.org) <= 1:
+ self.raise_error("Input to rig type must be a chain of 2 or more bones.")
+
+ def parent_bones(self):
+ self.rig_parent_bone = self.get_bone_parent(self.bones.org[0])
+
+ bbone_segments = None
+
+ ##############################
+ # BONES
+ #
+ # org[]:
+ # ORG bones
+ # ctrl:
+ # fk[]:
+ # FK control chain.
+ # deform[]:
+ # DEF bones
+ #
+ ##############################
+
+ ##############################
+ # Control chain
+
+ @stage.generate_bones
+ def make_control_chain(self):
+ self.bones.ctrl.fk = map_list(self.make_control_bone, count(0), self.bones.org)
+
+ def make_control_bone(self, i, org):
+ return self.copy_bone(org, make_derived_name(org, 'ctrl'), parent=True)
+
+ @stage.parent_bones
+ def parent_control_chain(self):
+ self.parent_bone_chain(self.bones.ctrl.fk, use_connect=True)
+
+ @stage.configure_bones
+ def configure_control_chain(self):
+ for args in zip(count(0), self.bones.ctrl.fk, self.bones.org):
+ self.configure_control_bone(*args)
+
+ def configure_control_bone(self, i, ctrl, org):
+ self.copy_bone_properties(org, ctrl)
+
+ @stage.generate_widgets
+ def make_control_widgets(self):
+ for ctrl in self.bones.ctrl.fk:
+ self.make_control_widget(ctrl)
+
+ def make_control_widget(self, ctrl):
+ create_bone_widget(self.obj, ctrl)
+
+ ##############################
+ # ORG chain
+
+ @stage.parent_bones
+ def parent_org_chain(self):
+ pass
+
+ @stage.rig_bones
+ def rig_org_chain(self):
+ for args in zip(count(0), self.bones.org, self.bones.ctrl.fk):
+ self.rig_org_bone(*args)
+
+ def rig_org_bone(self, i, org, ctrl):
+ self.make_constraint(org, 'COPY_TRANSFORMS', ctrl)
+
+ ##############################
+ # Deform chain
+
+ @stage.generate_bones
+ def make_deform_chain(self):
+ self.bones.deform = map_list(self.make_deform_bone, count(0), self.bones.org)
+
+ def make_deform_bone(self, i, org):
+ name = self.copy_bone(org, make_derived_name(org, 'def'), parent=True, bbone=True)
+ if self.bbone_segments:
+ self.get_bone(name).bbone_segments = self.bbone_segments
+ return name
+
+ @stage.parent_bones
+ def parent_deform_chain(self):
+ self.parent_bone_chain(self.bones.deform, use_connect=True)
+
+ @stage.rig_bones
+ def rig_deform_chain(self):
+ for args in zip(count(0), self.bones.deform, self.bones.org):
+ self.rig_deform_bone(*args)
+
+ def rig_deform_bone(self, i, deform, org):
+ self.make_constraint(deform, 'COPY_TRANSFORMS', org)
+
+
+class TweakChainRig(SimpleChainRig):
+ """A rig that adds tweak controls to the triple chain."""
+
+ ##############################
+ # BONES
+ #
+ # org[]:
+ # ORG bones
+ # ctrl:
+ # fk[]:
+ # FK control chain.
+ # tweak[]:
+ # Tweak control chain.
+ # deform[]:
+ # DEF bones
+ #
+ ##############################
+
+ ##############################
+ # Tweak chain
+
+ @stage.generate_bones
+ def make_tweak_chain(self):
+ orgs = self.bones.org
+ self.bones.ctrl.tweak = map_list(self.make_tweak_bone, count(0), orgs + orgs[-1:])
+
+ def make_tweak_bone(self, i, org):
+ name = self.copy_bone(org, 'tweak_' + strip_org(org), parent=False, scale=0.5)
+
+ if i == len(self.bones.org):
+ put_bone(self.obj, name, self.get_bone(org).tail)
+
+ return name
+
+ @stage.parent_bones
+ def parent_tweak_chain(self):
+ ctrl = self.bones.ctrl
+ for tweak, main in zip(ctrl.tweak, ctrl.fk + ctrl.fk[-1:]):
+ self.set_bone_parent(tweak, main)
+
+ @stage.configure_bones
+ def configure_tweak_chain(self):
+ for args in zip(count(0), self.bones.ctrl.tweak):
+ self.configure_tweak_bone(*args)
+
+ def configure_tweak_bone(self, i, tweak):
+ tweak_pb = self.get_bone(tweak)
+ tweak_pb.rotation_mode = 'ZXY'
+
+ if i == len(self.bones.org):
+ tweak_pb.lock_rotation_w = True
+ tweak_pb.lock_rotation = (True, True, True)
+ tweak_pb.lock_scale = (True, True, True)
+ else:
+ tweak_pb.lock_rotation_w = False
+ tweak_pb.lock_rotation = (True, False, True)
+ tweak_pb.lock_scale = (False, True, False)
+
+ @stage.generate_widgets
+ def make_tweak_widgets(self):
+ for tweak in self.bones.ctrl.tweak:
+ self.make_tweak_widget(tweak)
+
+ def make_tweak_widget(self, tweak):
+ create_sphere_widget(self.obj, tweak)
+
+ ##############################
+ # ORG chain
+
+ @stage.rig_bones
+ def rig_org_chain(self):
+ tweaks = self.bones.ctrl.tweak
+ for args in zip(count(0), self.bones.org, tweaks, tweaks[1:]):
+ self.rig_org_bone(*args)
+
+ def rig_org_bone(self, i, org, tweak, next_tweak):
+ self.make_constraint(org, 'COPY_TRANSFORMS', tweak)
+ if next_tweak:
+ self.make_constraint(org, 'DAMPED_TRACK', next_tweak)
+ self.make_constraint(org, 'STRETCH_TO', next_tweak)
+
+
+class ConnectingChainRig(TweakChainRig):
+ """Chain rig that can attach to an end of the parent, merging bbone chains."""
+
+ bbone_segments = 8
+ use_connect_reverse = None
+
+ def initialize(self):
+ super().initialize()
+
+ self.use_connect_chain = self.params.connect_chain
+ self.connected_tweak = None
+
+ if self.use_connect_chain:
+ first_org = self.bones.org[0]
+ parent = self.rigify_parent
+ parent_orgs = parent.bones.org
+
+ if not isinstance(parent, SimpleChainRig):
+ self.raise_error("Cannot connect to non-chain parent rig.")
+
+ ok_reverse = is_same_position(self.obj, parent_orgs[0], first_org)
+ ok_direct = is_connected_position(self.obj, parent_orgs[-1], first_org)
+
+ if self.use_connect_reverse is None:
+ self.use_connect_reverse = ok_reverse and not ok_direct
+
+ if not (ok_reverse if self.use_connect_reverse else ok_direct):
+ self.raise_error("Cannot connect chain - bone position is disjoint.")
+
+ if isinstance(parent, ConnectingChainRig) and parent.use_connect_reverse:
+ self.raise_error("Cannot connect chain - parent is reversed.")
+
+ def prepare_bones(self):
+ # Exactly match bone position to parent
+ if self.use_connect_chain:
+ first_bone = self.get_bone(self.bones.org[0])
+ parent_orgs = self.rigify_parent.bones.org
+
+ if self.use_connect_reverse:
+ first_bone.head = self.get_bone(parent_orgs[0]).head
+ else:
+ first_bone.head = self.get_bone(parent_orgs[-1]).tail
+
+ def parent_bones(self):
+ # Use the parent of the shared tweak as the rig parent
+ root = self.connected_tweak or self.bones.org[0]
+
+ self.rig_parent_bone = self.get_bone_parent(root)
+
+ ##############################
+ # Control chain
+
+ @stage.parent_bones
+ def parent_control_chain(self):
+ super().parent_control_chain()
+
+ self.set_bone_parent(self.bones.ctrl.fk[0], self.rig_parent_bone)
+
+ ##############################
+ # Tweak chain
+
+ def check_connect_tweak(self, org):
+ """ Check if it is possible to share the last parent tweak control. """
+
+ assert self.connected_tweak is None
+
+ if self.use_connect_chain and isinstance(self.rigify_parent, TweakChainRig):
+ # Share the last tweak bone of the parent rig
+ parent_tweaks = self.rigify_parent.bones.ctrl.tweak
+ index = 0 if self.use_connect_reverse else -1
+ name = parent_tweaks[index]
+
+ if not is_same_position(self.obj, name, org):
+ self.raise_error("Cannot connect tweaks - position mismatch.")
+
+ if not self.use_connect_reverse:
+ copy_bone_position(self.obj, org, name, scale=0.5)
+
+ name = self.rename_bone(name, 'tweak_' + strip_org(org))
+
+ self.connected_tweak = parent_tweaks[index] = name
+
+ return name
+ else:
+ return None
+
+ def make_tweak_bone(self, i, org):
+ if i == 0 and self.check_connect_tweak(org):
+ return self.connected_tweak
+ else:
+ return super().make_tweak_bone(i, org)
+
+ @stage.parent_bones
+ def parent_tweak_chain(self):
+ ctrl = self.bones.ctrl
+ for i, tweak, main in zip(count(0), ctrl.tweak, ctrl.fk + ctrl.fk[-1:]):
+ if i > 0 or not (self.connected_tweak and self.use_connect_reverse):
+ self.set_bone_parent(tweak, main)
+
+ def configure_tweak_bone(self, i, tweak):
+ super().configure_tweak_bone(i, tweak)
+
+ if self.use_connect_chain and self.use_connect_reverse and i == len(self.bones.org):
+ tweak_pb = self.get_bone(tweak)
+ tweak_pb.lock_rotation_w = False
+ tweak_pb.lock_rotation = (True, False, True)
+ tweak_pb.lock_scale = (False, True, False)
+
+ ##############################
+ # ORG chain
+
+ @stage.parent_bones
+ def parent_org_chain(self):
+ if self.use_connect_chain and self.use_connect_reverse:
+ flip_bone_chain(self.obj, self.bones.org)
+
+ for org, tweak in zip(self.bones.org, self.bones.ctrl.tweak[1:]):
+ self.set_bone_parent(org, tweak)
+
+ else:
+ self.set_bone_parent(self.bones.org[0], self.rig_parent_bone)
+
+ def rig_org_bone(self, i, org, tweak, next_tweak):
+ if self.use_connect_chain and self.use_connect_reverse:
+ self.make_constraint(org, 'DAMPED_TRACK', tweak)
+ self.make_constraint(org, 'STRETCH_TO', tweak)
+ else:
+ super().rig_org_bone(i, org, tweak, next_tweak)
+
+ ##############################
+ # Deform chain
+
+ def make_deform_bone(self, i, org):
+ name = super().make_deform_bone(i, org)
+
+ if self.use_connect_chain and self.use_connect_reverse:
+ self.set_bone_parent(name, None)
+ flip_bone(self.obj, name)
+
+ return name
+
+ @stage.parent_bones
+ def parent_deform_chain(self):
+ if self.use_connect_chain:
+ deform = self.bones.deform
+ parent_deform = self.rigify_parent.bones.deform
+
+ if self.use_connect_reverse:
+ self.set_bone_parent(deform[-1], self.bones.org[-1])
+ self.parent_bone_chain(reversed(deform), use_connect=True)
+
+ connect_bbone_chain_handles(self.obj, [ deform[0], parent_deform[0] ])
+ return
+
+ else:
+ self.set_bone_parent(deform[0], parent_deform[-1], use_connect=True)
+
+ super().parent_deform_chain()
+
+ ##############################
+ # Settings
+
+ @classmethod
+ def add_parameters(self, params):
+ params.connect_chain = bpy.props.BoolProperty(
+ name='Connect chain',
+ default=False,
+ description='Connect the B-Bone chain to the parent rig'
+ )
+
+ @classmethod
+ def parameters_ui(self, layout, params):
+ r = layout.row()
+ r.prop(params, "connect_chain")
diff --git a/rigify/rigs/experimental/super_chain.py b/rigify/rigs/experimental/super_chain.py
index f3d0e182..592441fe 100644
--- a/rigify/rigs/experimental/super_chain.py
+++ b/rigify/rigs/experimental/super_chain.py
@@ -14,7 +14,7 @@ class Rig:
def __init__(self, obj, bone_name, params):
""" Chain with pivot Rig """
- eb = obj.data.edit_bones
+ eb = obj.data.bones
self.obj = obj
self.org_bones = [bone_name] + connected_children_names(obj, bone_name)
diff --git a/rigify/rigs/faces/super_face.py b/rigify/rigs/faces/super_face.py
index 7e36ce68..4eda647d 100644
--- a/rigify/rigs/faces/super_face.py
+++ b/rigify/rigs/faces/super_face.py
@@ -44,7 +44,7 @@ class Rig:
grand_children += connected_children_names( self.obj, child )
self.org_bones = [bone_name] + children + grand_children
- self.face_length = obj.data.edit_bones[ self.org_bones[0] ].length
+ self.face_length = obj.data.bones[ self.org_bones[0] ].length
self.params = params
if params.primary_layers_extra:
diff --git a/rigify/rigs/limbs/arm.py b/rigify/rigs/limbs/arm.py
index 3b2f3658..aacc1e86 100644
--- a/rigify/rigs/limbs/arm.py
+++ b/rigify/rigs/limbs/arm.py
@@ -15,17 +15,8 @@ from ...utils.mechanism import make_property, make_driver
from ..widgets import create_ikarrow_widget
from math import trunc, pi
-extra_script = """
-controls = [%s]
-ctrl = '%s'
-
-if is_selected( controls ):
- layout.prop( pose_bones[ ctrl ], '["%s"]')
- if '%s' in pose_bones[ctrl].keys():
- layout.prop( pose_bones[ ctrl ], '["%s"]', slider = True )
- if '%s' in pose_bones[ctrl].keys():
- layout.prop( pose_bones[ ctrl ], '["%s"]', slider = True )
-"""
+from ...utils.switch_parent import SwitchParentBuilder
+
IMPLEMENTATION = True # Include and set True if Rig is just an implementation for a wrapper class
# add_parameters and parameters_ui are unused for implementation classes
@@ -561,35 +552,6 @@ class Rig:
eb[ bones['ik']['mch_target'] ].parent = eb[ ctrl ]
eb[ bones['ik']['mch_target'] ].use_connect = False
- # MCH for ik control
- ctrl_socket = copy_bone(self.obj, org_bones[2], get_bone_name( org_bones[2], 'mch', 'ik_socket'))
- eb[ctrl_socket].tail = eb[ctrl_socket].head + 0.8*(eb[ctrl_socket].tail-eb[ctrl_socket].head)
- eb[ctrl_socket].parent = None
- eb[ctrl].parent = eb[ctrl_socket]
-
- # MCH for pole ik control
- ctrl_pole_socket = copy_bone(self.obj, org_bones[2], get_bone_name(org_bones[2], 'mch', 'pole_ik_socket'))
- eb[ctrl_pole_socket].tail = eb[ctrl_pole_socket].head + 0.8 * (eb[ctrl_pole_socket].tail - eb[ctrl_pole_socket].head)
- eb[ctrl_pole_socket].parent = None
- eb[pole_target].parent = eb[ctrl_pole_socket]
-
- ctrl_root = copy_bone(self.obj, org_bones[2], get_bone_name( org_bones[2], 'mch', 'ik_root'))
- eb[ctrl_root].tail = eb[ctrl_root].head + 0.7*(eb[ctrl_root].tail-eb[ctrl_root].head)
- eb[ctrl_root].use_connect = False
- eb[ctrl_root].parent = eb['root']
-
- if eb[org_bones[0]].parent:
- arm_parent = eb[org_bones[0]].parent
- ctrl_parent = copy_bone(self.obj, org_bones[2], get_bone_name( org_bones[2], 'mch', 'ik_parent'))
- eb[ctrl_parent].tail = eb[ctrl_parent].head + 0.6*(eb[ctrl_parent].tail-eb[ctrl_parent].head)
- eb[ctrl_parent].use_connect = False
- if eb[org_bones[0]].parent_recursive:
- eb[ctrl_parent].parent = eb[org_bones[0]].parent_recursive[-1]
- else:
- eb[ctrl_parent].parent = eb[org_bones[0]].parent
- else:
- arm_parent = None
-
mch_name = get_bone_name(strip_org(org_bones[0]), 'mch', 'parent_socket')
mch_main_parent = copy_bone(self.obj, org_bones[0], mch_name)
eb[mch_main_parent].length = eb[org_bones[0]].length / 12
@@ -597,30 +559,28 @@ class Rig:
eb[mch_main_parent].roll = 0.0
eb[bones['main_parent']].parent = eb[mch_main_parent]
- # Set up constraints
+ # Switchable parent
+ pbuilder = SwitchParentBuilder(self.rigify_generator)
- # Constrain ik ctrl to root / parent
+ if eb[org_bones[0]].parent:
+ pbuilder.register_parent(self.rigify_wrapper, eb[org_bones[0]].parent.name)
- make_constraint( self, ctrl_socket, {
- 'constraint' : 'COPY_TRANSFORMS',
- 'subtarget' : ctrl_root,
- })
+ pbuilder.register_parent(self.rigify_wrapper, org_bones[2], exclude_self=True)
- make_constraint(self, ctrl_pole_socket, {
- 'constraint': 'COPY_TRANSFORMS',
- 'subtarget': ctrl_root,
- })
+ pcontrols = [ bones['main_parent'], bones['ik']['ctrl']['limb'], ctrl, pole_target ]
- if arm_parent:
- make_constraint( self, ctrl_socket, {
- 'constraint' : 'COPY_TRANSFORMS',
- 'subtarget' : ctrl_parent,
- })
+ pbuilder.build_child(
+ self.rigify_wrapper, ctrl,
+ prop_bone=bones['main_parent'], prop_id='IK_parent', prop_name='IK Parent', controls=pcontrols,
+ )
- make_constraint(self, ctrl_pole_socket, {
- 'constraint': 'COPY_TRANSFORMS',
- 'subtarget': ctrl_parent,
- })
+ pbuilder.build_child(
+ self.rigify_wrapper, pole_target, extra_parents=[ctrl],
+ prop_bone=bones['main_parent'], prop_id='pole_parent', prop_name='Pole Parent', controls=pcontrols,
+ no_fix_rotation=True, no_fix_scale=True
+ )
+
+ # Set up constraints
# Constrain mch target bone to the ik control and mch stretch
make_constraint( self, bones['ik']['mch_target'], {
@@ -675,10 +635,6 @@ class Rig:
create_hand_widget(self.obj, ctrl, bone_transform_name=None)
bones['ik']['ctrl']['terminal'] = [ctrl]
- if arm_parent:
- bones['ik']['mch_hand'] = [ctrl_socket, ctrl_pole_socket, ctrl_root, ctrl_parent]
- else:
- bones['ik']['mch_hand'] = [ctrl_socket, ctrl_pole_socket, ctrl_root]
return bones
@@ -687,13 +643,10 @@ class Rig:
bpy.ops.object.mode_set(mode='OBJECT')
pb = self.obj.pose.bones
- ctrl = pb[bones['ik']['mch_hand'][0]]
- ctrl_pole = pb[bones['ik']['mch_hand'][1]]
-
#owner = pb[bones['ik']['ctrl']['limb']]
owner = pb[bones['main_parent']]
- props = ["IK_follow", "root/parent", "pole_vector", "pole_follow"]
+ props = ["pole_vector"]
for prop in props:
@@ -723,30 +676,6 @@ class Rig:
else:
make_driver(cns, "mute", variables=[(self.obj, owner, prop)], polynomial=[1.0, -1.0])
- elif prop == 'IK_follow':
- make_property(owner, prop, True)
-
- make_driver(ctrl.constraints[0], "mute", variables=[(self.obj, owner, prop)], polynomial=[1.0, -1.0])
-
- if len(ctrl.constraints) > 1:
- make_driver(ctrl.constraints[1], "mute", variables=[(self.obj, owner, prop)], polynomial=[1.0, -1.0])
-
- make_driver(ctrl_pole.constraints[0], "mute", variables=[(self.obj, owner, prop)], polynomial=[1.0, -1.0])
-
- if len(ctrl_pole.constraints) > 1:
- make_driver(ctrl_pole.constraints[1], "mute", variables=[(self.obj, owner, prop)], polynomial=[1.0, -1.0])
-
- elif prop == 'root/parent':
- if len(ctrl.constraints) > 1:
- make_property(owner, prop, 0.0)
-
- make_driver(ctrl.constraints[1], "influence", variables=[(self.obj, owner, prop)])
-
- elif prop == 'pole_follow':
- if len(ctrl_pole.constraints) > 1:
- make_property(owner, prop, 0.0)
-
- make_driver(ctrl_pole.constraints[1], "influence", variables=[(self.obj, owner, prop)])
@staticmethod
def get_future_names(bones):
@@ -822,22 +751,13 @@ class Rig:
bones = self.create_arm(bones)
self.create_drivers(bones)
- controls = [bones['ik']['ctrl']['limb'], bones['ik']['ctrl']['terminal'][0]]
-
- controls.append(bones['main_parent'])
-
# Create UI
- controls_string = ", ".join(["'" + x + "'" for x in controls])
-
script = create_script(bones, 'arm')
- script += extra_script % (controls_string, bones['main_parent'], 'IK_follow',
- 'pole_follow', 'pole_follow', 'root/parent', 'root/parent')
return {
'script': [script],
'utilities': UTILITIES_RIG_ARM,
'register': REGISTER_RIG_ARM,
- 'noparent_bones': [bones['ik']['mch_hand'][i] for i in [0,1]],
}
@@ -858,7 +778,7 @@ def add_parameters(params):
default = 'automatic'
)
- params.auto_align_extremity = bpy.BoolProperty(
+ params.auto_align_extremity = bpy.props.BoolProperty(
name='auto_align_extremity',
default=False,
description="Auto Align Extremity Bone"
diff --git a/rigify/rigs/limbs/leg.py b/rigify/rigs/limbs/leg.py
index 2b846eca..59e6f799 100644
--- a/rigify/rigs/limbs/leg.py
+++ b/rigify/rigs/limbs/leg.py
@@ -17,17 +17,8 @@ from ...utils.mechanism import make_property, make_driver
from ..widgets import create_ikarrow_widget
from math import trunc, pi
-extra_script = """
-controls = [%s]
-ctrl = '%s'
-
-if is_selected( controls ):
- layout.prop( pose_bones[ ctrl ], '["%s"]')
- if '%s' in pose_bones[ctrl].keys():
- layout.prop( pose_bones[ ctrl ], '["%s"]', slider = True )
- if '%s' in pose_bones[ctrl].keys():
- layout.prop( pose_bones[ ctrl ], '["%s"]', slider = True )
-"""
+from ...utils.switch_parent import SwitchParentBuilder
+
IMPLEMENTATION = True # Include and set True if Rig is just an implementation for a wrapper class
# add_parameters and parameters_ui are unused for implementation classes
@@ -599,35 +590,6 @@ class Rig:
eb[ctrl].parent = None
eb[ctrl].use_connect = False
- # MCH for ik control
- ctrl_socket = copy_bone(self.obj, org_bones[2], get_bone_name( org_bones[2], 'mch', 'ik_socket'))
- eb[ctrl_socket].tail = eb[ctrl_socket].head + 0.8*(eb[ctrl_socket].tail-eb[ctrl_socket].head)
- eb[ctrl_socket].parent = None
- eb[ctrl].parent = eb[ctrl_socket]
-
- # MCH for pole ik control
- ctrl_pole_socket = copy_bone(self.obj, org_bones[2], get_bone_name(org_bones[2], 'mch', 'pole_ik_socket'))
- eb[ctrl_pole_socket].tail = eb[ctrl_pole_socket].head + 0.8 * (eb[ctrl_pole_socket].tail - eb[ctrl_pole_socket].head)
- eb[ctrl_pole_socket].parent = None
- eb[pole_target].parent = eb[ctrl_pole_socket]
-
- ctrl_root = copy_bone(self.obj, org_bones[2], get_bone_name( org_bones[2], 'mch', 'ik_root'))
- eb[ctrl_root].tail = eb[ctrl_root].head + 0.7*(eb[ctrl_root].tail-eb[ctrl_root].head)
- eb[ctrl_root].use_connect = False
- eb[ctrl_root].parent = eb['root']
-
- if eb[org_bones[0]].parent:
- leg_parent = eb[org_bones[0]].parent
- ctrl_parent = copy_bone(self.obj, org_bones[2], get_bone_name( org_bones[2], 'mch', 'ik_parent'))
- eb[ctrl_parent].tail = eb[ctrl_parent].head + 0.6*(eb[ctrl_parent].tail-eb[ctrl_parent].head)
- eb[ctrl_parent].use_connect = False
- if eb[org_bones[0]].parent_recursive:
- eb[ctrl_parent].parent = eb[org_bones[0]].parent_recursive[-1]
- else:
- eb[ctrl_parent].parent = eb[org_bones[0]].parent
- else:
- leg_parent = None
-
mch_name = get_bone_name(strip_org(org_bones[0]), 'mch', 'parent_socket')
mch_main_parent = copy_bone(self.obj, org_bones[0], mch_name)
eb[mch_main_parent].length = eb[org_bones[0]].length / 12
@@ -658,6 +620,26 @@ class Rig:
eb[ctrl].tail[2] = eb[ctrl].head[2]
eb[ctrl].roll = 0
+ # Switchable parent
+ pbuilder = SwitchParentBuilder(self.rigify_generator)
+
+ if eb[org_bones[0]].parent:
+ pbuilder.register_parent(self.rigify_wrapper, eb[org_bones[0]].parent.name)
+
+ pbuilder.register_parent(self.rigify_wrapper, org_bones[2], exclude_self=True)
+
+ pcontrols = [ bones['main_parent'], bones['ik']['ctrl']['limb'], heel, ctrl, pole_target ]
+
+ pbuilder.build_child(
+ self.rigify_wrapper, ctrl,
+ prop_bone=bones['main_parent'], prop_id='IK_parent', prop_name='IK Parent', controls=pcontrols,
+ )
+
+ pbuilder.build_child(
+ self.rigify_wrapper, pole_target, extra_parents=[(bones['ik']['mch_target'], ctrl)],
+ prop_bone=bones['main_parent'], prop_id='pole_parent', prop_name='Pole Parent', controls=pcontrols,
+ no_fix_rotation=True, no_fix_scale=True
+ )
# Parent
eb[ heel ].use_connect = False
@@ -847,30 +829,6 @@ class Rig:
# Set up constraints
- # Constrain ik ctrl to root / parent
-
- make_constraint( self, ctrl_socket, {
- 'constraint' : 'COPY_TRANSFORMS',
- 'subtarget' : ctrl_root,
- })
-
- make_constraint(self, ctrl_pole_socket, {
- 'constraint': 'COPY_TRANSFORMS',
- 'subtarget': ctrl_root,
- })
-
- if leg_parent:
- make_constraint( self, ctrl_socket, {
- 'constraint' : 'COPY_TRANSFORMS',
- 'subtarget' : ctrl_parent,
- 'influence' : 0.0,
- })
-
- make_constraint(self, ctrl_pole_socket, {
- 'constraint': 'COPY_TRANSFORMS',
- 'subtarget': bones['ik']['mch_target'],
- })
-
# Constrain mch target bone to the ik control and mch stretch
make_constraint( self, bones['ik']['mch_target'], {
'constraint' : 'COPY_LOCATION',
@@ -982,11 +940,6 @@ class Rig:
bones['ik']['ctrl']['terminal'] += [ heel, ctrl ]
- if leg_parent:
- bones['ik']['mch_foot'] = [ctrl_socket, ctrl_pole_socket, ctrl_root, ctrl_parent]
- else:
- bones['ik']['mch_foot'] = [ctrl_socket, ctrl_pole_socket, ctrl_root]
-
return bones
def create_drivers(self, bones):
@@ -994,13 +947,10 @@ class Rig:
bpy.ops.object.mode_set(mode='OBJECT')
pb = self.obj.pose.bones
- ctrl = pb[bones['ik']['mch_foot'][0]]
- ctrl_pole = pb[bones['ik']['mch_foot'][1]]
-
#owner = pb[bones['ik']['ctrl']['limb']]
owner = pb[bones['main_parent']]
- props = ["IK_follow", "root/parent", "pole_vector", "pole_follow"]
+ props = ["pole_vector"]
for prop in props:
@@ -1031,31 +981,6 @@ class Rig:
make_driver(cns, "mute", variables=[(self.obj, owner, prop)], polynomial=[1.0, -1.0])
- elif prop == 'IK_follow':
- make_property(owner, prop, True)
-
- make_driver(ctrl.constraints[0], "mute", variables=[(self.obj, owner, prop)], polynomial=[1.0, -1.0])
-
- if len(ctrl.constraints) > 1:
- make_driver(ctrl.constraints[1], "mute", variables=[(self.obj, owner, prop)], polynomial=[1.0, -1.0])
-
- make_driver(ctrl_pole.constraints[0], "mute", variables=[(self.obj, owner, prop)], polynomial=[1.0, -1.0])
-
- if len(ctrl_pole.constraints) > 1:
- make_driver(ctrl_pole.constraints[1], "mute", variables=[(self.obj, owner, prop)], polynomial=[1.0, -1.0])
-
- elif prop == 'root/parent':
- if len(ctrl.constraints) > 1:
- make_property(owner, prop, 0.0)
-
- make_driver(ctrl.constraints[1], "influence", variables=[(self.obj, owner, prop)])
-
- elif prop == 'pole_follow':
- if len(ctrl_pole.constraints) > 1:
- make_property(owner, prop, 0.0)
-
- make_driver(ctrl_pole.constraints[1], "influence", variables=[(self.obj, owner, prop)])
-
@staticmethod
def get_future_names(bones):
@@ -1133,22 +1058,13 @@ class Rig:
bones = self.create_leg(bones)
self.create_drivers(bones)
- controls = [bones['ik']['ctrl']['limb'], bones['ik']['ctrl']['terminal'][-1], bones['ik']['ctrl']['terminal'][-2] ]
-
- controls.append(bones['main_parent'])
-
# Create UI
- controls_string = ", ".join(["'" + x + "'" for x in controls])
-
script = create_script(bones, 'leg')
- script += extra_script % (controls_string, bones['main_parent'], 'IK_follow',
- 'pole_follow', 'pole_follow', 'root/parent', 'root/parent')
return {
'script': [script],
'utilities': UTILITIES_RIG_LEG,
'register': REGISTER_RIG_LEG,
- 'noparent_bones': [bones['ik']['mch_foot'][i] for i in [0,1]],
}
diff --git a/rigify/rigs/limbs/super_limb.py b/rigify/rigs/limbs/super_limb.py
index 3d2bb8e2..0d557bb7 100644
--- a/rigify/rigs/limbs/super_limb.py
+++ b/rigify/rigs/limbs/super_limb.py
@@ -21,6 +21,8 @@ class Rig:
self.limb = pawRig(obj, bone_name, params)
def generate(self):
+ self.limb.rigify_generator = self.rigify_generator
+ self.limb.rigify_wrapper = self.rigify_wrapper
return self.limb.generate()
diff --git a/rigify/rigs/spines/super_spine.py b/rigify/rigs/spines/super_spine.py
index 5afe15b0..ebc76fcb 100644
--- a/rigify/rigs/spines/super_spine.py
+++ b/rigify/rigs/spines/super_spine.py
@@ -8,6 +8,8 @@ from ...utils import MetarigError, make_mechanism_name, create_cube_widget
from ...utils import ControlLayersOption
from ...utils.mechanism import make_property, make_driver
+from ...utils.switch_parent import SwitchParentBuilder
+
script = """
controls = [%s]
torso = '%s'
@@ -951,6 +953,14 @@ class Rig:
bones['chest'] = self.create_chest(upper_torso_bones)
bones['hips'] = self.create_hips(lower_torso_bones)
+ # Register viable parent bones
+ pbuilder = SwitchParentBuilder(self.rigify_generator)
+ pbuilder.register_parent(self.rigify_wrapper, bones['pivot']['ctrl'], name='Torso')
+ pbuilder.register_parent(self.rigify_wrapper, bone_chains['lower'][0], name='Hips')
+ pbuilder.register_parent(self.rigify_wrapper, bone_chains['upper'][-1], name='Chest')
+ if self.use_head:
+ pbuilder.register_parent(self.rigify_wrapper, bone_chains['neck'][-1], name='Head')
+
# TODO: Add create tail
if tail_bones:
bones['tail'] = self.create_tail(tail_bones)