669 lines
24 KiB
Python
669 lines
24 KiB
Python
# SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
import bpy
|
|
import json
|
|
|
|
from itertools import count
|
|
|
|
from ...utils.bones import put_bone, flip_bone, align_chain_x_axis, set_bone_widget_transform
|
|
from ...utils.naming import make_derived_name
|
|
from ...utils.widgets import create_widget
|
|
from ...utils.widgets_basic import create_circle_widget, create_sphere_widget
|
|
from ...utils.misc import map_list
|
|
from ...utils.layers import ControlLayersOption
|
|
from ...utils.switch_parent import SwitchParentBuilder
|
|
from ...utils.animation import add_generic_snap, add_fk_ik_snap_buttons
|
|
|
|
from ...base_rig import stage
|
|
|
|
from ..chain_rigs import SimpleChainRig
|
|
|
|
|
|
class Rig(SimpleChainRig):
|
|
"""A finger rig with master control."""
|
|
def initialize(self):
|
|
super().initialize()
|
|
|
|
self.bbone_segments = self.params.bbones
|
|
self.make_ik = self.params.make_extra_ik_control
|
|
|
|
def prepare_bones(self):
|
|
if self.params.primary_rotation_axis == 'automatic':
|
|
align_chain_x_axis(self.obj, self.bones.org)
|
|
|
|
def parent_bones(self):
|
|
self.rig_parent_bone = self.get_bone_parent(self.bones.org[0])
|
|
|
|
##############################
|
|
# Master Control
|
|
|
|
@stage.generate_bones
|
|
def make_master_control(self):
|
|
orgs = self.bones.org
|
|
name = self.copy_bone(orgs[0], make_derived_name(orgs[0], 'ctrl', '_master'), parent=True)
|
|
self.bones.ctrl.master = name
|
|
|
|
first_bone = self.get_bone(orgs[0])
|
|
last_bone = self.get_bone(orgs[-1])
|
|
self.get_bone(name).length += (last_bone.tail - first_bone.head).length * 1.25
|
|
|
|
@stage.configure_bones
|
|
def configure_master_control(self):
|
|
master = self.bones.ctrl.master
|
|
|
|
self.copy_bone_properties(self.bones.org[0], master, props=False, widget=False)
|
|
|
|
bone = self.get_bone(master)
|
|
bone.lock_scale = True, False, True
|
|
|
|
@stage.generate_widgets
|
|
def make_master_control_widget(self):
|
|
master_name = self.bones.ctrl.master
|
|
|
|
w = create_widget(self.obj, master_name)
|
|
if w is not None:
|
|
mesh = w.data
|
|
verts = [(0, 0, 0), (0, 1, 0), (0.05, 1, 0), (0.05, 1.1, 0), (-0.05, 1.1, 0), (-0.05, 1, 0)]
|
|
if 'Z' in self.params.primary_rotation_axis:
|
|
# Flip x/z coordinates
|
|
temp = []
|
|
for v in verts:
|
|
temp += [(v[2], v[1], v[0])]
|
|
verts = temp
|
|
edges = [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 1)]
|
|
mesh.from_pydata(verts, edges, [])
|
|
mesh.update()
|
|
|
|
##############################
|
|
# Control chain
|
|
|
|
@stage.generate_bones
|
|
def make_control_chain(self):
|
|
orgs = self.bones.org
|
|
self.bones.ctrl.fk = map_list(self.make_control_bone, count(0), orgs)
|
|
self.bones.ctrl.fk += [self.make_tip_control_bone(orgs[-1], orgs[0])]
|
|
|
|
def make_control_bone(self, i, org):
|
|
return self.copy_bone(org, make_derived_name(org, 'ctrl'), inherit_scale=True)
|
|
|
|
def make_tip_control_bone(self, org, name_org):
|
|
name = self.copy_bone(org, make_derived_name(name_org, 'ctrl'), parent=False)
|
|
|
|
flip_bone(self.obj, name)
|
|
self.get_bone(name).length /= 2
|
|
|
|
return name
|
|
|
|
@stage.parent_bones
|
|
def parent_control_chain(self):
|
|
ctrls = self.bones.ctrl.fk
|
|
for args in zip(ctrls, self.bones.mch.bend + ctrls[-2:]):
|
|
self.set_bone_parent(*args)
|
|
|
|
@stage.configure_bones
|
|
def configure_control_chain(self):
|
|
for args in zip(count(0), self.bones.ctrl.fk, self.bones.org + [None]):
|
|
self.configure_control_bone(*args)
|
|
|
|
ControlLayersOption.TWEAK.assign(self.params, self.obj, self.bones.ctrl.fk)
|
|
|
|
def configure_control_bone(self, i, ctrl, org):
|
|
if org:
|
|
self.copy_bone_properties(org, ctrl)
|
|
else:
|
|
bone = self.get_bone(ctrl)
|
|
bone.lock_rotation_w = True
|
|
bone.lock_rotation = (True, True, True)
|
|
bone.lock_scale = (True, True, True)
|
|
|
|
def make_control_widget(self, i, ctrl):
|
|
if ctrl == self.bones.ctrl.fk[-1]:
|
|
# Tip control
|
|
create_circle_widget(self.obj, ctrl, radius=0.3, head_tail=0.0)
|
|
else:
|
|
set_bone_widget_transform(self.obj, ctrl, self.bones.org[i])
|
|
|
|
create_circle_widget(self.obj, ctrl, radius=0.3, head_tail=0.5)
|
|
|
|
##############################
|
|
# IK Control
|
|
|
|
@stage.generate_bones
|
|
def make_ik_control(self):
|
|
if self.make_ik:
|
|
self.bones.ctrl.ik = self.make_ik_control_bone(self.bones.org)
|
|
|
|
self.build_ik_parent_switch(SwitchParentBuilder(self.generator))
|
|
|
|
def make_ik_control_bone(self, orgs):
|
|
name = self.copy_bone(orgs[-1], make_derived_name(orgs[0], 'ctrl', '_ik'), scale=0.7)
|
|
put_bone(self.obj, name, self.get_bone(orgs[-1]).tail)
|
|
return name
|
|
|
|
def build_ik_parent_switch(self, pbuilder):
|
|
ctrl = self.bones.ctrl
|
|
|
|
pbuilder.build_child(
|
|
self, ctrl.ik, prop_bone=ctrl.ik,
|
|
select_tags=['held_object', 'limb_ik', {'child', 'limb_end'}], only_selected=True,
|
|
prop_id='IK_parent', prop_name='IK Parent', controls=[ ctrl.ik ],
|
|
no_fix_rotation=True, no_fix_scale=True,
|
|
)
|
|
|
|
@stage.parent_bones
|
|
def parent_ik_control(self):
|
|
if self.make_ik:
|
|
bone = self.get_bone(self.bones.ctrl.ik)
|
|
bone.use_local_location = self.params.ik_local_location
|
|
|
|
@stage.configure_bones
|
|
def configure_ik_control(self):
|
|
if self.make_ik:
|
|
bone = self.get_bone(self.bones.ctrl.ik)
|
|
bone.lock_rotation = True, True, True
|
|
bone.lock_rotation_w = True
|
|
bone.lock_scale = True, True, True
|
|
|
|
ControlLayersOption.EXTRA_IK.assign_rig(self, [self.bones.ctrl.ik])
|
|
|
|
@stage.configure_bones
|
|
def configure_ik_control_properties(self):
|
|
if self.make_ik:
|
|
ctrl = self.bones.ctrl
|
|
rig_name = ctrl.fk[0]
|
|
|
|
panel = self.script.panel_with_selected_check(self, self.bones.ctrl.flatten())
|
|
|
|
self.make_property(
|
|
ctrl.ik, 'FK_IK', 0.0,
|
|
description="Enable simple IK correction on top of FK posing"
|
|
)
|
|
panel.custom_prop(ctrl.ik, 'FK_IK', text="Finger IK ({})".format(rig_name), slider=True)
|
|
|
|
axis = self.params.primary_rotation_axis
|
|
|
|
add_finger_snap_fk_to_ik(
|
|
panel, master=ctrl.master, fk_bones=ctrl.fk,
|
|
ik_bones=self.bones.org, ik_control=ctrl.ik,
|
|
ik_constraint_bone=self.bones.org[-1],
|
|
axis=self.axis_options[axis]['id'],
|
|
rig_name=rig_name, compact=True,
|
|
)
|
|
|
|
add_generic_snap(
|
|
panel, output_bones=[ctrl.ik], input_bones=ctrl.fk[-1:],
|
|
input_ctrl_bones=[ctrl.master, *ctrl.fk],
|
|
label='IK->FK', rig_name=rig_name, tooltip='IK to FK',
|
|
compact=True, locks=(False,True,True),
|
|
)
|
|
|
|
|
|
@stage.generate_widgets
|
|
def make_ik_control_widget(self):
|
|
if self.make_ik:
|
|
create_sphere_widget(self.obj, self.bones.ctrl.ik)
|
|
|
|
##############################
|
|
# MCH bend chain
|
|
|
|
@stage.generate_bones
|
|
def make_mch_bend_chain(self):
|
|
self.bones.mch.bend = map_list(self.make_mch_bend_bone, self.bones.org)
|
|
|
|
def make_mch_bend_bone(self, org):
|
|
return self.copy_bone(org, make_derived_name(org, 'mch', '_drv'), inherit_scale=True, scale=0.3)
|
|
|
|
@stage.parent_bones
|
|
def parent_mch_bend_chain(self):
|
|
ctrls = self.bones.ctrl.fk
|
|
for args in zip(self.bones.mch.bend, [self.rig_parent_bone] + ctrls):
|
|
self.set_bone_parent(*args)
|
|
|
|
# Match axis to expression
|
|
axis_options = {
|
|
"automatic": {"axis": 0, "id": '+X',
|
|
"expr": '(1-sy)*pi'},
|
|
"X": {"axis": 0, "id": '+X',
|
|
"expr": '(1-sy)*pi'},
|
|
"-X": {"axis": 0, "id": '-X',
|
|
"expr": '-((1-sy)*pi)'},
|
|
"Y": {"axis": 1, "id": '+Y',
|
|
"expr": '(1-sy)*pi'},
|
|
"-Y": {"axis": 1, "id": '-Y',
|
|
"expr": '-((1-sy)*pi)'},
|
|
"Z": {"axis": 2, "id": '+Z',
|
|
"expr": '(1-sy)*pi'},
|
|
"-Z": {"axis": 2, "id": '-Z',
|
|
"expr": '-((1-sy)*pi)'}
|
|
}
|
|
|
|
@stage.rig_bones
|
|
def rig_mch_bend_chain(self):
|
|
for args in zip(count(0), self.bones.mch.bend):
|
|
self.rig_mch_bend_bone(*args)
|
|
|
|
def rig_mch_bend_bone(self, i, mch):
|
|
master = self.bones.ctrl.master
|
|
if i == 0:
|
|
self.make_constraint(mch, 'COPY_LOCATION', master)
|
|
self.make_constraint(mch, 'COPY_ROTATION', master, space='LOCAL')
|
|
else:
|
|
axis = self.params.primary_rotation_axis
|
|
options = self.axis_options[axis]
|
|
|
|
bone = self.get_bone(mch)
|
|
bone.rotation_mode = 'YZX'
|
|
|
|
self.make_driver(
|
|
bone, 'rotation_euler', index=options['axis'],
|
|
expression=options['expr'],
|
|
variables={'sy': (master, '.scale.y')}
|
|
)
|
|
|
|
##############################
|
|
# MCH stretch chain
|
|
|
|
@stage.generate_bones
|
|
def make_mch_stretch_chain(self):
|
|
self.bones.mch.stretch = map_list(self.make_mch_stretch_bone, self.bones.org)
|
|
|
|
def make_mch_stretch_bone(self, org):
|
|
return self.copy_bone(org, make_derived_name(org, 'mch'), parent=False)
|
|
|
|
@stage.parent_bones
|
|
def parent_mch_stretch_chain(self):
|
|
ctrls = self.bones.ctrl.fk
|
|
for args in zip(self.bones.mch.stretch, [self.rig_parent_bone] + ctrls[1:]):
|
|
self.set_bone_parent(*args)
|
|
|
|
@stage.rig_bones
|
|
def rig_mch_stretch_chain(self):
|
|
ctrls = self.bones.ctrl.fk
|
|
for args in zip(count(0), self.bones.mch.stretch, ctrls, ctrls[1:]):
|
|
self.rig_mch_stretch_bone(*args)
|
|
|
|
def rig_mch_stretch_bone(self, i, mch, ctrl, ctrl_next):
|
|
if i == 0:
|
|
self.make_constraint(mch, 'COPY_LOCATION', ctrl)
|
|
self.make_constraint(mch, 'COPY_SCALE', ctrl)
|
|
|
|
self.make_constraint(mch, 'STRETCH_TO', ctrl_next, volume='NO_VOLUME', keep_axis='SWING_Y')
|
|
|
|
##############################
|
|
# ORG chain
|
|
|
|
@stage.rig_bones
|
|
def rig_org_chain(self):
|
|
for args in zip(count(0), self.bones.org, self.bones.mch.stretch):
|
|
self.rig_org_bone(*args)
|
|
|
|
if self.make_ik:
|
|
self.rig_org_ik(self.bones.org, self.bones.ctrl.ik)
|
|
|
|
def rig_org_ik(self, orgs, ik_ctrl):
|
|
axis = self.params.primary_rotation_axis
|
|
options = self.axis_options[axis]
|
|
|
|
# Lock IK axis on child bones, using stiffness to preserve
|
|
# original rotation rather than zeroing it out.
|
|
stiffness = [1.0, 1.0, 1.0]
|
|
stiffness[options['axis']] = 0.0
|
|
|
|
for org in orgs[1:]:
|
|
bone = self.get_bone(org)
|
|
bone.ik_stiffness_x, bone.ik_stiffness_y, bone.ik_stiffness_z = stiffness
|
|
|
|
# Add the constraint
|
|
con = self.make_constraint(
|
|
orgs[-1], 'IK', ik_ctrl, name='FingerIK',
|
|
chain_count=len(orgs), use_stretch=False,
|
|
)
|
|
|
|
self.make_driver(con, "influence", variables=[(ik_ctrl, 'FK_IK')])
|
|
|
|
##############################
|
|
# Deform chain
|
|
|
|
@stage.configure_bones
|
|
def configure_master_properties(self):
|
|
master = self.bones.ctrl.master
|
|
|
|
if self.bbone_segments > 1:
|
|
self.make_property(master, 'finger_curve', 0.0, description="Rubber hose finger cartoon effect")
|
|
|
|
# Create UI
|
|
panel = self.script.panel_with_selected_check(self, self.bones.ctrl.flatten())
|
|
panel.custom_prop(master, 'finger_curve', text="Curvature", slider=True)
|
|
|
|
def rig_deform_bone(self, i, deform, org):
|
|
master = self.bones.ctrl.master
|
|
bone = self.get_bone(deform)
|
|
|
|
self.make_constraint(deform, 'COPY_TRANSFORMS', org)
|
|
|
|
if self.bbone_segments > 1:
|
|
self.make_driver(bone.bone, 'bbone_easein', variables=[(master, 'finger_curve')])
|
|
self.make_driver(bone.bone, 'bbone_easeout', variables=[(master, 'finger_curve')])
|
|
|
|
###############
|
|
# OPTIONS
|
|
|
|
@classmethod
|
|
def add_parameters(self, params):
|
|
""" Add the parameters of this rig type to the
|
|
RigifyParameters PropertyGroup
|
|
"""
|
|
items = [('automatic', 'Automatic', ''), ('X', 'X manual', ''), ('Y', 'Y manual', ''), ('Z', 'Z manual', ''),
|
|
('-X', '-X manual', ''), ('-Y', '-Y manual', ''), ('-Z', '-Z manual', '')]
|
|
params.primary_rotation_axis = bpy.props.EnumProperty(items=items, name="Primary Rotation Axis", default='automatic')
|
|
|
|
params.bbones = bpy.props.IntProperty(
|
|
name = 'B-Bone Segments',
|
|
default = 10,
|
|
min = 1,
|
|
description = 'Number of B-Bone segments'
|
|
)
|
|
|
|
params.make_extra_ik_control = bpy.props.BoolProperty(
|
|
name = "Extra IK Control",
|
|
default = False,
|
|
description = "Create an optional IK control"
|
|
)
|
|
|
|
params.ik_local_location = bpy.props.BoolProperty(
|
|
name = 'IK Local Location',
|
|
default = True,
|
|
description = "Specifies the value of the Local Location option for IK controls, which decides if the location channels are aligned to the local control orientation or world",
|
|
)
|
|
|
|
ControlLayersOption.TWEAK.add_parameters(params)
|
|
ControlLayersOption.EXTRA_IK.add_parameters(params)
|
|
|
|
@classmethod
|
|
def parameters_ui(self, layout, params):
|
|
""" Create the ui for the rig parameters.
|
|
"""
|
|
r = layout.row()
|
|
r.label(text="Bend rotation axis:")
|
|
r.prop(params, "primary_rotation_axis", text="")
|
|
|
|
layout.prop(params, 'bbones')
|
|
layout.prop(params, 'make_extra_ik_control', text='IK Control')
|
|
|
|
if params.make_extra_ik_control:
|
|
layout.prop(params, 'ik_local_location')
|
|
|
|
ControlLayersOption.TWEAK.parameters_ui(layout, params)
|
|
|
|
if params.make_extra_ik_control:
|
|
ControlLayersOption.EXTRA_IK.parameters_ui(layout, params)
|
|
|
|
#############################
|
|
# Finger FK to IK operator ##
|
|
#############################
|
|
|
|
SCRIPT_REGISTER_OP_SNAP_FK_IK = ['POSE_OT_rigify_finger_fk2ik', 'POSE_OT_rigify_finger_fk2ik_bake']
|
|
|
|
SCRIPT_UTILITIES_OP_SNAP_FK_IK = ['''
|
|
########################
|
|
## Limb Snap IK to FK ##
|
|
########################
|
|
|
|
class RigifyFingerFk2IkBase:
|
|
ik_control: StringProperty(name="IK Control")
|
|
ik_chain: StringProperty(name="IK output chain")
|
|
constraint_bone: StringProperty(name="Bone With the IK Constraint")
|
|
fk_master: StringProperty(name="FK Master Control")
|
|
fk_chain: StringProperty(name="FK Bone Chain")
|
|
axis: StringProperty(name="Main Rotation Axis", default="+X")
|
|
|
|
def init_execute(self, context):
|
|
self.ik_chain_list = json.loads(self.ik_chain)
|
|
self.fk_chain_list = json.loads(self.fk_chain)
|
|
|
|
# Extracting the IK state - requires forcing IK on temporarily
|
|
def find_constraint_drivers(self, obj):
|
|
self.driver_fcurves = {}
|
|
self.ik_constraint = None
|
|
|
|
if self.constraint_bone:
|
|
bone = obj.pose.bones[self.constraint_bone]
|
|
self.ik_constraint = con = bone.constraints['FingerIK']
|
|
self.driver_fcurves = DriverCurveTable(obj).get_prop_curves(con, 'influence')
|
|
|
|
def before_save_state(self, context, obj):
|
|
self.find_constraint_drivers(obj)
|
|
|
|
if self.ik_constraint:
|
|
for fcu in self.driver_fcurves.values():
|
|
fcu.mute = True
|
|
|
|
self.ik_constraint.influence = 1
|
|
|
|
context.view_layer.update()
|
|
|
|
def get_fk_axis_angles(self, obj):
|
|
options = self.axis_options[self.axis]
|
|
angles = []
|
|
|
|
for bone in self.fk_chain_list[1:-1]:
|
|
matrix = obj.pose.bones[bone].matrix_basis
|
|
eulers = matrix.to_euler(options['order'])
|
|
angles.append(eulers[options['axis']])
|
|
|
|
return angles
|
|
|
|
def get_ik_original_matrix(self, obj):
|
|
bone = obj.pose.bones[self.ik_chain_list[0]]
|
|
|
|
if len(bone.constraints) == 1 and bone.constraints[0].type == 'COPY_TRANSFORMS':
|
|
target = bone.constraints[0].subtarget
|
|
return obj.pose.bones[target].matrix
|
|
|
|
def save_frame_state(self, context, obj):
|
|
matrices = get_chain_transform_matrices(obj, self.ik_chain_list)
|
|
fk_matrices = get_chain_transform_matrices(obj, self.fk_chain_list)
|
|
angles = self.get_fk_axis_angles(obj)
|
|
ik_original = self.get_ik_original_matrix(obj)
|
|
return (matrices, fk_matrices, angles, ik_original)
|
|
|
|
def after_save_state(self, context, obj):
|
|
if self.ik_constraint:
|
|
for fcu in self.driver_fcurves.values():
|
|
fcu.mute = False
|
|
|
|
context.view_layer.update()
|
|
|
|
# Applying the state
|
|
axis_options = {
|
|
"+X": {"axis": 0, "sign": 1, "order": 'ZYX'},
|
|
"-X": {"axis": 0, "sign": -1, "order": 'ZYX'},
|
|
"+Y": {"axis": 1, "sign": 1, "order": 'ZXY'},
|
|
"-Y": {"axis": 1, "sign": -1, "order": 'ZXY'},
|
|
"+Z": {"axis": 2, "sign": 1, "order": 'XYZ'},
|
|
"-Z": {"axis": 2, "sign": -1, "order": 'XYZ'},
|
|
}
|
|
|
|
def apply_frame_state(self, context, obj, state):
|
|
matrices, fk_matrices, old_angles, ik_original = state
|
|
|
|
fk_master = obj.pose.bones[self.fk_master]
|
|
fk_chain = [ obj.pose.bones[k] for k in self.fk_chain_list ]
|
|
|
|
# Set the master control position and rotation.
|
|
master_mat = matrices[0]
|
|
|
|
if ik_original:
|
|
master_mat = master_mat @ ik_original.inverted() @ fk_matrices[0]
|
|
|
|
master_mat = obj.convert_space(
|
|
pose_bone=fk_chain[0].parent, matrix=master_mat,
|
|
from_space='POSE', to_space='LOCAL'
|
|
)
|
|
master_mat = obj.convert_space(
|
|
pose_bone=fk_master, matrix=master_mat,
|
|
from_space='LOCAL', to_space='POSE'
|
|
)
|
|
master_mat.translation = matrices[0].translation
|
|
|
|
set_transform_from_matrix(obj, self.fk_master, master_mat)
|
|
|
|
fk_master.scale = (1, 1, 1)
|
|
|
|
if self.keyflags is not None:
|
|
keyframe_transform_properties(obj, self.fk_master, self.keyflags)
|
|
|
|
context.view_layer.update()
|
|
|
|
# Apply the detail controls
|
|
set_chain_transforms_from_matrices(
|
|
context, obj, self.fk_chain_list[:-1], matrices, keyflags=self.keyflags,
|
|
)
|
|
|
|
set_transform_from_matrix(
|
|
obj, self.fk_chain_list[-1], Matrix.Identity(4), space='LOCAL', keyflags=self.keyflags
|
|
)
|
|
|
|
# Compute the master scale from average control angle, biased by original
|
|
options = self.axis_options[self.axis]
|
|
|
|
angles = self.get_fk_axis_angles(obj)
|
|
avg_angle = sum(a - b for a, b in zip(angles, old_angles)) / len(angles)
|
|
|
|
fk_master.scale[1] = 1 - avg_angle * options['sign'] / pi
|
|
|
|
if self.keyflags is not None:
|
|
keyframe_transform_properties(obj, self.fk_master, self.keyflags)
|
|
|
|
context.view_layer.update()
|
|
|
|
# Re-apply the rest of the detail controls
|
|
set_chain_transforms_from_matrices(
|
|
context, obj, self.fk_chain_list[1:-1], matrices[1:], keyflags=self.keyflags,
|
|
)
|
|
|
|
class POSE_OT_rigify_finger_fk2ik(RigifyFingerFk2IkBase, RigifySingleUpdateMixin, bpy.types.Operator):
|
|
bl_idname = "pose.rigify_finger_fk2ik_" + rig_id
|
|
bl_label = "Snap FK->IK"
|
|
bl_description = "Snap the FK chain to IK result"
|
|
|
|
class POSE_OT_rigify_finger_fk2ik_bake(RigifyFingerFk2IkBase, RigifyBakeKeyframesMixin, bpy.types.Operator):
|
|
bl_idname = "pose.rigify_finger_fk2ik_bake_" + rig_id
|
|
bl_label = "Apply Snap FK->IK To Keyframes"
|
|
bl_description = "Snap the FK chain keyframes to IK result"
|
|
|
|
def execute_scan_curves(self, context, obj):
|
|
fk_bones = [self.fk_master, *self.fk_chain_list]
|
|
self.bake_add_bone_frames(fk_bones + [self.ik_control], TRANSFORM_PROPS_ALL)
|
|
return self.bake_get_all_bone_curves(fk_bones, TRANSFORM_PROPS_ALL)
|
|
''']
|
|
|
|
def add_finger_snap_fk_to_ik(panel, *, master=None, fk_bones=[], ik_bones=[], ik_control=None, ik_constraint_bone=None, axis='+X', rig_name='', compact=None):
|
|
panel.use_bake_settings()
|
|
panel.script.add_utilities(SCRIPT_UTILITIES_OP_SNAP_FK_IK)
|
|
panel.script.register_classes(SCRIPT_REGISTER_OP_SNAP_FK_IK)
|
|
|
|
op_props = {
|
|
'fk_master': master,
|
|
'fk_chain': json.dumps(fk_bones),
|
|
'ik_chain': json.dumps(ik_bones),
|
|
'ik_control': ik_control,
|
|
'constraint_bone': ik_constraint_bone,
|
|
'axis': axis,
|
|
}
|
|
|
|
add_fk_ik_snap_buttons(
|
|
panel, 'pose.rigify_finger_fk2ik_{rig_id}', 'pose.rigify_finger_fk2ik_bake_{rig_id}',
|
|
label='FK->IK', rig_name=rig_name, properties=op_props,
|
|
clear_bones=[master, *fk_bones], compact=compact,
|
|
)
|
|
|
|
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.04.L')
|
|
bone.head[:] = 0.0043, -0.0030, -0.0026
|
|
bone.tail[:] = 0.0642, 0.0037, -0.0469
|
|
bone.roll = -2.5155
|
|
bone.use_connect = False
|
|
bones['palm.04.L'] = bone.name
|
|
bone = arm.edit_bones.new('f_pinky.01.L')
|
|
bone.head[:] = 0.0642, 0.0037, -0.0469
|
|
bone.tail[:] = 0.0703, 0.0039, -0.0741
|
|
bone.roll = -1.9749
|
|
bone.use_connect = False
|
|
bone.parent = arm.edit_bones[bones['palm.04.L']]
|
|
bones['f_pinky.01.L'] = bone.name
|
|
bone = arm.edit_bones.new('f_pinky.02.L')
|
|
bone.head[:] = 0.0703, 0.0039, -0.0741
|
|
bone.tail[:] = 0.0732, 0.0044, -0.0965
|
|
bone.roll = -1.9059
|
|
bone.use_connect = True
|
|
bone.parent = arm.edit_bones[bones['f_pinky.01.L']]
|
|
bones['f_pinky.02.L'] = bone.name
|
|
bone = arm.edit_bones.new('f_pinky.03.L')
|
|
bone.head[:] = 0.0732, 0.0044, -0.0965
|
|
bone.tail[:] = 0.0725, 0.0046, -0.1115
|
|
bone.roll = -1.7639
|
|
bone.use_connect = True
|
|
bone.parent = arm.edit_bones[bones['f_pinky.02.L']]
|
|
bones['f_pinky.03.L'] = bone.name
|
|
|
|
bpy.ops.object.mode_set(mode='OBJECT')
|
|
pbone = obj.pose.bones[bones['palm.04.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 = 'YXZ'
|
|
pbone = obj.pose.bones[bones['f_pinky.01.L']]
|
|
pbone.rigify_type = 'limbs.super_finger'
|
|
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.extra_layers = [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, False, False, False]
|
|
except AttributeError:
|
|
pass
|
|
try:
|
|
pbone.rigify_parameters.tweak_extra_layers = False
|
|
except AttributeError:
|
|
pass
|
|
try:
|
|
pbone.rigify_parameters.ik_local_location = False
|
|
except AttributeError:
|
|
pass
|
|
pbone = obj.pose.bones[bones['f_pinky.02.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['f_pinky.03.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'
|
|
|
|
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
|