forked from bartvdbraak/blender
split up metarig hierarchy evaluation and modifying the metarig into 2 steps,
original bone names cant be changed anymore but this means the bones can be re-parented without confusing scripts that run after the rig is modified. support for defining a bone to have multiple types and automatically blending between 2 generated rigs
This commit is contained in:
parent
f287762678
commit
65edb7341f
@ -91,6 +91,7 @@ def _bone_class_instance_copy(self, from_prefix="", to_prefix=""):
|
||||
|
||||
new_slot_ls.append(attr)
|
||||
from_name_ls.append(bone_name)
|
||||
bone_name_orig = bone_name_orig.replace("ORG-", "") # XXX - we need a better way to do this
|
||||
new_name_ls.append(to_prefix + bone_name_orig)
|
||||
|
||||
new_bones = copy_bone_simple_list(self.obj.data, from_name_ls, new_name_ls, True)
|
||||
@ -103,8 +104,10 @@ def _bone_class_instance_copy(self, from_prefix="", to_prefix=""):
|
||||
|
||||
return new_bc
|
||||
|
||||
def _bone_class_instance_names(self):
|
||||
return [getattr(self, attr) for attr in self.attr_names]
|
||||
|
||||
def _bone_class_instance_blend(self, from_bc, to_bc, target_bone=None, target_prop="blend", use_loc=True, use_rot=True):
|
||||
def _bone_class_instance_blend(self, from_bc, to_bc, target_bone=None, target_prop="blend"):
|
||||
'''
|
||||
Use for blending bone chains.
|
||||
|
||||
@ -113,78 +116,19 @@ def _bone_class_instance_blend(self, from_bc, to_bc, target_bone=None, target_pr
|
||||
|
||||
XXX - toggles editmode, need to re-validate all editbones :(
|
||||
'''
|
||||
|
||||
if self.attr_names != from_bc.attr_names or self.attr_names != to_bc.attr_names:
|
||||
raise Exception("can only blend between matching chains")
|
||||
|
||||
obj = self.obj
|
||||
|
||||
if obj.mode == 'EDIT':
|
||||
raise Exception("blending cant be called in editmode")
|
||||
|
||||
# setup the blend property
|
||||
if target_bone is None:
|
||||
target_bone = self.attr_names[-1]
|
||||
|
||||
prop_pbone = obj.pose.bones[target_bone]
|
||||
if prop_pbone.get(target_bone, None) is None:
|
||||
prop = rna_idprop_ui_prop_get(prop_pbone, target_prop, create=True)
|
||||
prop_pbone[target_prop] = 0.5
|
||||
prop["soft_min"] = 0.0
|
||||
prop["soft_max"] = 1.0
|
||||
|
||||
driver_path = prop_pbone.path_to_id() + ('["%s"]' % target_prop)
|
||||
|
||||
def blend_target(driver):
|
||||
tar = driver.targets.new()
|
||||
tar.name = target_bone
|
||||
tar.id_type = 'OBJECT'
|
||||
tar.id = obj
|
||||
tar.rna_path = driver_path
|
||||
|
||||
for attr in self.attr_names:
|
||||
new_pbone = getattr(self, attr + "_p")
|
||||
from_bone_name = getattr(from_bc, attr)
|
||||
to_bone_name = getattr(to_bc, attr)
|
||||
|
||||
if from_bone_name == to_bone_name:
|
||||
raise Exception("Matching from/to bone names:" + from_bone_name)
|
||||
|
||||
if use_loc:
|
||||
con = new_pbone.constraints.new('COPY_LOCATION')
|
||||
con.target = obj
|
||||
con.subtarget = from_bone_name
|
||||
|
||||
con = new_pbone.constraints.new('COPY_LOCATION')
|
||||
con.target = obj
|
||||
con.subtarget = to_bone_name
|
||||
|
||||
fcurve = con.driver_add("influence", 0)
|
||||
driver = fcurve.driver
|
||||
driver.type = 'AVERAGE'
|
||||
fcurve.modifiers.remove(0) # grr dont need a modifier
|
||||
|
||||
blend_target(driver)
|
||||
|
||||
if use_rot:
|
||||
con = new_pbone.constraints.new('COPY_ROTATION')
|
||||
con.target = obj
|
||||
con.subtarget = from_bone_name
|
||||
|
||||
con = new_pbone.constraints.new('COPY_ROTATION')
|
||||
con.target = obj
|
||||
con.subtarget = to_bone_name
|
||||
|
||||
fcurve = con.driver_add("influence", 0)
|
||||
driver = fcurve.driver
|
||||
driver.type = 'AVERAGE'
|
||||
fcurve.modifiers.remove(0) # grr dont need a modifier
|
||||
|
||||
blend_target(driver)
|
||||
apply_bones = [getattr(self, attr) for attr in self.attr_names]
|
||||
from_bones = [getattr(from_bc, attr) for attr in from_bc.attr_names]
|
||||
to_bones = [getattr(to_bc, attr) for attr in to_bc.attr_names]
|
||||
|
||||
blend_bone_list(self.obj, apply_bones, from_bones, to_bones, target_bone, target_prop)
|
||||
|
||||
def bone_class_instance(obj, slots, name="BoneContainer"):
|
||||
attr_names = tuple(slots) # dont modify the original
|
||||
slots = slots[:] # dont modify the original
|
||||
slots = list(slots) # dont modify the original
|
||||
for i in range(len(slots)):
|
||||
member = slots[i]
|
||||
slots.append(member + "_b") # bone bone
|
||||
@ -196,6 +140,7 @@ def bone_class_instance(obj, slots, name="BoneContainer"):
|
||||
"attr_names":attr_names, \
|
||||
"update":_bone_class_instance_update, \
|
||||
"rename":_bone_class_instance_rename, \
|
||||
"names":_bone_class_instance_names, \
|
||||
"copy":_bone_class_instance_copy, \
|
||||
"blend":_bone_class_instance_blend, \
|
||||
}
|
||||
@ -254,6 +199,78 @@ def copy_bone_simple_list(arm, from_bones, to_bones, parent=False):
|
||||
|
||||
return copy_bones
|
||||
|
||||
def blend_bone_list(obj, apply_bones, from_bones, to_bones, target_bone=None, target_prop="blend"):
|
||||
|
||||
if obj.mode == 'EDIT':
|
||||
raise Exception("blending cant be called in editmode")
|
||||
|
||||
# setup the blend property
|
||||
if target_bone is None:
|
||||
target_bone = apply_bones[-1] # default to the last bone
|
||||
|
||||
prop_pbone = obj.pose.bones[target_bone]
|
||||
if prop_pbone.get(target_bone, None) is None:
|
||||
prop = rna_idprop_ui_prop_get(prop_pbone, target_prop, create=True)
|
||||
prop_pbone[target_prop] = 0.5
|
||||
prop["soft_min"] = 0.0
|
||||
prop["soft_max"] = 1.0
|
||||
|
||||
driver_path = prop_pbone.path_to_id() + ('["%s"]' % target_prop)
|
||||
|
||||
def blend_target(driver):
|
||||
tar = driver.targets.new()
|
||||
tar.name = target_bone
|
||||
tar.id_type = 'OBJECT'
|
||||
tar.id = obj
|
||||
tar.rna_path = driver_path
|
||||
|
||||
def blend_location(new_pbone, from_bone_name, to_bone_name):
|
||||
con = new_pbone.constraints.new('COPY_LOCATION')
|
||||
con.target = obj
|
||||
con.subtarget = from_bone_name
|
||||
|
||||
con = new_pbone.constraints.new('COPY_LOCATION')
|
||||
con.target = obj
|
||||
con.subtarget = to_bone_name
|
||||
|
||||
fcurve = con.driver_add("influence", 0)
|
||||
driver = fcurve.driver
|
||||
driver.type = 'AVERAGE'
|
||||
fcurve.modifiers.remove(0) # grr dont need a modifier
|
||||
|
||||
blend_target(driver)
|
||||
|
||||
def blend_rotation(new_pbone, from_bone_name, to_bone_name):
|
||||
con = new_pbone.constraints.new('COPY_ROTATION')
|
||||
con.target = obj
|
||||
con.subtarget = from_bone_name
|
||||
|
||||
con = new_pbone.constraints.new('COPY_ROTATION')
|
||||
con.target = obj
|
||||
con.subtarget = to_bone_name
|
||||
|
||||
fcurve = con.driver_add("influence", 0)
|
||||
driver = fcurve.driver
|
||||
driver.type = 'AVERAGE'
|
||||
fcurve.modifiers.remove(0) # grr dont need a modifier
|
||||
|
||||
blend_target(driver)
|
||||
|
||||
for i, new_bone_name in enumerate(apply_bones):
|
||||
from_bone_name = from_bones[i]
|
||||
to_bone_name = to_bones[i]
|
||||
|
||||
# allow skipping some bones by having None in the list
|
||||
if None in (new_bone_name, from_bone_name, to_bone_name):
|
||||
continue
|
||||
|
||||
new_pbone = obj.pose.bones[new_bone_name]
|
||||
|
||||
if not new_pbone.bone.connected:
|
||||
blend_location(new_pbone, from_bone_name, to_bone_name)
|
||||
|
||||
blend_rotation(new_pbone, from_bone_name, to_bone_name)
|
||||
|
||||
|
||||
def add_stretch_to(obj, from_name, to_name, name):
|
||||
'''
|
||||
@ -342,7 +359,8 @@ def add_pole_target_bone(obj, base_name, name, mode='CROSS'):
|
||||
return poll_name
|
||||
|
||||
|
||||
def generate_rig(context, ob):
|
||||
def generate_rig(context, obj_orig, prefix="ORG-"):
|
||||
from collections import OrderedDict
|
||||
|
||||
global_undo = context.user_preferences.edit.global_undo
|
||||
context.user_preferences.edit.global_undo = False
|
||||
@ -351,50 +369,118 @@ def generate_rig(context, ob):
|
||||
|
||||
|
||||
# copy object and data
|
||||
ob.selected = False
|
||||
ob_new = ob.copy()
|
||||
ob_new.data = ob.data.copy()
|
||||
obj_orig.selected = False
|
||||
obj = obj_orig.copy()
|
||||
obj.data = obj_orig.data.copy()
|
||||
scene = context.scene
|
||||
scene.objects.link(ob_new)
|
||||
scene.objects.active = ob_new
|
||||
ob_new.selected = True
|
||||
scene.objects.link(obj)
|
||||
scene.objects.active = obj
|
||||
obj.selected = True
|
||||
|
||||
# enter armature editmode
|
||||
arm = obj.data
|
||||
|
||||
# Only reference bones that have a type, means we can rename any others without lookup errors
|
||||
pose_names = [pbone.name for pbone in ob_new.pose.bones if "type" in pbone]
|
||||
# original name mapping
|
||||
base_names = {}
|
||||
|
||||
#for pbone_name in ob_new.pose.bones.keys():
|
||||
for pbone_name in pose_names:
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
for bone in arm.edit_bones:
|
||||
bone_name = bone.name
|
||||
bone.name = prefix + bone_name
|
||||
base_names[bone.name] = bone_name # new -> old mapping
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
bone_type = ob_new.pose.bones[pbone_name].get("type", "")
|
||||
# key: bone name
|
||||
# value: {type:definition, ...}
|
||||
# where type is the submodule name - leg, arm etc
|
||||
# and definition is a list of bone names
|
||||
bone_definitions = {}
|
||||
|
||||
if bone_type == "":
|
||||
# key: bone name
|
||||
# value: [functions, ...]
|
||||
# each function is from the module. eg leg.ik, arm.main
|
||||
bone_typeinfos = {}
|
||||
|
||||
# inspect all bones and assign their definitions before modifying
|
||||
for pbone in obj.pose.bones:
|
||||
bone_name = pbone.name
|
||||
bone_type = obj.pose.bones[bone_name].get("type", "")
|
||||
bone_type_list = [bt for bt in bone_type.replace(",", " ").split()]
|
||||
|
||||
for bone_type in bone_type_list:
|
||||
type_pair = bone_type.split(".")
|
||||
|
||||
# 'leg.ik' will look for an ik function in the leg module
|
||||
# 'leg' will look up leg.main
|
||||
if len(type_pair) == 1:
|
||||
type_pair = type_pair[0], "main"
|
||||
|
||||
submod_name, func_name = type_pair
|
||||
|
||||
# from rigify import leg
|
||||
submod = __import__(name="%s.%s" % (__package__, submod_name), fromlist=[submod_name])
|
||||
reload(submod)
|
||||
|
||||
bone_def_dict = bone_definitions.setdefault(bone_name, {})
|
||||
|
||||
# Only calculate bone definitions once
|
||||
if submod_name not in bone_def_dict:
|
||||
metarig_definition_func = getattr(submod, "metarig_definition")
|
||||
bone_def_dict[submod_name] = metarig_definition_func(obj, bone_name)
|
||||
|
||||
|
||||
bone_typeinfo = bone_typeinfos.setdefault(bone_name, [])
|
||||
type_func = getattr(submod, func_name)
|
||||
bone_typeinfo.append((submod_name, type_func))
|
||||
|
||||
|
||||
# now we have all the info about bones we can start operating on them
|
||||
|
||||
for pbone in obj.pose.bones:
|
||||
bone_name = pbone.name
|
||||
|
||||
if bone_name not in bone_typeinfos:
|
||||
continue
|
||||
|
||||
# submodule = getattr(self, bone_type)
|
||||
# exec("from rigify import %s as submodule")
|
||||
submodule = __import__(name="%s.%s" % (__package__, bone_type), fromlist=[bone_type])
|
||||
bone_def_dict = bone_definitions[bone_name]
|
||||
|
||||
reload(submodule) # XXX, dev only
|
||||
# Only blend results from the same submodule, eg.
|
||||
# leg.ik and arm.fk could not be blended.
|
||||
results = OrderedDict()
|
||||
|
||||
for submod_name, type_func in bone_typeinfos[bone_name]:
|
||||
# this bones definition of the current typeinfo
|
||||
definition = bone_def_dict[submod_name]
|
||||
|
||||
# Toggle editmode so the pose data is always up to date
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
submodule.main(ob_new, pbone_name)
|
||||
ret = type_func(obj, definition, base_names)
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
if ret:
|
||||
result_submod = results.setdefault(submod_name, [])
|
||||
|
||||
if result_submod and len(result_submod[-1]) != len(ret):
|
||||
raise Exception("bone lists not compatible: %s, %s" % (result_submod[-1], ret))
|
||||
|
||||
result_submod.append(ret)
|
||||
|
||||
for result_submod in results.values():
|
||||
# blend 2 chains
|
||||
definition = bone_def_dict[submod_name]
|
||||
|
||||
if len(result_submod) == 2:
|
||||
blend_bone_list(obj, definition, result_submod[0], result_submod[1])
|
||||
|
||||
# needed to update driver deps
|
||||
# context.scene.update()
|
||||
|
||||
# Only for demo'ing
|
||||
|
||||
# ob.restrict_view = True
|
||||
ob_new.data.draw_axes = False
|
||||
# obj.restrict_view = True
|
||||
obj.data.draw_axes = False
|
||||
|
||||
context.user_preferences.edit.global_undo = global_undo
|
||||
|
||||
return ob_new
|
||||
return obj
|
||||
|
||||
|
||||
def write_meta_rig(obj, func_name="metarig_template"):
|
||||
@ -462,11 +548,11 @@ def generate_test(context):
|
||||
|
||||
scene = context.scene
|
||||
def create_empty_armature(name):
|
||||
ob_new = bpy.data.add_object('ARMATURE', name)
|
||||
obj_new = bpy.data.add_object('ARMATURE', name)
|
||||
armature = bpy.data.add_armature(name)
|
||||
ob_new.data = armature
|
||||
scene.objects.link(ob_new)
|
||||
scene.objects.active = ob_new
|
||||
obj_new.data = armature
|
||||
scene.objects.link(obj_new)
|
||||
scene.objects.active = obj_new
|
||||
|
||||
files = os.listdir(os.path.dirname(__file__))
|
||||
for f in files:
|
||||
@ -484,10 +570,10 @@ def generate_test(context):
|
||||
if metarig_template:
|
||||
create_empty_armature("meta_" + module_name) # sets active
|
||||
metarig_template()
|
||||
ob = context.object
|
||||
ob_new = generate_rig(context, ob)
|
||||
obj = context.object
|
||||
obj_new = generate_rig(context, obj)
|
||||
|
||||
new_objects.append((ob, ob_new))
|
||||
new_objects.append((obj, obj_new))
|
||||
else:
|
||||
print("note: rig type '%s' has no metarig_template(), can't test this", module_name)
|
||||
|
||||
@ -505,12 +591,12 @@ def generate_test_all(context):
|
||||
|
||||
base_name = os.path.splitext(bpy.data.filename)[0]
|
||||
for obj, obj_new in new_objects:
|
||||
for ob in (obj, obj_new):
|
||||
fn = base_name + "-" + bpy.utils.clean_name(ob.name)
|
||||
for obj in (obj, obj_new):
|
||||
fn = base_name + "-" + bpy.utils.clean_name(obj.name)
|
||||
|
||||
path_dot = fn + ".dot"
|
||||
path_png = fn + ".png"
|
||||
saved = graphviz_export.graph_armature(ob, path_dot, CONSTRAINTS=True, DRIVERS=True)
|
||||
saved = graphviz_export.graph_armature(obj, path_dot, CONSTRAINTS=True, DRIVERS=True)
|
||||
|
||||
#if saved:
|
||||
# os.system("dot -Tpng %s > %s; eog %s" % (path_dot, path_png, path_png))
|
||||
|
@ -20,6 +20,7 @@ import bpy
|
||||
from rigify import bone_class_instance, copy_bone_simple, add_pole_target_bone, add_stretch_to
|
||||
from rna_prop_ui import rna_idprop_ui_get, rna_idprop_ui_prop_get
|
||||
|
||||
METARIG_NAMES = "shoulder", "arm", "forearm", "hand"
|
||||
|
||||
def metarig_template():
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
@ -54,31 +55,8 @@ def metarig_template():
|
||||
pbone['type'] = 'arm'
|
||||
|
||||
|
||||
def main(obj, orig_bone_name):
|
||||
"""
|
||||
the bone with the 'arm' property is the upper arm, this assumes a chain as follows.
|
||||
[shoulder, upper_arm, forearm, hand]
|
||||
...where this bone is 'upper_arm'
|
||||
|
||||
there are 3 chains
|
||||
- Original
|
||||
- IK, MCH-%s_ik
|
||||
- IKSwitch, MCH-%s ()
|
||||
"""
|
||||
|
||||
# Since there are 3 chains, this gets confusing so divide into 3 chains
|
||||
# Initialize container classes for convenience
|
||||
mt = bone_class_instance(obj, ["shoulder", "arm", "forearm", "hand"]) # meta
|
||||
ik = bone_class_instance(obj, ["arm", "forearm", "pole", "hand"]) # ik
|
||||
sw = bone_class_instance(obj, ["socket", "shoulder", "arm", "forearm", "hand"]) # hinge
|
||||
ex = bone_class_instance(obj, ["arm_hinge"]) # hinge & extras
|
||||
|
||||
|
||||
def chain_init():
|
||||
'''
|
||||
Sanity check and return the arm as a list of bone names.
|
||||
'''
|
||||
# do a sanity check
|
||||
def metarig_definition(obj, orig_bone_name):
|
||||
mt = bone_class_instance(obj, METARIG_NAMES) # meta
|
||||
mt.arm = orig_bone_name
|
||||
mt.update()
|
||||
|
||||
@ -86,8 +64,7 @@ def main(obj, orig_bone_name):
|
||||
mt.shoulder = mt.shoulder_p.name
|
||||
|
||||
if not mt.shoulder_p:
|
||||
print("could not find 'arm' parent, skipping:", orig_bone_name)
|
||||
return
|
||||
raise Exception("could not find 'arm' parent, skipping:", orig_bone_name)
|
||||
|
||||
# We could have some bones attached, find the bone that has this as its 2nd parent
|
||||
hands = []
|
||||
@ -97,8 +74,7 @@ def main(obj, orig_bone_name):
|
||||
hands.append(pbone)
|
||||
|
||||
if len(hands) > 1:
|
||||
print("more then 1 hand found on:", orig_bone_name)
|
||||
return
|
||||
raise Exception("more then 1 hand found on:", orig_bone_name)
|
||||
|
||||
# first add the 2 new bones
|
||||
mt.hand_p = hands[0]
|
||||
@ -107,6 +83,32 @@ def main(obj, orig_bone_name):
|
||||
mt.forearm_p = mt.hand_p.parent
|
||||
mt.forearm = mt.forearm_p.name
|
||||
|
||||
return mt.names()
|
||||
|
||||
|
||||
def main(obj, definitions, base_names):
|
||||
"""
|
||||
the bone with the 'arm' property is the upper arm, this assumes a chain as follows.
|
||||
[shoulder, upper_arm, forearm, hand]
|
||||
...where this bone is 'upper_arm'
|
||||
|
||||
there are 3 chains
|
||||
- Original
|
||||
- IK, MCH-%s_ik
|
||||
- IKSwitch, MCH-%s ()
|
||||
|
||||
|
||||
"""
|
||||
|
||||
# Since there are 3 chains, this gets confusing so divide into 3 chains
|
||||
# Initialize container classes for convenience
|
||||
mt = bone_class_instance(obj, METARIG_NAMES) # meta
|
||||
mt.shoulder, mt.arm, mt.forearm, mt.hand = definitions
|
||||
|
||||
ik = bone_class_instance(obj, ["arm", "forearm", "pole", "hand"]) # ik
|
||||
sw = bone_class_instance(obj, ["socket", "shoulder", "arm", "forearm", "hand"]) # hinge
|
||||
ex = bone_class_instance(obj, ["arm_hinge"]) # hinge & extras
|
||||
|
||||
arm = obj.data
|
||||
|
||||
|
||||
@ -346,3 +348,5 @@ def main(obj, orig_bone_name):
|
||||
|
||||
# Shoulder with its delta and hinge.
|
||||
|
||||
# TODO - return a list for fk and IK
|
||||
return None
|
||||
|
@ -19,23 +19,39 @@
|
||||
import bpy
|
||||
from rigify import get_bone_data
|
||||
|
||||
def main(obj, delta_name):
|
||||
'''
|
||||
Use this bone to define a delta thats applied to its child in pose mode.
|
||||
'''
|
||||
# not used, defined for completeness
|
||||
METARIG_NAMES = tuple()
|
||||
|
||||
def metarig_definition(obj, orig_bone_name):
|
||||
'''
|
||||
The bone given is the head, its parent is the body,
|
||||
# its only child the first of a chain with matching basenames.
|
||||
eg.
|
||||
body -> head -> neck_01 -> neck_02 -> neck_03.... etc
|
||||
'''
|
||||
arm = obj.data
|
||||
|
||||
mode_orig = obj.mode
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
delta_pbone = obj.pose.bones[delta_name]
|
||||
children = delta_pbone.children
|
||||
delta = arm.bones[orig_bone_name]
|
||||
children = delta.children
|
||||
|
||||
if len(children) != 1:
|
||||
print("only 1 child supported for delta")
|
||||
|
||||
child_name = children[0].name
|
||||
bone_definition = [delta.name, children[0].name]
|
||||
|
||||
return bone_definition
|
||||
|
||||
def main(obj, bone_definition, base_names):
|
||||
'''
|
||||
Use this bone to define a delta thats applied to its child in pose mode.
|
||||
'''
|
||||
|
||||
mode_orig = obj.mode
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
delta_name, child_name = bone_definition
|
||||
|
||||
delta_pbone = obj.pose.bones[delta_name]
|
||||
|
||||
arm, child_pbone, child_bone = get_bone_data(obj, child_name)
|
||||
|
||||
delta_phead = delta_pbone.head.copy()
|
||||
@ -62,7 +78,7 @@ def main(obj, delta_name):
|
||||
child_tail = child_ebone.tail.copy()
|
||||
|
||||
arm.edit_bones.remove(delta_ebone)
|
||||
del delta_ebone # cant use thz
|
||||
del delta_ebone # cant use this
|
||||
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
@ -107,3 +123,6 @@ def main(obj, delta_name):
|
||||
|
||||
bpy.ops.object.mode_set(mode=mode_orig)
|
||||
|
||||
# no blendeing
|
||||
return None
|
||||
|
@ -17,10 +17,11 @@
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
import bpy
|
||||
from rigify import get_bone_data, empty_layer
|
||||
from rigify import get_bone_data, empty_layer, copy_bone_simple
|
||||
from rna_prop_ui import rna_idprop_ui_get, rna_idprop_ui_prop_get
|
||||
from functools import reduce
|
||||
|
||||
METARIG_NAMES = "finger_01", "finger_02", "finger_03"
|
||||
|
||||
def metarig_template():
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
@ -48,13 +49,43 @@ def metarig_template():
|
||||
pbone = obj.pose.bones['finger.01']
|
||||
pbone['type'] = 'finger'
|
||||
|
||||
def metarig_definition(obj, orig_bone_name):
|
||||
'''
|
||||
The bone given is the first in a chain
|
||||
Expects a chain of at least 2 children.
|
||||
eg.
|
||||
finger -> finger_01 -> finger_02
|
||||
'''
|
||||
|
||||
def main(obj, orig_bone_name):
|
||||
bone_definition = []
|
||||
|
||||
orig_bone = obj.data.bones[orig_bone_name]
|
||||
|
||||
bone_definition.append(orig_bone.name)
|
||||
|
||||
bone = orig_bone
|
||||
chain = 0
|
||||
while chain < 2: # first 2 bones only have 1 child
|
||||
children = bone.children
|
||||
|
||||
if len(children) != 1:
|
||||
raise Exception("expected the chain to have 2 children without a fork")
|
||||
bone = children[0]
|
||||
bone_definition.append(bone.name) # finger_02, finger_03
|
||||
chain += 1
|
||||
|
||||
if len(bone_definition) != len(METARIG_NAMES):
|
||||
raise Exception("internal problem, expected %d bones" % len(METARIG_NAMES))
|
||||
|
||||
return bone_definition
|
||||
|
||||
|
||||
def main(obj, bone_definition, base_names):
|
||||
|
||||
# *** EDITMODE
|
||||
|
||||
# get assosiated data
|
||||
arm, orig_pbone, orig_ebone = get_bone_data(obj, orig_bone_name)
|
||||
arm, orig_pbone, orig_ebone = get_bone_data(obj, bone_definition[0])
|
||||
|
||||
obj.animation_data_create() # needed if its a new armature with no keys
|
||||
|
||||
@ -63,22 +94,16 @@ def main(obj, orig_bone_name):
|
||||
children = orig_pbone.children_recursive
|
||||
tot_len = reduce(lambda f, pbone: f + pbone.bone.length, children, orig_pbone.bone.length)
|
||||
|
||||
base_name = orig_pbone.basename
|
||||
base_name = base_names[bone_definition[0]].rsplit(".", 1)[0]
|
||||
|
||||
# first make a new bone at the location of the finger
|
||||
control_ebone = arm.edit_bones.new(base_name)
|
||||
#control_ebone = arm.edit_bones.new(base_name)
|
||||
control_ebone = copy_bone_simple(arm, base_name, base_name)
|
||||
control_bone_name = control_ebone.name # we dont know if we get the name requested
|
||||
|
||||
control_ebone.connected = orig_ebone.connected
|
||||
control_ebone.parent = orig_ebone.parent
|
||||
|
||||
# Place the finger bone
|
||||
head = orig_ebone.head.copy()
|
||||
tail = orig_ebone.tail.copy()
|
||||
|
||||
control_ebone.head = head
|
||||
control_ebone.tail = head + ((tail - head).normalize() * tot_len)
|
||||
control_ebone.roll = orig_ebone.roll
|
||||
control_ebone.length = tot_len
|
||||
|
||||
# now add bones inbetween this and its children recursively
|
||||
|
||||
@ -99,19 +124,10 @@ def main(obj, orig_bone_name):
|
||||
driver_bone_name = child_bone_name.split('.')
|
||||
driver_bone_name = driver_bone_name[0] + "_driver." + ".".join(driver_bone_name[1:])
|
||||
|
||||
driver_ebone = arm.edit_bones.new(driver_bone_name)
|
||||
driver_bone_name = driver_ebone.name # cant be too sure!
|
||||
driver_ebone = copy_bone_simple(arm, child_ebone.name, driver_bone_name)
|
||||
driver_ebone.length *= 0.5
|
||||
driver_ebone.layer = other_layer
|
||||
|
||||
new_len = pbone_child.bone.length / 2.0
|
||||
|
||||
head = child_ebone.head.copy()
|
||||
tail = child_ebone.tail.copy()
|
||||
|
||||
driver_ebone.head = head
|
||||
driver_ebone.tail = head + ((tail - head).normalize() * new_len)
|
||||
driver_ebone.roll = child_ebone.roll
|
||||
|
||||
# Insert driver_ebone in the chain without connected parents
|
||||
driver_ebone.connected = False
|
||||
driver_ebone.parent = child_ebone.parent
|
||||
@ -129,7 +145,7 @@ def main(obj, orig_bone_name):
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
|
||||
arm, orig_pbone, orig_bone = get_bone_data(obj, orig_bone_name)
|
||||
arm, orig_pbone, orig_bone = get_bone_data(obj, bone_definition[0])
|
||||
arm, control_pbone, control_bone= get_bone_data(obj, control_bone_name)
|
||||
|
||||
|
||||
@ -199,3 +215,5 @@ def main(obj, orig_bone_name):
|
||||
|
||||
i += 1
|
||||
|
||||
# no blending the result of this
|
||||
return None
|
||||
|
@ -17,9 +17,11 @@
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
import bpy
|
||||
from rigify import bone_class_instance, copy_bone_simple, add_pole_target_bone, add_stretch_to
|
||||
from rigify import bone_class_instance, copy_bone_simple, copy_bone_simple_list, add_pole_target_bone, add_stretch_to
|
||||
from rna_prop_ui import rna_idprop_ui_get, rna_idprop_ui_prop_get
|
||||
|
||||
METARIG_NAMES = "hips", "thigh", "shin", "foot", "toe", "heel"
|
||||
|
||||
def metarig_template():
|
||||
# generated by rigify.write_meta_rig
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
@ -65,37 +67,60 @@ def metarig_template():
|
||||
pbone = obj.pose.bones['thigh']
|
||||
pbone['type'] = 'leg'
|
||||
|
||||
|
||||
def validate(obj, orig_bone_name):
|
||||
def metarig_definition(obj, orig_bone_name):
|
||||
'''
|
||||
The bone given is the first in a chain
|
||||
Expects a chain of at least 3 children.
|
||||
eg.
|
||||
thigh -> shin -> foot -> [toe, heel]
|
||||
'''
|
||||
|
||||
bone_definition = []
|
||||
|
||||
orig_bone = obj.data.bones[orig_bone_name]
|
||||
orig_bone_parent = orig_bone.parent
|
||||
|
||||
if orig_bone_parent is None:
|
||||
raise Exception("expected the thigh bone to have a parent hip bone")
|
||||
|
||||
bone_definition.append(orig_bone_parent.name)
|
||||
bone_definition.append(orig_bone.name)
|
||||
|
||||
|
||||
bone = orig_bone
|
||||
chain = 0
|
||||
while chain < 3: # first 2 bones only have 1 child
|
||||
while chain < 2: # first 2 bones only have 1 child
|
||||
children = bone.children
|
||||
|
||||
if len(children) != 1:
|
||||
return "expected the thigh bone to have 3 children without a fork"
|
||||
raise Exception("expected the thigh bone to have 3 children without a fork")
|
||||
bone = children[0]
|
||||
bone_definition.append(bone.name) # shin, foot
|
||||
chain += 1
|
||||
|
||||
children = bone.children
|
||||
# Now there must be 2 children, only one connected
|
||||
if len(children) != 2:
|
||||
return "expected the foot to have 2 children"
|
||||
raise Exception("expected the foot to have 2 children")
|
||||
|
||||
if children[0].connected == children[1].connected:
|
||||
return "expected one bone to be connected"
|
||||
raise Exception("expected one bone to be connected")
|
||||
|
||||
return ''
|
||||
toe, heel = children
|
||||
if heel.connected:
|
||||
toe, heel = heel, toe
|
||||
|
||||
|
||||
def main(obj, orig_bone_name):
|
||||
bone_definition.append(toe.name)
|
||||
bone_definition.append(heel.name)
|
||||
|
||||
if len(bone_definition) != len(METARIG_NAMES):
|
||||
raise Exception("internal problem, expected %d bones" % len(METARIG_NAMES))
|
||||
|
||||
return bone_definition
|
||||
|
||||
|
||||
def ik(obj, bone_definition, base_names):
|
||||
from Mathutils import Vector
|
||||
arm = obj.data
|
||||
|
||||
@ -107,55 +132,26 @@ def main(obj, orig_bone_name):
|
||||
# children of ik_foot
|
||||
ik = bone_class_instance(obj, ["foot", "foot_roll", "foot_roll_01", "foot_roll_02", "knee_target"])
|
||||
|
||||
mt_chain.thigh_e = arm.edit_bones[orig_bone_name]
|
||||
mt_chain.thigh = orig_bone_name
|
||||
|
||||
mt.hips_e = mt_chain.thigh_e.parent
|
||||
mt.hips_e.name = "ORG-" + mt.hips_e.name
|
||||
mt.hips = mt.hips_e.name
|
||||
|
||||
mt_chain.shin_e = mt_chain.thigh_e.children[0]
|
||||
mt_chain.shin = mt_chain.shin_e.name
|
||||
|
||||
mt_chain.foot_e = mt_chain.shin_e.children[0]
|
||||
mt_chain.foot = mt_chain.foot_e.name
|
||||
|
||||
mt_chain.toe_e, mt.heel_e = mt_chain.foot_e.children
|
||||
|
||||
# We dont know which is which, but know the heel is disconnected
|
||||
if not mt_chain.toe_e.connected:
|
||||
mt_chain.toe_e, mt.heel_e = mt.heel_e, mt_chain.toe_e
|
||||
mt.heel_e.name = "ORG-" + mt.heel_e.name
|
||||
mt_chain.toe, mt.heel = mt_chain.toe_e.name, mt.heel_e.name
|
||||
|
||||
ex.thigh_socket_e = copy_bone_simple(arm, mt_chain.thigh, "MCH-%s_socket" % mt_chain.thigh, parent=True)
|
||||
ex.thigh_socket = ex.thigh_socket_e.name
|
||||
ex.thigh_socket_e.tail = ex.thigh_socket_e.head + Vector(0.0, 0.0, ex.thigh_socket_e.length / 4.0)
|
||||
|
||||
ex.thigh_hinge_e = copy_bone_simple(arm, mt_chain.thigh, "MCH-%s_hinge" % mt_chain.thigh, parent=True)
|
||||
ex.thigh_hinge = ex.thigh_hinge_e.name
|
||||
ex.thigh_hinge_e.tail = ex.thigh_hinge_e.head + Vector(0.0, 0.0, mt_chain.thigh_e.head.length)
|
||||
ex.thigh_hinge_e.translate(Vector(-(mt.hips_e.head.x - mt_chain.thigh_e.head.x), 0.0, 0.0))
|
||||
ex.thigh_hinge_e.length = mt.hips_e.length
|
||||
|
||||
# XXX - duplicate below
|
||||
for bone_class in (mt, mt_chain):
|
||||
for attr in bone_class.attr_names:
|
||||
i = METARIG_NAMES.index(attr)
|
||||
ebone = arm.edit_bones[bone_definition[i]]
|
||||
setattr(bone_class, attr, ebone.name)
|
||||
bone_class.update()
|
||||
# XXX - end dupe
|
||||
|
||||
|
||||
# Make a new chain, ORG are the original bones renamed.
|
||||
fk_chain = mt_chain.copy(from_prefix="ORG-") # fk has no prefix!
|
||||
ik_chain = fk_chain.copy(to_prefix="MCH-")
|
||||
|
||||
fk_chain.thigh_e.connected = False
|
||||
fk_chain.thigh_e.parent = ex.thigh_hinge_e
|
||||
|
||||
# fk_chain.thigh_socket_e.parent = MCH-leg_hinge
|
||||
ik_chain = mt_chain.copy(to_prefix="MCH-")
|
||||
|
||||
# simple rename
|
||||
ik_chain.rename("thigh", ik_chain.thigh + "_ik")
|
||||
ik_chain.rename("shin", ik_chain.shin + "_ik")
|
||||
|
||||
# ik foot, no parents
|
||||
base_foot_name = fk_chain.foot # whatever the foot is called, use that!
|
||||
ik.foot_e = copy_bone_simple(arm, fk_chain.foot, "%s_ik" % base_foot_name)
|
||||
base_foot_name = base_names[mt_chain.foot] # whatever the foot is called, use that!, XXX - ORG!
|
||||
ik.foot_e = copy_bone_simple(arm, mt_chain.foot, "%s_ik" % base_foot_name)
|
||||
ik.foot = ik.foot_e.name
|
||||
ik.foot_e.tail.z = ik.foot_e.head.z
|
||||
ik.foot_e.roll = 0.0
|
||||
@ -183,7 +179,7 @@ def main(obj, orig_bone_name):
|
||||
|
||||
# rename 'MCH-toe' --> to 'toe_ik' and make the child of ik.foot_roll_01
|
||||
# ------------------ FK or IK?
|
||||
ik_chain.rename("toe", fk_chain.toe + "_ik") # only fk for the basename
|
||||
ik_chain.rename("toe", base_names[mt_chain.toe] + "_ik")
|
||||
ik_chain.toe_e.connected = False
|
||||
ik_chain.toe_e.parent = ik.foot_roll_01_e
|
||||
|
||||
@ -213,43 +209,6 @@ def main(obj, orig_bone_name):
|
||||
ex.update()
|
||||
mt_chain.update()
|
||||
ik_chain.update()
|
||||
fk_chain.update()
|
||||
|
||||
con = fk_chain.thigh_p.constraints.new('COPY_LOCATION')
|
||||
con.target = obj
|
||||
con.subtarget = ex.thigh_socket
|
||||
|
||||
# hinge
|
||||
prop = rna_idprop_ui_prop_get(fk_chain.thigh_p, "hinge", create=True)
|
||||
fk_chain.thigh_p["hinge"] = 0.5
|
||||
prop["soft_min"] = 0.0
|
||||
prop["soft_max"] = 1.0
|
||||
|
||||
con = ex.thigh_hinge_p.constraints.new('COPY_ROTATION')
|
||||
con.target = obj
|
||||
con.subtarget = mt.hips
|
||||
|
||||
# add driver
|
||||
hinge_driver_path = fk_chain.thigh_p.path_to_id() + '["hinge"]'
|
||||
|
||||
fcurve = con.driver_add("influence", 0)
|
||||
driver = fcurve.driver
|
||||
tar = driver.targets.new()
|
||||
driver.type = 'AVERAGE'
|
||||
tar.name = "var"
|
||||
tar.id_type = 'OBJECT'
|
||||
tar.id = obj
|
||||
tar.rna_path = hinge_driver_path
|
||||
|
||||
mod = fcurve.modifiers[0]
|
||||
mod.poly_order = 1
|
||||
mod.coefficients[0] = 1.0
|
||||
mod.coefficients[1] = -1.0
|
||||
|
||||
|
||||
# adds constraints to the original bones.
|
||||
mt_chain.blend(fk_chain, ik_chain, target_bone=ik.foot, target_prop="ik", use_loc=False)
|
||||
|
||||
|
||||
# IK
|
||||
con = ik_chain.shin_p.constraints.new('IK')
|
||||
@ -290,3 +249,79 @@ def main(obj, orig_bone_name):
|
||||
else:
|
||||
con.minimum_x = -180.0 # XXX -deg
|
||||
con.maximum_x = 0.0
|
||||
|
||||
return None, ik_chain.thigh, ik_chain.shin, ik_chain.foot, ik_chain.toe, None
|
||||
|
||||
|
||||
def fk(obj, bone_definition, base_names):
|
||||
from Mathutils import Vector
|
||||
arm = obj.data
|
||||
|
||||
# these account for all bones in METARIG_NAMES
|
||||
mt_chain = bone_class_instance(obj, ["thigh", "shin", "foot", "toe"])
|
||||
mt = bone_class_instance(obj, ["hips", "heel"])
|
||||
|
||||
# new bones
|
||||
ex = bone_class_instance(obj, ["thigh_socket", "thigh_hinge"])
|
||||
|
||||
for bone_class in (mt, mt_chain):
|
||||
for attr in bone_class.attr_names:
|
||||
i = METARIG_NAMES.index(attr)
|
||||
ebone = arm.edit_bones[bone_definition[i]]
|
||||
setattr(bone_class, attr, ebone.name)
|
||||
bone_class.update()
|
||||
|
||||
ex.thigh_socket_e = copy_bone_simple(arm, mt_chain.thigh, "MCH-%s_socket" % base_names[mt_chain.thigh], parent=True)
|
||||
ex.thigh_socket = ex.thigh_socket_e.name
|
||||
ex.thigh_socket_e.tail = ex.thigh_socket_e.head + Vector(0.0, 0.0, ex.thigh_socket_e.length / 4.0)
|
||||
|
||||
ex.thigh_hinge_e = copy_bone_simple(arm, mt_chain.thigh, "MCH-%s_hinge" % base_names[mt_chain.thigh], parent=True)
|
||||
ex.thigh_hinge = ex.thigh_hinge_e.name
|
||||
ex.thigh_hinge_e.tail = ex.thigh_hinge_e.head + Vector(0.0, 0.0, mt_chain.thigh_e.head.length)
|
||||
ex.thigh_hinge_e.translate(Vector(-(mt.hips_e.head.x - mt_chain.thigh_e.head.x), 0.0, 0.0))
|
||||
ex.thigh_hinge_e.length = mt.hips_e.length
|
||||
|
||||
fk_chain = mt_chain.copy() # fk has no prefix!
|
||||
|
||||
fk_chain.thigh_e.connected = False
|
||||
fk_chain.thigh_e.parent = ex.thigh_hinge_e
|
||||
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
ex.update()
|
||||
mt_chain.update()
|
||||
fk_chain.update()
|
||||
|
||||
con = fk_chain.thigh_p.constraints.new('COPY_LOCATION')
|
||||
con.target = obj
|
||||
con.subtarget = ex.thigh_socket
|
||||
|
||||
# hinge
|
||||
prop = rna_idprop_ui_prop_get(fk_chain.thigh_p, "hinge", create=True)
|
||||
fk_chain.thigh_p["hinge"] = 0.5
|
||||
prop["soft_min"] = 0.0
|
||||
prop["soft_max"] = 1.0
|
||||
|
||||
con = ex.thigh_hinge_p.constraints.new('COPY_ROTATION')
|
||||
con.target = obj
|
||||
con.subtarget = mt.hips
|
||||
|
||||
# add driver
|
||||
hinge_driver_path = fk_chain.thigh_p.path_to_id() + '["hinge"]'
|
||||
|
||||
fcurve = con.driver_add("influence", 0)
|
||||
driver = fcurve.driver
|
||||
tar = driver.targets.new()
|
||||
driver.type = 'AVERAGE'
|
||||
tar.name = "var"
|
||||
tar.id_type = 'OBJECT'
|
||||
tar.id = obj
|
||||
tar.rna_path = hinge_driver_path
|
||||
|
||||
mod = fcurve.modifiers[0]
|
||||
mod.poly_order = 1
|
||||
mod.coefficients[0] = 1.0
|
||||
mod.coefficients[1] = -1.0
|
||||
|
||||
# dont blend the hips or heel
|
||||
return None, fk_chain.thigh, fk_chain.shin, fk_chain.foot, fk_chain.toe, None
|
||||
|
@ -20,6 +20,8 @@ import bpy
|
||||
from rigify import bone_class_instance, copy_bone_simple
|
||||
from rna_prop_ui import rna_idprop_ui_prop_get
|
||||
|
||||
# not used, defined for completeness
|
||||
METARIG_NAMES = ("body", "head")
|
||||
|
||||
def metarig_template():
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
@ -72,30 +74,43 @@ def metarig_template():
|
||||
pbone['type'] = 'neck'
|
||||
|
||||
|
||||
def main(obj, orig_bone_name):
|
||||
def metarig_definition(obj, orig_bone_name):
|
||||
'''
|
||||
The bone given is the head, its parent is the body,
|
||||
# its only child the first of a chain with matching basenames.
|
||||
eg.
|
||||
body -> head -> neck_01 -> neck_02 -> neck_03.... etc
|
||||
'''
|
||||
arm = obj.data
|
||||
head = arm.bones[orig_bone_name]
|
||||
body = head.parent
|
||||
|
||||
children = head.children
|
||||
if len(children) != 1:
|
||||
print("expected the head to have only 1 child.")
|
||||
|
||||
child = children[0]
|
||||
bone_definition = [body.name, head.name, child.name]
|
||||
bone_definition.extend([child.name for child in child.children_recursive_basename])
|
||||
return bone_definition
|
||||
|
||||
|
||||
def main(obj, bone_definition, base_names):
|
||||
from Mathutils import Vector
|
||||
|
||||
arm = obj.data
|
||||
|
||||
# Initialize container classes for convenience
|
||||
mt = bone_class_instance(obj, ["body", "head"]) # meta
|
||||
mt.head = orig_bone_name
|
||||
mt.update()
|
||||
mt.body = mt.head_e.parent.name
|
||||
mt.body = bone_definition[0]
|
||||
mt.head = bone_definition[1]
|
||||
mt.update()
|
||||
|
||||
# child chain of the 'head'
|
||||
children = mt.head_e.children
|
||||
if len(children) != 1:
|
||||
print("expected the head to have only 1 child.")
|
||||
|
||||
child = children[0]
|
||||
neck_chain = [child] + child.children_recursive_basename
|
||||
neck_chain = [child.name for child in neck_chain]
|
||||
neck_chain = bone_definition[2:]
|
||||
|
||||
mt_chain = bone_class_instance(obj, [("neck_%.2d" % (i + 1)) for i in range(len(neck_chain))]) # 99 bones enough eh?
|
||||
for i, child_name in enumerate(neck_chain):
|
||||
setattr(mt_chain, ("neck_%.2d" % (i + 1)), child_name)
|
||||
for i, attr in enumerate(mt_chain.attr_names):
|
||||
setattr(mt_chain, attr, neck_chain[i])
|
||||
mt_chain.update()
|
||||
|
||||
neck_chain_basename = mt_chain.neck_01_e.basename
|
||||
@ -135,8 +150,8 @@ def main(obj, orig_bone_name):
|
||||
mt.head_e.head.y += head_length / 4.0
|
||||
mt.head_e.tail.y += head_length / 4.0
|
||||
|
||||
for i in range(len(neck_chain)):
|
||||
neck_e = getattr(mt_chain, "neck_%.2d_e" % (i + 1))
|
||||
for i, attr in enumerate(mt_chain.attr_names):
|
||||
neck_e = getattr(mt_chain, attr + "_e")
|
||||
|
||||
# dont store parent names, re-reference as each chain bones parent.
|
||||
neck_e_parent = arm.edit_bones.new("MCH-rot_%s" % neck_e.name)
|
||||
@ -206,8 +221,8 @@ def main(obj, orig_bone_name):
|
||||
expression_suffix = "/max(0.001,%s)" % "+".join(target_names)
|
||||
|
||||
|
||||
for i in range(len(neck_chain)):
|
||||
neck_p = getattr(mt_chain, "neck_%.2d_p" % (i + 1))
|
||||
for i, attr in enumerate(mt_chain.attr_names):
|
||||
neck_p = getattr(mt_chain, attr + "_p")
|
||||
neck_p.lock_location = True, True, True
|
||||
neck_p.lock_location = True, True, True
|
||||
neck_p.lock_rotations_4d = True
|
||||
@ -243,3 +258,6 @@ def main(obj, orig_bone_name):
|
||||
tar.id_type = 'OBJECT'
|
||||
tar.id = obj
|
||||
tar.rna_path = head_driver_path + ('["bend_%.2d"]' % (j + 1))
|
||||
|
||||
# no blending the result of this
|
||||
return None
|
||||
|
@ -20,6 +20,8 @@ import bpy
|
||||
from rigify import get_bone_data, copy_bone_simple
|
||||
from rna_prop_ui import rna_idprop_ui_get, rna_idprop_ui_prop_get
|
||||
|
||||
# not used, defined for completeness
|
||||
METARIG_NAMES = tuple()
|
||||
|
||||
def metarig_template():
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
@ -72,20 +74,39 @@ def metarig_template():
|
||||
pbone['type'] = 'palm'
|
||||
|
||||
|
||||
def main(obj, orig_bone_name):
|
||||
arm, palm_pbone, palm_ebone = get_bone_data(obj, orig_bone_name)
|
||||
def metarig_definition(obj, orig_bone_name):
|
||||
'''
|
||||
The bone given is the first in a chain
|
||||
Expects an array of children sorted with the little finger lowest.
|
||||
eg.
|
||||
parent -> [pinky, ring... etc]
|
||||
'''
|
||||
arm = obj.data
|
||||
bone_definition = [orig_bone_name]
|
||||
palm_ebone = arm.bones[orig_bone_name]
|
||||
|
||||
children = [ebone.name for ebone in palm_ebone.children]
|
||||
children.sort() # simply assume the pinky has the lowest name
|
||||
bone_definition.extend(children)
|
||||
|
||||
return bone_definition
|
||||
|
||||
|
||||
def main(obj, bone_definition, base_names):
|
||||
arm, palm_pbone, palm_ebone = get_bone_data(obj, bone_definition[0])
|
||||
children = bone_definition[1:]
|
||||
|
||||
# Make a copy of the pinky
|
||||
# simply assume the pinky has the lowest name
|
||||
pinky_ebone = arm.edit_bones[children[0]]
|
||||
ring_ebone = arm.edit_bones[children[1]]
|
||||
|
||||
control_ebone = copy_bone_simple(arm, pinky_ebone.name, "palm_control", parent=True)
|
||||
control_name = control_ebone.name
|
||||
|
||||
offset = (arm.edit_bones[children[0]].head - arm.edit_bones[children[1]].head)
|
||||
offset = (pinky_ebone.head - ring_ebone.head)
|
||||
|
||||
control_ebone.head += offset
|
||||
control_ebone.tail += offset
|
||||
control_ebone.translate(offset)
|
||||
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
@ -180,3 +201,5 @@ def main(obj, orig_bone_name):
|
||||
child_pbone = obj.pose.bones[children[-1]]
|
||||
child_pbone.rotation_mode = 'QUATERNION'
|
||||
|
||||
# no blending the result of this
|
||||
return None
|
||||
|
@ -20,6 +20,8 @@ import bpy
|
||||
from rigify import bone_class_instance, copy_bone_simple
|
||||
from rna_prop_ui import rna_idprop_ui_prop_get
|
||||
|
||||
# not used, defined for completeness
|
||||
METARIG_NAMES = ("pelvis", "ribcage")
|
||||
|
||||
def metarig_template():
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
@ -84,30 +86,32 @@ def metarig_template():
|
||||
pbone['type'] = 'spine'
|
||||
|
||||
|
||||
def validate(obj, orig_bone_name):
|
||||
def metarig_definition(obj, orig_bone_name):
|
||||
'''
|
||||
The bone given is the second in a chain.
|
||||
Expects at least 1 parent and a chain of children withe the same basename
|
||||
eg.
|
||||
pelvis -> rib_cage -> spine.01 -> spine.02 -> spine.03
|
||||
|
||||
note: same as neck.
|
||||
'''
|
||||
orig_bone = obj.data.bones[orig_bone_name]
|
||||
if not orig_bone.parent:
|
||||
return "expected spine bone '%s' to have a parent" % orig_bone_name
|
||||
|
||||
children = orig_bone.children
|
||||
arm = obj.data
|
||||
ribcage = arm.bones[orig_bone_name]
|
||||
pelvis = ribcage.parent
|
||||
|
||||
children = ribcage.children
|
||||
if len(children) != 1:
|
||||
return "expected spine bone '%s' to have only 1 child for the sine chain" % orig_bone_name
|
||||
print("expected the ribcage to have only 1 child.")
|
||||
|
||||
children_spine = children[0].children_recursive_basename
|
||||
child = children[0]
|
||||
bone_definition = [pelvis.name, ribcage.name, child.name]
|
||||
bone_definition.extend([child.name for child in child.children_recursive_basename])
|
||||
return bone_definition
|
||||
|
||||
if len(children_spine) == 0:
|
||||
return "expected '%s' to define a chain of children with its basename (2 or more)" % children[0]
|
||||
def fk(*args):
|
||||
main(*args)
|
||||
|
||||
return ''
|
||||
|
||||
def main(obj, orig_bone_name):
|
||||
def main(obj, bone_definition, base_names):
|
||||
from Mathutils import Vector, Matrix, RotationMatrix
|
||||
from math import radians, pi
|
||||
|
||||
@ -115,17 +119,26 @@ def main(obj, orig_bone_name):
|
||||
|
||||
# Initialize container classes for convenience
|
||||
mt = bone_class_instance(obj, ["pelvis", "ribcage"]) # meta
|
||||
mt.ribcage = orig_bone_name
|
||||
mt.update()
|
||||
mt.pelvis = mt.ribcage_e.parent.name
|
||||
mt.pelvis = bone_definition[0]
|
||||
mt.ribcage = bone_definition[1]
|
||||
mt.update()
|
||||
|
||||
spine_chain_orig = bone_definition[2:]
|
||||
spine_chain = [arm.edit_bones[child_name] for child_name in spine_chain_orig]
|
||||
spine_chain_basename = base_names[spine_chain[0].name].rsplit(".", 1) # probably 'ORG-spine.01' -> 'spine'
|
||||
spine_chain_len = len(spine_chain_orig)
|
||||
|
||||
'''
|
||||
children = mt.ribcage_e.children
|
||||
child = children[0] # validate checks for 1 only.
|
||||
spine_chain_basename = child.basename # probably 'spine'
|
||||
spine_chain_segment_length = child.length
|
||||
spine_chain = [child] + child.children_recursive_basename
|
||||
spine_chain_orig = [child.name for child in spine_chain]
|
||||
'''
|
||||
|
||||
child = spine_chain[0]
|
||||
spine_chain_segment_length = child.length
|
||||
child.parent = mt.pelvis_e # was mt.ribcage
|
||||
|
||||
# The first bone in the chain happens to be the basis of others, create them now
|
||||
@ -177,16 +190,17 @@ def main(obj, orig_bone_name):
|
||||
# - original (ORG_*)
|
||||
# - copy (*use original name*)
|
||||
# - reverse (MCH-rev_*)
|
||||
spine_chain_attrs = [("spine_%.2d" % (i + 1)) for i in range(len(spine_chain_orig))]
|
||||
spine_chain_attrs = [("spine_%.2d" % (i + 1)) for i in range(spine_chain_len)]
|
||||
|
||||
mt_chain = bone_class_instance(obj, spine_chain_attrs) # ORG_*
|
||||
rv_chain = bone_class_instance(obj, spine_chain_attrs) # *
|
||||
ex_chain = bone_class_instance(obj, spine_chain_attrs) # MCH-rev_*
|
||||
del spine_chain_attrs
|
||||
|
||||
for i, child_name in enumerate(spine_chain):
|
||||
child_name_orig = spine_chain_orig[i]
|
||||
|
||||
attr = spine_chain_attrs[i] # eg. spine_04
|
||||
attr = mt_chain.attr_names[i] # eg. spine_04
|
||||
|
||||
setattr(mt_chain, attr, spine_chain[i]) # use the new name
|
||||
|
||||
@ -203,12 +217,12 @@ def main(obj, orig_bone_name):
|
||||
|
||||
# Now we need to re-parent these chains
|
||||
for i, child_name in enumerate(spine_chain_orig):
|
||||
attr = spine_chain_attrs[i] + "_e"
|
||||
attr = ex_chain.attr_names[i] + "_e"
|
||||
|
||||
if i == 0:
|
||||
getattr(ex_chain, attr).parent = mt.pelvis_e
|
||||
else:
|
||||
attr_parent = spine_chain_attrs[i-1] + "_e"
|
||||
attr_parent = ex_chain.attr_names[i-1] + "_e"
|
||||
getattr(ex_chain, attr).parent = getattr(ex_chain, attr_parent)
|
||||
|
||||
# intentional! get the parent from the other paralelle chain member
|
||||
@ -217,9 +231,9 @@ def main(obj, orig_bone_name):
|
||||
|
||||
# ex_chain needs to interlace bones!
|
||||
# Note, skip the first bone
|
||||
for i in range(1, len(spine_chain_attrs)): # similar to neck
|
||||
for i in range(1, spine_chain_len): # similar to neck
|
||||
child_name_orig = spine_chain_orig[i]
|
||||
spine_e = getattr(mt_chain, spine_chain_attrs[i] + "_e")
|
||||
spine_e = getattr(mt_chain, mt_chain.attr_names[i] + "_e")
|
||||
|
||||
# dont store parent names, re-reference as each chain bones parent.
|
||||
spine_e_parent = arm.edit_bones.new("MCH-rot_%s" % child_name_orig)
|
||||
@ -227,7 +241,7 @@ def main(obj, orig_bone_name):
|
||||
spine_e_parent.tail = spine_e.head + Vector(0.0, 0.0, spine_chain_segment_length / 2.0)
|
||||
spine_e_parent.roll = 0.0
|
||||
|
||||
spine_e = getattr(ex_chain, spine_chain_attrs[i] + "_e")
|
||||
spine_e = getattr(ex_chain, ex_chain.attr_names[i] + "_e")
|
||||
orig_parent = spine_e.parent
|
||||
spine_e.connected = False
|
||||
spine_e.parent = spine_e_parent
|
||||
@ -239,8 +253,8 @@ def main(obj, orig_bone_name):
|
||||
# Rotate the rev chain 180 about the by the first bones center point
|
||||
pivot = (rv_chain.spine_01_e.head + rv_chain.spine_01_e.tail) * 0.5
|
||||
matrix = RotationMatrix(radians(180), 3, 'X')
|
||||
for i in range(len(spine_chain_attrs)): # similar to neck
|
||||
spine_e = getattr(rv_chain, spine_chain_attrs[i] + "_e")
|
||||
for i, attr in enumerate(rv_chain.attr_names): # similar to neck
|
||||
spine_e = getattr(rv_chain, attr + "_e")
|
||||
# use the first bone as the pivot
|
||||
|
||||
spine_e.head = ((spine_e.head - pivot) * matrix) + pivot
|
||||
@ -326,12 +340,12 @@ def main(obj, orig_bone_name):
|
||||
# ex.ribcage_p / MCH-wgt_rib_cage
|
||||
con = ex.ribcage_p.constraints.new('COPY_LOCATION')
|
||||
con.target = obj
|
||||
con.subtarget = getattr(mt_chain, spine_chain_attrs[-1])
|
||||
con.subtarget = getattr(mt_chain, mt_chain.attr_names[-1])
|
||||
con.head_tail = 0.0
|
||||
|
||||
con = ex.ribcage_p.constraints.new('COPY_ROTATION')
|
||||
con.target = obj
|
||||
con.subtarget = getattr(mt_chain, spine_chain_attrs[-1])
|
||||
con.subtarget = getattr(mt_chain, mt_chain.attr_names[-1])
|
||||
|
||||
# mt.pelvis_p / rib_cage
|
||||
con = mt.ribcage_p.constraints.new('COPY_LOCATION')
|
||||
@ -347,10 +361,10 @@ def main(obj, orig_bone_name):
|
||||
|
||||
prop = rna_idprop_ui_prop_get(mt.ribcage_p, "pivot_slide", create=True)
|
||||
mt.ribcage_p["pivot_slide"] = 0.5
|
||||
prop["soft_min"] = 1.0 / len(spine_chain_attrs)
|
||||
prop["soft_min"] = 1.0 / spine_chain_len
|
||||
prop["soft_max"] = 1.0
|
||||
|
||||
for i in range(len(spine_chain_attrs) - 1):
|
||||
for i in range(spine_chain_len - 1):
|
||||
prop_name = "bend_%.2d" % (i + 1)
|
||||
prop = rna_idprop_ui_prop_get(mt.ribcage_p, prop_name, create=True)
|
||||
mt.ribcage_p[prop_name] = 1.0
|
||||
@ -361,9 +375,9 @@ def main(obj, orig_bone_name):
|
||||
# positioned at the tip.
|
||||
|
||||
# reverse bones / MCH-rev_spine.##
|
||||
for i in range(1, len(spine_chain_attrs)):
|
||||
spine_p = getattr(rv_chain, spine_chain_attrs[i] + "_p")
|
||||
spine_fake_parent_name = getattr(rv_chain, spine_chain_attrs[i - 1])
|
||||
for i in range(1, spine_chain_len):
|
||||
spine_p = getattr(rv_chain, rv_chain.attr_names[i] + "_p")
|
||||
spine_fake_parent_name = getattr(rv_chain, rv_chain.attr_names[i - 1])
|
||||
|
||||
con = spine_p.constraints.new('COPY_LOCATION')
|
||||
con.target = obj
|
||||
@ -375,14 +389,14 @@ def main(obj, orig_bone_name):
|
||||
# Constrain 'inbetween' bones
|
||||
|
||||
# b01/max(0.001,b01+b02+b03+b04+b05)
|
||||
target_names = [("b%.2d" % (i + 1)) for i in range(len(spine_chain_attrs) - 1)]
|
||||
target_names = [("b%.2d" % (i + 1)) for i in range(spine_chain_len - 1)]
|
||||
expression_suffix = "/max(0.001,%s)" % "+".join(target_names)
|
||||
|
||||
rib_driver_path = mt.ribcage_p.path_to_id()
|
||||
|
||||
for i in range(1, len(spine_chain_attrs)):
|
||||
for i in range(1, spine_chain_len):
|
||||
|
||||
spine_p = getattr(ex_chain, spine_chain_attrs[i] + "_p")
|
||||
spine_p = getattr(ex_chain, ex_chain.attr_names[i] + "_p")
|
||||
spine_p_parent = spine_p.parent # interlaced bone
|
||||
|
||||
con = spine_p_parent.constraints.new('COPY_ROTATION')
|
||||
@ -400,7 +414,7 @@ def main(obj, orig_bone_name):
|
||||
driver.expression = target_names[i - 1] + expression_suffix
|
||||
fcurve.modifiers.remove(0) # grr dont need a modifier
|
||||
|
||||
for j in range(len(spine_chain_attrs) - 1):
|
||||
for j in range(spine_chain_len - 1):
|
||||
tar = driver.targets.new()
|
||||
tar.name = target_names[j]
|
||||
tar.id_type = 'OBJECT'
|
||||
@ -410,12 +424,12 @@ def main(obj, orig_bone_name):
|
||||
|
||||
# original bone drivers
|
||||
# note: the first bone has a lot more constraints, but also this simple one is first.
|
||||
for i in range(len(spine_chain_attrs)):
|
||||
spine_p = getattr(mt_chain, spine_chain_attrs[i] + "_p")
|
||||
for i in attr, enumerate(mt_chain.attr_names):
|
||||
spine_p = getattr(mt_chain, attr + "_p")
|
||||
|
||||
con = spine_p.constraints.new('COPY_ROTATION')
|
||||
con.target = obj
|
||||
con.subtarget = getattr(ex_chain, spine_chain_attrs[i]) # lock to the copy's rotation
|
||||
con.subtarget = getattr(ex_chain, attr) # lock to the copy's rotation
|
||||
del spine_p
|
||||
|
||||
# pivot slide: - lots of copy location constraints.
|
||||
@ -425,19 +439,19 @@ def main(obj, orig_bone_name):
|
||||
con.target = obj
|
||||
con.subtarget = rv_chain.spine_01 # lock to the reverse location
|
||||
|
||||
for i in range(1, len(spine_chain_attrs) + 1):
|
||||
for i in range(1, spine_chain_len + 1):
|
||||
con = mt_chain.spine_01_p.constraints.new('COPY_LOCATION')
|
||||
con.name = "slide_%d" % i
|
||||
con.target = obj
|
||||
|
||||
if i == len(spine_chain_attrs):
|
||||
attr = spine_chain_attrs[i - 1]
|
||||
if i == spine_chain_len:
|
||||
attr = mt_chain.attr_names[i - 1]
|
||||
else:
|
||||
attr = spine_chain_attrs[i]
|
||||
attr = mt_chain.attr_names[i]
|
||||
|
||||
con.subtarget = getattr(rv_chain, attr) # lock to the reverse location
|
||||
|
||||
if i == len(spine_chain_attrs):
|
||||
if i == spine_chain_len:
|
||||
con.head_tail = 1.0
|
||||
|
||||
fcurve = con.driver_add("influence", 0)
|
||||
@ -452,5 +466,8 @@ def main(obj, orig_bone_name):
|
||||
mod = fcurve.modifiers[0]
|
||||
mod.poly_order = 1
|
||||
mod.coefficients[0] = - (i - 1)
|
||||
mod.coefficients[1] = len(spine_chain_attrs)
|
||||
mod.coefficients[1] = spine_chain_len
|
||||
|
||||
# no support for blending chains
|
||||
return None
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user