171 lines
5.6 KiB
Python
171 lines
5.6 KiB
Python
# 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
|
|
|
|
from itertools import count
|
|
|
|
from ...base_rig import stage
|
|
|
|
from .limb_rigs import BaseLimbRig
|
|
from .paw import Rig as pawRig, create_sample as create_paw_sample
|
|
|
|
|
|
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)
|
|
#
|
|
####################################################
|
|
|
|
####################################################
|
|
# IK controls
|
|
|
|
def get_middle_ik_controls(self):
|
|
return [self.bones.ctrl.heel]
|
|
|
|
def get_ik_fk_position_chains(self):
|
|
ik_chain, tail_chain, fk_chain = super().get_ik_fk_position_chains()
|
|
assert not tail_chain
|
|
if not self.use_heel2:
|
|
return [*ik_chain, ik_chain[-1]], [], [*fk_chain, fk_chain[-1]]
|
|
return ik_chain, tail_chain, fk_chain
|
|
|
|
def get_extra_ik_controls(self):
|
|
extra = [self.bones.ctrl.heel2] if self.use_heel2 else []
|
|
return BaseLimbRig.get_extra_ik_controls(self) + extra
|
|
|
|
def get_ik_pole_parents(self):
|
|
return [(self.bones.mch.ik2_target, self.bones.ctrl.ik)]
|
|
|
|
|
|
####################################################
|
|
# Heel control
|
|
|
|
@stage.parent_bones
|
|
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)
|
|
|
|
use_mch_ik_base = True
|
|
|
|
def get_ik2_input_bone(self):
|
|
return self.bones.ctrl.heel2 if self.use_heel2 else self.bones.mch.toe_socket
|
|
|
|
@stage.generate_bones
|
|
def make_ik2_mch_stretch(self):
|
|
orgs = self.bones.org.main
|
|
|
|
self.bones.mch.ik2_target = self.make_ik2_mch_target_bone(orgs)
|
|
|
|
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)
|
|
|
|
@stage.generate_bones
|
|
def make_ik2_mch_chain(self):
|
|
orgs = self.bones.org.main
|
|
chain = map_list(self.make_ik2_mch_bone, count(0), orgs[0:2])
|
|
self.bones.mch.ik2_chain = chain
|
|
|
|
org_bones = map_list(self.get_bone, orgs)
|
|
chain_bones = map_list(self.get_bone, chain)
|
|
|
|
# Extend the base IK control (used in the ik2 chain) with the projected length of org2
|
|
chain_bones[0].length += org_bones[2].vector.dot(chain_bones[0].vector.normalized())
|
|
chain_bones[1].head = chain_bones[0].tail
|
|
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):
|
|
return self.copy_bone(org, make_derived_name(org, 'mch', '_ik2'))
|
|
|
|
@stage.parent_bones
|
|
def parent_ik2_mch_chain(self):
|
|
mch = self.bones.mch
|
|
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)
|
|
|
|
@stage.configure_bones
|
|
def configure_ik2_mch_chain(self):
|
|
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):
|
|
bone = self.get_bone(mch)
|
|
bone.ik_stretch = 0.1
|
|
if i == 1:
|
|
bone.lock_ik_x = bone.lock_ik_y = bone.lock_ik_z = True
|
|
setattr(bone, 'lock_ik_' + self.main_axis, False)
|
|
|
|
@stage.rig_bones
|
|
def rig_ik2_mch_chain(self):
|
|
mch = self.bones.mch
|
|
input_bone = self.get_ik2_input_bone()
|
|
head_tail = 1 if self.use_heel2 else 0
|
|
|
|
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)
|
|
|
|
@stage.generate_bones
|
|
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):
|
|
return self.copy_bone(org, make_derived_name(org, 'mch', '_ik3'))
|
|
|
|
@stage.parent_bones
|
|
def parent_ik3_mch_chain(self):
|
|
mch = self.bones.mch
|
|
|
|
self.set_bone_parent(mch.ik3_chain[0], mch.ik2_chain[0])
|
|
self.parent_bone_chain(mch.ik3_chain, use_connect=True)
|
|
|
|
@stage.configure_bones
|
|
def configure_ik3_mch_chain(self):
|
|
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):
|
|
bone = self.get_bone(mch)
|
|
bone.ik_stretch = 0.1
|
|
if i == 0:
|
|
bone.lock_ik_x = bone.lock_ik_y = bone.lock_ik_z = True
|
|
setattr(bone, 'lock_ik_' + self.main_axis, False)
|
|
|
|
@stage.rig_bones
|
|
def rig_ik3_mch_chain(self):
|
|
mch = self.bones.mch
|
|
# Mostly cancel ik2 scaling.
|
|
self.make_constraint(
|
|
mch.ik3_chain[0], 'COPY_SCALE', self.bones.ctrl.ik_base,
|
|
use_make_uniform=True, influence=0.75,
|
|
)
|
|
self.make_constraint(mch.ik3_chain[-1], 'IK', mch.ik2_target, chain_count=2)
|
|
|
|
|
|
def create_sample(obj):
|
|
bones = create_paw_sample(obj)
|
|
pbone = obj.pose.bones[bones['thigh.L']]
|
|
pbone.rigify_type = 'limbs.rear_paw'
|
|
return bones
|