Rigify: support switchable parents in the basic.pivot rig.
Implement options to generate a parent switch mechanism for the main pivot control, and to register the pivot as a parent for other rigs to use (including support for one level parent injection).
This commit is contained in:
parent
db3e15be7a
commit
67f1fbca14
|
@ -23,7 +23,9 @@ import bpy
|
|||
from ...base_rig import BaseRig
|
||||
|
||||
from ...utils.naming import make_derived_name
|
||||
from ...utils.bones import set_bone_widget_transform
|
||||
from ...utils.widgets_basic import create_cube_widget, create_pivot_widget
|
||||
from ...utils.switch_parent import SwitchParentBuilder
|
||||
|
||||
|
||||
class Rig(BaseRig):
|
||||
|
@ -34,54 +36,134 @@ class Rig(BaseRig):
|
|||
|
||||
def initialize(self):
|
||||
self.make_control = self.params.make_extra_control
|
||||
self.make_pivot = self.params.make_control or not self.make_control
|
||||
self.make_deform = self.params.make_extra_deform
|
||||
|
||||
|
||||
def generate_bones(self):
|
||||
org = self.bones.org
|
||||
|
||||
self.bones.ctrl.pivot = self.copy_bone(org, make_derived_name(org, 'ctrl'), parent=not self.make_control)
|
||||
|
||||
if self.make_control:
|
||||
self.bones.ctrl.master = self.copy_bone(org, make_derived_name(org, 'ctrl', '_master'), parent=True)
|
||||
self.bones.ctrl.master = name = self.copy_bone(org, make_derived_name(org, 'ctrl'), parent=True)
|
||||
|
||||
if self.make_pivot:
|
||||
self.bones.ctrl.pivot = self.copy_bone(org, make_derived_name(org, 'ctrl', '_pivot'))
|
||||
|
||||
if self.params.make_parent_switch:
|
||||
self.build_parent_switch(name)
|
||||
|
||||
if self.params.register_parent:
|
||||
self.register_parent(name, self.get_parent_tags())
|
||||
|
||||
else:
|
||||
self.bones.ctrl.pivot = self.copy_bone(org, make_derived_name(org, 'ctrl'), parent=True)
|
||||
|
||||
if self.make_deform:
|
||||
self.bones.deform = self.copy_bone(org, make_derived_name(org, 'def'), bbone=True)
|
||||
|
||||
|
||||
def parent_bones(self):
|
||||
if self.make_control:
|
||||
self.set_bone_parent(self.bones.ctrl.pivot, self.bones.ctrl.master, use_connect=False)
|
||||
def build_parent_switch(self, master_name):
|
||||
pbuilder = SwitchParentBuilder(self.generator)
|
||||
|
||||
self.set_bone_parent(self.bones.org, self.bones.ctrl.pivot, use_connect=False)
|
||||
org_parent = self.get_bone_parent(self.bones.org)
|
||||
parents = [org_parent] if org_parent else []
|
||||
|
||||
pbuilder.build_child(
|
||||
self, master_name,
|
||||
context_rig=self.rigify_parent, allow_self=True,
|
||||
prop_name="Parent ({})".format(master_name),
|
||||
extra_parents=parents, select_parent=org_parent,
|
||||
controls=lambda: self.bones.ctrl.flatten()
|
||||
)
|
||||
|
||||
def get_parent_tags(self):
|
||||
tags = {t.strip() for t in self.params.register_parent_tags.split(',')}
|
||||
|
||||
if self.params.make_parent_switch:
|
||||
tags.add('child')
|
||||
|
||||
tags.discard('')
|
||||
return tags
|
||||
|
||||
def register_parent(self, master_name, tags):
|
||||
pbuilder = SwitchParentBuilder(self.generator)
|
||||
|
||||
inject = self.rigify_parent if 'injected' in tags else None
|
||||
|
||||
pbuilder.register_parent(
|
||||
self, self.bones.org, name=master_name,
|
||||
inject_into=inject, tags=tags
|
||||
)
|
||||
|
||||
|
||||
def parent_bones(self):
|
||||
ctrl = self.bones.ctrl
|
||||
|
||||
if self.make_pivot:
|
||||
if self.make_control:
|
||||
self.set_bone_parent(ctrl.pivot, ctrl.master, use_connect=False)
|
||||
|
||||
self.set_bone_parent(self.bones.org, ctrl.pivot, use_connect=False)
|
||||
|
||||
else:
|
||||
self.set_bone_parent(self.bones.org, ctrl.master, use_connect=False)
|
||||
|
||||
if self.make_deform:
|
||||
self.set_bone_parent(self.bones.deform, self.bones.org, use_connect=False)
|
||||
|
||||
|
||||
def configure_bones(self):
|
||||
self.copy_bone_properties(self.bones.org, self.bones.ctrl.pivot)
|
||||
|
||||
if self.make_control:
|
||||
self.copy_bone_properties(self.bones.org, self.bones.ctrl.master)
|
||||
|
||||
else:
|
||||
self.copy_bone_properties(self.bones.org, self.bones.ctrl.pivot)
|
||||
|
||||
|
||||
def rig_bones(self):
|
||||
self.make_constraint(
|
||||
self.bones.org, 'COPY_LOCATION', self.bones.ctrl.pivot,
|
||||
space='LOCAL', invert_xyz=(True,)*3
|
||||
)
|
||||
if self.make_pivot:
|
||||
self.make_constraint(
|
||||
self.bones.org, 'COPY_LOCATION', self.bones.ctrl.pivot,
|
||||
space='LOCAL', invert_xyz=(True,)*3
|
||||
)
|
||||
|
||||
|
||||
def generate_widgets(self):
|
||||
create_pivot_widget(self.obj, self.bones.ctrl.pivot, square=True, axis_size=2.0)
|
||||
if self.make_pivot:
|
||||
create_pivot_widget(self.obj, self.bones.ctrl.pivot, square=True, axis_size=2.0)
|
||||
|
||||
if self.make_control:
|
||||
set_bone_widget_transform(self.obj, self.bones.ctrl.master, self.bones.org)
|
||||
|
||||
create_cube_widget(self.obj, self.bones.ctrl.master, radius=0.5)
|
||||
|
||||
|
||||
@classmethod
|
||||
def add_parameters(self, params):
|
||||
params.make_control = bpy.props.BoolProperty(
|
||||
name = "Control",
|
||||
default = True,
|
||||
description = "Create a control bone for the copy"
|
||||
)
|
||||
|
||||
params.make_parent_switch = bpy.props.BoolProperty(
|
||||
name = "Switchable Parent",
|
||||
default = False,
|
||||
description = "Allow switching the parent of the master control"
|
||||
)
|
||||
|
||||
params.register_parent = bpy.props.BoolProperty(
|
||||
name = "Register Parent",
|
||||
default = False,
|
||||
description = "Register the control as a switchable parent candidate"
|
||||
)
|
||||
|
||||
params.register_parent_tags = bpy.props.StringProperty(
|
||||
name = "Parent Tags",
|
||||
default = "",
|
||||
description = "Comma-separated tags to use for the registered parent"
|
||||
)
|
||||
|
||||
params.make_extra_control = bpy.props.BoolProperty(
|
||||
name = "Extra Control",
|
||||
default = False,
|
||||
|
@ -98,7 +180,18 @@ class Rig(BaseRig):
|
|||
@classmethod
|
||||
def parameters_ui(self, layout, params):
|
||||
r = layout.row()
|
||||
r.prop(params, "make_extra_control", text="Extra Master Control")
|
||||
r.prop(params, "make_extra_control", text="Master Control")
|
||||
|
||||
if params.make_extra_control:
|
||||
layout.prop(params, "make_parent_switch")
|
||||
layout.prop(params, "register_parent")
|
||||
|
||||
r = layout.row()
|
||||
r.active = params.register_parent
|
||||
r.prop(params, "register_parent_tags", text="Tags")
|
||||
|
||||
layout.prop(params, "make_control", text="Pivot Control")
|
||||
|
||||
r = layout.row()
|
||||
r.prop(params, "make_extra_deform", text="Deform Bone")
|
||||
|
||||
|
@ -112,15 +205,15 @@ def create_sample(obj):
|
|||
|
||||
bones = {}
|
||||
|
||||
bone = arm.edit_bones.new('Bone')
|
||||
bone = arm.edit_bones.new('pivot')
|
||||
bone.head[:] = 0.0000, 0.0000, 0.0000
|
||||
bone.tail[:] = 0.0000, 0.0000, 0.2000
|
||||
bone.tail[:] = 0.0000, 0.5000, 0.0000
|
||||
bone.roll = 0.0000
|
||||
bone.use_connect = False
|
||||
bones['Bone'] = bone.name
|
||||
bones['pivot'] = bone.name
|
||||
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
pbone = obj.pose.bones[bones['Bone']]
|
||||
pbone = obj.pose.bones[bones['pivot']]
|
||||
pbone.rigify_type = 'basic.pivot'
|
||||
pbone.lock_location = (False, False, False)
|
||||
pbone.lock_rotation = (False, False, False)
|
||||
|
|
|
@ -24,6 +24,7 @@ import re
|
|||
from math import cos, pi
|
||||
from itertools import count, repeat
|
||||
|
||||
from rigify.utils.rig import is_rig_base_bone
|
||||
from rigify.utils.naming import strip_org, make_derived_name
|
||||
from rigify.utils.widgets import create_widget
|
||||
from rigify.utils.misc import map_list
|
||||
|
@ -43,7 +44,7 @@ def bone_siblings(obj, bone):
|
|||
bones = []
|
||||
|
||||
for b in parent.children:
|
||||
if b.name != bone:
|
||||
if b.name != bone and not is_rig_base_bone(obj, b.name):
|
||||
bones += [b.name]
|
||||
|
||||
return bones
|
||||
|
|
|
@ -322,7 +322,10 @@ class Rig(BaseHeadTailRig):
|
|||
def register_parent_bones(self):
|
||||
rig = self.rigify_parent or self
|
||||
builder = SwitchParentBuilder(self.generator)
|
||||
builder.register_parent(rig, self.bones.org[-1], name='Head', exclude_self=True, tags={'head'})
|
||||
builder.register_parent(
|
||||
self, self.bones.org[-1], name='Head',
|
||||
inject_into=rig, exclude_self=True, tags={'head'},
|
||||
)
|
||||
|
||||
@stage.configure_bones
|
||||
def configure_bbone_chain(self):
|
||||
|
|
|
@ -51,7 +51,7 @@ class SwitchParentBuilder(GeneratorPlugin, MechanismUtilityMixin):
|
|||
##############################
|
||||
# API
|
||||
|
||||
def register_parent(self, rig, bone, *, name=None, is_global=False, exclude_self=False, tags=None):
|
||||
def register_parent(self, rig, bone, *, name=None, is_global=False, exclude_self=False, inject_into=None, tags=None):
|
||||
"""
|
||||
Registers a bone of the specified rig as a possible parent.
|
||||
|
||||
|
@ -61,6 +61,7 @@ class SwitchParentBuilder(GeneratorPlugin, MechanismUtilityMixin):
|
|||
name Name of the parent for mouse-over hint.
|
||||
is_global The parent is accessible to all rigs, instead of just children of owner.
|
||||
exclude_self The parent is invisible to the owner rig itself.
|
||||
inject_into Make this parent available to children of the specified rig.
|
||||
tags Set of tags to use for default parent selection.
|
||||
|
||||
Lazy creation:
|
||||
|
@ -70,10 +71,19 @@ class SwitchParentBuilder(GeneratorPlugin, MechanismUtilityMixin):
|
|||
|
||||
assert not self.frozen
|
||||
assert isinstance(bone, str) or callable(bone)
|
||||
assert callable(bone) or _rig_is_child(rig, self.generator.bone_owners[bone])
|
||||
assert _rig_is_child(rig, inject_into)
|
||||
|
||||
real_rig = rig
|
||||
|
||||
if inject_into and inject_into is not rig:
|
||||
rig = inject_into
|
||||
tags = (tags or set()) | {'injected'}
|
||||
|
||||
entry = {
|
||||
'rig': rig, 'bone': bone, 'name': name, 'tags': tags,
|
||||
'is_global': is_global, 'exclude_self': exclude_self, 'used': False,
|
||||
'is_global': is_global, 'exclude_self': exclude_self,
|
||||
'real_rig': real_rig, 'used': False,
|
||||
}
|
||||
|
||||
if is_global:
|
||||
|
@ -95,7 +105,8 @@ class SwitchParentBuilder(GeneratorPlugin, MechanismUtilityMixin):
|
|||
select_tags List of parent tags to try for default selection.
|
||||
ignore_global Ignore the is_global flag of potential parents.
|
||||
exclude_self Ignore parents registered by the rig itself.
|
||||
context_rig Rig to use for selecting parents.
|
||||
allow_self Ignore the 'exclude_self' setting of the parent.
|
||||
context_rig Rig to use for selecting parents; defaults to rig.
|
||||
no_implicit Only use parents listed as extra_parents.
|
||||
only_selected Like no_implicit, but allow the 'default' selected parent.
|
||||
|
||||
|
@ -159,7 +170,8 @@ class SwitchParentBuilder(GeneratorPlugin, MechanismUtilityMixin):
|
|||
child_option_table = {
|
||||
'extra_parents': None,
|
||||
'prop_bone': None, 'prop_id': None, 'prop_name': None, 'controls': None,
|
||||
'select_parent': None, 'ignore_global': False, 'exclude_self': False,
|
||||
'select_parent': None, 'ignore_global': False,
|
||||
'exclude_self': False, 'allow_self': False,
|
||||
'context_rig': None, 'select_tags': None,
|
||||
'no_implicit': False, 'only_selected': False,
|
||||
'ctrl_bone': None,
|
||||
|
@ -200,16 +212,23 @@ class SwitchParentBuilder(GeneratorPlugin, MechanismUtilityMixin):
|
|||
parents = []
|
||||
|
||||
for parent in self.get_rig_parent_candidates(child_rig):
|
||||
parent_rig = parent['rig']
|
||||
|
||||
# Exclude injected parents
|
||||
if parent['real_rig'] is not parent_rig:
|
||||
if _rig_is_child(parent_rig, child_rig):
|
||||
continue
|
||||
|
||||
if parent['rig'] is child_rig:
|
||||
if parent['exclude_self'] or child['exclude_self']:
|
||||
if (parent['exclude_self'] and not child['allow_self']) or child['exclude_self']:
|
||||
continue
|
||||
elif parent['is_global'] and not child['ignore_global']:
|
||||
# Can't use parents from own children, even if global (cycle risk)
|
||||
if _rig_is_child(parent['rig'], child_rig):
|
||||
if _rig_is_child(parent_rig, child_rig):
|
||||
continue
|
||||
else:
|
||||
# Required to be a child of the parent's rig
|
||||
if not _rig_is_child(child_rig, parent['rig']):
|
||||
if not _rig_is_child(child_rig, parent_rig):
|
||||
continue
|
||||
|
||||
parent['used'] = True
|
||||
|
|
Loading…
Reference in New Issue