272 lines
9.6 KiB
Python
272 lines
9.6 KiB
Python
|
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
||
|
|
|
||
|
|
import bpy
|
||
|
|
|
||
|
|
from ...utils.bones import align_bone_roll, put_bone, copy_bone_position, flip_bone
|
||
|
|
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
|
||
|
|
|
||
|
|
|
||
|
|
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
|
||
|
|
#
|
||
|
|
####################################################
|
||
|
|
|
||
|
|
####################################################
|
||
|
|
# 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.get_ik2_target_bone(), self.bones.ctrl.ik)]
|
||
|
|
|
||
|
|
|
||
|
|
####################################################
|
||
|
|
# Second IK system (pre-driving heel)
|
||
|
|
|
||
|
|
use_mch_ik_base = True
|
||
|
|
|
||
|
|
def get_ik2_target_bone(self):
|
||
|
|
return self.bones.mch.ik2_target if self.use_heel2 else self.bones.mch.toe_socket
|
||
|
|
|
||
|
|
@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
|
||
|
|
|
||
|
|
if self.use_heel2:
|
||
|
|
self.bones.mch.ik2_target = self.make_ik2_mch_target_bone(orgs)
|
||
|
|
|
||
|
|
# Connect the chain end to the target
|
||
|
|
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):
|
||
|
|
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):
|
||
|
|
return self.copy_bone(org, make_derived_name(org, 'mch', '_ik2'))
|
||
|
|
|
||
|
|
@stage.parent_bones
|
||
|
|
def parent_ik2_mch_chain(self):
|
||
|
|
mch = self.bones.mch
|
||
|
|
if self.use_heel2:
|
||
|
|
self.set_bone_parent(mch.ik2_target, self.bones.ctrl.heel2)
|
||
|
|
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):
|
||
|
|
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
|
||
|
|
|
||
|
|
@stage.generate_bones
|
||
|
|
def make_heel_track_bones(self):
|
||
|
|
orgs = self.bones.org.main
|
||
|
|
mch = self.bones.mch
|
||
|
|
mch.heel_track = self.copy_bone(orgs[2], make_derived_name(orgs[2], 'mch', '_track'))
|
||
|
|
mch.heel_parent = self.copy_bone(orgs[2], make_derived_name(orgs[2], 'mch', '_parent'))
|
||
|
|
|
||
|
|
# This two bone setup is used to move the damped track singularity out
|
||
|
|
# of the way to a forbidden zone of the rig, and thus avoid flipping.
|
||
|
|
# The bones are aligned to the center of the valid transformation zone.
|
||
|
|
self.align_ik_control_bone(mch.heel_track)
|
||
|
|
put_bone(self.obj, mch.heel_track, self.get_bone(orgs[2]).tail, scale=1/3)
|
||
|
|
copy_bone_position(self.obj, mch.heel_track, mch.heel_parent, scale=3/4)
|
||
|
|
|
||
|
|
@stage.parent_bones
|
||
|
|
def parent_heel_control_bone(self):
|
||
|
|
self.set_bone_parent(self.bones.ctrl.heel, self.bones.mch.heel_parent)
|
||
|
|
|
||
|
|
@stage.parent_bones
|
||
|
|
def parent_heel_track_bones(self):
|
||
|
|
# Parenting heel_parent deferred to apply_bones.
|
||
|
|
self.set_bone_parent(self.bones.mch.heel_track, self.get_ik2_target_bone())
|
||
|
|
|
||
|
|
@stage.configure_bones
|
||
|
|
def prerig_heel_track_bones(self):
|
||
|
|
# Assign the constraint before the apply stage.
|
||
|
|
self.make_constraint(
|
||
|
|
self.bones.mch.heel_track, 'DAMPED_TRACK', self.bones.mch.ik2_chain[1],
|
||
|
|
influence=self.params.front_paw_heel_influence
|
||
|
|
)
|
||
|
|
|
||
|
|
@stage.preapply_bones
|
||
|
|
def preapply_heel_track_bones(self):
|
||
|
|
# Assign local transform negating the effect of the constraint at rest.
|
||
|
|
track_bone = self.get_bone(self.bones.mch.heel_track)
|
||
|
|
bone = self.get_bone(self.bones.mch.heel_parent)
|
||
|
|
bone.matrix_basis = track_bone.matrix.inverted() @ bone.matrix
|
||
|
|
|
||
|
|
@stage.apply_bones
|
||
|
|
def apply_heel_track_bones(self):
|
||
|
|
# 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):
|
||
|
|
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'
|
||
|
|
)
|
||
|
|
|
||
|
|
@classmethod
|
||
|
|
def parameters_ui(self, layout, params):
|
||
|
|
r = layout.row()
|
||
|
|
r.prop(params, "front_paw_heel_influence", slider=True)
|
||
|
|
|
||
|
|
super().parameters_ui(layout, params)
|
||
|
|
|
||
|
|
|
||
|
|
def create_sample(obj):
|
||
|
|
# generated by rigify.utils.write_metarig
|
||
|
|
bpy.ops.object.mode_set(mode='EDIT')
|
||
|
|
arm = obj.data
|
||
|
|
|
||
|
|
bones = {}
|
||
|
|
|
||
|
|
bone = arm.edit_bones.new('front_thigh.L')
|
||
|
|
bone.head = 0.0000, 0.0000, 0.6902
|
||
|
|
bone.tail = 0.0000, 0.0916, 0.4418
|
||
|
|
bone.roll = 0.0000
|
||
|
|
bone.use_connect = False
|
||
|
|
bones['front_thigh.L'] = bone.name
|
||
|
|
bone = arm.edit_bones.new('front_shin.L')
|
||
|
|
bone.head = 0.0000, 0.0916, 0.4418
|
||
|
|
bone.tail = 0.0000, 0.1014, 0.1698
|
||
|
|
bone.roll = 0.0000
|
||
|
|
bone.use_connect = True
|
||
|
|
bone.parent = arm.edit_bones[bones['front_thigh.L']]
|
||
|
|
bones['front_shin.L'] = bone.name
|
||
|
|
bone = arm.edit_bones.new('front_foot.L')
|
||
|
|
bone.head = 0.0000, 0.1014, 0.1698
|
||
|
|
bone.tail = 0.0000, 0.0699, 0.0411
|
||
|
|
bone.roll = 0.0000
|
||
|
|
bone.use_connect = True
|
||
|
|
bone.parent = arm.edit_bones[bones['front_shin.L']]
|
||
|
|
bones['front_foot.L'] = bone.name
|
||
|
|
bone = arm.edit_bones.new('front_toe.L')
|
||
|
|
bone.head = 0.0000, 0.0699, 0.0411
|
||
|
|
bone.tail = 0.0000, -0.0540, 0.0000
|
||
|
|
bone.roll = 3.1416
|
||
|
|
bone.use_connect = True
|
||
|
|
bone.parent = arm.edit_bones[bones['front_foot.L']]
|
||
|
|
bones['front_toe.L'] = bone.name
|
||
|
|
|
||
|
|
bpy.ops.object.mode_set(mode='OBJECT')
|
||
|
|
pbone = obj.pose.bones[bones['front_thigh.L']]
|
||
|
|
pbone.rigify_type = 'limbs.front_paw'
|
||
|
|
pbone.lock_location = (False, False, False)
|
||
|
|
pbone.lock_rotation = (False, False, False)
|
||
|
|
pbone.lock_rotation_w = False
|
||
|
|
pbone.lock_scale = (False, False, False)
|
||
|
|
pbone.rotation_mode = 'QUATERNION'
|
||
|
|
try:
|
||
|
|
pbone.rigify_parameters.limb_type = "paw"
|
||
|
|
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]
|
||
|
|
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]
|
||
|
|
except AttributeError:
|
||
|
|
pass
|
||
|
|
pbone = obj.pose.bones[bones['front_shin.L']]
|
||
|
|
pbone.rigify_type = ''
|
||
|
|
pbone.lock_location = (False, False, False)
|
||
|
|
pbone.lock_rotation = (False, False, False)
|
||
|
|
pbone.lock_rotation_w = False
|
||
|
|
pbone.lock_scale = (False, False, False)
|
||
|
|
pbone.rotation_mode = 'QUATERNION'
|
||
|
|
pbone = obj.pose.bones[bones['front_foot.L']]
|
||
|
|
pbone.rigify_type = ''
|
||
|
|
pbone.lock_location = (False, False, False)
|
||
|
|
pbone.lock_rotation = (False, False, False)
|
||
|
|
pbone.lock_rotation_w = False
|
||
|
|
pbone.lock_scale = (False, False, False)
|
||
|
|
pbone.rotation_mode = 'QUATERNION'
|
||
|
|
pbone = obj.pose.bones[bones['front_toe.L']]
|
||
|
|
pbone.rigify_type = ''
|
||
|
|
pbone.lock_location = (False, False, False)
|
||
|
|
pbone.lock_rotation = (False, False, False)
|
||
|
|
pbone.lock_rotation_w = False
|
||
|
|
pbone.lock_scale = (False, False, False)
|
||
|
|
pbone.rotation_mode = 'QUATERNION'
|
||
|
|
try:
|
||
|
|
pbone.rigify_parameters.limb_type = "paw"
|
||
|
|
except AttributeError:
|
||
|
|
pass
|
||
|
|
|
||
|
|
bpy.ops.object.mode_set(mode='EDIT')
|
||
|
|
for bone in arm.edit_bones:
|
||
|
|
bone.select = False
|
||
|
|
bone.select_head = False
|
||
|
|
bone.select_tail = False
|
||
|
|
for b in bones:
|
||
|
|
bone = arm.edit_bones[bones[b]]
|
||
|
|
bone.select = True
|
||
|
|
bone.select_head = True
|
||
|
|
bone.select_tail = True
|
||
|
|
bone.bbone_x = bone.bbone_z = bone.length * 0.05
|
||
|
|
arm.edit_bones.active = bone
|
||
|
|
|
||
|
|
return bones
|