expre-gen/blender/3.4/scripts/addons/rigify/rigs/limbs/front_paw.py
2025-10-17 09:47:23 +08:00

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