850 lines
30 KiB
Python
850 lines
30 KiB
Python
# SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
import bpy
|
|
from mathutils import Vector
|
|
from ...utils import copy_bone, put_bone, org, align_bone_y_axis, align_bone_x_axis, align_bone_z_axis
|
|
from ...utils import strip_org, make_deformer_name, connected_children_names
|
|
from ...utils import create_chain_widget
|
|
from ...utils import make_mechanism_name
|
|
from ...utils import ControlLayersOption
|
|
from ..limbs.limb_utils import get_bone_name
|
|
|
|
|
|
class Rig:
|
|
|
|
def __init__(self, obj, bone_name, params):
|
|
""" Chain with pivot Rig """
|
|
|
|
eb = obj.data.bones
|
|
|
|
self.obj = obj
|
|
self.org_bones = [bone_name] + connected_children_names(obj, bone_name)
|
|
self.params = params
|
|
self.spine_length = sum([eb[b].length for b in self.org_bones])
|
|
self.bbones = params.bbones
|
|
self.SINGLE_BONE = (len(self.org_bones) == 1)
|
|
|
|
def orient_bone(self, eb, axis, scale, reverse=False):
|
|
v = Vector((0, 0, 0))
|
|
|
|
setattr(v, axis, scale)
|
|
|
|
if reverse:
|
|
tail_vec = v @ self.obj.matrix_world
|
|
eb.head[:] = eb.tail
|
|
eb.tail[:] = eb.head + tail_vec
|
|
else:
|
|
tail_vec = v @ self.obj.matrix_world
|
|
eb.tail[:] = eb.head + tail_vec
|
|
|
|
def create_pivot(self, pivot=None):
|
|
""" Create the pivot control and mechanism bones """
|
|
|
|
org_bones = self.org_bones
|
|
|
|
bpy.ops.object.mode_set(mode='EDIT')
|
|
eb = self.obj.data.edit_bones
|
|
|
|
if not pivot:
|
|
pivot = int(len(org_bones)/2)
|
|
|
|
pivot_name = org_bones[pivot]
|
|
if '.L' in pivot_name:
|
|
prefix = strip_org(org_bones[pivot]).split('.')[0] + '.L'
|
|
elif '.R' in pivot_name:
|
|
prefix = strip_org(org_bones[pivot]).split('.')[0] + '.R'
|
|
else:
|
|
prefix = strip_org(org_bones[pivot]).split('.')[0]
|
|
|
|
ctrl_name = get_bone_name(prefix, 'ctrl', 'pivot')
|
|
ctrl_name = copy_bone(self.obj, pivot_name, ctrl_name)
|
|
ctrl_eb = eb[ctrl_name]
|
|
|
|
self.orient_bone(ctrl_eb, 'y', self.spine_length / 2.5)
|
|
|
|
pivot_loc = eb[pivot_name].head + ((eb[pivot_name].tail - eb[pivot_name].head)/2)*(len(org_bones) % 2)
|
|
|
|
put_bone(self.obj, ctrl_name, pivot_loc)
|
|
|
|
v = eb[org_bones[-1]].tail - eb[org_bones[0]].head # Create a vector from head of first ORG to tail of last
|
|
v.normalize()
|
|
v_proj = eb[org_bones[0]].y_axis.dot(v)*v # projection of first ORG to v
|
|
v_point = eb[org_bones[0]].y_axis - v_proj # a vector co-planar to first ORG and v directed out of the chain
|
|
|
|
if v_point.magnitude < eb[org_bones[0]].y_axis.magnitude*1e-03: # if v_point is too small it's not usable
|
|
v_point = eb[org_bones[0]].x_axis
|
|
|
|
if self.params.tweak_axis == 'auto':
|
|
align_bone_y_axis(self.obj, ctrl_name, v)
|
|
align_bone_z_axis(self.obj, ctrl_name, -v_point)
|
|
elif self.params.tweak_axis == 'x':
|
|
align_bone_y_axis(self.obj, ctrl_name, Vector((1, 0, 0)))
|
|
align_bone_x_axis(self.obj, ctrl_name, Vector((0, 0, 1)))
|
|
elif self.params.tweak_axis == 'y':
|
|
align_bone_y_axis(self.obj, ctrl_name, Vector((0, 1, 0)))
|
|
align_bone_x_axis(self.obj, ctrl_name, Vector((1, 0, 0)))
|
|
elif self.params.tweak_axis == 'z':
|
|
align_bone_y_axis(self.obj, ctrl_name, Vector((0, 0, 1)))
|
|
align_bone_x_axis(self.obj, ctrl_name, Vector((1, 0, 0)))
|
|
|
|
return {
|
|
'ctrl': ctrl_name
|
|
}
|
|
|
|
def create_deform(self):
|
|
org_bones = self.org_bones
|
|
|
|
bpy.ops.object.mode_set(mode='EDIT')
|
|
|
|
def_bones = []
|
|
for o in org_bones:
|
|
def_name = make_deformer_name(strip_org(o))
|
|
def_name = copy_bone(self.obj, o, def_name)
|
|
def_bones.append(def_name)
|
|
|
|
bpy.ops.object.mode_set(mode='POSE')
|
|
# Create bbone segments
|
|
for bone in def_bones:
|
|
self.obj.data.bones[bone].bbone_segments = self.bbones
|
|
|
|
if not self.SINGLE_BONE:
|
|
self.obj.data.bones[def_bones[0]].bbone_easein = 0.0
|
|
self.obj.data.bones[def_bones[-1]].bbone_easeout = 0.0
|
|
else:
|
|
self.obj.data.bones[def_bones[0]].bbone_easein = 1.0
|
|
self.obj.data.bones[def_bones[-1]].bbone_easeout = 1.0
|
|
bpy.ops.object.mode_set(mode='EDIT')
|
|
|
|
conv_def = ""
|
|
if self.params.conv_bone and self.params.conv_def:
|
|
b = org(self.params.conv_bone)
|
|
conv_def = make_deformer_name(strip_org(b))
|
|
conv_def = copy_bone(self.obj, b, conv_def)
|
|
|
|
return def_bones, conv_def
|
|
|
|
def create_chain(self):
|
|
org_bones = self.org_bones
|
|
|
|
bpy.ops.object.mode_set(mode='EDIT')
|
|
eb = self.obj.data.edit_bones
|
|
|
|
twk, mch, mch_ctrl, ctrl = [], [], [], []
|
|
|
|
suffix = ''
|
|
if '.L' in org_bones[0]:
|
|
suffix = '.L'
|
|
elif '.R' in org_bones[0]:
|
|
suffix = '.R'
|
|
|
|
mch_auto = ''
|
|
if not self.SINGLE_BONE:
|
|
mch_name = copy_bone(self.obj, org(org_bones[0]),
|
|
'MCH-AUTO-' + strip_org(org_bones[0]).split('.')[0] + suffix)
|
|
eb[mch_name].head = eb[org_bones[0]].head
|
|
eb[mch_name].tail = eb[org_bones[-1]].tail
|
|
|
|
mch_auto = mch_name
|
|
|
|
# Intermediary bones
|
|
for b in org_bones: # All
|
|
|
|
if self.SINGLE_BONE:
|
|
mch_name = copy_bone(self.obj, org(b), make_mechanism_name(strip_org(b)))
|
|
eb[mch_name].length /= 4
|
|
put_bone(self.obj, mch_name, eb[b].head - (eb[mch_name].tail - eb[mch_name].head))
|
|
align_bone_z_axis(self.obj, mch_name, eb[b].z_axis)
|
|
mch += [mch_name]
|
|
mch_name = copy_bone(self.obj, org(b), make_mechanism_name(strip_org(b)))
|
|
eb[mch_name].length /= 4
|
|
put_bone(self.obj, mch_name, eb[b].tail)
|
|
mch += [mch_name]
|
|
break
|
|
else:
|
|
mch_name = copy_bone(self.obj, org(b), make_mechanism_name(strip_org(b)))
|
|
eb[mch_name].length /= 4
|
|
|
|
mch += [mch_name]
|
|
|
|
if b == org_bones[-1]: # Add extra
|
|
mch_name = copy_bone(self.obj, org(b), make_mechanism_name(strip_org(b)))
|
|
eb[mch_name].length /= 4
|
|
put_bone(self.obj, mch_name, eb[b].tail)
|
|
mch += [mch_name]
|
|
|
|
# Tweak & Ctrl bones
|
|
v = eb[org_bones[-1]].tail - eb[org_bones[0]].head # Create a vector from head of first ORG to tail of last
|
|
v.normalize()
|
|
v_proj = eb[org_bones[0]].y_axis.dot(v)*v # projection of first ORG to v
|
|
v_point = eb[org_bones[0]].y_axis - v_proj # a vector co-planar to first ORG and v directed out of the chain
|
|
|
|
if v_point.magnitude < eb[org_bones[0]].y_axis.magnitude*1e-03:
|
|
v_point = eb[org_bones[0]].x_axis
|
|
|
|
for b in org_bones: # All
|
|
|
|
suffix = ''
|
|
if '.L' in b:
|
|
suffix = '.L'
|
|
elif '.R' in b:
|
|
suffix = '.R'
|
|
|
|
if b == org_bones[0]:
|
|
name = get_bone_name(b.split('.')[0] + suffix, 'ctrl', 'ctrl')
|
|
name = copy_bone(self.obj, org(b), name)
|
|
align_bone_x_axis(self.obj, name, eb[org(b)].x_axis)
|
|
ctrl += [name]
|
|
else:
|
|
name = 'tweak_' + strip_org(b)
|
|
name = copy_bone(self.obj, org(b), name)
|
|
twk += [name]
|
|
|
|
self.orient_bone(eb[name], 'y', eb[name].length / 2)
|
|
|
|
if self.params.tweak_axis == 'auto':
|
|
align_bone_y_axis(self.obj, name, v)
|
|
align_bone_z_axis(self.obj, name, -v_point) # invert?
|
|
elif self.params.tweak_axis == 'x':
|
|
align_bone_y_axis(self.obj, name, Vector((1, 0, 0)))
|
|
align_bone_x_axis(self.obj, name, Vector((0, 0, 1)))
|
|
elif self.params.tweak_axis == 'y':
|
|
align_bone_y_axis(self.obj, name, Vector((0, 1, 0)))
|
|
align_bone_x_axis(self.obj, name, Vector((1, 0, 0)))
|
|
elif self.params.tweak_axis == 'z':
|
|
align_bone_y_axis(self.obj, name, Vector((0, 0, 1)))
|
|
align_bone_x_axis(self.obj, name, Vector((1, 0, 0)))
|
|
|
|
if b == org_bones[-1]: # Add extra
|
|
ctrl_name = get_bone_name(b.split('.')[0] + suffix, 'ctrl', 'ctrl')
|
|
ctrl_name = copy_bone(self.obj, org(b), ctrl_name)
|
|
|
|
self.orient_bone(eb[ctrl_name], 'y', eb[ctrl_name].length / 2)
|
|
|
|
# TODO check this if else
|
|
if self.params.conv_bone:
|
|
align_bone_y_axis(self.obj, ctrl_name, eb[org(self.params.conv_bone)].y_axis)
|
|
align_bone_x_axis(self.obj, ctrl_name, eb[org(self.params.conv_bone)].x_axis)
|
|
align_bone_z_axis(self.obj, ctrl_name, eb[org(self.params.conv_bone)].z_axis)
|
|
|
|
else:
|
|
if twk:
|
|
lastname = twk[-1]
|
|
else:
|
|
lastname = ctrl[-1]
|
|
align_bone_y_axis(self.obj, ctrl_name, eb[lastname].y_axis)
|
|
align_bone_x_axis(self.obj, ctrl_name, eb[lastname].x_axis)
|
|
put_bone(self.obj, ctrl_name, eb[b].tail)
|
|
|
|
ctrl += [ctrl_name]
|
|
|
|
conv_twk = ''
|
|
# Convergence tweak
|
|
if self.params.conv_bone:
|
|
conv_twk = 'tweak_' + strip_org(self.params.conv_bone)
|
|
|
|
if not(conv_twk in eb.keys()):
|
|
conv_twk = copy_bone(self.obj, org(self.params.conv_bone), conv_twk)
|
|
|
|
for b in org_bones:
|
|
|
|
if self.SINGLE_BONE:
|
|
break
|
|
|
|
# Mch controls
|
|
suffix = ''
|
|
if '.L' in b:
|
|
suffix = '.L'
|
|
elif '.R' in b:
|
|
suffix = '.R'
|
|
|
|
mch_ctrl_name = "MCH-CTRL-" + strip_org(b).split('.')[0] + suffix
|
|
mch_ctrl_name = copy_bone(self.obj, twk[0] if twk else ctrl[0], mch_ctrl_name)
|
|
|
|
eb[mch_ctrl_name].length /= 6
|
|
|
|
put_bone(self.obj, mch_ctrl_name, eb[b].head)
|
|
|
|
mch_ctrl += [mch_ctrl_name]
|
|
|
|
if b == org_bones[-1]: # Add extra
|
|
mch_ctrl_name = "MCH-CTRL-" + strip_org(b).split('.')[0] + suffix
|
|
mch_ctrl_name = copy_bone(self.obj, twk[0] if twk else ctrl[0], mch_ctrl_name)
|
|
|
|
eb[mch_ctrl_name].length /= 6
|
|
|
|
put_bone(self.obj, mch_ctrl_name, eb[b].tail)
|
|
|
|
mch_ctrl += [mch_ctrl_name]
|
|
|
|
return {
|
|
'mch': mch,
|
|
'mch_ctrl': mch_ctrl,
|
|
'mch_auto': mch_auto,
|
|
'tweak': twk,
|
|
'ctrl': ctrl,
|
|
'conv': conv_twk
|
|
}
|
|
|
|
def parent_bones(self, bones):
|
|
|
|
bpy.ops.object.mode_set(mode='EDIT')
|
|
eb = self.obj.data.edit_bones
|
|
|
|
# Parent deform bones
|
|
for i, b in enumerate(bones['def']):
|
|
if i > 0: # For all bones but the first (which has no parent)
|
|
eb[b].parent = eb[bones['def'][i-1]] # to previous
|
|
eb[b].use_connect = True
|
|
elif self.SINGLE_BONE:
|
|
eb[b].parent = eb[bones['chain']['mch'][0]]
|
|
eb[b].use_connect = True
|
|
|
|
# Todo check case when sup_chain is in bigger rig
|
|
eb[bones['def'][0]].parent = eb[bones['chain']['mch'][0]]
|
|
|
|
for i, twk in enumerate(bones['chain']['tweak']):
|
|
eb[twk].parent = eb[bones['chain']['mch_ctrl'][i+1]]
|
|
eb[twk].inherit_scale = 'NONE'
|
|
|
|
eb[bones['chain']['ctrl'][0]].parent = eb[bones['chain']['mch_ctrl'][0]] if bones['chain']['mch_ctrl'] else None
|
|
eb[bones['chain']['ctrl'][0]].inherit_scale = 'NONE'
|
|
eb[bones['chain']['ctrl'][1]].parent = eb[bones['chain']['mch_ctrl'][-1]] if bones['chain']['mch_ctrl'] else None
|
|
eb[bones['chain']['ctrl'][1]].inherit_scale = 'NONE'
|
|
|
|
if 'pivot' in bones.keys():
|
|
eb[bones['pivot']['ctrl']].inherit_scale = 'NONE'
|
|
|
|
for i, mch in enumerate(bones['chain']['mch']):
|
|
if mch == bones['chain']['mch'][0]:
|
|
eb[mch].parent = eb[bones['chain']['ctrl'][0]]
|
|
elif mch == bones['chain']['mch'][-1]:
|
|
eb[mch].parent = eb[bones['chain']['ctrl'][1]]
|
|
else:
|
|
eb[mch].parent = eb[bones['chain']['tweak'][i-1]]
|
|
|
|
if 'parent' in bones.keys():
|
|
if self.SINGLE_BONE:
|
|
eb[bones['chain']['ctrl'][0]].parent = eb[bones['parent']]
|
|
eb[bones['chain']['ctrl'][-1]].parent = eb[bones['parent']]
|
|
else:
|
|
eb[bones['chain']['mch_auto']].parent = eb[bones['parent']]
|
|
eb[bones['chain']['mch_ctrl'][0]].parent = eb[bones['parent']]
|
|
eb[bones['chain']['mch_ctrl'][-1]].parent = eb[bones['parent']]
|
|
|
|
for i, mch_ctrl in enumerate(bones['chain']['mch_ctrl'][1:-1]):
|
|
eb[mch_ctrl].parent = eb[bones['chain']['mch_auto']]
|
|
|
|
if 'pivot' in bones.keys():
|
|
eb[bones['pivot']['ctrl']].parent = eb[bones['chain']['mch_auto']]
|
|
|
|
if bones['chain']['conv']:
|
|
eb[bones['chain']['ctrl'][-1]].parent = eb[bones['chain']['conv']]
|
|
eb[bones['chain']['conv']].parent = eb[bones['chain']['conv']].parent
|
|
|
|
if self.SINGLE_BONE:
|
|
eb[bones['chain']['mch'][0]].parent = eb[bones['chain']['ctrl'][0]]
|
|
eb[bones['chain']['mch'][1]].parent = eb[bones['chain']['ctrl'][1]]
|
|
|
|
return
|
|
|
|
def make_constraint(self, bone, constraint):
|
|
bpy.ops.object.mode_set(mode='OBJECT')
|
|
pb = self.obj.pose.bones
|
|
|
|
owner_pb = pb[bone]
|
|
const = owner_pb.constraints.new(constraint['constraint'])
|
|
const.target = self.obj
|
|
|
|
# filter constraint props to those that actually exist in the current type of constraint,
|
|
# then assign values to each
|
|
for p in [k for k in constraint.keys() if k in dir(const)]:
|
|
setattr(const, p, constraint[p])
|
|
|
|
def constrain_bones(self, bones):
|
|
|
|
deform = bones['def']
|
|
mch = bones['chain']['mch']
|
|
mch_ctrl = bones['chain']['mch_ctrl']
|
|
ctrls = bones['chain']['ctrl']
|
|
tweaks = [ctrls[0]] + bones['chain']['tweak'] + [ctrls[-1]]
|
|
|
|
# ORG bones
|
|
for i, org_bone in enumerate(self.org_bones):
|
|
self.make_constraint(org_bone, {
|
|
'constraint': 'COPY_TRANSFORMS',
|
|
'subtarget': tweaks[i],
|
|
'owner_space': 'WORLD',
|
|
'target_space': 'WORLD'
|
|
})
|
|
|
|
# DEF bones
|
|
|
|
for i, d in enumerate(deform):
|
|
|
|
if len(deform) > 1:
|
|
self.make_constraint(d, {
|
|
'constraint': 'COPY_TRANSFORMS',
|
|
'subtarget': mch[i],
|
|
'owner_space': 'POSE',
|
|
'target_space': 'POSE'
|
|
})
|
|
|
|
self.make_constraint(d, {
|
|
'constraint': 'STRETCH_TO',
|
|
'subtarget': tweaks[i+1]
|
|
})
|
|
|
|
if bones['conv_def']:
|
|
self.make_constraint(bones['conv_def'], {
|
|
'constraint': 'COPY_TRANSFORMS',
|
|
'subtarget': bones['chain']['conv'],
|
|
'owner_space': 'POSE',
|
|
'target_space': 'POSE'
|
|
})
|
|
|
|
if 'pivot' in bones.keys():
|
|
step = 2/(len(self.org_bones))
|
|
for i, b in enumerate(mch_ctrl):
|
|
xval = i*step
|
|
influence = 2*xval - xval**2 # parabolic influence of pivot
|
|
if (i != 0) and (i != len(mch_ctrl)-1):
|
|
self.make_constraint(b, {
|
|
'constraint': 'COPY_TRANSFORMS',
|
|
'subtarget': bones['pivot']['ctrl'],
|
|
'influence': influence,
|
|
'owner_space': 'LOCAL',
|
|
'target_space': 'LOCAL'
|
|
})
|
|
|
|
# MCH-AUTO
|
|
|
|
mch_auto = bones['chain']['mch_auto']
|
|
|
|
if mch_auto:
|
|
self.make_constraint(mch_auto, {
|
|
'constraint': 'COPY_LOCATION',
|
|
'subtarget': mch[0],
|
|
'owner_space': 'WORLD',
|
|
'target_space': 'WORLD'
|
|
})
|
|
|
|
self.make_constraint(mch_auto, {
|
|
'constraint': 'STRETCH_TO',
|
|
'subtarget': tweaks[-1]
|
|
})
|
|
|
|
# PIVOT CTRL
|
|
|
|
if 'pivot' in bones.keys():
|
|
|
|
pivot = bones['pivot']['ctrl']
|
|
|
|
self.make_constraint(pivot, {
|
|
'constraint': 'COPY_ROTATION',
|
|
'subtarget': tweaks[0],
|
|
'influence': 0.33,
|
|
'owner_space': 'LOCAL',
|
|
'target_space': 'LOCAL'
|
|
})
|
|
|
|
self.make_constraint(pivot, {
|
|
'constraint': 'COPY_ROTATION',
|
|
'subtarget': tweaks[-1],
|
|
'influence': 0.33,
|
|
'owner_space': 'LOCAL',
|
|
'target_space': 'LOCAL'
|
|
})
|
|
|
|
return
|
|
|
|
def stick_to_bendy_bones(self, bones):
|
|
bpy.ops.object.mode_set(mode='OBJECT')
|
|
deform = bones['def']
|
|
pb = self.obj.pose.bones
|
|
|
|
if len(deform) > 1: # Only for single bone sup chain
|
|
return
|
|
|
|
def_pb = pb[deform[0]]
|
|
ctrl_start = pb[bones['chain']['ctrl'][0]]
|
|
ctrl_end = pb[bones['chain']['ctrl'][-1]]
|
|
mch_start = pb[bones['chain']['mch'][0]]
|
|
mch_end = pb[bones['chain']['mch_ctrl'][-1]] if bones['chain']['mch_ctrl'] else pb[bones['chain']['mch'][-1]]
|
|
|
|
if not self.SINGLE_BONE:
|
|
def_pb.bone.bbone_custom_handle_start = ctrl_start.bone
|
|
def_pb.bone.bbone_custom_handle_end = ctrl_end.bone
|
|
else:
|
|
def_pb.bone.bbone_custom_handle_start = mch_start.bone
|
|
def_pb.bone.bbone_custom_handle_end = mch_end.bone
|
|
|
|
def_pb.bone.bbone_handle_type_start = 'ABSOLUTE'
|
|
def_pb.bone.bbone_handle_type_end = 'ABSOLUTE'
|
|
|
|
def create_drivers(self, bones):
|
|
bpy.ops.object.mode_set(mode='OBJECT')
|
|
pb = self.obj.pose.bones
|
|
|
|
# Setting the torso's props
|
|
torso = pb[bones['pivot']['ctrl']]
|
|
|
|
props = ["head_follow", "neck_follow"]
|
|
owners = [bones['neck']['mch_head'], bones['neck']['mch_neck']]
|
|
|
|
for prop in props:
|
|
if prop == 'neck_follow':
|
|
defval = 0.5
|
|
else:
|
|
defval = 0.0
|
|
|
|
make_property(torso, prop, defval)
|
|
|
|
# driving the follow rotation switches for neck and head
|
|
for bone, prop, in zip(owners, props):
|
|
# Add driver to copy rotation constraint
|
|
make_driver(pb[bone].constraints[0], "influence", variables=[(self.obj, torso, prop)], polynomial=[1.0, -1.0])
|
|
|
|
def locks_and_widgets(self, bones):
|
|
bpy.ops.object.mode_set(mode='OBJECT')
|
|
pb = self.obj.pose.bones
|
|
|
|
# Locks
|
|
mch_ctrl = bones['chain']['mch_ctrl']
|
|
|
|
for b in mch_ctrl:
|
|
pb[b].lock_rotation = False, False, False
|
|
pb[b].lock_location = False, False, False
|
|
pb[b].lock_scale = False, False, False
|
|
|
|
for b in bones['chain']['tweak']:
|
|
pb[b].lock_rotation = True, False, True
|
|
|
|
for b in bones['chain']['ctrl']:
|
|
pb[b].lock_rotation = True, False, True
|
|
|
|
if 'pivot' in bones.keys():
|
|
pb[bones['pivot']['ctrl']].lock_rotation = True, False, True
|
|
|
|
# Assigning a widget to main ctrl bone
|
|
if 'pivot' in bones.keys():
|
|
create_chain_widget(
|
|
self.obj,
|
|
bones['pivot']['ctrl'],
|
|
cube=True,
|
|
radius=0.15,
|
|
bone_transform_name=None,
|
|
axis=self.params.wgt_align_axis,
|
|
offset=self.params.wgt_offset*pb[bones['chain']['ctrl'][0]].length
|
|
)
|
|
|
|
for bone in bones['chain']['tweak']:
|
|
create_chain_widget(
|
|
self.obj,
|
|
bone,
|
|
cube=True,
|
|
radius=0.2,
|
|
bone_transform_name=None,
|
|
axis=self.params.wgt_align_axis,
|
|
offset=self.params.wgt_offset*pb[bones['chain']['ctrl'][0]].length
|
|
)
|
|
|
|
create_chain_widget(
|
|
self.obj,
|
|
bones['chain']['ctrl'][0],
|
|
invert=False,
|
|
radius=0.3,
|
|
bone_transform_name=None,
|
|
axis=self.params.wgt_align_axis,
|
|
offset=self.params.wgt_offset*pb[bones['chain']['ctrl'][0]].length
|
|
)
|
|
|
|
invert_last = True
|
|
if self.params.wgt_align_axis not in {'y', '-y'}:
|
|
invert_last = False
|
|
|
|
create_chain_widget(
|
|
self.obj,
|
|
bones['chain']['ctrl'][-1],
|
|
invert=invert_last,
|
|
radius=0.3,
|
|
bone_transform_name=None,
|
|
axis=self.params.wgt_align_axis,
|
|
offset=self.params.wgt_offset*pb[bones['chain']['ctrl'][0]].length
|
|
)
|
|
|
|
if bones['chain']['conv']:
|
|
create_chain_widget(
|
|
self.obj,
|
|
bones['chain']['conv'],
|
|
cube=True,
|
|
radius=0.5,
|
|
bone_transform_name=None,
|
|
axis=self.params.wgt_align_axis,
|
|
offset=self.params.wgt_offset*pb[bones['chain']['ctrl'][0]].length
|
|
)
|
|
|
|
# Assigning layers to tweaks and ctrls
|
|
ControlLayersOption.TWEAK.assign(self.params, pb, bones['chain']['tweak'])
|
|
|
|
return
|
|
|
|
def aggregate_ctrls(self, bones):
|
|
|
|
if not self.params.cluster_ctrls:
|
|
return
|
|
|
|
other_ctrls = []
|
|
all_ctrls = []
|
|
eb = self.obj.data.edit_bones
|
|
|
|
head_start = eb[bones['chain']['ctrl'][0]].head
|
|
head_tip = eb[bones['chain']['ctrl'][-1]].head
|
|
|
|
all_ctrls.extend(bones['chain']['ctrl'])
|
|
all_ctrls.extend(bones['chain']['tweak'])
|
|
all_ctrls.extend(bones['chain']['conv'])
|
|
for bname in eb.keys():
|
|
if bname not in all_ctrls and (bname.startswith('tweak') or 'ctrl' in bname):
|
|
other_ctrls.append(bname)
|
|
|
|
for bname in other_ctrls:
|
|
if eb[bname].head == head_start:
|
|
for child in eb[bones['chain']['ctrl'][0]].children:
|
|
child.parent = eb[bname]
|
|
eb.remove(eb[bones['chain']['ctrl'][0]])
|
|
bones['chain']['ctrl'][0] = bname
|
|
break
|
|
|
|
for bname in other_ctrls:
|
|
if eb[bname].head == head_tip:
|
|
eb.remove(eb[bones['chain']['ctrl'][-1]])
|
|
bones['chain']['ctrl'][-1] = bname
|
|
break
|
|
|
|
def generate(self):
|
|
|
|
bpy.ops.object.mode_set(mode='EDIT')
|
|
eb = self.obj.data.edit_bones
|
|
|
|
bones = {}
|
|
if eb[self.org_bones[0]].parent:
|
|
def_name = make_deformer_name(strip_org(eb[self.org_bones[0]].parent.name))
|
|
if self.params.def_parenting and def_name in eb.keys():
|
|
bones['parent'] = def_name
|
|
else:
|
|
bones['parent'] = eb[self.org_bones[0]].parent.name
|
|
|
|
# Clear parents for org bones
|
|
for bone in self.org_bones[0:]:
|
|
eb[bone].use_connect = False
|
|
eb[bone].parent = None
|
|
|
|
bones['def'], bones['conv_def'] = self.create_deform()
|
|
if len(self.org_bones) > 2:
|
|
bones['pivot'] = self.create_pivot()
|
|
bones['chain'] = self.create_chain()
|
|
|
|
self.parent_bones(bones)
|
|
|
|
# ctrls snapping pass
|
|
#self.aggregate_ctrls(bones)
|
|
|
|
self.constrain_bones(bones)
|
|
self.stick_to_bendy_bones(bones)
|
|
self.locks_and_widgets(bones)
|
|
|
|
return
|
|
|
|
|
|
def add_parameters(params):
|
|
""" Add the parameters of this rig type to the
|
|
RigifyParameters PropertyGroup
|
|
"""
|
|
|
|
items = [
|
|
('auto', 'Auto', ''),
|
|
('x', 'X-Global', ''),
|
|
('y', 'Y-Global', ''),
|
|
('z', 'Z-Global', '')
|
|
]
|
|
|
|
params.tweak_axis = bpy.props.EnumProperty(
|
|
items=items,
|
|
name="Orient y-axis to",
|
|
description="Targets all ctrls y-axis to defined axis (global space)",
|
|
default='auto'
|
|
)
|
|
|
|
axes = [
|
|
('x', 'X', ''),
|
|
('y', 'Y', ''),
|
|
('z', 'Z', ''),
|
|
('-x', '-X', ''),
|
|
('-y', '-Y', ''),
|
|
('-z', '-Z', '')
|
|
]
|
|
|
|
params.wgt_align_axis = bpy.props.EnumProperty(
|
|
items=axes,
|
|
name="Custom Widget orient",
|
|
description="Targets custom WGTs to defined axis (global space)",
|
|
default='y'
|
|
)
|
|
|
|
params.conv_bone = bpy.props.StringProperty(
|
|
name='Convergence bone',
|
|
default=''
|
|
)
|
|
|
|
params.conv_def = bpy.props.BoolProperty(
|
|
name="Add DEF on convergence",
|
|
default=False,
|
|
description=""
|
|
)
|
|
|
|
params.def_parenting = bpy.props.BoolProperty(
|
|
name="Prefer DEF parenting",
|
|
default=False,
|
|
description=""
|
|
)
|
|
|
|
params.cluster_ctrls = bpy.props.BoolProperty(
|
|
name="Cluster controls",
|
|
default=False,
|
|
description="Clusterize controls in the same position"
|
|
)
|
|
|
|
params.bbones = bpy.props.IntProperty(
|
|
name = 'B-Bone Segments',
|
|
default = 10,
|
|
min = 1,
|
|
description = 'Number of B-Bone segments'
|
|
)
|
|
|
|
params.wgt_offset = bpy.props.FloatProperty(
|
|
name='Widget Offset',
|
|
default=0.0,
|
|
min=-10.0,
|
|
max=10.0
|
|
)
|
|
|
|
ControlLayersOption.TWEAK.add_parameters(params)
|
|
|
|
|
|
def parameters_ui(layout, params):
|
|
""" Create the ui for the rig parameters."""
|
|
|
|
pb = bpy.context.object.pose
|
|
|
|
r = layout.row()
|
|
col = r.column(align=True)
|
|
row = col.row(align=True)
|
|
row.prop(params, "tweak_axis")
|
|
|
|
r = layout.row()
|
|
r.prop(params, "wgt_align_axis")
|
|
|
|
r = layout.row()
|
|
r.prop(params, "wgt_offset")
|
|
|
|
r = layout.row()
|
|
r.prop(params, "bbones")
|
|
|
|
r = layout.row()
|
|
r.prop_search(params, 'conv_bone', pb, "bones", text="Convergence Bone")
|
|
|
|
r = layout.row()
|
|
r.prop(params, 'conv_def')
|
|
|
|
r = layout.row()
|
|
r.prop(params, 'def_parenting')
|
|
|
|
#r = layout.row()
|
|
#r.prop(params, 'cluster_ctrls')
|
|
|
|
ControlLayersOption.TWEAK.parameters_ui(layout, params)
|
|
|
|
|
|
def create_sample(obj):
|
|
# generated by rigify.utils.write_metarig
|
|
bpy.ops.object.mode_set(mode='EDIT')
|
|
arm = obj.data
|
|
|
|
bones = {}
|
|
|
|
bone = arm.edit_bones.new('spine')
|
|
bone.head[:] = 0.0000, 0.0000, 0.0000
|
|
bone.tail[:] = 0.0000, 0.5000/8, 1.0000/8
|
|
bone.roll = 0.0000
|
|
bone.use_connect = False
|
|
bones['spine'] = bone.name
|
|
bone = arm.edit_bones.new('spine.001')
|
|
bone.head[:] = 0.0000, 0.5000/8, 1.0000/8
|
|
bone.tail[:] = 0.0000, 0.7500/8, 2.0000/8
|
|
bone.roll = 0.0000
|
|
bone.use_connect = True
|
|
bone.parent = arm.edit_bones[bones['spine']]
|
|
bones['spine.001'] = bone.name
|
|
bone = arm.edit_bones.new('spine.002')
|
|
bone.head[:] = 0.0000, 0.7500/8, 2.0000/8
|
|
bone.tail[:] = 0.0000, 0.5000/8, 3.0000/8
|
|
bone.roll = 0.0000
|
|
bone.use_connect = True
|
|
bone.parent = arm.edit_bones[bones['spine.001']]
|
|
bones['spine.002'] = bone.name
|
|
bone = arm.edit_bones.new('spine.003')
|
|
bone.head[:] = 0.0000, 0.5000/8, 3.0000/8
|
|
bone.tail[:] = 0.0000, 0.0000, 4.0000/8
|
|
bone.roll = 0.0000
|
|
bone.use_connect = True
|
|
bone.parent = arm.edit_bones[bones['spine.002']]
|
|
bones['spine.003'] = bone.name
|
|
|
|
bpy.ops.object.mode_set(mode='OBJECT')
|
|
pbone = obj.pose.bones[bones['spine']]
|
|
pbone.rigify_type = 'experimental.super_chain'
|
|
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.bone.layers = [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, False, False, False, False, False]
|
|
pbone = obj.pose.bones[bones['spine.001']]
|
|
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.bone.layers = [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, False, False, False, False, False]
|
|
pbone = obj.pose.bones[bones['spine.002']]
|
|
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.bone.layers = [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, False, False, False, False, False]
|
|
pbone = obj.pose.bones[bones['spine.003']]
|
|
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.bone.layers = [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, False, False, False, False, False]
|
|
|
|
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
|
|
|
|
arm.layers = [(x in [0]) for x in range(32)]
|