Rigify: rework limb IK stretch limit and add a manual swing structure.
After introduction of the Custom space it is possible to easily use Limit Distance within rigs while accounting for rig scale. This allows replacing the Stretch To + Limit Scale mechanism used for the IK stretch switch in rigify. Instead, use the freed bone to manually handle limb swing before allowing the actual IK solver to handle limb contraction. This improves stability in marginal cases of limbs nearly straight in the rest pose, because previously the solver could destroy the slight knee bend in the process of swinging the limb forward, causing a flip.
This commit is contained in:
parent
0573bc7dae
commit
293cc140f5
|
@ -162,8 +162,8 @@ class BaseLimbRig(BaseRig):
|
|||
# FK chain parents (or None)
|
||||
# ik_pivot
|
||||
# Custom IK pivot result (optional).
|
||||
# ik_stretch
|
||||
# IK stretch switch implementation.
|
||||
# ik_swing
|
||||
# Bone that tracks ik_target to manually handle limb swing.
|
||||
# ik_target
|
||||
# Corrected target position.
|
||||
# ik_base
|
||||
|
@ -422,7 +422,10 @@ class BaseLimbRig(BaseRig):
|
|||
|
||||
@stage.parent_bones
|
||||
def parent_ik_controls(self):
|
||||
self.set_bone_parent(self.bones.ctrl.ik_base, self.bones.mch.follow)
|
||||
if self.use_mch_ik_base:
|
||||
self.set_bone_parent(self.bones.ctrl.ik_base, self.bones.mch.follow)
|
||||
else:
|
||||
self.set_bone_parent(self.bones.ctrl.ik_base, self.bones.mch.ik_swing)
|
||||
|
||||
@stage.configure_bones
|
||||
def configure_ik_controls(self):
|
||||
|
@ -515,16 +518,17 @@ class BaseLimbRig(BaseRig):
|
|||
if self.use_mch_ik_base:
|
||||
self.bones.mch.ik_base = self.make_ik_mch_base_bone(orgs)
|
||||
|
||||
self.bones.mch.ik_stretch = self.make_ik_mch_stretch_bone(orgs)
|
||||
self.bones.mch.ik_swing = self.make_ik_mch_swing_bone(orgs)
|
||||
self.bones.mch.ik_target = self.make_ik_mch_target_bone(orgs)
|
||||
self.bones.mch.ik_end = self.copy_bone(orgs[1], make_derived_name(orgs[1], 'mch', '_ik'))
|
||||
|
||||
def make_ik_mch_base_bone(self, orgs):
|
||||
return self.copy_bone(orgs[0], make_derived_name(orgs[0], 'mch', '_ik'))
|
||||
|
||||
def make_ik_mch_stretch_bone(self, orgs):
|
||||
name = self.copy_bone(orgs[0], make_derived_name(orgs[0], 'mch', '_ik_stretch'))
|
||||
self.get_bone(name).tail = self.get_bone(orgs[2]).head
|
||||
def make_ik_mch_swing_bone(self, orgs):
|
||||
name = self.copy_bone(orgs[0], make_derived_name(orgs[0], 'mch', '_ik_swing'))
|
||||
bone = self.get_bone(name)
|
||||
bone.tail = bone.head + (self.get_bone(orgs[2]).head - bone.head).normalized() * bone.length * 0.3
|
||||
return name
|
||||
|
||||
def make_ik_mch_target_bone(self, orgs):
|
||||
|
@ -533,8 +537,11 @@ class BaseLimbRig(BaseRig):
|
|||
@stage.parent_bones
|
||||
def parent_ik_mch_chain(self):
|
||||
if self.use_mch_ik_base:
|
||||
self.set_bone_parent(self.bones.mch.ik_base, self.bones.ctrl.ik_base, inherit_scale='AVERAGE')
|
||||
self.set_bone_parent(self.bones.mch.ik_stretch, self.bones.mch.follow)
|
||||
self.set_bone_parent(self.bones.mch.ik_swing, self.bones.ctrl.ik_base, inherit_scale='AVERAGE')
|
||||
self.set_bone_parent(self.bones.mch.ik_base, self.bones.mch.ik_swing)
|
||||
else:
|
||||
self.set_bone_parent(self.bones.mch.ik_swing, self.bones.mch.follow)
|
||||
|
||||
self.set_bone_parent(self.bones.mch.ik_target, self.get_ik_input_bone())
|
||||
self.set_bone_parent(self.bones.mch.ik_end, self.get_ik_chain_base())
|
||||
|
||||
|
@ -608,26 +615,29 @@ class BaseLimbRig(BaseRig):
|
|||
mch = self.bones.mch
|
||||
input_bone = self.get_ik_input_bone()
|
||||
|
||||
self.rig_ik_mch_stretch_bones(mch.ik_target, mch.ik_stretch, input_bone, self.ik_input_head_tail, 2)
|
||||
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_end_bone(mch.ik_end, mch.ik_target, self.bones.ctrl.ik_pole)
|
||||
|
||||
def rig_ik_mch_stretch_bones(self, mch_target, mch_stretch, input_bone, head_tail, org_count, bias=1.035):
|
||||
def rig_ik_mch_stretch_limit(self, mch_target, base_bone, input_bone, head_tail, org_count, bias=1.035):
|
||||
# Compute increase in length to fully straighten
|
||||
orgs = self.bones.org.main[0:org_count]
|
||||
len_cur = (self.get_bone(orgs[-1]).tail - self.get_bone(orgs[0]).head).length
|
||||
len_full = sum(self.get_bone(org).length for org in orgs)
|
||||
len_scale = len_full / len_cur
|
||||
|
||||
# Limited stretch on the stretch bone
|
||||
self.make_constraint(mch_stretch, 'STRETCH_TO', input_bone, head_tail=head_tail, keep_axis='SWING_Y')
|
||||
# Snap the target to the input position
|
||||
self.make_constraint(mch_target, 'COPY_LOCATION', input_bone, head_tail=head_tail)
|
||||
|
||||
con = self.make_constraint(mch_stretch, 'LIMIT_SCALE', min_y=0.0, max_y=len_scale*bias, owner_space='LOCAL')
|
||||
# Limit distance from the base of the limb
|
||||
con = self.make_constraint(
|
||||
mch_target, 'LIMIT_DISTANCE', base_bone,
|
||||
limit_mode='LIMITDIST_INSIDE', distance=len_full*bias,
|
||||
# Use custom space to tolerate rig scaling
|
||||
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])
|
||||
|
||||
# Snap the target to the end of the stretch bone
|
||||
self.make_constraint(mch_target, 'COPY_LOCATION', mch_stretch, head_tail=1.0)
|
||||
|
||||
def rig_ik_mch_end_bone(self, mch_ik, mch_target, ctrl_pole, chain=2):
|
||||
con = self.make_constraint(
|
||||
mch_ik, 'IK', mch_target, chain_count=chain,
|
||||
|
|
|
@ -39,7 +39,7 @@ class Rig(pawRig):
|
|||
# EXTRA BONES
|
||||
#
|
||||
# mch:
|
||||
# ik2_stretch, ik2_target
|
||||
# ik2_target
|
||||
# Three bone IK stretch limit
|
||||
# ik2_chain[2]
|
||||
# Second IK system (pre-driving thigh and ik3)
|
||||
|
@ -88,14 +88,8 @@ class Rig(pawRig):
|
|||
def make_ik2_mch_stretch(self):
|
||||
orgs = self.bones.org.main
|
||||
|
||||
self.bones.mch.ik2_stretch = self.make_ik2_mch_stretch_bone(orgs)
|
||||
self.bones.mch.ik2_target = self.make_ik2_mch_target_bone(orgs)
|
||||
|
||||
def make_ik2_mch_stretch_bone(self, orgs):
|
||||
name = self.copy_bone(orgs[0], make_derived_name(orgs[0], 'mch', '_ik2_stretch'))
|
||||
self.get_bone(name).tail = self.get_bone(orgs[3]).head
|
||||
return name
|
||||
|
||||
def make_ik2_mch_target_bone(self, orgs):
|
||||
return self.copy_bone(orgs[3], make_derived_name(orgs[0], 'mch', '_ik2_target'), scale=1/2)
|
||||
|
||||
|
@ -120,7 +114,6 @@ class Rig(pawRig):
|
|||
@stage.parent_bones
|
||||
def parent_ik2_mch_chain(self):
|
||||
mch = self.bones.mch
|
||||
self.set_bone_parent(mch.ik2_stretch, mch.follow)
|
||||
self.set_bone_parent(mch.ik2_target, self.get_ik2_input_bone())
|
||||
self.set_bone_parent(mch.ik2_chain[0], self.bones.ctrl.ik_base, inherit_scale='AVERAGE')
|
||||
self.parent_bone_chain(mch.ik2_chain, use_connect=True)
|
||||
|
@ -143,7 +136,7 @@ class Rig(pawRig):
|
|||
input_bone = self.get_ik2_input_bone()
|
||||
head_tail = 1 if self.use_heel2 else 0
|
||||
|
||||
self.rig_ik_mch_stretch_bones(mch.ik2_target, mch.ik2_stretch, input_bone, head_tail, 3)
|
||||
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)
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue