diff options
author | Alexander Gavrilov <angavrilov@gmail.com> | 2019-11-02 16:08:26 +0300 |
---|---|---|
committer | Alexander Gavrilov <angavrilov@gmail.com> | 2019-11-02 16:09:39 +0300 |
commit | 67f1fbca1482d9d9362a4001332e785c3fd5d230 (patch) | |
tree | 27cf708ca5fca23bea8cc7e999178e98ca21922a /rigify | |
parent | db3e15be7a0f7b403bedb5b762d79e1970a4fc59 (diff) |
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).
Diffstat (limited to 'rigify')
-rw-r--r-- | rigify/rigs/basic/pivot.py | 129 | ||||
-rw-r--r-- | rigify/rigs/limbs/super_palm.py | 3 | ||||
-rw-r--r-- | rigify/rigs/spines/super_head.py | 5 | ||||
-rw-r--r-- | rigify/utils/switch_parent.py | 33 |
4 files changed, 143 insertions, 27 deletions
diff --git a/rigify/rigs/basic/pivot.py b/rigify/rigs/basic/pivot.py index c0625c1a..e5d31659 100644 --- a/rigify/rigs/basic/pivot.py +++ b/rigify/rigs/basic/pivot.py @@ -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 build_parent_switch(self, master_name): + pbuilder = SwitchParentBuilder(self.generator) + + 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): - if self.make_control: - self.set_bone_parent(self.bones.ctrl.pivot, self.bones.ctrl.master, use_connect=False) + 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, self.bones.ctrl.pivot, 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) diff --git a/rigify/rigs/limbs/super_palm.py b/rigify/rigs/limbs/super_palm.py index 8bcbabf8..9c03b2fe 100644 --- a/rigify/rigs/limbs/super_palm.py +++ b/rigify/rigs/limbs/super_palm.py @@ -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 diff --git a/rigify/rigs/spines/super_head.py b/rigify/rigs/spines/super_head.py index 65db79cc..15f011f7 100644 --- a/rigify/rigs/spines/super_head.py +++ b/rigify/rigs/spines/super_head.py @@ -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): diff --git a/rigify/utils/switch_parent.py b/rigify/utils/switch_parent.py index 8247a7c8..61721266 100644 --- a/rigify/utils/switch_parent.py +++ b/rigify/utils/switch_parent.py @@ -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 |