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

414 lines
14 KiB
Python

# SPDX-License-Identifier: GPL-2.0-or-later
import bpy
import re
from math import cos, pi
from itertools import count, repeat
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.base_rig import BaseRig, stage
def bone_siblings(obj, bone):
""" Returns a list of the siblings of the given bone.
This requires that the bones has a parent.
"""
parent = obj.data.bones[bone].parent
if parent is None:
return []
bones = []
for b in parent.children:
if b.name != bone and not is_rig_base_bone(obj, b.name):
bones += [b.name]
return bones
class Rig(BaseRig):
""" A "palm" rig. A set of sibling bones that bend with each other.
This is a control and deformation rig.
"""
def find_org_bones(self, bone):
base_head = bone.bone.head
siblings = bone_siblings(self.obj, bone.name)
# Sort list by name and distance
siblings.sort()
siblings.sort(key=lambda b: (self.get_bone(b).bone.head - base_head).length)
return [bone.name] + siblings
def initialize(self):
if len(self.bones.org) <= 1:
self.raise_error('The palm rig must have a parent and at least one sibling')
self.palm_rotation_axis = self.params.palm_rotation_axis
self.make_secondary = self.params.palm_both_sides
self.make_fk = self.params.make_extra_control
self.order = 'YXZ' if 'X' in self.palm_rotation_axis else 'YZX'
# Figure out the name for the control bone (remove the last .##)
self.ctrl_name = re.sub(r"([0-9]+\.)", "", strip_org(self.bones.org[-1])[::-1], count=1)[::-1]
def parent_bones(self):
self.rig_parent_bone = self.get_bone_parent(self.bones.org[0])
# Parent to the deform bone of the parent if exists
def_bone = choose_derived_bone(self.generator, self.rig_parent_bone, 'def')
if def_bone:
self.rig_parent_bone = def_bone
####################################################
# 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
#
####################################################
####################################################
# Master control
@stage.generate_bones
def make_master_control(self):
orgs = self.bones.org
self.bones.ctrl.master = self.copy_bone(orgs[-1], self.ctrl_name, parent=True)
if self.make_secondary:
second_name = make_derived_name(orgs[0], 'ctrl')
self.bones.ctrl.secondary = self.copy_bone(orgs[0], second_name, parent=True)
@stage.parent_bones
def parent_master_control(self):
self.set_bone_parent(self.bones.ctrl.master, self.rig_parent_bone, inherit_scale='AVERAGE')
if self.make_secondary:
self.set_bone_parent(self.bones.ctrl.secondary, self.rig_parent_bone, inherit_scale='AVERAGE')
@stage.configure_bones
def configure_master_control(self):
self.configure_control_bone(self.bones.ctrl.master, self.bones.org[-1])
if self.make_secondary:
self.configure_control_bone(self.bones.ctrl.secondary, self.bones.org[0])
def configure_control_bone(self, ctrl, org):
self.copy_bone_properties(org, ctrl)
self.get_bone(ctrl).lock_scale = (True, True, True)
@stage.generate_widgets
def make_master_control_widgets(self):
self.make_control_widget(self.bones.ctrl.master)
if self.make_secondary:
self.make_control_widget(self.bones.ctrl.secondary)
def make_control_widget(self, ctrl):
make_palm_widget(self.obj, ctrl, axis=self.palm_rotation_axis, radius=0.4)
####################################################
# FK controls
@stage.generate_bones
def make_fk_controls(self):
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):
return self.copy_bone(org, make_derived_name(org, 'ctrl', '_fk'))
@stage.parent_bones
def parent_fk_controls(self):
if self.make_fk:
for fk, mch in zip(self.bones.ctrl.fk, self.bones.mch.fk_parents):
self.set_bone_parent(fk, mch, inherit_scale='ALIGNED')
@stage.configure_bones
def configure_fk_controls(self):
if self.make_fk:
for fk, org in zip(self.bones.ctrl.fk, self.bones.org):
self.copy_bone_properties(org, fk)
@stage.generate_widgets
def make_fk_control_widgets(self):
if self.make_fk:
for fk in self.bones.ctrl.fk:
create_bone_widget(self.obj, fk)
####################################################
# FK parent bones
@stage.generate_bones
def make_mch_fk_parents(self):
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):
return self.copy_bone(org, make_derived_name(org, 'mch', '_fk_parent'))
@stage.parent_bones
def parent_mch_fk_parents(self):
if self.make_fk:
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):
self.set_bone_parent(mch, self.rig_parent_bone, inherit_scale='NONE')
@stage.rig_bones
def rig_mch_fk_parents(self):
if self.make_fk:
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):
num_orgs = len(self.bones.org)
ctrl = self.bones.ctrl
fac = i / (num_orgs - 1)
if fac > 0:
self.make_constraint(
org, 'COPY_TRANSFORMS', ctrl.master, space='LOCAL',
influence=fac
)
if self.make_secondary and fac < 1:
self.make_constraint(
org, 'COPY_LOCATION', ctrl.secondary, space='LOCAL',
use_offset=True, influence=1-fac
)
self.make_constraint(
org, 'COPY_ROTATION', ctrl.secondary, space='LOCAL',
euler_order=self.order, mix_mode='ADD', influence=1-fac
)
elif self.make_secondary:
self.make_constraint(
org, 'COPY_TRANSFORMS', ctrl.secondary, space='LOCAL'
)
self.make_constraint(org, 'COPY_SCALE', self.rig_parent_bone)
self.rig_mch_back_rotation(org, ctrl.master, fac)
if self.make_secondary:
self.rig_mch_back_rotation(org, ctrl.secondary, 1 - fac)
def rig_mch_back_rotation(self, org, ctrl, fac):
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),
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),
euler_order=self.order, mix_mode='ADD', influence=inf
)
####################################################
# ORG bones
@stage.parent_bones
def parent_org_chain(self):
if self.make_fk:
for org, fk in zip(self.bones.org, self.bones.ctrl.fk):
self.set_bone_parent(org, fk)
else:
for i, org in enumerate(self.bones.org):
self.parent_mch_fk_parent_bone(i, org)
@stage.rig_bones
def rig_org_chain(self):
if not self.make_fk:
for i, org in enumerate(self.bones.org):
self.rig_mch_fk_parent_bone(i, org)
####################################################
# DEF bones
@stage.generate_bones
def make_def_chain(self):
self.bones.deform = map_list(self.make_deform_bone, self.bones.org)
def make_deform_bone(self, org):
return self.copy_bone(org, make_derived_name(org, 'def'))
@stage.parent_bones
def parent_deform_chain(self):
for deform, org in zip(self.bones.deform, self.bones.org):
self.set_bone_parent(deform, org, use_connect=False)
####################################################
# Settings
@classmethod
def add_parameters(cls, params):
items = [('X', 'X', ''), ('Z', 'Z', '')]
params.palm_rotation_axis = bpy.props.EnumProperty(
items=items,
name="Palm Rotation Axis",
default='X',
)
params.palm_both_sides = bpy.props.BoolProperty(
name="Both Sides",
default=False,
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"
)
@classmethod
def parameters_ui(cls, layout, params):
r = layout.row()
r.label(text="Primary rotation axis:")
r.prop(params, "palm_rotation_axis", text="")
layout.prop(params, "palm_both_sides")
layout.prop(params, "make_extra_control", text="Extra FK Controls")
@widget_generator(register="palm", subsurf=2)
def make_palm_widget(geom, axis='X', radius=0.5):
sx = radius / 0.4
sz = radius / 0.3
v = [(0.1578, 0.0, -0.3), (0.1578, 1.0, -0.2), (-0.1578, 1.0, -0.2), (-0.1578, -0.0, -0.3),
(-0.1578, -0.0, 0.3), (-0.1578, 1.0, 0.2), (0.1578, 1.0, 0.2), (0.1578, 0.0, 0.3),
(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]
if 'Z' in axis:
# Flip x/z coordinates
geom.verts = [v[::-1] for v in geom.verts]
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')
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('palm.parent')
bone.head[:] = 0.0000, 0.0000, 0.0000
bone.tail[:] = 0.0577, 0.0000, -0.0000
bone.roll = 3.1416
bone.use_connect = False
bones['palm.parent'] = bone.name
bone = arm.edit_bones.new('palm.04')
bone.head[:] = 0.0577, 0.0315, -0.0000
bone.tail[:] = 0.1627, 0.0315, -0.0000
bone.roll = 3.1416
bone.use_connect = False
bone.parent = arm.edit_bones[bones['palm.parent']]
bones['palm.04'] = bone.name
bone = arm.edit_bones.new('palm.03')
bone.head[:] = 0.0577, 0.0105, -0.0000
bone.tail[:] = 0.1627, 0.0105, -0.0000
bone.roll = 3.1416
bone.use_connect = False
bone.parent = arm.edit_bones[bones['palm.parent']]
bones['palm.03'] = bone.name
bone = arm.edit_bones.new('palm.02')
bone.head[:] = 0.0577, -0.0105, -0.0000
bone.tail[:] = 0.1627, -0.0105, -0.0000
bone.roll = 3.1416
bone.use_connect = False
bone.parent = arm.edit_bones[bones['palm.parent']]
bones['palm.02'] = bone.name
bone = arm.edit_bones.new('palm.01')
bone.head[:] = 0.0577, -0.0315, -0.0000
bone.tail[:] = 0.1627, -0.0315, -0.0000
bone.roll = 3.1416
bone.use_connect = False
bone.parent = arm.edit_bones[bones['palm.parent']]
bones['palm.01'] = bone.name
bpy.ops.object.mode_set(mode='OBJECT')
pbone = obj.pose.bones[bones['palm.parent']]
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['palm.04']]
pbone.rigify_type = ''
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, True, True)
pbone.lock_rotation_w = False
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'YXZ'
pbone = obj.pose.bones[bones['palm.03']]
pbone.rigify_type = ''
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, True, True)
pbone.lock_rotation_w = False
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'YXZ'
pbone = obj.pose.bones[bones['palm.02']]
pbone.rigify_type = ''
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, True, True)
pbone.lock_rotation_w = False
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'YXZ'
pbone = obj.pose.bones[bones['palm.01']]
pbone.rigify_type = 'limbs.super_palm'
pbone.lock_location = (False, False, False)
pbone.lock_rotation = (False, True, True)
pbone.lock_rotation_w = False
pbone.lock_scale = (False, False, False)
pbone.rotation_mode = 'YXZ'
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
arm.edit_bones.active = bone