Pitchipoy FK/IK switch implemented
This commit is contained in:
parent
d1dafb1ec7
commit
efd386991e
|
@ -32,6 +32,8 @@ from .utils import create_root_widget
|
|||
from .utils import random_id
|
||||
from .utils import copy_attributes
|
||||
from .rig_ui_template import UI_SLIDERS, layers_ui, UI_REGISTER
|
||||
from .rig_ui_pitchipoy_template import UI_P_SLIDERS, layers_P_ui, UI_P_REGISTER
|
||||
|
||||
|
||||
RIG_MODULE = "rigs"
|
||||
ORG_LAYER = [n == 31 for n in range(0, 32)] # Armature layer that original bones should be moved to.
|
||||
|
@ -385,18 +387,35 @@ def generate_rig(context, metarig):
|
|||
print( l.name )
|
||||
layer_layout += [(l.name, l.row)]
|
||||
|
||||
# Generate the UI script
|
||||
if "rig_ui.py" in bpy.data.texts:
|
||||
script = bpy.data.texts["rig_ui.py"]
|
||||
script.clear()
|
||||
|
||||
if isPitchipoy(metarig):
|
||||
|
||||
# Generate the UI Pitchipoy script
|
||||
if "rig_ui.py" in bpy.data.texts:
|
||||
script = bpy.data.texts["rig_ui.py"]
|
||||
script.clear()
|
||||
else:
|
||||
script = bpy.data.texts.new("rig_ui.py")
|
||||
script.write(UI_P_SLIDERS % rig_id)
|
||||
for s in ui_scripts:
|
||||
script.write("\n " + s.replace("\n", "\n ") + "\n")
|
||||
script.write(layers_P_ui(vis_layers, layer_layout))
|
||||
script.write(UI_P_REGISTER)
|
||||
script.use_module = True
|
||||
|
||||
else:
|
||||
script = bpy.data.texts.new("rig_ui.py")
|
||||
script.write(UI_SLIDERS % rig_id)
|
||||
for s in ui_scripts:
|
||||
script.write("\n " + s.replace("\n", "\n ") + "\n")
|
||||
script.write(layers_ui(vis_layers, layer_layout))
|
||||
script.write(UI_REGISTER)
|
||||
script.use_module = True
|
||||
# Generate the UI script
|
||||
if "rig_ui.py" in bpy.data.texts:
|
||||
script = bpy.data.texts["rig_ui.py"]
|
||||
script.clear()
|
||||
else:
|
||||
script = bpy.data.texts.new("rig_ui.py")
|
||||
script.write(UI_SLIDERS % rig_id)
|
||||
for s in ui_scripts:
|
||||
script.write("\n " + s.replace("\n", "\n ") + "\n")
|
||||
script.write(layers_ui(vis_layers, layer_layout))
|
||||
script.write(UI_REGISTER)
|
||||
script.use_module = True
|
||||
|
||||
# Run UI script
|
||||
exec(script.as_string(), {})
|
||||
|
@ -451,3 +470,13 @@ def param_name(param_name, rig_type):
|
|||
""" Get the actual parameter name, sans-rig-type.
|
||||
"""
|
||||
return param_name[len(rig_type) + 1:]
|
||||
|
||||
def isPitchipoy(metarig):
|
||||
""" Returns True if metarig is type pitchipoy.
|
||||
"""
|
||||
pbones=metarig.pose.bones
|
||||
for pb in pbones:
|
||||
words = pb.rigify_type.partition('.')
|
||||
if words[0] == 'pitchipoy':
|
||||
return True
|
||||
return False
|
|
@ -0,0 +1,717 @@
|
|||
#====================== BEGIN GPL LICENSE BLOCK ======================
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#======================= END GPL LICENSE BLOCK ========================
|
||||
|
||||
# <pep8 compliant>
|
||||
|
||||
UI_P_SLIDERS = '''
|
||||
import bpy
|
||||
from mathutils import Matrix, Vector
|
||||
from math import acos, pi, radians
|
||||
|
||||
rig_id = "%s"
|
||||
|
||||
|
||||
############################
|
||||
## Math utility functions ##
|
||||
############################
|
||||
|
||||
def perpendicular_vector(v):
|
||||
""" Returns a vector that is perpendicular to the one given.
|
||||
The returned vector is _not_ guaranteed to be normalized.
|
||||
"""
|
||||
# Create a vector that is not aligned with v.
|
||||
# It doesn't matter what vector. Just any vector
|
||||
# that's guaranteed to not be pointing in the same
|
||||
# direction.
|
||||
if abs(v[0]) < abs(v[1]):
|
||||
tv = Vector((1,0,0))
|
||||
else:
|
||||
tv = Vector((0,1,0))
|
||||
|
||||
# Use cross prouct to generate a vector perpendicular to
|
||||
# both tv and (more importantly) v.
|
||||
return v.cross(tv)
|
||||
|
||||
|
||||
def rotation_difference(mat1, mat2):
|
||||
""" Returns the shortest-path rotational difference between two
|
||||
matrices.
|
||||
"""
|
||||
q1 = mat1.to_quaternion()
|
||||
q2 = mat2.to_quaternion()
|
||||
angle = acos(min(1,max(-1,q1.dot(q2)))) * 2
|
||||
if angle > pi:
|
||||
angle = -angle + (2*pi)
|
||||
return angle
|
||||
|
||||
def tail_distance(angle,bone_ik,bone_fk):
|
||||
""" Returns the distance between the tails of two bones
|
||||
after rotating bone_ik in AXIS_ANGLE mode.
|
||||
"""
|
||||
rot_mod=bone_ik.rotation_mode
|
||||
if rot_mod != 'AXIS_ANGLE':
|
||||
bone_ik.rotation_mode = 'AXIS_ANGLE'
|
||||
bone_ik.rotation_axis_angle[0] = angle
|
||||
bpy.context.scene.update()
|
||||
|
||||
dv = (bone_fk.tail - bone_ik.tail).length
|
||||
|
||||
bone_ik.rotation_mode = rot_mod
|
||||
return dv
|
||||
|
||||
def find_min_range(bone_ik,bone_fk,f=tail_distance,delta=pi/8):
|
||||
""" finds the range where lies the minimum of function f applied on bone_ik and bone_fk
|
||||
at a certain angle.
|
||||
"""
|
||||
rot_mod=bone_ik.rotation_mode
|
||||
if rot_mod != 'AXIS_ANGLE':
|
||||
bone_ik.rotation_mode = 'AXIS_ANGLE'
|
||||
|
||||
start_angle = bone_ik.rotation_axis_angle[0]
|
||||
angle = start_angle
|
||||
while (angle > (start_angle - 2*pi)) and (angle < (start_angle + 2*pi)):
|
||||
l_dist = f(angle-delta,bone_ik,bone_fk)
|
||||
c_dist = f(angle,bone_ik,bone_fk)
|
||||
r_dist = f(angle+delta,bone_ik,bone_fk)
|
||||
if min((l_dist,c_dist,r_dist)) == c_dist:
|
||||
bone_ik.rotation_mode = rot_mod
|
||||
return (angle-delta,angle+delta)
|
||||
else:
|
||||
angle=angle+delta
|
||||
|
||||
def ternarySearch(f, left, right, bone_ik, bone_fk, absolutePrecision):
|
||||
"""
|
||||
Find minimum of unimodal function f() within [left, right]
|
||||
To find the maximum, revert the if/else statement or revert the comparison.
|
||||
"""
|
||||
while True:
|
||||
#left and right are the current bounds; the maximum is between them
|
||||
if abs(right - left) < absolutePrecision:
|
||||
return (left + right)/2
|
||||
|
||||
leftThird = left + (right - left)/3
|
||||
rightThird = right - (right - left)/3
|
||||
|
||||
if f(leftThird, bone_ik, bone_fk) > f(rightThird, bone_ik, bone_fk):
|
||||
left = leftThird
|
||||
else:
|
||||
right = rightThird
|
||||
|
||||
#########################################
|
||||
## "Visual Transform" helper functions ##
|
||||
#########################################
|
||||
|
||||
def get_pose_matrix_in_other_space(mat, pose_bone):
|
||||
""" Returns the transform matrix relative to pose_bone's current
|
||||
transform space. In other words, presuming that mat is in
|
||||
armature space, slapping the returned matrix onto pose_bone
|
||||
should give it the armature-space transforms of mat.
|
||||
TODO: try to handle cases with axis-scaled parents better.
|
||||
"""
|
||||
rest = pose_bone.bone.matrix_local.copy()
|
||||
rest_inv = rest.inverted()
|
||||
if pose_bone.parent:
|
||||
par_mat = pose_bone.parent.matrix.copy()
|
||||
par_inv = par_mat.inverted()
|
||||
par_rest = pose_bone.parent.bone.matrix_local.copy()
|
||||
else:
|
||||
par_mat = Matrix()
|
||||
par_inv = Matrix()
|
||||
par_rest = Matrix()
|
||||
|
||||
# Get matrix in bone's current transform space
|
||||
smat = rest_inv * (par_rest * (par_inv * mat))
|
||||
|
||||
# Compensate for non-local location
|
||||
#if not pose_bone.bone.use_local_location:
|
||||
# loc = smat.to_translation() * (par_rest.inverted() * rest).to_quaternion()
|
||||
# smat.translation = loc
|
||||
|
||||
return smat
|
||||
|
||||
|
||||
def get_local_pose_matrix(pose_bone):
|
||||
""" Returns the local transform matrix of the given pose bone.
|
||||
"""
|
||||
return get_pose_matrix_in_other_space(pose_bone.matrix, pose_bone)
|
||||
|
||||
|
||||
def set_pose_translation(pose_bone, mat):
|
||||
""" Sets the pose bone's translation to the same translation as the given matrix.
|
||||
Matrix should be given in bone's local space.
|
||||
"""
|
||||
if pose_bone.bone.use_local_location == True:
|
||||
pose_bone.location = mat.to_translation()
|
||||
else:
|
||||
loc = mat.to_translation()
|
||||
|
||||
rest = pose_bone.bone.matrix_local.copy()
|
||||
if pose_bone.bone.parent:
|
||||
par_rest = pose_bone.bone.parent.matrix_local.copy()
|
||||
else:
|
||||
par_rest = Matrix()
|
||||
|
||||
q = (par_rest.inverted() * rest).to_quaternion()
|
||||
pose_bone.location = q * loc
|
||||
|
||||
|
||||
def set_pose_rotation(pose_bone, mat):
|
||||
""" Sets the pose bone's rotation to the same rotation as the given matrix.
|
||||
Matrix should be given in bone's local space.
|
||||
"""
|
||||
q = mat.to_quaternion()
|
||||
|
||||
if pose_bone.rotation_mode == 'QUATERNION':
|
||||
pose_bone.rotation_quaternion = q
|
||||
elif pose_bone.rotation_mode == 'AXIS_ANGLE':
|
||||
pose_bone.rotation_axis_angle[0] = q.angle
|
||||
pose_bone.rotation_axis_angle[1] = q.axis[0]
|
||||
pose_bone.rotation_axis_angle[2] = q.axis[1]
|
||||
pose_bone.rotation_axis_angle[3] = q.axis[2]
|
||||
else:
|
||||
pose_bone.rotation_euler = q.to_euler(pose_bone.rotation_mode)
|
||||
|
||||
|
||||
def set_pose_scale(pose_bone, mat):
|
||||
""" Sets the pose bone's scale to the same scale as the given matrix.
|
||||
Matrix should be given in bone's local space.
|
||||
"""
|
||||
pose_bone.scale = mat.to_scale()
|
||||
|
||||
|
||||
def match_pose_translation(pose_bone, target_bone):
|
||||
""" Matches pose_bone's visual translation to target_bone's visual
|
||||
translation.
|
||||
This function assumes you are in pose mode on the relevant armature.
|
||||
"""
|
||||
mat = get_pose_matrix_in_other_space(target_bone.matrix, pose_bone)
|
||||
set_pose_translation(pose_bone, mat)
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
bpy.ops.object.mode_set(mode='POSE')
|
||||
|
||||
|
||||
def match_pose_rotation(pose_bone, target_bone):
|
||||
""" Matches pose_bone's visual rotation to target_bone's visual
|
||||
rotation.
|
||||
This function assumes you are in pose mode on the relevant armature.
|
||||
"""
|
||||
mat = get_pose_matrix_in_other_space(target_bone.matrix, pose_bone)
|
||||
set_pose_rotation(pose_bone, mat)
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
bpy.ops.object.mode_set(mode='POSE')
|
||||
|
||||
|
||||
def match_pose_scale(pose_bone, target_bone):
|
||||
""" Matches pose_bone's visual scale to target_bone's visual
|
||||
scale.
|
||||
This function assumes you are in pose mode on the relevant armature.
|
||||
"""
|
||||
mat = get_pose_matrix_in_other_space(target_bone.matrix, pose_bone)
|
||||
set_pose_scale(pose_bone, mat)
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
bpy.ops.object.mode_set(mode='POSE')
|
||||
|
||||
def correct_rotation(bone_ik, bone_fk):
|
||||
""" Corrects the ik rotation in ik2fk snapping functions
|
||||
"""
|
||||
|
||||
alfarange = find_min_range(bone_ik,bone_fk)
|
||||
alfamin = ternarySearch(tail_distance,alfarange[0],alfarange[1],bone_ik,bone_fk,0.1)
|
||||
|
||||
rot_mod = bone_ik.rotation_mode
|
||||
if rot_mod != 'AXIS_ANGLE':
|
||||
bone_ik.rotation_mode = 'AXIS_ANGLE'
|
||||
bone_ik.rotation_axis_angle[0] = alfamin
|
||||
bone_ik.rotation_mode = rot_mod
|
||||
|
||||
##############################
|
||||
## IK/FK snapping functions ##
|
||||
##############################
|
||||
|
||||
def match_pole_target(ik_first, ik_last, pole, match_bone, length):
|
||||
""" Places an IK chain's pole target to match ik_first's
|
||||
transforms to match_bone. All bones should be given as pose bones.
|
||||
You need to be in pose mode on the relevant armature object.
|
||||
ik_first: first bone in the IK chain
|
||||
ik_last: last bone in the IK chain
|
||||
pole: pole target bone for the IK chain
|
||||
match_bone: bone to match ik_first to (probably first bone in a matching FK chain)
|
||||
length: distance pole target should be placed from the chain center
|
||||
"""
|
||||
a = ik_first.matrix.to_translation()
|
||||
b = ik_last.matrix.to_translation() + ik_last.vector
|
||||
|
||||
# Vector from the head of ik_first to the
|
||||
# tip of ik_last
|
||||
ikv = b - a
|
||||
|
||||
# Get a vector perpendicular to ikv
|
||||
pv = perpendicular_vector(ikv).normalized() * length
|
||||
|
||||
def set_pole(pvi):
|
||||
""" Set pole target's position based on a vector
|
||||
from the arm center line.
|
||||
"""
|
||||
# Translate pvi into armature space
|
||||
ploc = a + (ikv/2) + pvi
|
||||
|
||||
# Set pole target to location
|
||||
mat = get_pose_matrix_in_other_space(Matrix.Translation(ploc), pole)
|
||||
set_pose_translation(pole, mat)
|
||||
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
bpy.ops.object.mode_set(mode='POSE')
|
||||
|
||||
set_pole(pv)
|
||||
|
||||
# Get the rotation difference between ik_first and match_bone
|
||||
angle = rotation_difference(ik_first.matrix, match_bone.matrix)
|
||||
|
||||
# Try compensating for the rotation difference in both directions
|
||||
pv1 = Matrix.Rotation(angle, 4, ikv) * pv
|
||||
set_pole(pv1)
|
||||
ang1 = rotation_difference(ik_first.matrix, match_bone.matrix)
|
||||
|
||||
pv2 = Matrix.Rotation(-angle, 4, ikv) * pv
|
||||
set_pole(pv2)
|
||||
ang2 = rotation_difference(ik_first.matrix, match_bone.matrix)
|
||||
|
||||
# Do the one with the smaller angle
|
||||
if ang1 < ang2:
|
||||
set_pole(pv1)
|
||||
|
||||
|
||||
def fk2ik_arm(obj, fk, ik):
|
||||
""" Matches the fk bones in an arm rig to the ik bones.
|
||||
obj: armature object
|
||||
fk: list of fk bone names
|
||||
ik: list of ik bone names
|
||||
"""
|
||||
uarm = obj.pose.bones[fk[0]]
|
||||
farm = obj.pose.bones[fk[1]]
|
||||
hand = obj.pose.bones[fk[2]]
|
||||
uarmi = obj.pose.bones[ik[0]]
|
||||
farmi = obj.pose.bones[ik[1]]
|
||||
handi = obj.pose.bones[ik[2]]
|
||||
|
||||
# Stretch
|
||||
#if handi['auto_stretch'] == 0.0:
|
||||
# uarm['stretch_length'] = handi['stretch_length']
|
||||
#else:
|
||||
# diff = (uarmi.vector.length + farmi.vector.length) / (uarm.vector.length + farm.vector.length)
|
||||
# uarm['stretch_length'] *= diff
|
||||
|
||||
# Upper arm position
|
||||
match_pose_translation(uarm, uarmi)
|
||||
match_pose_rotation(uarm, uarmi)
|
||||
match_pose_scale(uarm, uarmi)
|
||||
|
||||
# Forearm position
|
||||
#match_pose_translation(hand, handi)
|
||||
match_pose_rotation(farm, farmi)
|
||||
match_pose_scale(farm, farmi)
|
||||
|
||||
# Hand position
|
||||
match_pose_translation(hand, handi)
|
||||
match_pose_rotation(hand, handi)
|
||||
match_pose_scale(hand, handi)
|
||||
|
||||
|
||||
def ik2fk_arm(obj, fk, ik):
|
||||
""" Matches the ik bones in an arm rig to the fk bones.
|
||||
obj: armature object
|
||||
fk: list of fk bone names
|
||||
ik: list of ik bone names
|
||||
"""
|
||||
uarm = obj.pose.bones[fk[0]]
|
||||
farm = obj.pose.bones[fk[1]]
|
||||
hand = obj.pose.bones[fk[2]]
|
||||
uarmi = obj.pose.bones[ik[0]]
|
||||
farmi = obj.pose.bones[ik[1]]
|
||||
handi = obj.pose.bones[ik[2]]
|
||||
#pole = obj.pose.bones[ik[3]]
|
||||
|
||||
# Stretch
|
||||
#handi['stretch_length'] = uarm['stretch_length']
|
||||
|
||||
# Hand position
|
||||
match_pose_translation(handi, hand)
|
||||
match_pose_rotation(handi, hand)
|
||||
match_pose_scale(handi, hand)
|
||||
|
||||
# Upper Arm position
|
||||
match_pose_translation(uarmi, uarm)
|
||||
match_pose_rotation(uarmi, uarm)
|
||||
match_pose_scale(uarmi, uarm)
|
||||
|
||||
# Rotation Correction
|
||||
correct_rotation(uarmi, uarm)
|
||||
|
||||
# farmi.constraints["IK"].pole_target = obj
|
||||
# farmi.constraints["IK"].pole_subtarget = farm.name
|
||||
# farmi.constraints["IK"].pole_angle = -1.74533
|
||||
#
|
||||
# bpy.ops.object.mode_set(mode='POSE')
|
||||
# bpy.ops.pose.select_all(action='DESELECT')
|
||||
# uarmi.bone.select = True
|
||||
# bpy.ops.pose.visual_transform_apply()
|
||||
# farmi.constraints["IK"].pole_target = None
|
||||
|
||||
# Pole target position
|
||||
#match_pole_target(uarmi, farmi, pole, uarm, (uarmi.length + farmi.length))
|
||||
|
||||
|
||||
def fk2ik_leg(obj, fk, ik):
|
||||
""" Matches the fk bones in a leg rig to the ik bones.
|
||||
obj: armature object
|
||||
fk: list of fk bone names
|
||||
ik: list of ik bone names
|
||||
"""
|
||||
thigh = obj.pose.bones[fk[0]]
|
||||
shin = obj.pose.bones[fk[1]]
|
||||
foot = obj.pose.bones[fk[2]]
|
||||
mfoot = obj.pose.bones[fk[3]]
|
||||
thighi = obj.pose.bones[ik[0]]
|
||||
shini = obj.pose.bones[ik[1]]
|
||||
footi = obj.pose.bones[ik[2]]
|
||||
mfooti = obj.pose.bones[ik[3]]
|
||||
|
||||
# Stretch
|
||||
#if footi['auto_stretch'] == 0.0:
|
||||
# thigh['stretch_length'] = footi['stretch_length']
|
||||
#else:
|
||||
# diff = (thighi.vector.length + shini.vector.length) / (thigh.vector.length + shin.vector.length)
|
||||
# thigh['stretch_length'] *= diff
|
||||
|
||||
# Thigh position
|
||||
match_pose_translation(thigh, thighi)
|
||||
match_pose_rotation(thigh, thighi)
|
||||
match_pose_scale(thigh, thighi)
|
||||
|
||||
# Shin position
|
||||
match_pose_rotation(shin, shini)
|
||||
match_pose_scale(shin, shini)
|
||||
|
||||
# Foot position
|
||||
mat = mfoot.bone.matrix_local.inverted() * foot.bone.matrix_local
|
||||
footmat = get_pose_matrix_in_other_space(mfooti.matrix, foot) * mat
|
||||
set_pose_rotation(foot, footmat)
|
||||
set_pose_scale(foot, footmat)
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
bpy.ops.object.mode_set(mode='POSE')
|
||||
|
||||
|
||||
def ik2fk_leg(obj, fk, ik):
|
||||
""" Matches the ik bones in a leg rig to the fk bones.
|
||||
obj: armature object
|
||||
fk: list of fk bone names
|
||||
ik: list of ik bone names
|
||||
"""
|
||||
thigh = obj.pose.bones[fk[0]]
|
||||
shin = obj.pose.bones[fk[1]]
|
||||
mfoot = obj.pose.bones[fk[2]]
|
||||
thighi = obj.pose.bones[ik[0]]
|
||||
shini = obj.pose.bones[ik[1]]
|
||||
footi = obj.pose.bones[ik[2]]
|
||||
footroll = obj.pose.bones[ik[3]]
|
||||
#pole = obj.pose.bones[ik[4]]
|
||||
#mfooti = obj.pose.bones[ik[5]]
|
||||
mfooti = obj.pose.bones[ik[4]]
|
||||
foot = obj.pose.bones[fk[3]]
|
||||
|
||||
# Stretch
|
||||
#footi['stretch_length'] = thigh['stretch_length']
|
||||
|
||||
# Clear footroll
|
||||
set_pose_rotation(footroll, Matrix())
|
||||
|
||||
# Foot position
|
||||
mat = mfooti.bone.matrix_local.inverted() * footi.bone.matrix_local
|
||||
footmat = get_pose_matrix_in_other_space(foot.matrix, footi) * mat
|
||||
set_pose_translation(footi, footmat)
|
||||
set_pose_rotation(footi, footmat)
|
||||
set_pose_scale(footi, footmat)
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
bpy.ops.object.mode_set(mode='POSE')
|
||||
|
||||
# Thigh position
|
||||
match_pose_translation(thighi, thigh)
|
||||
match_pose_rotation(thighi, thigh)
|
||||
match_pose_scale(thighi, thigh)
|
||||
|
||||
# Rotation Correction
|
||||
correct_rotation(thighi,thigh)
|
||||
|
||||
# shini.constraints["IK"].pole_target = obj
|
||||
# shini.constraints["IK"].pole_subtarget = shin.name
|
||||
# shini.constraints["IK"].pole_angle = -1.74533
|
||||
#
|
||||
# bpy.ops.object.mode_set(mode='POSE')
|
||||
# bpy.ops.pose.select_all(action='DESELECT')
|
||||
# thighi.bone.select = True
|
||||
# bpy.ops.pose.visual_transform_apply()
|
||||
# shini.constraints["IK"].pole_target = None
|
||||
|
||||
# Pole target position
|
||||
#match_pole_target(thighi, shini, pole, thigh, (thighi.length + shini.length))
|
||||
|
||||
|
||||
##############################
|
||||
## IK/FK snapping operators ##
|
||||
##############################
|
||||
|
||||
class Rigify_Arm_FK2IK(bpy.types.Operator):
|
||||
""" Snaps an FK arm to an IK arm.
|
||||
"""
|
||||
bl_idname = "pose.rigify_arm_fk2ik_" + rig_id
|
||||
bl_label = "Rigify Snap FK arm to IK"
|
||||
bl_options = {'UNDO'}
|
||||
|
||||
uarm_fk = bpy.props.StringProperty(name="Upper Arm FK Name")
|
||||
farm_fk = bpy.props.StringProperty(name="Forerm FK Name")
|
||||
hand_fk = bpy.props.StringProperty(name="Hand FK Name")
|
||||
|
||||
uarm_ik = bpy.props.StringProperty(name="Upper Arm IK Name")
|
||||
farm_ik = bpy.props.StringProperty(name="Forearm IK Name")
|
||||
hand_ik = bpy.props.StringProperty(name="Hand IK Name")
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return (context.active_object != None and context.mode == 'POSE')
|
||||
|
||||
def execute(self, context):
|
||||
use_global_undo = context.user_preferences.edit.use_global_undo
|
||||
context.user_preferences.edit.use_global_undo = False
|
||||
try:
|
||||
fk2ik_arm(context.active_object, fk=[self.uarm_fk, self.farm_fk, self.hand_fk], ik=[self.uarm_ik, self.farm_ik, self.hand_ik])
|
||||
finally:
|
||||
context.user_preferences.edit.use_global_undo = use_global_undo
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class Rigify_Arm_IK2FK(bpy.types.Operator):
|
||||
""" Snaps an IK arm to an FK arm.
|
||||
"""
|
||||
bl_idname = "pose.rigify_arm_ik2fk_" + rig_id
|
||||
bl_label = "Rigify Snap IK arm to FK"
|
||||
bl_options = {'UNDO'}
|
||||
|
||||
uarm_fk = bpy.props.StringProperty(name="Upper Arm FK Name")
|
||||
farm_fk = bpy.props.StringProperty(name="Forerm FK Name")
|
||||
hand_fk = bpy.props.StringProperty(name="Hand FK Name")
|
||||
|
||||
uarm_ik = bpy.props.StringProperty(name="Upper Arm IK Name")
|
||||
farm_ik = bpy.props.StringProperty(name="Forearm IK Name")
|
||||
hand_ik = bpy.props.StringProperty(name="Hand IK Name")
|
||||
#pole = bpy.props.StringProperty(name="Pole IK Name")
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return (context.active_object != None and context.mode == 'POSE')
|
||||
|
||||
def execute(self, context):
|
||||
use_global_undo = context.user_preferences.edit.use_global_undo
|
||||
context.user_preferences.edit.use_global_undo = False
|
||||
try:
|
||||
#ik2fk_arm(context.active_object, fk=[self.uarm_fk, self.farm_fk, self.hand_fk], ik=[self.uarm_ik, self.farm_ik, self.hand_ik, self.pole])
|
||||
ik2fk_arm(context.active_object, fk=[self.uarm_fk, self.farm_fk, self.hand_fk], ik=[self.uarm_ik, self.farm_ik, self.hand_ik])
|
||||
finally:
|
||||
context.user_preferences.edit.use_global_undo = use_global_undo
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class Rigify_Leg_FK2IK(bpy.types.Operator):
|
||||
""" Snaps an FK leg to an IK leg.
|
||||
"""
|
||||
bl_idname = "pose.rigify_leg_fk2ik_" + rig_id
|
||||
bl_label = "Rigify Snap FK leg to IK"
|
||||
bl_options = {'UNDO'}
|
||||
|
||||
thigh_fk = bpy.props.StringProperty(name="Thigh FK Name")
|
||||
shin_fk = bpy.props.StringProperty(name="Shin FK Name")
|
||||
foot_fk = bpy.props.StringProperty(name="Foot FK Name")
|
||||
mfoot_fk = bpy.props.StringProperty(name="MFoot FK Name")
|
||||
|
||||
thigh_ik = bpy.props.StringProperty(name="Thigh IK Name")
|
||||
shin_ik = bpy.props.StringProperty(name="Shin IK Name")
|
||||
foot_ik = bpy.props.StringProperty(name="Foot IK Name")
|
||||
mfoot_ik = bpy.props.StringProperty(name="MFoot IK Name")
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return (context.active_object != None and context.mode == 'POSE')
|
||||
|
||||
def execute(self, context):
|
||||
use_global_undo = context.user_preferences.edit.use_global_undo
|
||||
context.user_preferences.edit.use_global_undo = False
|
||||
try:
|
||||
fk2ik_leg(context.active_object, fk=[self.thigh_fk, self.shin_fk, self.foot_fk, self.mfoot_fk], ik=[self.thigh_ik, self.shin_ik, self.foot_ik, self.mfoot_ik])
|
||||
finally:
|
||||
context.user_preferences.edit.use_global_undo = use_global_undo
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class Rigify_Leg_IK2FK(bpy.types.Operator):
|
||||
""" Snaps an IK leg to an FK leg.
|
||||
"""
|
||||
bl_idname = "pose.rigify_leg_ik2fk_" + rig_id
|
||||
bl_label = "Rigify Snap IK leg to FK"
|
||||
bl_options = {'UNDO'}
|
||||
|
||||
thigh_fk = bpy.props.StringProperty(name="Thigh FK Name")
|
||||
shin_fk = bpy.props.StringProperty(name="Shin FK Name")
|
||||
mfoot_fk = bpy.props.StringProperty(name="MFoot FK Name")
|
||||
foot_fk = bpy.props.StringProperty(name="Foot FK Name")
|
||||
thigh_ik = bpy.props.StringProperty(name="Thigh IK Name")
|
||||
shin_ik = bpy.props.StringProperty(name="Shin IK Name")
|
||||
foot_ik = bpy.props.StringProperty(name="Foot IK Name")
|
||||
footroll = bpy.props.StringProperty(name="Foot Roll Name")
|
||||
#pole = bpy.props.StringProperty(name="Pole IK Name")
|
||||
mfoot_ik = bpy.props.StringProperty(name="MFoot IK Name")
|
||||
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return (context.active_object != None and context.mode == 'POSE')
|
||||
|
||||
def execute(self, context):
|
||||
use_global_undo = context.user_preferences.edit.use_global_undo
|
||||
context.user_preferences.edit.use_global_undo = False
|
||||
try:
|
||||
#ik2fk_leg(context.active_object, fk=[self.thigh_fk, self.shin_fk, self.mfoot_fk], ik=[self.thigh_ik, self.shin_ik, self.foot_ik, self.footroll, self.pole, self.mfoot_ik])
|
||||
ik2fk_leg(context.active_object, fk=[self.thigh_fk, self.shin_fk, self.mfoot_fk, self.foot_fk], ik=[self.thigh_ik, self.shin_ik, self.foot_ik, self.footroll, self.mfoot_ik])
|
||||
finally:
|
||||
context.user_preferences.edit.use_global_undo = use_global_undo
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
###################
|
||||
## Rig UI Panels ##
|
||||
###################
|
||||
|
||||
class RigUI(bpy.types.Panel):
|
||||
bl_space_type = 'VIEW_3D'
|
||||
bl_region_type = 'UI'
|
||||
bl_label = "Rig Main Properties"
|
||||
bl_idname = rig_id + "_PT_rig_ui"
|
||||
|
||||
@classmethod
|
||||
def poll(self, context):
|
||||
if context.mode != 'POSE':
|
||||
return False
|
||||
try:
|
||||
return (context.active_object.data.get("rig_id") == rig_id)
|
||||
except (AttributeError, KeyError, TypeError):
|
||||
return False
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
pose_bones = context.active_object.pose.bones
|
||||
try:
|
||||
selected_bones = [bone.name for bone in context.selected_pose_bones]
|
||||
selected_bones += [context.active_pose_bone.name]
|
||||
except (AttributeError, TypeError):
|
||||
return
|
||||
|
||||
def is_selected(names):
|
||||
# Returns whether any of the named bones are selected.
|
||||
if type(names) == list:
|
||||
for name in names:
|
||||
if name in selected_bones:
|
||||
return True
|
||||
elif names in selected_bones:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
'''
|
||||
|
||||
|
||||
def layers_P_ui(layers, layout):
|
||||
""" Turn a list of booleans + a list of names into a layer UI.
|
||||
"""
|
||||
|
||||
code = '''
|
||||
class RigLayers(bpy.types.Panel):
|
||||
bl_space_type = 'VIEW_3D'
|
||||
bl_region_type = 'UI'
|
||||
bl_label = "Rig Layers"
|
||||
bl_idname = rig_id + "_PT_rig_layers"
|
||||
|
||||
@classmethod
|
||||
def poll(self, context):
|
||||
try:
|
||||
return (context.active_object.data.get("rig_id") == rig_id)
|
||||
except (AttributeError, KeyError, TypeError):
|
||||
return False
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
col = layout.column()
|
||||
'''
|
||||
rows = {}
|
||||
for i in range(28):
|
||||
if layers[i]:
|
||||
if layout[i][1] not in rows:
|
||||
rows[layout[i][1]] = []
|
||||
rows[layout[i][1]] += [(layout[i][0], i)]
|
||||
|
||||
keys = list(rows.keys())
|
||||
keys.sort()
|
||||
|
||||
for key in keys:
|
||||
code += "\n row = col.row()\n"
|
||||
i = 0
|
||||
for l in rows[key]:
|
||||
if i > 3:
|
||||
code += "\n row = col.row()\n"
|
||||
i = 0
|
||||
code += " row.prop(context.active_object.data, 'layers', index=%s, toggle=True, text='%s')\n" % (str(l[1]), l[0])
|
||||
i += 1
|
||||
|
||||
# Root layer
|
||||
code += "\n row = col.row()"
|
||||
code += "\n row.separator()"
|
||||
code += "\n row = col.row()"
|
||||
code += "\n row.separator()\n"
|
||||
code += "\n row = col.row()\n"
|
||||
code += " row.prop(context.active_object.data, 'layers', index=28, toggle=True, text='Root')\n"
|
||||
|
||||
return code
|
||||
|
||||
|
||||
UI_P_REGISTER = '''
|
||||
|
||||
def register():
|
||||
bpy.utils.register_class(Rigify_Arm_FK2IK)
|
||||
bpy.utils.register_class(Rigify_Arm_IK2FK)
|
||||
bpy.utils.register_class(Rigify_Leg_FK2IK)
|
||||
bpy.utils.register_class(Rigify_Leg_IK2FK)
|
||||
bpy.utils.register_class(RigUI)
|
||||
bpy.utils.register_class(RigLayers)
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_class(Rigify_Arm_FK2IK)
|
||||
bpy.utils.unregister_class(Rigify_Arm_IK2FK)
|
||||
bpy.utils.unregister_class(Rigify_Leg_FK2IK)
|
||||
bpy.utils.unregister_class(Rigify_Leg_IK2FK)
|
||||
bpy.utils.unregister_class(RigUI)
|
||||
bpy.utils.unregister_class(RigLayers)
|
||||
|
||||
register()
|
||||
'''
|
|
@ -1,13 +1,29 @@
|
|||
script = """
|
||||
script_arm = """
|
||||
controls = [%s]
|
||||
tweaks = [%s]
|
||||
ik_ctrl = '%s'
|
||||
ik_ctrl = [%s]
|
||||
fk_ctrl = '%s'
|
||||
parent = '%s'
|
||||
|
||||
# IK/FK Switch on all Control Bones
|
||||
if is_selected( controls ):
|
||||
layout.prop( pose_bones[ parent ], '["%s"]', slider = True )
|
||||
props = layout.operator("pose.rigify_arm_fk2ik_" + rig_id, text="Snap FK->IK (" + fk_ctrl + ")")
|
||||
props.uarm_fk = controls[1]
|
||||
props.farm_fk = controls[2]
|
||||
props.hand_fk = controls[3]
|
||||
props.uarm_ik = controls[0]
|
||||
props.farm_ik = ik_ctrl[1]
|
||||
props.hand_ik = controls[4]
|
||||
props = layout.operator("pose.rigify_arm_ik2fk_" + rig_id, text="Snap IK->FK (" + fk_ctrl + ")")
|
||||
props.uarm_fk = controls[1]
|
||||
props.farm_fk = controls[2]
|
||||
props.hand_fk = controls[3]
|
||||
props.uarm_ik = controls[0]
|
||||
props.farm_ik = ik_ctrl[1]
|
||||
props.hand_ik = controls[4]
|
||||
#props.pole = ik_arm[3]
|
||||
|
||||
|
||||
# BBone rubber hose on each Respective Tweak
|
||||
for t in tweaks:
|
||||
|
@ -23,14 +39,56 @@ if is_selected( fk_ctrl ):
|
|||
layout.prop( pose_bones[ parent ], '["%s"]', slider = True )
|
||||
"""
|
||||
|
||||
def create_script( bones, limb_type=None): # limb_type arg is added for future fk/ik
|
||||
# switch to add in UI scripts
|
||||
# scripts are different between arms and
|
||||
# legs and paws
|
||||
script_leg = """
|
||||
controls = [%s]
|
||||
tweaks = [%s]
|
||||
ik_ctrl = [%s]
|
||||
fk_ctrl = '%s'
|
||||
parent = '%s'
|
||||
|
||||
# IK/FK Switch on all Control Bones
|
||||
if is_selected( controls ):
|
||||
layout.prop( pose_bones[ parent ], '["%s"]', slider = True )
|
||||
props = layout.operator("pose.rigify_leg_fk2ik_" + rig_id, text="Snap FK->IK (" + fk_ctrl + ")")
|
||||
props.thigh_fk = controls[1]
|
||||
props.shin_fk = controls[2]
|
||||
props.foot_fk = controls[3]
|
||||
props.mfoot_fk = controls[7]
|
||||
props.thigh_ik = controls[0]
|
||||
props.shin_ik = ik_ctrl[1]
|
||||
props.foot_ik = ik_ctrl[2]
|
||||
props.mfoot_ik = ik_ctrl[2]
|
||||
props = layout.operator("pose.rigify_leg_ik2fk_" + rig_id, text="Snap IK->FK (" + fk_ctrl + ")")
|
||||
props.thigh_fk = controls[1]
|
||||
props.shin_fk = controls[2]
|
||||
props.foot_fk = controls[3]
|
||||
props.mfoot_fk = controls[7]
|
||||
props.thigh_ik = controls[0]
|
||||
props.shin_ik = ik_ctrl[1]
|
||||
props.foot_ik = controls[6]
|
||||
#props.pole = ik_leg[3]
|
||||
props.footroll = controls[5]
|
||||
props.mfoot_ik = ik_ctrl[2]
|
||||
|
||||
# BBone rubber hose on each Respective Tweak
|
||||
for t in tweaks:
|
||||
if is_selected( t ):
|
||||
layout.prop( pose_bones[ t ], '["%s"]', slider = True )
|
||||
|
||||
# IK Stretch on IK Control bone
|
||||
if is_selected( ik_ctrl ):
|
||||
layout.prop( pose_bones[ parent ], '["%s"]', slider = True )
|
||||
|
||||
# FK limb follow
|
||||
if is_selected( fk_ctrl ):
|
||||
layout.prop( pose_bones[ parent ], '["%s"]', slider = True )
|
||||
"""
|
||||
|
||||
def create_script( bones, limb_type=None):
|
||||
# All ctrls have IK/FK switch
|
||||
controls = [ bones['ik']['ctrl']['limb'] ] + bones['fk']['ctrl']
|
||||
controls += bones['ik']['ctrl']['terminal']
|
||||
controls += [ bones['fk']['mch'] ]
|
||||
|
||||
controls_string = ", ".join(["'" + x + "'" for x in controls])
|
||||
|
||||
|
@ -39,17 +97,47 @@ def create_script( bones, limb_type=None): # limb_type arg is added for fut
|
|||
tweaks_string = ", ".join(["'" + x + "'" for x in tweaks])
|
||||
|
||||
# IK ctrl has IK stretch
|
||||
ik_ctrl = bones['ik']['ctrl']['terminal'][-1]
|
||||
ik_ctrl = [ bones['ik']['ctrl']['terminal'][-1] ]
|
||||
ik_ctrl += [ bones['ik']['mch_ik'] ]
|
||||
ik_ctrl += [ bones['ik']['mch_target'] ]
|
||||
|
||||
return script % (
|
||||
controls_string,
|
||||
tweaks_string,
|
||||
ik_ctrl,
|
||||
bones['fk']['ctrl'][0],
|
||||
bones['parent'],
|
||||
'IK/FK',
|
||||
'rubber_tweak',
|
||||
'IK_Strertch',
|
||||
'FK_limb_follow'
|
||||
)
|
||||
ik_ctrl_string = ", ".join(["'" + x + "'" for x in ik_ctrl])
|
||||
|
||||
if limb_type == 'arm':
|
||||
return script_arm % (
|
||||
controls_string,
|
||||
tweaks_string,
|
||||
ik_ctrl_string,
|
||||
bones['fk']['ctrl'][0],
|
||||
bones['parent'],
|
||||
'IK/FK',
|
||||
'rubber_tweak',
|
||||
'IK_Strertch',
|
||||
'FK_limb_follow'
|
||||
)
|
||||
|
||||
elif limb_type == 'leg':
|
||||
return script_leg % (
|
||||
controls_string,
|
||||
tweaks_string,
|
||||
ik_ctrl_string,
|
||||
bones['fk']['ctrl'][0],
|
||||
bones['parent'],
|
||||
'IK/FK',
|
||||
'rubber_tweak',
|
||||
'IK_Strertch',
|
||||
'FK_limb_follow'
|
||||
)
|
||||
|
||||
elif limb_type == 'paw':
|
||||
return script_leg % (
|
||||
controls_string,
|
||||
tweaks_string,
|
||||
ik_ctrl_string,
|
||||
bones['fk']['ctrl'][0],
|
||||
bones['parent'],
|
||||
'IK/FK',
|
||||
'rubber_tweak',
|
||||
'IK_Strertch',
|
||||
'FK_limb_follow'
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue