Pitchipoy FK/IK switch implemented

This commit is contained in:
Lucio Rossi 2016-07-28 18:32:36 +02:00
parent d1dafb1ec7
commit efd386991e
3 changed files with 863 additions and 29 deletions

View File

@ -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

View File

@ -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()
'''

View File

@ -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'
)