Rigify: annotate and cleanup warnings in limb rigs.

- Fix uses of BaseRig.ToplevelBones to work correctly in subclasses.
- Enhance parameter annotations for bone and mechanism mixins.
- Annotate types and fix warnings in limb rigs.
This commit is contained in:
Alexander Gavrilov 2022-11-15 19:49:06 +02:00
parent b4adc4100d
commit 4e7ed6259a
22 changed files with 615 additions and 403 deletions

View File

@ -180,9 +180,13 @@ class BaseRigMixin(RaiseErrorMixin, BoneUtilityMixin, MechanismUtilityMixin):
class MchBones(TypedBoneDict):
pass
# Subclass and use the above CtrlBones and MchBones classes in overrides.
# It is necessary to reference them via absolute strings, e.g. 'Rig.CtrlBones',
# because when using just CtrlBones the annotation won't work fully in subclasses
# of the rig class in PyCharm (no warnings about unknown attribute access).
bones: ToplevelBones[str | list[str] | BoneDict,
str | list[str] | BoneDict, # Use CtrlBones in overrides
str | list[str] | BoneDict, # Use MchBones in overrides
str | list[str] | BoneDict,
str | list[str] | BoneDict,
str | list[str] | BoneDict]

View File

@ -10,4 +10,5 @@ def _install_path():
import os
return os.path.join(bpy.utils.script_path_user(), 'rigify')
__path__ = [ _install_path() ]
__path__ = [_install_path()]

View File

@ -21,7 +21,12 @@ class Rig(BaseRig):
class MchBones(BaseRig.MchBones):
pass
bones: BaseRig.ToplevelBones[str, CtrlBones, MchBones, str]
bones: BaseRig.ToplevelBones[
str,
'Rig.CtrlBones',
'Rig.MchBones',
str
]
make_control: bool
make_pivot: bool

View File

@ -45,7 +45,12 @@ class SimpleChainRig(BaseRig):
class MchBones(BaseRig.MchBones):
pass
bones: BaseRig.ToplevelBones[list[str], CtrlBones, MchBones, list[str]]
bones: BaseRig.ToplevelBones[
list[str],
'SimpleChainRig.CtrlBones',
'SimpleChainRig.MchBones',
list[str]
]
##############################
# Control chain
@ -130,7 +135,12 @@ class TweakChainRig(SimpleChainRig):
class MchBones(SimpleChainRig.MchBones):
pass
bones: BaseRig.ToplevelBones[list[str], CtrlBones, MchBones, list[str]]
bones: BaseRig.ToplevelBones[
list[str],
'TweakChainRig.CtrlBones',
'TweakChainRig.MchBones',
list[str]
]
##############################
# Tweak chain

View File

@ -7,6 +7,7 @@ from ...utils import strip_org, make_deformer_name, connected_children_names
from ...utils import create_chain_widget
from ...utils import make_mechanism_name
from ...utils import ControlLayersOption
from ...utils.mechanism import make_property, make_driver
from ..limbs.limb_utils import get_bone_name
@ -306,9 +307,11 @@ class Rig:
eb[twk].parent = eb[bones['chain']['mch_ctrl'][i+1]]
eb[twk].inherit_scale = 'NONE'
eb[bones['chain']['ctrl'][0]].parent = eb[bones['chain']['mch_ctrl'][0]] if bones['chain']['mch_ctrl'] else None
eb[bones['chain']['ctrl'][0]].parent =\
eb[bones['chain']['mch_ctrl'][0]] if bones['chain']['mch_ctrl'] else None
eb[bones['chain']['ctrl'][0]].inherit_scale = 'NONE'
eb[bones['chain']['ctrl'][1]].parent = eb[bones['chain']['mch_ctrl'][-1]] if bones['chain']['mch_ctrl'] else None
eb[bones['chain']['ctrl'][1]].parent =\
eb[bones['chain']['mch_ctrl'][-1]] if bones['chain']['mch_ctrl'] else None
eb[bones['chain']['ctrl'][1]].inherit_scale = 'NONE'
if 'pivot' in bones.keys():
@ -405,8 +408,8 @@ class Rig:
if 'pivot' in bones.keys():
step = 2/(len(self.org_bones))
for i, b in enumerate(mch_ctrl):
xval = i*step
influence = 2*xval - xval**2 # parabolic influence of pivot
x_val = i*step
influence = 2*x_val - x_val**2 # parabolic influence of pivot
if (i != 0) and (i != len(mch_ctrl)-1):
self.make_constraint(b, {
'constraint': 'COPY_TRANSFORMS',
@ -493,16 +496,19 @@ class Rig:
for prop in props:
if prop == 'neck_follow':
defval = 0.5
def_val = 0.5
else:
defval = 0.0
def_val = 0.0
make_property(torso, prop, defval)
make_property(torso, prop, def_val)
# driving the follow rotation switches for neck and head
for bone, prop, in zip(owners, props):
# Add driver to copy rotation constraint
make_driver(pb[bone].constraints[0], "influence", variables=[(self.obj, torso, prop)], polynomial=[1.0, -1.0])
make_driver(
pb[bone].constraints[0], "influence",
variables=[(self.obj, torso, prop)], polynomial=[1.0, -1.0]
)
def locks_and_widgets(self, bones):
bpy.ops.object.mode_set(mode='OBJECT')
@ -603,22 +609,22 @@ class Rig:
all_ctrls.extend(bones['chain']['ctrl'])
all_ctrls.extend(bones['chain']['tweak'])
all_ctrls.extend(bones['chain']['conv'])
for bname in eb.keys():
if bname not in all_ctrls and (bname.startswith('tweak') or 'ctrl' in bname):
other_ctrls.append(bname)
for bone_name in eb.keys():
if bone_name not in all_ctrls and (bone_name.startswith('tweak') or 'ctrl' in bone_name):
other_ctrls.append(bone_name)
for bname in other_ctrls:
if eb[bname].head == head_start:
for bone_name in other_ctrls:
if eb[bone_name].head == head_start:
for child in eb[bones['chain']['ctrl'][0]].children:
child.parent = eb[bname]
child.parent = eb[bone_name]
eb.remove(eb[bones['chain']['ctrl'][0]])
bones['chain']['ctrl'][0] = bname
bones['chain']['ctrl'][0] = bone_name
break
for bname in other_ctrls:
if eb[bname].head == head_tip:
for bone_name in other_ctrls:
if eb[bone_name].head == head_tip:
eb.remove(eb[bones['chain']['ctrl'][-1]])
bones['chain']['ctrl'][-1] = bname
bones['chain']['ctrl'][-1] = bone_name
break
def generate(self):
@ -647,7 +653,7 @@ class Rig:
self.parent_bones(bones)
# ctrls snapping pass
#self.aggregate_ctrls(bones)
# self.aggregate_ctrls(bones)
self.constrain_bones(bones)
self.stick_to_bendy_bones(bones)
@ -715,10 +721,10 @@ def add_parameters(params):
)
params.bbones = bpy.props.IntProperty(
name = 'B-Bone Segments',
default = 10,
min = 1,
description = 'Number of B-Bone segments'
name='B-Bone Segments',
default=10,
min=1,
description='Number of B-Bone segments'
)
params.wgt_offset = bpy.props.FloatProperty(
@ -759,8 +765,8 @@ def parameters_ui(layout, params):
r = layout.row()
r.prop(params, 'def_parenting')
#r = layout.row()
#r.prop(params, 'cluster_ctrls')
# r = layout.row()
# r.prop(params, 'cluster_ctrls')
ControlLayersOption.TWEAK.parameters_ui(layout, params)
@ -808,7 +814,10 @@ def create_sample(obj):
pbone.lock_rotation_w = False
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
pbone.bone.layers = [True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
pbone.bone.layers = [
True, False, False, False, False, False, False, False, False, False, False, False, False,
False, False, False, False, False, False, False, False, False, False, False, False, False,
False, False, False, False, False, False]
pbone = obj.pose.bones[bones['spine.001']]
pbone.rigify_type = ''
pbone.lock_location = (False, False, False)
@ -816,7 +825,10 @@ def create_sample(obj):
pbone.lock_rotation_w = False
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
pbone.bone.layers = [True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
pbone.bone.layers = [
True, False, False, False, False, False, False, False, False, False, False, False, False,
False, False, False, False, False, False, False, False, False, False, False, False, False,
False, False, False, False, False, False]
pbone = obj.pose.bones[bones['spine.002']]
pbone.rigify_type = ''
pbone.lock_location = (False, False, False)
@ -824,7 +836,10 @@ def create_sample(obj):
pbone.lock_rotation_w = False
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
pbone.bone.layers = [True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
pbone.bone.layers = [
True, False, False, False, False, False, False, False, False, False, False, False, False,
False, False, False, False, False, False, False, False, False, False, False, False, False,
False, False, False, False, False, False]
pbone = obj.pose.bones[bones['spine.003']]
pbone.rigify_type = ''
pbone.lock_location = (False, False, False)
@ -832,7 +847,10 @@ def create_sample(obj):
pbone.lock_rotation_w = False
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
pbone.bone.layers = [True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
pbone.bone.layers = [
True, False, False, False, False, False, False, False, False, False, False, False, False,
False, False, False, False, False, False, False, False, False, False, False, False, False,
False, False, False, False, False, False]
bpy.ops.object.mode_set(mode='EDIT')
for bone in arm.edit_bones:

View File

@ -21,9 +21,10 @@ class Rig(BaseLimbRig):
min_valid_orgs = max_valid_orgs = 3
make_wrist_pivot: bool
def initialize(self):
super().initialize()
self.make_wrist_pivot = self.params.make_ik_wrist_pivot
def prepare_bones(self):
@ -40,6 +41,22 @@ class Rig(BaseLimbRig):
align_bone_z_axis(self.obj, orgs[2], axis)
####################################################
# BONES
class CtrlBones(BaseLimbRig.CtrlBones):
ik_wrist: str # Wrist pivot control (if enabled)
class MchBones(BaseLimbRig.MchBones):
ik_wrist: str # Wrist pivot control output (if enabled)
bones: BaseLimbRig.ToplevelBones[
'Rig.OrgBones',
'Rig.CtrlBones',
'Rig.MchBones',
list[str]
]
####################################################
# Overrides
@ -104,7 +121,7 @@ class Rig(BaseLimbRig):
# Settings
@classmethod
def add_parameters(self, params):
def add_parameters(cls, params):
super().add_parameters(params)
params.make_ik_wrist_pivot = bpy.props.BoolProperty(
@ -113,10 +130,10 @@ class Rig(BaseLimbRig):
)
@classmethod
def parameters_ui(self, layout, params):
def parameters_ui(cls, layout, params, end='Hand'):
layout.prop(params, "make_ik_wrist_pivot")
super().parameters_ui(layout, params, 'Hand')
super().parameters_ui(layout, params, end)
def create_sample(obj, limb=False):
@ -155,11 +172,17 @@ def create_sample(obj, limb=False):
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
try:
pbone.rigify_parameters.tweak_layers = [False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
pbone.rigify_parameters.tweak_layers = [
False, False, False, False, False, False, False, False, False, True, False, False,
False, False, False, False, False, False, False, False, False, False, False, False,
False, False, False, False, False, False, False, False]
except AttributeError:
pass
try:
pbone.rigify_parameters.fk_layers = [False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
pbone.rigify_parameters.fk_layers = [
False, False, False, False, False, False, False, False, True, False, False, False,
False, False, False, False, False, False, False, False, False, False, False, False,
False, False, False, False, False, False, False, False]
except AttributeError:
pass
try:

View File

@ -2,7 +2,7 @@
import bpy
from ...utils.bones import align_bone_roll, put_bone, copy_bone_position, flip_bone
from ...utils.bones import align_bone_roll, put_bone, copy_bone_position
from ...utils.naming import make_derived_name
from ...utils.misc import map_list
@ -18,17 +18,23 @@ class Rig(pawRig):
"""Front paw rig with special IK automation."""
####################################################
# EXTRA BONES
#
# mch:
# ik2_chain[2]
# Second IK system (pre-driving heel)
# heel_track
# Bone tracking IK2 to rotate heel
# heel_parent
# Parent of the heel control
#
####################################################
# BONES
class CtrlBones(pawRig.CtrlBones):
pass
class MchBones(pawRig.MchBones):
ik2_chain: list[str] # Second IK system (pre-driving heel)
ik2_target: str # Second IK system target (if heel2)
heel_track: str # Bone tracking IK2 to rotate heel
heel_parent: str # Parent of the heel control
bones: pawRig.ToplevelBones[
pawRig.OrgBones,
'Rig.CtrlBones',
'Rig.MchBones',
list[str]
]
####################################################
# IK controls
@ -50,7 +56,6 @@ class Rig(pawRig):
def get_ik_pole_parents(self):
return [(self.get_ik2_target_bone(), self.bones.ctrl.ik)]
####################################################
# Second IK system (pre-driving heel)
@ -72,10 +77,10 @@ class Rig(pawRig):
self.get_bone(chain[1]).tail = self.get_bone(orgs[2]).tail
align_bone_roll(self.obj, chain[1], orgs[1])
def make_ik2_mch_target_bone(self, orgs):
def make_ik2_mch_target_bone(self, orgs: list[str]):
return self.copy_bone(orgs[3], make_derived_name(orgs[0], 'mch', '_ik2_target'), scale=1/2)
def make_ik2_mch_bone(self, i, org):
def make_ik2_mch_bone(self, _i: int, org: str):
return self.copy_bone(org, make_derived_name(org, 'mch', '_ik2'))
@stage.parent_bones
@ -91,7 +96,7 @@ class Rig(pawRig):
for i, mch in enumerate(self.bones.mch.ik2_chain):
self.configure_ik2_mch_bone(i, mch)
def configure_ik2_mch_bone(self, i, mch):
def configure_ik2_mch_bone(self, i: int, mch: str):
bone = self.get_bone(mch)
bone.ik_stretch = 0.1
if i == 1:
@ -103,7 +108,6 @@ class Rig(pawRig):
target_bone = self.get_ik2_target_bone()
self.rig_ik_mch_end_bone(self.bones.mch.ik2_chain[-1], target_bone, self.bones.ctrl.ik_pole)
####################################################
# Heel tracking from IK2
@ -150,28 +154,27 @@ class Rig(pawRig):
# Complete the parent chain.
self.set_bone_parent(self.bones.mch.heel_parent, self.bones.mch.heel_track)
####################################################
# Settings
@classmethod
def add_parameters(self, params):
def add_parameters(cls, params):
super().add_parameters(params)
params.front_paw_heel_influence = bpy.props.FloatProperty(
name = 'Heel IK Influence',
default = 0.8,
min = 0,
max = 1,
description = 'Influence of the secondary IK on the heel control rotation'
name='Heel IK Influence',
default=0.8,
min=0,
max=1,
description='Influence of the secondary IK on the heel control rotation'
)
@classmethod
def parameters_ui(self, layout, params):
def parameters_ui(cls, layout, params, end='Claw'):
r = layout.row()
r.prop(params, "front_paw_heel_influence", slider=True)
super().parameters_ui(layout, params)
super().parameters_ui(layout, params, end)
def create_sample(obj):
@ -222,11 +225,17 @@ def create_sample(obj):
except AttributeError:
pass
try:
pbone.rigify_parameters.fk_layers = [False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
pbone.rigify_parameters.fk_layers = [
False, False, False, False, False, False, False, False, True, False, False, False,
False, False, False, False, False, False, False, False, False, False, False, False,
False, False, False, False, False, False, False, False]
except AttributeError:
pass
try:
pbone.rigify_parameters.tweak_layers = [False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
pbone.rigify_parameters.tweak_layers = [
False, False, False, False, False, False, False, False, False, True, False, False,
False, False, False, False, False, False, False, False, False, False, False, False,
False, False, False, False, False, False, False, False]
except AttributeError:
pass
pbone = obj.pose.bones[bones['front_shin.L']]

View File

@ -28,6 +28,13 @@ class Rig(BaseLimbRig):
min_valid_orgs = max_valid_orgs = 4
pivot_type: str
heel_euler_order: str
use_ik_toe: bool
ik_matrix: Matrix
roll_matrix: Matrix
def find_org_bones(self, bone):
bones = super().find_org_bones(bone)
@ -86,25 +93,26 @@ class Rig(BaseLimbRig):
self.roll_matrix = matrix_from_axis_pair(ik_y_axis, foot_x, self.main_axis)
####################################################
# EXTRA BONES
#
# org:
# heel:
# Heel location marker bone
# ctrl:
# ik_spin:
# Toe spin control.
# heel:
# Foot roll control
# ik_toe:
# If enabled, toe control for IK chain.
# mch:
# heel[]:
# Chain of bones implementing foot roll.
# ik_toe_parent:
# If using split IK toe, parent of the IK toe control.
#
####################################################
# BONES
class OrgBones(BaseLimbRig.OrgBones):
heel: str # Heel location marker bone
class CtrlBones(BaseLimbRig.CtrlBones):
ik_spin: str # Toe spin control
heel: str # Foot roll control
ik_toe: str # If enabled, toe control for IK chain.
class MchBones(BaseLimbRig.MchBones):
heel: list[str] # Chain of bones implementing foot roll.
ik_toe_parent: str # If using split IK toe, parent of the IK toe control.
bones: BaseLimbRig.ToplevelBones[
'Rig.OrgBones',
'Rig.CtrlBones',
'Rig.MchBones',
list[str]
]
####################################################
# IK controls
@ -162,7 +170,7 @@ class Rig(BaseLimbRig):
if self.pivot_type == 'ANKLE_TOE':
self.bones.ctrl.ik_spin = self.make_ik_spin_bone(self.bones.org.main)
def make_ik_spin_bone(self, orgs):
def make_ik_spin_bone(self, orgs: list[str]):
name = self.copy_bone(orgs[2], make_derived_name(orgs[2], 'ctrl', '_spin_ik'))
put_bone(self.obj, name, self.get_bone(orgs[3]).head, matrix=self.ik_matrix, scale=0.5)
return name
@ -176,8 +184,8 @@ class Rig(BaseLimbRig):
def make_ik_spin_control_widget(self):
if self.pivot_type == 'ANKLE_TOE':
obj = create_ballsocket_widget(self.obj, self.bones.ctrl.ik_spin, size=0.75)
rotfix = Matrix.Rotation(math.pi/2, 4, self.main_axis.upper())
adjust_widget_transform_mesh(obj, rotfix, local=True)
rot_fix = Matrix.Rotation(math.pi/2, 4, self.main_axis.upper())
adjust_widget_transform_mesh(obj, rot_fix, local=True)
####################################################
# Heel control
@ -214,10 +222,10 @@ class Rig(BaseLimbRig):
self.bones.ctrl.ik_toe = self.make_ik_toe_control_bone(toe)
self.bones.mch.ik_toe_parent = self.make_ik_toe_parent_mch_bone(toe)
def make_ik_toe_control_bone(self, org):
def make_ik_toe_control_bone(self, org: str):
return self.copy_bone(org, make_derived_name(org, 'ctrl', '_ik'))
def make_ik_toe_parent_mch_bone(self, org):
def make_ik_toe_parent_mch_bone(self, org: str):
return self.copy_bone(org, make_derived_name(org, 'mch', '_ik_parent'), scale=1/3)
@stage.parent_bones
@ -255,8 +263,7 @@ class Rig(BaseLimbRig):
orgs = self.bones.org.main
self.bones.mch.heel = self.make_roll_mch_bones(orgs[2], orgs[3], self.bones.org.heel)
def make_roll_mch_bones(self, foot, toe, heel):
foot_bone = self.get_bone(foot)
def make_roll_mch_bones(self, foot: str, toe: str, heel: str):
heel_bone = self.get_bone(heel)
heel_middle = (heel_bone.head + heel_bone.tail) / 2
@ -273,7 +280,7 @@ class Rig(BaseLimbRig):
put_bone(self.obj, rock1, heel_bone.tail, matrix=self.roll_matrix, scale=0.5)
put_bone(self.obj, rock2, heel_bone.head, matrix=self.roll_matrix, scale=0.5)
return [ rock2, rock1, roll2, roll1, result ]
return [rock2, rock1, roll2, roll1, result]
@stage.parent_bones
def parent_roll_mch_chain(self):
@ -285,7 +292,7 @@ class Rig(BaseLimbRig):
def rig_roll_mch_chain(self):
self.rig_roll_mch_bones(self.bones.mch.heel, self.bones.ctrl.heel, self.bones.org.heel)
def rig_roll_mch_bones(self, chain, heel, org_heel):
def rig_roll_mch_bones(self, chain: list[str], heel: str, org_heel: str):
rock2, rock1, roll2, roll1, result = chain
# This order is required for correct working of the constraints
@ -318,7 +325,6 @@ class Rig(BaseLimbRig):
self.make_constraint(rock1, 'LIMIT_ROTATION', max_y=DEG_360, space='LOCAL')
self.make_constraint(rock2, 'LIMIT_ROTATION', min_y=-DEG_360, space='LOCAL')
####################################################
# FK parents MCH chain
@ -356,12 +362,11 @@ class Rig(BaseLimbRig):
self.set_bone_parent(self.bones.mch.ik_target, self.bones.mch.heel[-1])
####################################################
# Settings
@classmethod
def add_parameters(self, params):
def add_parameters(cls, params):
super().add_parameters(params)
items = [
@ -374,9 +379,9 @@ class Rig(BaseLimbRig):
]
params.foot_pivot_type = bpy.props.EnumProperty(
items = items,
name = "Foot Pivot",
default = 'ANKLE_TOE'
items=items,
name="Foot Pivot",
default='ANKLE_TOE'
)
params.extra_ik_toe = bpy.props.BoolProperty(
@ -385,13 +390,12 @@ class Rig(BaseLimbRig):
description="Generate a separate IK toe control for better IK/FK snapping"
)
@classmethod
def parameters_ui(self, layout, params):
def parameters_ui(cls, layout, params, end='Foot'):
layout.prop(params, 'foot_pivot_type')
layout.prop(params, 'extra_ik_toe')
super().parameters_ui(layout, params, 'Foot')
super().parameters_ui(layout, params, end)
def create_sample(obj):
@ -436,7 +440,6 @@ def create_sample(obj):
bone.parent = arm.edit_bones[bones['foot.L']]
bones['heel.02.L'] = bone.name
bpy.ops.object.mode_set(mode='OBJECT')
pbone = obj.pose.bones[bones['thigh.L']]
pbone.rigify_type = 'limbs.leg'
@ -450,7 +453,10 @@ def create_sample(obj):
except AttributeError:
pass
try:
pbone.rigify_parameters.ik_layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
pbone.rigify_parameters.ik_layers = [
False, False, False, False, False, False, False, False, False, False, False, False,
False, False, True, False, False, False, False, False, False, False, False, False,
False, False, False, False, False, False, False, False]
except AttributeError:
pass
try:
@ -458,7 +464,10 @@ def create_sample(obj):
except AttributeError:
pass
try:
pbone.rigify_parameters.hose_layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
pbone.rigify_parameters.hose_layers = [
False, False, False, False, False, False, False, False, False, False, False, False,
False, False, False, True, False, False, False, False, False, False, False, False,
False, False, False, False, False, False, False, False]
except AttributeError:
pass
try:
@ -466,11 +475,17 @@ def create_sample(obj):
except AttributeError:
pass
try:
pbone.rigify_parameters.fk_layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
pbone.rigify_parameters.fk_layers = [
False, False, False, False, False, False, False, False, False, False, False, False,
False, False, True, False, False, False, False, False, False, False, False, False,
False, False, False, False, False, False, False, False]
except AttributeError:
pass
try:
pbone.rigify_parameters.tweak_layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
pbone.rigify_parameters.tweak_layers = [
False, False, False, False, False, False, False, False, False, False, False, False,
False, False, False, True, False, False, False, False, False, False, False, False,
False, False, False, False, False, False, False, False]
except AttributeError:
pass
try:
@ -523,8 +538,12 @@ def create_sample(obj):
arm.edit_bones.active = bone
for eb in arm.edit_bones:
eb.layers = (False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False)
eb.layers = (False, False, False, False, False, False, False, False, False, False, False,
False, False, True, False, False, False, False, False, False, False, False,
False, False, False, False, False, False, False, False, False, False)
arm.layers = (False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False)
arm.layers = (False, False, False, False, False, False, False, False, False, False, False,
False, False, True, False, False, False, False, False, False, False, False,
False, False, False, False, False, False, False, False, False, False)
return bones

View File

@ -3,9 +3,12 @@
import bpy
import json
from typing import Optional, NamedTuple
from bpy.types import PoseBone, EditBone
from ...utils.animation import add_generic_snap_fk_to_ik, add_fk_ik_snap_buttons
from ...utils.rig import connected_children_names
from ...utils.bones import BoneDict, put_bone, align_bone_orientation, set_bone_widget_transform
from ...utils.bones import put_bone, align_bone_orientation, set_bone_widget_transform, TypedBoneDict
from ...utils.naming import strip_org, make_derived_name
from ...utils.layers import ControlLayersOption
from ...utils.misc import pairwise_nozip, padnone, map_list
@ -17,26 +20,47 @@ from ...base_rig import stage, BaseRig
from ...utils.widgets_basic import create_circle_widget, create_sphere_widget, create_line_widget, create_limb_widget
from ..widgets import create_gear_widget, create_ikarrow_widget
from ...rig_ui_template import UTILITIES_FUNC_COMMON_IKFK
from ...rig_ui_template import UTILITIES_FUNC_COMMON_IKFK, PanelLayout
from math import pi
from itertools import count, chain
from itertools import count
from mathutils import Vector
from collections import namedtuple
SegmentEntry = namedtuple('SegmentEntry', ['org', 'org_idx', 'seg_idx', 'pos'])
class SegmentEntry(NamedTuple):
org: str # ORG bone
org_idx: int # Index in the ORG chain
seg_idx: int | None # Segment index within ORG bone
pos: Vector # Position of the segment start
class BaseLimbRig(BaseRig):
"""Common base for limb rigs."""
segmented_orgs = 2 # Number of org bones to segment
segmented_orgs = 2 # Number of org bones to segment
min_valid_orgs = None
max_valid_orgs = None
def find_org_bones(self, bone):
return BoneDict(
segments: int # Number of tweak segments per org bone
bbone_segments: int # Number of B-Bone segments per def bone
use_ik_pivot: bool
use_uniform_scale: bool
main_axis: str
aux_axis: str
segment_table: list[SegmentEntry]
segment_table_end: list[SegmentEntry]
segment_table_full: list[SegmentEntry]
segment_table_tweak: list[SegmentEntry]
rig_parent_bone: str
prop_bone: str
elbow_vector: Vector
pole_angle: float
def find_org_bones(self, bone: PoseBone) -> 'BaseLimbRig.OrgBones':
return self.OrgBones(
main=[bone.name] + connected_children_names(self.obj, bone.name),
)
@ -83,22 +107,22 @@ class BaseLimbRig(BaseRig):
self.pole_angle = self.compute_pole_angle(bones, self.elbow_vector)
self.rig_parent_bone = self.get_bone_parent(self.bones.org.main[0])
####################################################
# Utilities
def compute_elbow_vector(self, bones):
# noinspection PyMethodMayBeStatic
def compute_elbow_vector(self, bones: list[EditBone]) -> Vector:
lo_vector = bones[1].vector
tot_vector = bones[1].tail - bones[0].head
return (lo_vector.project(tot_vector) - lo_vector).normalized() * tot_vector.length
def get_main_axis(self, bone):
def get_main_axis(self, bone: PoseBone | EditBone) -> Vector:
return getattr(bone, self.main_axis + '_axis')
def get_aux_axis(self, bone):
def get_aux_axis(self, bone: PoseBone | EditBone) -> Vector:
return getattr(bone, self.aux_axis + '_axis')
def compute_pole_angle(self, bones, elbow_vector):
def compute_pole_angle(self, bones: list[PoseBone | EditBone], elbow_vector: Vector) -> float:
if self.params.rotation_axis == 'z':
return 0
@ -109,56 +133,49 @@ class BaseLimbRig(BaseRig):
else:
return pi/2
def get_segment_pos(self, org, seg):
def get_segment_pos(self, org: str, seg: int):
bone = self.get_bone(org)
return bone.head + bone.vector * (seg / self.segments)
@staticmethod
def vector_without_z(vector):
return Vector((vector[0], vector[1], 0))
def vector_without_z(vector: Vector) -> Vector:
return Vector((vector.x, vector.y, 0))
####################################################
# BONES
#
# org:
# main[]:
# Main ORG bone chain
# ctrl:
# master:
# Main property control.
# fk[]:
# FK control chain.
# tweak[]:
# Tweak control chain.
# ik_base, ik_pole, ik
# IK controls
# ik_vispole
# IK pole visualization.
# ik_pivot
# Custom IK pivot (optional).
# mch:
# master:
# Parent of the master control.
# follow:
# FK follow behavior.
# fk[]:
# FK chain parents (or None)
# ik_pivot
# Custom IK pivot result (optional).
# ik_scale
# Helper bone that implements uniform scaling.
# ik_swing
# Bone that tracks ik_target to manually handle limb swing.
# ik_target
# Corrected target position.
# ik_base
# Optionally the base of the ik chain (otherwise ctrl.ik_base)
# ik_end
# End of the IK chain: [ik_base, ik_end]
# deform[]:
# DEF bones
#
####################################################
class OrgBones(TypedBoneDict):
main: list[str]
# noinspection SpellCheckingInspection
class CtrlBones(BaseRig.CtrlBones):
master: str # Main property control.
fk: list[str] # FK control chain.
tweak: list[str] # Tweak control chain.
ik_base: str # IK base control.
ik_pole: str # IK pole control.
ik: str # IK limb end control.
ik_vispole: str # IK pole visualization line.
ik_pivot: str # Custom IK pivot (optional).
class MchBones(BaseRig.MchBones):
master: str # Parent of the master control.
follow: str # FK follow behavior.
fk: list[str | None] # FK chain parents.
tweak: list[str] # Tweak chain parents.
ik_pivot: str # Custom IK pivot result (optional).
ik_scale: str # Helper bone that implements uniform scaling.
ik_swing: str # Bone that tracks ik_target to manually handle limb swing.
ik_target: str # Corrected target position.
ik_base: str # Optionally the base of the ik chain (over ctrl.ik_base)
ik_end: str # End of the IK chain: [ik_base, ik_end]
bones: BaseRig.ToplevelBones[
'BaseLimbRig.OrgBones',
'BaseLimbRig.CtrlBones',
'BaseLimbRig.MchBones',
list[str]
]
####################################################
# Master control
@ -166,7 +183,7 @@ class BaseLimbRig(BaseRig):
@stage.generate_bones
def make_master_control(self):
org = self.bones.org.main[0]
self.bones.mch.master = name = self.copy_bone(org, make_derived_name(org, 'mch', '_parent_widget'), scale=1/12)
self.bones.mch.master = self.copy_bone(org, make_derived_name(org, 'mch', '_parent_widget'), scale=1/12)
self.bones.ctrl.master = name = self.copy_bone(org, make_derived_name(org, 'ctrl', '_parent'), scale=1/4)
self.get_bone(name).roll = 0
self.prop_bone = self.bones.ctrl.master
@ -197,7 +214,6 @@ class BaseLimbRig(BaseRig):
set_bone_widget_transform(self.obj, master, self.bones.mch.master)
create_gear_widget(self.obj, master, radius=1)
####################################################
# FK follow MCH
@ -236,7 +252,6 @@ class BaseLimbRig(BaseRig):
self.make_driver(con, 'influence', variables=[(self.prop_bone, 'FK_limb_follow')])
####################################################
# FK control chain
@ -247,10 +262,10 @@ class BaseLimbRig(BaseRig):
fk_name_suffix_cutoff = 2
fk_ik_layer_cutoff = 3
def get_fk_name(self, i, org, kind):
def get_fk_name(self, i: int, org: str, kind: str):
return make_derived_name(org, kind, '_fk' if i <= self.fk_name_suffix_cutoff else '')
def make_fk_control_bone(self, i, org):
def make_fk_control_bone(self, i: int, org: str):
return self.copy_bone(org, self.get_fk_name(i, org, 'ctrl'))
@stage.parent_bones
@ -259,7 +274,7 @@ class BaseLimbRig(BaseRig):
for args in zip(count(0), fk, [self.bones.mch.follow]+fk, self.bones.org.main, self.bones.mch.fk):
self.parent_fk_control_bone(*args)
def parent_fk_control_bone(self, i, ctrl, prev, org, parent_mch):
def parent_fk_control_bone(self, i: int, ctrl: str, prev: str, _org: str, parent_mch: str | None):
if parent_mch:
self.set_bone_parent(ctrl, parent_mch)
elif i == 0:
@ -276,7 +291,7 @@ class BaseLimbRig(BaseRig):
ControlLayersOption.FK.assign_rig(self, self.bones.ctrl.fk[0:cut])
ControlLayersOption.FK.assign_rig(self, self.bones.ctrl.fk[cut:], combine=True, priority=1)
def configure_fk_control_bone(self, i, ctrl, org):
def configure_fk_control_bone(self, i: int, ctrl: str, org: str):
self.copy_bone_properties(org, ctrl)
if i == 2:
@ -287,7 +302,7 @@ class BaseLimbRig(BaseRig):
for args in zip(count(0), self.bones.ctrl.fk):
self.make_fk_control_widget(*args)
def make_fk_control_widget(self, i, ctrl):
def make_fk_control_widget(self, i: int, ctrl: str):
if i < 2:
create_limb_widget(self.obj, ctrl)
elif i == 2:
@ -295,7 +310,6 @@ class BaseLimbRig(BaseRig):
else:
create_circle_widget(self.obj, ctrl, radius=0.4, head_tail=0.5)
####################################################
# FK parents MCH chain
@ -303,7 +317,7 @@ class BaseLimbRig(BaseRig):
def make_fk_parent_chain(self):
self.bones.mch.fk = map_list(self.make_fk_parent_bone, count(0), self.bones.org.main)
def make_fk_parent_bone(self, i, org):
def make_fk_parent_bone(self, i: int, org: str):
if i >= 2:
return self.copy_bone(org, self.get_fk_name(i, org, 'mch'), parent=True, scale=1/4)
@ -311,10 +325,11 @@ class BaseLimbRig(BaseRig):
def parent_fk_parent_chain(self):
mch = self.bones.mch
orgs = self.bones.org.main
for args in zip(count(0), mch.fk, [mch.follow]+self.bones.ctrl.fk, orgs, [None]+orgs):
for args in zip(count(0), mch.fk, [mch.follow]+self.bones.ctrl.fk, orgs, [None, *orgs]):
self.parent_fk_parent_bone(*args)
def parent_fk_parent_bone(self, i, parent_mch, prev_ctrl, org, prev_org):
def parent_fk_parent_bone(self, i: int, parent_mch: str | None,
prev_ctrl: str, org: str, prev_org: str | None):
if i >= 2:
self.set_bone_parent(parent_mch, prev_ctrl, use_connect=True, inherit_scale='NONE')
@ -323,13 +338,12 @@ class BaseLimbRig(BaseRig):
for args in zip(count(0), self.bones.mch.fk, self.bones.org.main):
self.rig_fk_parent_bone(*args)
def rig_fk_parent_bone(self, i, parent_mch, org):
def rig_fk_parent_bone(self, i: int, parent_mch: str | None, org: str):
if i >= 2:
self.make_constraint(
parent_mch, 'COPY_SCALE', self.bones.mch.follow, use_make_uniform=True
)
####################################################
# IK controls
@ -361,6 +375,8 @@ class BaseLimbRig(BaseRig):
*self.get_extra_ik_controls(),
]
component_ik_pivot: CustomPivotControl | None
@stage.generate_bones
def make_ik_controls(self):
orgs = self.bones.org.main
@ -375,10 +391,10 @@ class BaseLimbRig(BaseRig):
self.component_ik_pivot = self.build_ik_pivot(ik_name, parent=parent)
self.build_ik_parent_switch(SwitchParentBuilder(self.generator))
def make_ik_base_bone(self, orgs):
def make_ik_base_bone(self, orgs: list[str]):
return self.copy_bone(orgs[0], make_derived_name(orgs[0], 'ctrl', '_ik'))
def make_ik_pole_bone(self, orgs):
def make_ik_pole_bone(self, orgs: list[str]):
name = self.copy_bone(orgs[0], make_derived_name(orgs[0], 'ctrl', '_ik_target'))
pole = self.get_bone(name)
@ -388,17 +404,17 @@ class BaseLimbRig(BaseRig):
return name
def make_ik_control_bone(self, orgs):
def make_ik_control_bone(self, orgs: list[str]):
return self.copy_bone(orgs[2], make_derived_name(orgs[2], 'ctrl', '_ik'))
def make_ik_scale_bone(self, ctrl, orgs):
def make_ik_scale_bone(self, ctrl: str, orgs: list[str]):
return self.copy_bone(ctrl, make_derived_name(orgs[2], 'mch', '_ik_scale'), scale=1/2)
def build_ik_pivot(self, ik_name, **args):
def build_ik_pivot(self, ik_name: str, **args) -> CustomPivotControl | None:
if self.use_ik_pivot:
return CustomPivotControl(self, 'ik_pivot', ik_name, **args)
def get_ik_control_output(self):
def get_ik_control_output(self) -> str:
if self.component_ik_pivot:
return self.component_ik_pivot.output
elif self.use_uniform_scale:
@ -406,10 +422,10 @@ class BaseLimbRig(BaseRig):
else:
return self.bones.ctrl.ik
def get_ik_pole_parents(self):
def get_ik_pole_parents(self) -> list[tuple[str, str] | str]:
return [(self.bones.mch.ik_target, self.bones.ctrl.ik)]
def register_switch_parents(self, pbuilder):
def register_switch_parents(self, pbuilder: SwitchParentBuilder):
if self.rig_parent_bone:
pbuilder.register_parent(self, self.rig_parent_bone)
@ -418,22 +434,22 @@ class BaseLimbRig(BaseRig):
exclude_self=True, tags={'limb_ik', 'child'},
)
def build_ik_parent_switch(self, pbuilder):
def build_ik_parent_switch(self, pbuilder: SwitchParentBuilder):
ctrl = self.bones.ctrl
master = lambda: self.bones.ctrl.master
pcontrols = lambda: [ ctrl.master ] + self.get_all_ik_controls()
def master(): return self.bones.ctrl.master
def controls(): return [ctrl.master] + self.get_all_ik_controls()
self.register_switch_parents(pbuilder)
pbuilder.build_child(
self, ctrl.ik, prop_bone=master, select_parent='root',
prop_id='IK_parent', prop_name='IK Parent', controls=pcontrols,
prop_id='IK_parent', prop_name='IK Parent', controls=controls,
)
pbuilder.build_child(
self, ctrl.ik_pole, prop_bone=master, extra_parents=self.get_ik_pole_parents,
prop_id='pole_parent', prop_name='Pole Parent', controls=pcontrols,
prop_id='pole_parent', prop_name='Pole Parent', controls=controls,
no_fix_rotation=True, no_fix_scale=True,
)
@ -450,7 +466,7 @@ class BaseLimbRig(BaseRig):
self.set_ik_local_location(self.bones.ctrl.ik)
self.set_ik_local_location(self.bones.ctrl.ik_pole)
def set_ik_local_location(self, ctrl):
def set_ik_local_location(self, ctrl: str):
self.get_bone(ctrl).use_local_location = self.params.ik_local_location
@stage.configure_bones
@ -466,7 +482,7 @@ class BaseLimbRig(BaseRig):
if self.use_uniform_scale:
self.rig_ik_control_scale(self.bones.mch.ik_scale)
def rig_ik_control_scale(self, mch):
def rig_ik_control_scale(self, mch: str):
self.make_constraint(
mch, 'COPY_SCALE', self.bones.ctrl.master,
use_make_uniform=True, use_offset=True, space='LOCAL',
@ -485,7 +501,7 @@ class BaseLimbRig(BaseRig):
self.make_ik_pole_widget(ctrl.ik_pole)
self.make_ik_ctrl_widget(ctrl.ik)
def make_ik_base_widget(self, ctrl):
def make_ik_base_widget(self, ctrl: str):
if self.main_axis == 'x':
roll = 0
else:
@ -493,13 +509,12 @@ class BaseLimbRig(BaseRig):
create_ikarrow_widget(self.obj, ctrl, roll=roll)
def make_ik_pole_widget(self, ctrl):
def make_ik_pole_widget(self, ctrl: str):
create_sphere_widget(self.obj, ctrl)
def make_ik_ctrl_widget(self, ctrl):
def make_ik_ctrl_widget(self, ctrl: str):
raise NotImplementedError()
####################################################
# IK pole visualization
@ -529,7 +544,6 @@ class BaseLimbRig(BaseRig):
def make_ik_vispole_widget(self):
create_line_widget(self.obj, self.bones.ctrl.ik_vispole)
####################################################
# IK system MCH
@ -611,7 +625,7 @@ class BaseLimbRig(BaseRig):
self.add_ik_only_buttons(panel, rig_name)
def add_global_buttons(self, panel, rig_name):
def add_global_buttons(self, panel: PanelLayout, rig_name: str):
ctrl = self.bones.ctrl
ik_chain, tail_chain, fk_chain = self.get_ik_fk_position_chains()
@ -622,7 +636,6 @@ class BaseLimbRig(BaseRig):
rig_name=rig_name
)
add_limb_snap_ik_to_fk(
panel,
master=ctrl.master,
@ -632,7 +645,7 @@ class BaseLimbRig(BaseRig):
rig_name=rig_name
)
def add_ik_only_buttons(self, panel, rig_name):
def add_ik_only_buttons(self, panel: PanelLayout, rig_name: str):
ctrl = self.bones.ctrl
ik_chain, tail_chain, fk_chain = self.get_ik_fk_position_chains()
@ -650,10 +663,12 @@ class BaseLimbRig(BaseRig):
self.make_constraint(mch.ik_swing, 'DAMPED_TRACK', mch.ik_target)
self.rig_ik_mch_stretch_limit(mch.ik_target, mch.follow, input_bone, self.ik_input_head_tail, 2)
self.rig_ik_mch_stretch_limit(
mch.ik_target, mch.follow, input_bone, self.ik_input_head_tail, 2)
self.rig_ik_mch_end_bone(mch.ik_end, mch.ik_target, self.bones.ctrl.ik_pole)
def rig_ik_mch_stretch_limit(self, mch_target, base_bone, input_bone, head_tail, org_count, bias=1.035):
def rig_ik_mch_stretch_limit(self, mch_target: str, base_bone: str, input_bone: str,
head_tail: float, org_count: int, bias=1.035):
# Compute increase in length to fully straighten
orgs = self.bones.org.main[0:org_count]
len_full = sum(self.get_bone(org).length for org in orgs)
@ -662,6 +677,7 @@ class BaseLimbRig(BaseRig):
self.make_constraint(mch_target, 'COPY_LOCATION', input_bone, head_tail=head_tail)
# Limit distance from the base of the limb
# noinspection SpellCheckingInspection
con = self.make_constraint(
mch_target, 'LIMIT_DISTANCE', base_bone,
limit_mode='LIMITDIST_INSIDE', distance=len_full*bias,
@ -669,29 +685,31 @@ class BaseLimbRig(BaseRig):
space='CUSTOM', space_object=self.obj, space_subtarget=self.bones.mch.follow,
)
self.make_driver(con, "influence", variables=[(self.prop_bone, 'IK_Stretch')], polynomial=[1.0, -1.0])
self.make_driver(con, "influence",
variables=[(self.prop_bone, 'IK_Stretch')], polynomial=[1.0, -1.0])
def rig_ik_mch_end_bone(self, mch_ik, mch_target, ctrl_pole, chain=2):
def rig_ik_mch_end_bone(self, mch_ik: str, mch_target: str, ctrl_pole: str, chain=2):
con = self.make_constraint(
mch_ik, 'IK', mch_target, chain_count=chain,
)
self.make_driver(con, "mute", variables=[(self.prop_bone, 'pole_vector')], polynomial=[0.0, 1.0])
self.make_driver(con, "mute",
variables=[(self.prop_bone, 'pole_vector')], polynomial=[0.0, 1.0])
con_pole = self.make_constraint(
mch_ik, 'IK', mch_target, chain_count=chain,
pole_target=self.obj, pole_subtarget=ctrl_pole, pole_angle=self.pole_angle,
)
self.make_driver(con_pole, "mute", variables=[(self.prop_bone, 'pole_vector')], polynomial=[1.0, -1.0])
self.make_driver(con_pole, "mute",
variables=[(self.prop_bone, 'pole_vector')], polynomial=[1.0, -1.0])
def rig_hide_pole_control(self, name):
def rig_hide_pole_control(self, name: str):
self.make_driver(
self.get_bone(name).bone, "hide",
variables=[(self.prop_bone, 'pole_vector')], polynomial=[1.0, -1.0],
)
####################################################
# ORG chain
@ -707,14 +725,14 @@ class BaseLimbRig(BaseRig):
for args in zip(count(0), self.bones.org.main, self.bones.ctrl.fk, padnone(ik)):
self.rig_org_bone(*args)
def rig_org_bone(self, i, org, fk, ik):
def rig_org_bone(self, i: int, org: str, fk: str, ik: str | None):
self.make_constraint(org, 'COPY_TRANSFORMS', fk)
if ik:
con = self.make_constraint(org, 'COPY_TRANSFORMS', ik)
self.make_driver(con, 'influence', variables=[(self.prop_bone, 'IK_FK')], polynomial=[1.0, -1.0])
self.make_driver(con, 'influence',
variables=[(self.prop_bone, 'IK_FK')], polynomial=[1.0, -1.0])
####################################################
# Tweak control chain
@ -723,7 +741,7 @@ class BaseLimbRig(BaseRig):
def make_tweak_chain(self):
self.bones.ctrl.tweak = map_list(self.make_tweak_bone, count(0), self.segment_table_tweak)
def make_tweak_bone(self, i, entry):
def make_tweak_bone(self, _i: int, entry: SegmentEntry):
name = make_derived_name(entry.org, 'ctrl', '_tweak')
name = self.copy_bone(entry.org, name, scale=1/(2 * self.segments))
put_bone(self.obj, name, entry.pos)
@ -741,7 +759,7 @@ class BaseLimbRig(BaseRig):
ControlLayersOption.TWEAK.assign_rig(self, self.bones.ctrl.tweak)
def configure_tweak_bone(self, i, tweak, entry):
def configure_tweak_bone(self, i: int, tweak: str, entry: SegmentEntry):
tweak_pb = self.get_bone(tweak)
tweak_pb.lock_rotation = (True, False, True)
tweak_pb.lock_scale = (False, True, False)
@ -750,11 +768,11 @@ class BaseLimbRig(BaseRig):
if i > 0 and entry.seg_idx is not None:
self.make_rubber_tweak_property(i, tweak, entry)
def make_rubber_tweak_property(self, i, tweak, entry):
defval = 1.0 if entry.seg_idx else 0.0
def make_rubber_tweak_property(self, _i: int, tweak: str, entry: SegmentEntry):
def_val = 1.0 if entry.seg_idx else 0.0
text = 'Rubber Tweak ({})'.format(strip_org(entry.org))
self.make_property(tweak, 'rubber_tweak', defval, max=2.0, soft_max=1.0)
self.make_property(tweak, 'rubber_tweak', def_val, max=2.0, soft_max=1.0)
panel = self.script.panel_with_selected_check(self, [tweak])
panel.custom_prop(tweak, 'rubber_tweak', text=text, slider=True)
@ -764,10 +782,9 @@ class BaseLimbRig(BaseRig):
for tweak in self.bones.ctrl.tweak:
self.make_tweak_widget(tweak)
def make_tweak_widget(self, tweak):
def make_tweak_widget(self, tweak: str):
create_sphere_widget(self.obj, tweak)
####################################################
# Tweak MCH chain
@ -775,7 +792,7 @@ class BaseLimbRig(BaseRig):
def make_tweak_mch_chain(self):
self.bones.mch.tweak = map_list(self.make_tweak_mch_bone, count(0), self.segment_table_tweak)
def make_tweak_mch_bone(self, i, entry):
def make_tweak_mch_bone(self, _i: int, entry: SegmentEntry):
name = make_derived_name(entry.org, 'mch', '_tweak')
name = self.copy_bone(entry.org, name, scale=1/(4 * self.segments))
put_bone(self.obj, name, entry.pos)
@ -786,7 +803,7 @@ class BaseLimbRig(BaseRig):
for args in zip(count(0), self.bones.mch.tweak, self.segment_table_tweak):
self.parent_tweak_mch_bone(*args)
def parent_tweak_mch_bone(self, i, mch, entry):
def parent_tweak_mch_bone(self, i: int, mch: str, entry: SegmentEntry):
if i == 0:
self.set_bone_parent(mch, self.rig_parent_bone, inherit_scale='FIX_SHEAR')
else:
@ -797,7 +814,7 @@ class BaseLimbRig(BaseRig):
for args in zip(count(0), self.bones.mch.tweak, self.segment_table_tweak):
self.apply_tweak_mch_bone(*args)
def apply_tweak_mch_bone(self, i, tweak, entry):
def apply_tweak_mch_bone(self, i: int, tweak: str, entry: SegmentEntry):
if entry.seg_idx:
prev_tweak, next_tweak, fac = self.get_tweak_blend(i, entry)
@ -816,7 +833,7 @@ class BaseLimbRig(BaseRig):
for args in zip(count(0), self.bones.mch.tweak, self.segment_table_tweak):
self.rig_tweak_mch_bone(*args)
def get_tweak_blend(self, i, entry):
def get_tweak_blend(self, i: int, entry: SegmentEntry):
assert entry.seg_idx
tweaks = self.bones.ctrl.tweak
@ -826,7 +843,7 @@ class BaseLimbRig(BaseRig):
return prev_tweak, next_tweak, fac
def rig_tweak_mch_bone(self, i, tweak, entry):
def rig_tweak_mch_bone(self, i: int, tweak: str, entry: SegmentEntry):
if entry.seg_idx:
prev_tweak, next_tweak, fac = self.get_tweak_blend(i, entry)
@ -841,7 +858,6 @@ class BaseLimbRig(BaseRig):
self.make_constraint(tweak, 'COPY_LOCATION', entry.org)
self.make_constraint(tweak, 'DAMPED_TRACK', entry.org, head_tail=1)
####################################################
# Deform chain
@ -849,7 +865,7 @@ class BaseLimbRig(BaseRig):
def make_deform_chain(self):
self.bones.deform = map_list(self.make_deform_bone, count(0), self.segment_table_full)
def make_deform_bone(self, i, entry):
def make_deform_bone(self, _i: int, entry: SegmentEntry):
name = make_derived_name(entry.org, 'def')
if entry.seg_idx is None:
@ -874,7 +890,9 @@ class BaseLimbRig(BaseRig):
for args in zip(count(0), self.bones.deform, *entries, *tweaks):
self.rig_deform_bone(*args)
def rig_deform_bone(self, i, deform, entry, next_entry, tweak, next_tweak):
def rig_deform_bone(self, i: int, deform: str,
entry: SegmentEntry, next_entry: SegmentEntry | None,
tweak: str | None, next_tweak: str | None):
if tweak:
self.make_constraint(deform, 'COPY_TRANSFORMS', tweak)
@ -889,7 +907,8 @@ class BaseLimbRig(BaseRig):
else:
self.make_constraint(deform, 'COPY_TRANSFORMS', entry.org)
def rig_deform_easing(self, i, deform, tweak, next_tweak):
# noinspection SpellCheckingInspection
def rig_deform_easing(self, _i: int, deform: str, tweak: str, next_tweak: str):
pbone = self.get_bone(deform)
if 'rubber_tweak' in self.get_bone(tweak):
@ -902,12 +921,11 @@ class BaseLimbRig(BaseRig):
else:
pbone.bone.bbone_easeout = 0.0
####################################################
# Settings
@classmethod
def add_parameters(self, params):
def add_parameters(cls, params):
""" Add the parameters of this rig type to the
RigifyParameters PropertyGroup
"""
@ -919,9 +937,9 @@ class BaseLimbRig(BaseRig):
]
params.rotation_axis = bpy.props.EnumProperty(
items = items,
name = "Rotation Axis",
default = 'automatic'
items=items,
name="Rotation Axis",
default='automatic'
)
params.auto_align_extremity = bpy.props.BoolProperty(
@ -931,35 +949,37 @@ class BaseLimbRig(BaseRig):
)
params.segments = bpy.props.IntProperty(
name = 'Limb Segments',
default = 2,
min = 1,
description = 'Number of limb segments'
name='Limb Segments',
default=2,
min=1,
description='Number of limb segments'
)
params.bbones = bpy.props.IntProperty(
name = 'B-Bone Segments',
default = 10,
min = 1,
description = 'Number of B-Bone segments'
name='B-Bone Segments',
default=10,
min=1,
description='Number of B-Bone segments'
)
params.make_custom_pivot = bpy.props.BoolProperty(
name = "Custom Pivot Control",
default = False,
description = "Create a rotation pivot control that can be repositioned arbitrarily"
name="Custom Pivot Control",
default=False,
description="Create a rotation pivot control that can be repositioned arbitrarily"
)
params.ik_local_location = bpy.props.BoolProperty(
name = "IK Local Location",
default = True,
description = "Specifies the value of the Local Location option for IK controls, which decides if the location channels are aligned to the local control orientation or world",
name="IK Local Location",
default=True,
description="Specifies the value of the Local Location option for IK controls, which "
"decides if the location channels are aligned to the local control "
"orientation or world",
)
params.limb_uniform_scale = bpy.props.BoolProperty(
name = "Support Uniform Scaling",
default = False,
description = "Support uniformly scaling the limb via the gear control at the base"
name="Support Uniform Scaling",
default=False,
description="Support uniformly scaling the limb via the gear control at the base"
)
# Setting up extra layers for the FK and tweak
@ -967,7 +987,7 @@ class BaseLimbRig(BaseRig):
ControlLayersOption.TWEAK.add_parameters(params)
@classmethod
def parameters_ui(self, layout, params, end='End'):
def parameters_ui(cls, layout, params, end='End'):
""" Create the ui for the rig parameters."""
r = layout.row()
@ -997,6 +1017,7 @@ class BaseLimbRig(BaseRig):
SCRIPT_REGISTER_OP_SNAP_IK_FK = ['POSE_OT_rigify_limb_ik2fk', 'POSE_OT_rigify_limb_ik2fk_bake']
# noinspection SpellCheckingInspection
SCRIPT_UTILITIES_OP_SNAP_IK_FK = UTILITIES_FUNC_COMMON_IKFK + ['''
########################
## Limb Snap IK to FK ##
@ -1130,7 +1151,14 @@ class POSE_OT_rigify_limb_ik2fk_bake(RigifyLimbIk2FkBase, RigifyBakeKeyframesMix
return self.bake_get_all_bone_curves(self.ctrl_bone_list + self.extra_ctrl_list, TRANSFORM_PROPS_ALL)
''']
def add_limb_snap_ik_to_fk(panel, *, master=None, fk_bones=[], ik_bones=[], tail_bones=[], ik_ctrl_bones=[], ik_extra_ctrls=[], rig_name=''):
# noinspection PyDefaultArgument
def add_limb_snap_ik_to_fk(panel: 'PanelLayout', *,
master: Optional[str] = None,
fk_bones: list[str] = [],
ik_bones: list[str] = [], tail_bones: list[str] = [],
ik_ctrl_bones: list[str] = [], ik_extra_ctrls: list[str] = [],
rig_name=''):
panel.use_bake_settings()
panel.script.add_utilities(SCRIPT_UTILITIES_OP_SNAP_IK_FK)
panel.script.register_classes(SCRIPT_REGISTER_OP_SNAP_IK_FK)
@ -1152,12 +1180,14 @@ def add_limb_snap_ik_to_fk(panel, *, master=None, fk_bones=[], ik_bones=[], tail
clear_bones=ik_ctrl_bones + tail_bones + ik_extra_ctrls,
)
#########################
# Toggle Pole operator ##
#########################
SCRIPT_REGISTER_OP_TOGGLE_POLE = ['POSE_OT_rigify_limb_toggle_pole', 'POSE_OT_rigify_limb_toggle_pole_bake']
# noinspection SpellCheckingInspection
SCRIPT_UTILITIES_OP_TOGGLE_POLE = SCRIPT_UTILITIES_OP_SNAP_IK_FK + ['''
####################
## Toggle IK Pole ##
@ -1230,7 +1260,12 @@ class POSE_OT_rigify_limb_toggle_pole_bake(RigifyLimbTogglePoleBase, RigifyBakeK
self.layout.prop(self, 'use_pole')
''']
def add_limb_toggle_pole(panel, *, master=None, ik_bones=[], ik_ctrl_bones=[], ik_extra_ctrls=[]):
# noinspection PyDefaultArgument
def add_limb_toggle_pole(panel: 'PanelLayout', *,
master: Optional[str] = None,
ik_bones: list[str] = [], ik_ctrl_bones: list[str] = [],
ik_extra_ctrls: list[str] = []):
panel.use_bake_settings()
panel.script.add_utilities(SCRIPT_UTILITIES_OP_TOGGLE_POLE)
panel.script.register_classes(SCRIPT_REGISTER_OP_TOGGLE_POLE)
@ -1243,7 +1278,9 @@ def add_limb_toggle_pole(panel, *, master=None, ik_bones=[], ik_ctrl_bones=[], i
}
row = panel.row(align=True)
lsplit = row.split(factor=0.75, align=True)
lsplit.operator('pose.rigify_limb_toggle_pole_{rig_id}', icon='FORCE_MAGNETIC', properties=op_props)
lsplit.custom_prop(master, 'pole_vector', text='')
row.operator('pose.rigify_limb_toggle_pole_bake_{rig_id}', text='', icon='ACTION_TWEAK', properties=op_props)
left_split = row.split(factor=0.75, align=True)
left_split.operator('pose.rigify_limb_toggle_pole_{rig_id}',
icon='FORCE_MAGNETIC', properties=op_props)
left_split.custom_prop(master, 'pole_vector', text='')
row.operator('pose.rigify_limb_toggle_pole_bake_{rig_id}',
text='', icon='ACTION_TWEAK', properties=op_props)

View File

@ -1,16 +1,19 @@
# SPDX-License-Identifier: GPL-2.0-or-later
import bpy, re
import bpy
import re
from mathutils import Vector
from ...utils import org, strip_org, make_mechanism_name, make_deformer_name
from ...utils import MetarigError
bilateral_suffixes = ['.L','.R']
bilateral_suffixes = ['.L', '.R']
def orient_bone( cls, eb, axis, scale = 1.0, reverse = False ):
v = Vector((0,0,0))
setattr(v,axis,scale)
def orient_bone(cls, eb, axis, scale=1.0, reverse=False):
v = Vector((0, 0, 0))
setattr(v, axis, scale)
if reverse:
tail_vec = v @ cls.obj.matrix_world
@ -22,51 +25,52 @@ def orient_bone( cls, eb, axis, scale = 1.0, reverse = False ):
eb.roll = 0.0
def make_constraint( cls, bone, constraint ):
bpy.ops.object.mode_set(mode = 'OBJECT')
def make_constraint(cls, bone, constraint):
bpy.ops.object.mode_set(mode='OBJECT')
pb = cls.obj.pose.bones
owner_pb = pb[bone]
const = owner_pb.constraints.new( constraint['constraint'] )
const = owner_pb.constraints.new(constraint['constraint'])
constraint['target'] = cls.obj
# filter constraint props to those that actually exist in the current
# type of constraint, then assign values to each
for p in [ k for k in constraint.keys() if k in dir(const) ]:
if p in dir( const ):
setattr( const, p, constraint[p] )
for p in [k for k in constraint.keys() if k in dir(const)]:
if p in dir(const):
setattr(const, p, constraint[p])
else:
raise MetarigError(
"RIGIFY ERROR: property %s does not exist in %s constraint" % (
p, constraint['constraint']
))
))
def get_bone_name( name, btype, suffix = '' ):
def get_bone_name(name, btype, suffix=''):
# RE pattern match right or left parts
# match the letter "L" (or "R"), followed by an optional dot (".")
# and 0 or more digits at the end of the the string
# and 0 or more digits at the end of the string
pattern = r'^(\S+)(\.\S+)$'
name = strip_org( name )
name = strip_org(name)
types = {
'mch' : make_mechanism_name( name ),
'org' : org( name ),
'def' : make_deformer_name( name ),
'ctrl' : name
'mch': make_mechanism_name(name),
'org': org(name),
'def': make_deformer_name(name),
'ctrl': name
}
name = types[btype]
if suffix:
results = re.match( pattern, name )
bname, addition = ('','')
results = re.match(pattern, name)
if results:
bname, addition = results.groups()
name = bname + "_" + suffix + addition
bone_name, addition = results.groups()
name = bone_name + "_" + suffix + addition
else:
name = name + "_" + suffix
name = name + "_" + suffix
return name

View File

@ -22,6 +22,8 @@ class Rig(BaseLimbRig):
max_valid_orgs = 5
toe_bone_index = 3
use_heel2: bool
def initialize(self):
self.use_heel2 = len(self.bones.org.main) > 4
@ -54,7 +56,7 @@ class Rig(BaseLimbRig):
####################################################
# Utilities
def align_ik_control_bone(self, name):
def align_ik_control_bone(self, name: str):
if self.params.rotation_axis == 'automatic' or self.params.auto_align_extremity:
align_bone_to_axis(self.obj, name, 'y', flip=True)
@ -65,22 +67,23 @@ class Rig(BaseLimbRig):
bone.tail[2] = bone.head[2]
bone.roll = 0
####################################################
# EXTRA BONES
#
# ctrl:
# heel:
# Foot heel control
# heel2 (optional):
# Second foot heel control
# mch:
# toe_socket:
# IK toe orientation bone.
# ik_heel2 (optional):
# Final position of heel2 in the IK output.
#
####################################################
class CtrlBones(BaseLimbRig.CtrlBones):
heel: str # Foot heel control
heel2: str # Second foot heel control (optional)
class MchBones(BaseLimbRig.MchBones):
toe_socket: str # IK toe orientation bone
ik_heel2: str # Final position of heel2 in the IK output
bones: BaseLimbRig.ToplevelBones[
BaseLimbRig.OrgBones,
'Rig.CtrlBones',
'Rig.MchBones',
list[str]
]
####################################################
# IK controls
@ -92,10 +95,10 @@ class Rig(BaseLimbRig):
extra = [self.bones.ctrl.heel2] if self.use_heel2 else [self.bones.ctrl.heel]
return super().get_extra_ik_controls() + extra
def make_ik_control_bone(self, orgs):
def make_ik_control_bone(self, orgs: list[str]):
return self.make_paw_ik_control_bone(orgs[-2], orgs[-1], orgs[2])
def make_paw_ik_control_bone(self, org_one, org_two, org_name):
def make_paw_ik_control_bone(self, org_one: str, org_two: str, org_name: str):
name = self.copy_bone(org_two, make_derived_name(org_name, 'ctrl', '_ik'))
self.align_ik_control_bone(name)
@ -113,7 +116,6 @@ class Rig(BaseLimbRig):
def make_ik_ctrl_widget(self, ctrl):
create_foot_widget(self.obj, ctrl)
####################################################
# Heel control
@ -141,7 +143,6 @@ class Rig(BaseLimbRig):
def generate_heel_control_widget(self):
create_ballsocket_widget(self.obj, self.bones.ctrl.heel)
####################################################
# Second Heel control
@ -170,7 +171,6 @@ class Rig(BaseLimbRig):
if self.use_heel2:
create_ballsocket_widget(self.obj, self.bones.ctrl.heel2)
####################################################
# FK control chain
@ -182,7 +182,6 @@ class Rig(BaseLimbRig):
else:
create_circle_widget(self.obj, ctrl, radius=0.4, head_tail=0.5)
####################################################
# FK parents MCH chain
@ -211,7 +210,6 @@ class Rig(BaseLimbRig):
else:
super().rig_fk_parent_bone(i, parent_mch, org)
####################################################
# IK system MCH
@ -226,7 +224,6 @@ class Rig(BaseLimbRig):
self.set_bone_parent(self.bones.mch.ik_target, self.bones.ctrl.heel)
####################################################
# IK heel2 output
@ -250,7 +247,6 @@ class Rig(BaseLimbRig):
if self.use_heel2:
self.make_constraint(self.bones.mch.ik_heel2, 'COPY_LOCATION', self.bones.mch.ik_target, head_tail=1)
####################################################
# Deform chain
@ -260,13 +256,12 @@ class Rig(BaseLimbRig):
if tweak and not (next_tweak or next_entry):
self.make_constraint(deform, 'STRETCH_TO', entry.org, head_tail=1.0, keep_axis='SWING_Y')
####################################################
# Settings
@classmethod
def parameters_ui(self, layout, params):
super().parameters_ui(layout, params, 'Claw')
def parameters_ui(cls, layout, params, end='Claw'):
super().parameters_ui(layout, params, end)
def create_sample(obj):
@ -313,11 +308,17 @@ def create_sample(obj):
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'QUATERNION'
try:
pbone.rigify_parameters.fk_layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
pbone.rigify_parameters.fk_layers = [
False, False, False, False, False, False, False, False, False, False, False, False,
False, False, False, False, False, True, False, False, False, False, False, False,
False, False, False, False, False, False, False, False]
except AttributeError:
pass
try:
pbone.rigify_parameters.tweak_layers = [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False]
pbone.rigify_parameters.tweak_layers = [
False, False, False, False, False, False, False, False, False, False, False, False,
False, False, False, False, False, False, True, False, False, False, False, False,
False, False, False, False, False, False, False, False]
except AttributeError:
pass
try:

View File

@ -1,7 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-or-later
import bpy
from ...utils.bones import align_bone_roll
from ...utils.naming import make_derived_name
from ...utils.misc import map_list
@ -18,17 +16,22 @@ class Rig(pawRig):
"""Rear paw rig with special IK automation."""
####################################################
# EXTRA BONES
#
# mch:
# ik2_target
# Three bone IK stretch limit
# ik2_chain[2]
# Second IK system (pre-driving thigh and ik3)
# ik3_chain[2]
# Third IK system (pre-driving heel)
#
####################################################
# BONES
class CtrlBones(pawRig.CtrlBones):
pass
class MchBones(pawRig.MchBones):
ik2_target: str # Three bone IK stretch limit
ik2_chain: list[str] # Second IK system (pre-driving thigh and ik3)
ik3_chain: list[str] # Third IK system (pre-driving heel)
bones: pawRig.ToplevelBones[
pawRig.OrgBones,
'Rig.CtrlBones',
'Rig.MchBones',
list[str]
]
####################################################
# IK controls
@ -50,7 +53,6 @@ class Rig(pawRig):
def get_ik_pole_parents(self):
return [(self.bones.mch.ik2_target, self.bones.ctrl.ik)]
####################################################
# Heel control
@ -58,7 +60,6 @@ class Rig(pawRig):
def parent_heel_control_bone(self):
self.set_bone_parent(self.bones.ctrl.heel, self.bones.mch.ik3_chain[-1])
####################################################
# Second IK system (pre-driving thigh)
@ -73,7 +74,7 @@ class Rig(pawRig):
self.bones.mch.ik2_target = self.make_ik2_mch_target_bone(orgs)
def make_ik2_mch_target_bone(self, orgs):
def make_ik2_mch_target_bone(self, orgs: list[str]):
return self.copy_bone(orgs[3], make_derived_name(orgs[0], 'mch', '_ik2_target'), scale=1/2)
@stage.generate_bones
@ -91,7 +92,7 @@ class Rig(pawRig):
chain_bones[1].tail = org_bones[2].tail
align_bone_roll(self.obj, chain[1], orgs[1])
def make_ik2_mch_bone(self, i, org):
def make_ik2_mch_bone(self, _i: int, org: str):
return self.copy_bone(org, make_derived_name(org, 'mch', '_ik2'))
@stage.parent_bones
@ -106,7 +107,7 @@ class Rig(pawRig):
for i, mch in enumerate(self.bones.mch.ik2_chain):
self.configure_ik2_mch_bone(i, mch)
def configure_ik2_mch_bone(self, i, mch):
def configure_ik2_mch_bone(self, i: int, mch: str):
bone = self.get_bone(mch)
bone.ik_stretch = 0.1
if i == 1:
@ -122,7 +123,6 @@ class Rig(pawRig):
self.rig_ik_mch_stretch_limit(mch.ik2_target, mch.follow, input_bone, head_tail, 3)
self.rig_ik_mch_end_bone(mch.ik2_chain[-1], mch.ik2_target, self.bones.ctrl.ik_pole)
####################################################
# Third IK system (pre-driving heel control)
@ -130,7 +130,7 @@ class Rig(pawRig):
def make_ik3_mch_chain(self):
self.bones.mch.ik3_chain = map_list(self.make_ik3_mch_bone, count(0), self.bones.org.main[1:3])
def make_ik3_mch_bone(self, i, org):
def make_ik3_mch_bone(self, _i: int, org: str):
return self.copy_bone(org, make_derived_name(org, 'mch', '_ik3'))
@stage.parent_bones
@ -145,7 +145,7 @@ class Rig(pawRig):
for i, mch in enumerate(self.bones.mch.ik3_chain):
self.configure_ik3_mch_bone(i, mch)
def configure_ik3_mch_bone(self, i, mch):
def configure_ik3_mch_bone(self, i: int, mch: str):
bone = self.get_bone(mch)
bone.ik_stretch = 0.1
if i == 0:

View File

@ -50,7 +50,7 @@ class Rig(TweakChainRig):
@stage.rig_bones
def rig_control_chain(self):
ctrls = self.bones.ctrl.fk
for args in zip(count(0), ctrls, [None] + ctrls):
for args in zip(count(0), ctrls, [None, *ctrls]):
self.rig_control_bone(*args)
def rig_control_bone(self, _i: int, ctrl: str, prev_ctrl: Optional[str]):

View File

@ -50,7 +50,12 @@ class Rig(SimpleChainRig):
stretch: list[str] # Stretch system
bend: list[str] # Bend system
bones: SimpleChainRig.ToplevelBones[list[str], CtrlBones, MchBones, list[str]]
bones: SimpleChainRig.ToplevelBones[
list[str],
'Rig.CtrlBones',
'Rig.MchBones',
list[str]
]
##############################
# Master Control

View File

@ -9,12 +9,12 @@ from .limb_rigs import BaseLimbRig
from . import arm, leg, paw
RIGS = { 'arm': arm.Rig, 'leg': leg.Rig, 'paw': paw.Rig }
RIGS = {'arm': arm.Rig, 'leg': leg.Rig, 'paw': paw.Rig}
class Rig(SubstitutionRig):
def substitute(self):
return [ self.instantiate_rig(RIGS[self.params.limb_type], self.base_bone) ]
return [self.instantiate_rig(RIGS[self.params.limb_type], self.base_bone)]
def add_parameters(params):
@ -25,9 +25,9 @@ def add_parameters(params):
]
params.limb_type = bpy.props.EnumProperty(
items = items,
name = "Limb Type",
default = 'arm'
items=items,
name="Limb Type",
default='arm'
)
BaseLimbRig.add_parameters(params)

View File

@ -4,18 +4,20 @@ import bpy
import re
from math import cos, pi
from itertools import count, repeat
from itertools import count
from bpy.types import PoseBone
from rigify.utils.rig import is_rig_base_bone
from rigify.utils.naming import strip_org, make_derived_name, choose_derived_bone
from rigify.utils.widgets import widget_generator, register_widget
from rigify.utils.widgets_basic import create_bone_widget
from rigify.utils.misc import map_list
from rigify.utils.misc import map_list, ArmatureObject
from rigify.base_rig import BaseRig, stage
def bone_siblings(obj, bone):
def bone_siblings(obj: ArmatureObject, bone: str) -> list[str]:
""" Returns a list of the siblings of the given bone.
This requires that the bones has a parent.
"""
@ -38,7 +40,15 @@ class Rig(BaseRig):
This is a control and deformation rig.
"""
def find_org_bones(self, bone):
palm_rotation_axis: str
make_secondary: bool
make_fk: bool
rig_parent_bone: str
order: str
ctrl_name: str
def find_org_bones(self, bone: PoseBone) -> list[str]:
base_head = bone.bone.head
siblings = bone_siblings(self.obj, bone.name)
@ -72,23 +82,21 @@ class Rig(BaseRig):
####################################################
# BONES
#
# org[]:
# Original bones in order of distance.
# ctrl:
# master:
# Main control.
# secondary:
# Control for the other side.
# fk[]:
# Optional individual FK controls
# mch:
# fk_parents[]:
# Parents for the individual FK controls
# deform[]:
# DEF bones
#
####################################################
class CtrlBones(BaseRig.CtrlBones):
master: str # Main control.
secondary: str # Control for the other side.
fk: list[str] # Optional individual FK controls
class MchBones(BaseRig.MchBones):
fk_parents: list[str] # Parents for the individual FK controls
bones: BaseRig.ToplevelBones[
list[str], # Original bones in order of distance.
'Rig.CtrlBones',
'Rig.MchBones',
list[str]
]
####################################################
# Master control
@ -117,7 +125,7 @@ class Rig(BaseRig):
if self.make_secondary:
self.configure_control_bone(self.bones.ctrl.secondary, self.bones.org[0])
def configure_control_bone(self, ctrl, org):
def configure_control_bone(self, ctrl: str, org: str):
self.copy_bone_properties(org, ctrl)
self.get_bone(ctrl).lock_scale = (True, True, True)
@ -128,7 +136,7 @@ class Rig(BaseRig):
if self.make_secondary:
self.make_control_widget(self.bones.ctrl.secondary)
def make_control_widget(self, ctrl):
def make_control_widget(self, ctrl: str):
make_palm_widget(self.obj, ctrl, axis=self.palm_rotation_axis, radius=0.4)
####################################################
@ -139,7 +147,7 @@ class Rig(BaseRig):
if self.make_fk:
self.bones.ctrl.fk = map_list(self.make_fk_control_bone, count(0), self.bones.org)
def make_fk_control_bone(self, i, org):
def make_fk_control_bone(self, _i: int, org: str):
return self.copy_bone(org, make_derived_name(org, 'ctrl', '_fk'))
@stage.parent_bones
@ -168,7 +176,7 @@ class Rig(BaseRig):
if self.make_fk:
self.bones.mch.fk_parents = map_list(self.make_fk_parent_bone, count(0), self.bones.org)
def make_fk_parent_bone(self, i, org):
def make_fk_parent_bone(self, _i: int, org: str):
return self.copy_bone(org, make_derived_name(org, 'mch', '_fk_parent'))
@stage.parent_bones
@ -177,7 +185,7 @@ class Rig(BaseRig):
for i, mch in enumerate(self.bones.mch.fk_parents):
self.parent_mch_fk_parent_bone(i, mch)
def parent_mch_fk_parent_bone(self, i, mch):
def parent_mch_fk_parent_bone(self, _i: int, mch: str):
self.set_bone_parent(mch, self.rig_parent_bone, inherit_scale='NONE')
@stage.rig_bones
@ -186,7 +194,7 @@ class Rig(BaseRig):
for i, mch in enumerate(self.bones.mch.fk_parents):
self.rig_mch_fk_parent_bone(i, mch)
def rig_mch_fk_parent_bone(self, i, org):
def rig_mch_fk_parent_bone(self, i: int, org: str):
num_orgs = len(self.bones.org)
ctrl = self.bones.ctrl
fac = i / (num_orgs - 1)
@ -219,20 +227,20 @@ class Rig(BaseRig):
if self.make_secondary:
self.rig_mch_back_rotation(org, ctrl.secondary, 1 - fac)
def rig_mch_back_rotation(self, org, ctrl, fac):
def rig_mch_back_rotation(self, org: str, ctrl: str, fac: float):
if 0 < fac < 1:
inf = (fac + 1) * (fac + cos(fac * pi / 2) - 1)
if 'X' in self.palm_rotation_axis:
self.make_constraint(
org, 'COPY_ROTATION', ctrl, space='LOCAL',
invert_x=True, use_xyz=(True,False,False),
invert_x=True, use_xyz=(True, False, False),
euler_order=self.order, mix_mode='ADD', influence=inf
)
else:
self.make_constraint(
org, 'COPY_ROTATION', ctrl, space='LOCAL',
invert_z=True, use_xyz=(False,False,True),
invert_z=True, use_xyz=(False, False, True),
euler_order=self.order, mix_mode='ADD', influence=inf
)
@ -261,7 +269,7 @@ class Rig(BaseRig):
def make_def_chain(self):
self.bones.deform = map_list(self.make_deform_bone, self.bones.org)
def make_deform_bone(self, org):
def make_deform_bone(self, org: str):
return self.copy_bone(org, make_derived_name(org, 'def'))
@stage.parent_bones
@ -286,9 +294,9 @@ class Rig(BaseRig):
description="Create controls for both sides of the palm"
)
params.make_extra_control = bpy.props.BoolProperty(
name = "Extra Control",
default = False,
description = "Create an optional control"
name="Extra Control",
default=False,
description="Create an optional control"
)
@classmethod
@ -309,7 +317,7 @@ def make_palm_widget(geom, axis='X', radius=0.5):
(0.1578, 0.25, -0.275), (-0.1578, 0.25, -0.275), (0.1578, 0.75, -0.225), (-0.1578, 0.75, -0.225),
(0.1578, 0.75, 0.225), (0.1578, 0.25, 0.275), (-0.1578, 0.25, 0.275), (-0.1578, 0.75, 0.225)]
geom.verts = [(x*sx, y, z*sz) for x,y,z in v]
geom.verts = [(x*sx, y, z*sz) for x, y, z in v]
if 'Z' in axis:
# Flip x/z coordinates
@ -318,6 +326,7 @@ def make_palm_widget(geom, axis='X', radius=0.5):
geom.edges = [(1, 2), (0, 3), (4, 7), (5, 6), (8, 0), (9, 3), (10, 1), (11, 2), (12, 6),
(13, 7), (4, 14), (15, 5), (10, 8), (11, 9), (15, 14), (12, 13)]
register_widget("palm_z", make_palm_widget, axis='Z')

View File

@ -54,7 +54,12 @@ class Rig(BaseSpineRig):
wgt_hips: str # Hip widget position bone.
wgt_chest: str # Chest widget position bone.
bones: BaseSpineRig.ToplevelBones[list[str], CtrlBones, MchBones, list[str]]
bones: BaseSpineRig.ToplevelBones[
list[str],
'Rig.CtrlBones',
'Rig.MchBones',
list[str]
]
####################################################
# Master control bone

View File

@ -30,6 +30,22 @@ class Rig(BaseHeadTailRig):
if self.connected_tweak and self.use_connect_reverse:
self.rig_parent_bone = self.connected_tweak
####################################################
# BONES
class CtrlBones(BaseHeadTailRig.CtrlBones):
master: str # Master control.
class MchBones(BaseHeadTailRig.MchBones):
rot_tail: str # Tail follow system.
bones: BaseHeadTailRig.ToplevelBones[
list[str],
'Rig.CtrlBones',
'Rig.MchBones',
list[str]
]
####################################################
# Master control

View File

@ -44,7 +44,12 @@ class BaseSpineRig(TweakChainRig):
class MchBones(TweakChainRig.MchBones):
master_pivot: str # Final output of the custom pivot (conditional)
bones: TweakChainRig.ToplevelBones[list[str], CtrlBones, MchBones, list[str]]
bones: TweakChainRig.ToplevelBones[
list[str],
'BaseSpineRig.CtrlBones',
'BaseSpineRig.MchBones',
list[str]
]
####################################################
# Master control bone

View File

@ -48,7 +48,12 @@ class Rig(BaseHeadTailRig):
ik: list[str] # Long neck IK system
chain: list[str] # Tweak parents
bones: BaseHeadTailRig.ToplevelBones[list[str], CtrlBones, MchBones, list[str]]
bones: BaseHeadTailRig.ToplevelBones[
list[str],
'Rig.CtrlBones',
'Rig.MchBones',
list[str]
]
####################################################
# Main control bones

View File

@ -404,9 +404,8 @@ class BoneUtilityMixin(object):
return name
def copy_bone_properties(self, src_name: str, tgt_name: str, *,
props=True,
ui_controls: list[str] | bool | None = None,
**kwargs):
transforms=True, props=True, widget=True,
ui_controls: list[str] | bool | None = None):
"""Copy pose-mode properties of the bone. For using ui_controls, self must be a Rig."""
if ui_controls:
@ -420,7 +419,10 @@ class BoneUtilityMixin(object):
ui_controls = self.bones.flatten('ctrl')
copy_bone_properties(
self.obj, src_name, tgt_name, props=props and not ui_controls, **kwargs)
self.obj, src_name, tgt_name,
props=props and not ui_controls,
transforms=transforms, widget=widget,
)
if props and ui_controls:
from .mechanism import copy_custom_properties_with_ui

View File

@ -3,7 +3,7 @@
import bpy
import re
from typing import TYPE_CHECKING, Optional, Any, Collection
from typing import TYPE_CHECKING, Optional, Any, Sequence
from bpy.types import (bpy_prop_collection, Material, Object, PoseBone, Driver, FCurve,
DriverTarget, ID, bpy_struct, FModifierGenerator, Constraint, AnimData,
@ -12,7 +12,7 @@ from bpy.types import (bpy_prop_collection, Material, Object, PoseBone, Driver,
from rna_prop_ui import rna_idprop_ui_create
from rna_prop_ui import rna_idprop_quote_path as quote_property
from .misc import force_lazy, ArmatureObject, Lazy
from .misc import force_lazy, ArmatureObject, Lazy, OptionalLazy
if TYPE_CHECKING:
from ..base_rig import BaseRig
@ -37,14 +37,14 @@ def _set_default_attr(obj, options, attr, value):
def make_constraint(
owner: Object | PoseBone, con_type: str,
target: Optional[Object] = None,
subtarget: Optional[str] = None, *,
subtarget: OptionalLazy[str] = None, *,
insert_index: Optional[int] = None,
space: Optional[str] = None,
track_axis: Optional[str] = None,
use_xyz: Optional[Collection[bool]] = None,
use_limit_xyz: Optional[Collection[bool]] = None,
invert_xyz: Optional[Collection[bool]] = None,
targets: list[Lazy[str | tuple | dict]] = None,
use_xyz: Optional[Sequence[bool]] = None,
use_limit_xyz: Optional[Sequence[bool]] = None,
invert_xyz: Optional[Sequence[bool]] = None,
targets: Optional[list[Lazy[str | tuple | dict]]] = None,
**options):
"""
Creates and initializes constraint of the specified type for the owner bone.
@ -130,8 +130,9 @@ def make_constraint(
# noinspection PyShadowingBuiltins
def make_property(
owner, name: str, default, *, min=0.0, max=1.0, soft_min=None, soft_max=None,
description: Optional[str] = None, overridable=True, **options):
owner: bpy_struct, name: str, default, *, min=0.0, max=1.0, soft_min=None, soft_max=None,
description: Optional[str] = None, overridable=True, subtype: Optional[str] = None,
**options):
"""
Creates and initializes a custom property of owner.
@ -145,7 +146,8 @@ def make_property(
min=min, max=max, soft_min=soft_min, soft_max=soft_max,
description=description or name,
overridable=overridable,
**options
subtype=subtype,
**options # noqa
)
@ -593,17 +595,49 @@ class MechanismUtilityMixin(object):
Requires self.obj to be the armature object being worked on.
"""
def make_constraint(self, bone: str, con_type: str,
subtarget: OptionalLazy[str] = None, *,
insert_index: Optional[int] = None,
space: Optional[str] = None,
track_axis: Optional[str] = None,
use_xyz: Optional[Sequence[bool]] = None,
use_limit_xyz: Optional[Sequence[bool]] = None,
invert_xyz: Optional[Sequence[bool]] = None,
targets: Optional[list[Lazy[str | tuple | dict]]] = None,
**args):
assert(self.obj.mode == 'OBJECT')
return make_constraint(
self.obj.pose.bones[bone], con_type, self.obj, subtarget,
insert_index=insert_index, space=space, track_axis=track_axis,
use_xyz=use_xyz, use_limit_xyz=use_limit_xyz, invert_xyz=invert_xyz,
targets=targets,
**args)
# noinspection PyShadowingBuiltins
def make_constraint(self, bone, type, subtarget=None, **args):
def make_property(self, bone: str, name: str, default, *,
min=0.0, max=1.0, soft_min=None, soft_max=None,
description: Optional[str] = None, overridable=True,
subtype: Optional[str] = None,
**args):
assert(self.obj.mode == 'OBJECT')
return make_constraint(self.obj.pose.bones[bone], type, self.obj, subtarget, **args)
return make_property(
self.obj.pose.bones[bone], name, default,
min=min, max=max, soft_min=soft_min, soft_max=soft_max,
description=description, overridable=overridable, subtype=subtype,
**args
)
def make_property(self, bone, name, default, **args):
assert(self.obj.mode == 'OBJECT')
return make_property(self.obj.pose.bones[bone], name, default, **args)
def make_driver(self, owner, prop, **args):
# noinspection PyShadowingBuiltins,PyDefaultArgument
def make_driver(self, owner: str | bpy_struct, prop: str,
index=-1, type='SUM',
expression: Optional[str] = None,
variables: list | dict = {},
polynomial: Optional[list[float]] = None):
assert(self.obj.mode == 'OBJECT')
if isinstance(owner, str):
owner = self.obj.pose.bones[owner]
return make_driver(owner, prop, target_id=self.obj, **args)
return make_driver(
owner, prop, target_id=self.obj,
index=index, type=type, expression=expression,
variables=variables, polynomial=polynomial,
)