diff options
Diffstat (limited to 'rigify/rigs/spines')
-rw-r--r-- | rigify/rigs/spines/basic_spine.py | 145 | ||||
-rw-r--r-- | rigify/rigs/spines/basic_tail.py | 7 | ||||
-rw-r--r-- | rigify/rigs/spines/spine_rigs.py | 60 | ||||
-rw-r--r-- | rigify/rigs/spines/super_head.py | 5 | ||||
-rw-r--r-- | rigify/rigs/spines/super_spine.py | 4 |
5 files changed, 172 insertions, 49 deletions
diff --git a/rigify/rigs/spines/basic_spine.py b/rigify/rigs/spines/basic_spine.py index 269889cf..c2905463 100644 --- a/rigify/rigs/spines/basic_spine.py +++ b/rigify/rigs/spines/basic_spine.py @@ -19,13 +19,16 @@ # <pep8 compliant> import bpy +import math from itertools import count, repeat +from mathutils import Matrix from ...utils.errors import MetarigError from ...utils.layers import ControlLayersOption -from ...utils.naming import strip_org, make_deformer_name, make_mechanism_name -from ...utils.bones import BoneDict, put_bone, align_bone_to_axis +from ...utils.naming import strip_org, make_deformer_name, make_mechanism_name, make_derived_name +from ...utils.bones import BoneDict, put_bone, align_bone_to_axis, align_bone_orientation, set_bone_widget_transform +from ...utils.widgets import adjust_widget_transform_mesh from ...utils.widgets_basic import create_circle_widget from ...utils.misc import map_list @@ -43,6 +46,7 @@ class Rig(BaseSpineRig): # Check if user provided the pivot position self.pivot_pos = self.params.pivot_pos + self.use_fk = self.params.make_fk_controls if not (0 < self.pivot_pos < len(self.bones.org)): self.raise_error("Please specify a valid pivot bone position.") @@ -55,6 +59,9 @@ class Rig(BaseSpineRig): # ctrl: # master, hips, chest: # Main controls. + # fk: + # chest[], hips[]: + # FK controls. # tweak[]: # Tweak control chain. # mch: @@ -73,19 +80,15 @@ class Rig(BaseSpineRig): #################################################### # Master control bone - @stage.generate_bones - def make_master_control(self): - super().make_master_control() - - # Put the main control in the middle of the hip bone - base_bone = self.get_bone(self.bones.org[0]) - put_bone(self.obj, self.bones.ctrl.master, (base_bone.head + base_bone.tail) / 2) + def get_master_control_pos(self, orgs): + base_bone = self.get_bone(orgs[0]) + return (base_bone.head + base_bone.tail) / 2 #################################################### # Main control bones @stage.generate_bones - def make_control_chain(self): + def make_end_control_bones(self): orgs = self.bones.org pivot = self.pivot_pos @@ -103,33 +106,90 @@ class Rig(BaseSpineRig): return name @stage.parent_bones - def parent_control_chain(self): + def parent_end_control_bones(self): ctrl = self.bones.ctrl - self.set_bone_parent(ctrl.hips, ctrl.master) - self.set_bone_parent(ctrl.chest, ctrl.master) - - @stage.configure_bones - def configure_control_chain(self): - pass + pivot = self.get_master_control_output() + self.set_bone_parent(ctrl.hips, pivot) + self.set_bone_parent(ctrl.chest, pivot) @stage.generate_widgets - def make_control_widgets(self): + def make_end_control_widgets(self): ctrl = self.bones.ctrl mch = self.bones.mch - self.make_control_widget(ctrl.hips, mch.wgt_hips) - self.make_control_widget(ctrl.chest, mch.wgt_chest) + self.make_end_control_widget(ctrl.hips, mch.wgt_hips) + self.make_end_control_widget(ctrl.chest, mch.wgt_chest) + + def make_end_control_widget(self, ctrl, wgt_mch): + shape_bone = self.get_bone(wgt_mch) + is_horizontal = abs(shape_bone.z_axis.normalized().y) < 0.7 - def make_control_widget(self, ctrl, wgt_mch): - self.get_bone(ctrl).custom_shape_transform = self.get_bone(wgt_mch) + set_bone_widget_transform(self.obj, ctrl, wgt_mch) - create_circle_widget( + obj = create_circle_widget( self.obj, ctrl, - radius=1.0, + radius=1.2 if is_horizontal else 1.1, head_tail=0.0, head_tail_x=1.0, with_line=False, ) + if is_horizontal: + # Tilt the widget toward the ground for horizontal (animal) spines + angle = math.copysign(28, shape_bone.x_axis.x) + rotmat = Matrix.Rotation(math.radians(angle), 4, 'X') + adjust_widget_transform_mesh(obj, rotmat, local=True) + + #################################################### + # FK control bones + + @stage.generate_bones + def make_control_chain(self): + if self.use_fk: + orgs = self.bones.org + self.bones.ctrl.fk = self.fk_result = BoneDict( + hips = map_list(self.make_control_bone, count(0), orgs[0:self.pivot_pos], repeat(True)), + chest = map_list(self.make_control_bone, count(self.pivot_pos), orgs[self.pivot_pos:], repeat(False)), + ) + + def make_control_bone(self, i, org, is_hip): + name = self.copy_bone(org, make_derived_name(org, 'ctrl', '_fk'), parent=False) + if is_hip: + put_bone(self.obj, name, self.get_bone(name).tail) + return name + + @stage.parent_bones + def parent_control_chain(self): + if self.use_fk: + chain = self.bones.mch.chain + fk = self.bones.ctrl.fk + for child, parent in zip(fk.hips, chain.hips): + self.set_bone_parent(child, parent) + for child, parent in zip(fk.chest, chain.chest): + self.set_bone_parent(child, parent) + + @stage.configure_bones + def configure_control_chain(self): + if self.use_fk: + fk = self.bones.ctrl.fk + for args in zip(count(0), fk.hips + fk.chest, self.bones.org): + self.configure_control_bone(*args) + + ControlLayersOption.FK.assign_rig(self, fk.hips + fk.chest) + + @stage.generate_widgets + def make_control_widgets(self): + if self.use_fk: + fk = self.bones.ctrl.fk + for ctrl in fk.hips: + self.make_control_widget(ctrl, True) + for ctrl in fk.chest: + self.make_control_widget(ctrl, False) + + def make_control_widget(self, ctrl, is_hip): + obj = create_circle_widget(self.obj, ctrl, radius=1.0, head_tail=0.5) + if is_hip: + adjust_widget_transform_mesh(obj, Matrix.Diagonal((1, -1, 1, 1)), local=True) + #################################################### # MCH bones associated with main controls @@ -153,16 +213,16 @@ class Rig(BaseSpineRig): @stage.parent_bones def parent_mch_control_bones(self): mch = self.bones.mch - self.set_bone_parent(mch.pivot, mch.chain.chest[0]) - self.set_bone_parent(mch.wgt_hips, mch.chain.hips[0]) - self.set_bone_parent(mch.wgt_chest, mch.chain.chest[-1]) + fk = self.fk_result + self.set_bone_parent(mch.pivot, fk.chest[0]) + self.set_bone_parent(mch.wgt_hips, fk.hips[0]) + self.set_bone_parent(mch.wgt_chest, fk.chest[-1]) + align_bone_orientation(self.obj, mch.pivot, fk.hips[-1]) @stage.rig_bones def rig_mch_control_bones(self): mch = self.bones.mch - # Is it actually intending to compute average of these, or is this really intentional? - # This effectively adds rotations of the hip and chest controls. - self.make_constraint(mch.pivot, 'COPY_TRANSFORMS', mch.chain.hips[-1], space='LOCAL') + self.make_constraint(mch.pivot, 'COPY_TRANSFORMS', self.fk_result.hips[-1], influence=0.5) #################################################### # MCH chain for distributing hip & chest transform @@ -174,6 +234,8 @@ class Rig(BaseSpineRig): hips = map_list(self.make_mch_bone, orgs[0:self.pivot_pos], repeat(True)), chest = map_list(self.make_mch_bone, orgs[self.pivot_pos:], repeat(False)), ) + if not self.use_fk: + self.fk_result = self.bones.mch.chain def make_mch_bone(self, org, is_hip): name = self.copy_bone(org, make_mechanism_name(strip_org(org)), parent=False) @@ -182,10 +244,13 @@ class Rig(BaseSpineRig): @stage.parent_bones def parent_mch_chain(self): - master = self.bones.ctrl.master + master = self.get_master_control_output() chain = self.bones.mch.chain - self.parent_bone_chain([master, *reversed(chain.hips)]) - self.parent_bone_chain([master, *chain.chest]) + fk = self.fk_result + for child, parent in zip(reversed(chain.hips), [master, *reversed(fk.hips)]): + self.set_bone_parent(child, parent) + for child, parent in zip(chain.chest, [master, *fk.chest]): + self.set_bone_parent(child, parent) @stage.rig_bones def rig_mch_chain(self): @@ -205,7 +270,7 @@ class Rig(BaseSpineRig): @stage.parent_bones def parent_tweak_chain(self): mch = self.bones.mch - chain = mch.chain + chain = self.fk_result parents = [chain.hips[0], *chain.hips[0:-1], mch.pivot, *chain.chest[1:], chain.chest[-1]] for args in zip(self.bones.ctrl.tweak, parents): self.set_bone_parent(*args) @@ -224,6 +289,13 @@ class Rig(BaseSpineRig): super().add_parameters(params) + params.make_fk_controls = bpy.props.BoolProperty( + name="FK Controls", default=True, + description="Generate an FK control chain" + ) + + ControlLayersOption.FK.add_parameters(params) + @classmethod def parameters_ui(self, layout, params): r = layout.row() @@ -231,6 +303,11 @@ class Rig(BaseSpineRig): super().parameters_ui(layout, params) + layout.prop(params, 'make_fk_controls') + + if params.make_fk_controls: + ControlLayersOption.FK.parameters_ui(layout, params) + def create_sample(obj): # generated by rigify.utils.write_metarig diff --git a/rigify/rigs/spines/basic_tail.py b/rigify/rigs/spines/basic_tail.py index 845c3ca3..be054e7d 100644 --- a/rigify/rigs/spines/basic_tail.py +++ b/rigify/rigs/spines/basic_tail.py @@ -23,7 +23,8 @@ import bpy from itertools import count from ...utils.naming import strip_org, make_derived_name -from ...utils.bones import put_bone, flip_bone, is_same_position, connect_bbone_chain_handles, align_bone_orientation +from ...utils.bones import put_bone, flip_bone, is_same_position, connect_bbone_chain_handles +from ...utils.bones import align_bone_orientation, set_bone_widget_transform from ...utils.widgets_basic import create_circle_widget from ...utils.layers import ControlLayersOption from ...utils.misc import map_list @@ -69,7 +70,7 @@ class Rig(BaseHeadTailRig): @stage.generate_widgets def make_master_control_widget(self): bone = self.bones.ctrl.master - self.get_bone(bone).custom_shape_transform = self.get_bone(self.bones.ctrl.tweak[-1]) + set_bone_widget_transform(self.obj, bone, self.bones.ctrl.tweak[-1]) create_ballsocket_widget(self.obj, bone, size=0.7) #################################################### @@ -94,7 +95,7 @@ class Rig(BaseHeadTailRig): ) # Widgets - def make_control_widget(self, ctrl): + def make_control_widget(self, i, ctrl): create_circle_widget(self.obj, ctrl, radius=0.5, head_tail=0.75) #################################################### diff --git a/rigify/rigs/spines/spine_rigs.py b/rigify/rigs/spines/spine_rigs.py index 6628289f..070a6bd3 100644 --- a/rigify/rigs/spines/spine_rigs.py +++ b/rigify/rigs/spines/spine_rigs.py @@ -24,9 +24,10 @@ from itertools import count from ...utils.layers import ControlLayersOption from ...utils.naming import make_derived_name -from ...utils.bones import align_bone_orientation, align_bone_to_axis +from ...utils.bones import align_bone_orientation, align_bone_to_axis, put_bone, set_bone_widget_transform from ...utils.widgets_basic import create_cube_widget from ...utils.switch_parent import SwitchParentBuilder +from ...utils.components import CustomPivotControl from ...base_rig import stage @@ -44,6 +45,7 @@ class BaseSpineRig(TweakChainRig): if len(self.bones.org) < 3: self.raise_error("Input to rig type must be a chain of 3 or more bones.") + self.use_torso_pivot = self.params.make_custom_pivot self.length = sum([self.get_bone(b).length for b in self.bones.org]) #################################################### @@ -52,6 +54,11 @@ class BaseSpineRig(TweakChainRig): # ctrl: # master # Main control. + # master_pivot + # Custom pivot under the master control. + # mch: + # master_pivot + # Final output of the custom pivot. # #################################################### @@ -60,17 +67,43 @@ class BaseSpineRig(TweakChainRig): @stage.generate_bones def make_master_control(self): - self.bones.ctrl.master = name = self.copy_bone(self.bones.org[0], 'torso') + self.bones.ctrl.master = name = self.make_master_control_bone(self.bones.org) + self.component_torso_pivot = self.build_master_pivot(name) + self.build_parent_switch(name) + + def get_master_control_pos(self, orgs): + return self.get_bone(orgs[0]).head + + def make_master_control_bone(self, orgs): + name = self.copy_bone(orgs[0], 'torso') + put_bone(self.obj, name, self.get_master_control_pos(orgs)) align_bone_to_axis(self.obj, name, 'y', length=self.length * 0.6) + return name - self.build_parent_switch(name) + def build_master_pivot(self, master_name, **args): + if self.use_torso_pivot: + return CustomPivotControl( + self, 'master_pivot', master_name, parent=master_name, **args + ) + + def get_master_control_output(self): + if self.component_torso_pivot: + return self.component_torso_pivot.output + else: + return self.bones.ctrl.master def build_parent_switch(self, master_name): pbuilder = SwitchParentBuilder(self.generator) - pbuilder.register_parent(self, master_name, name='Torso') + + org_parent = self.get_bone_parent(self.bones.org[0]) + parents = [org_parent] if org_parent else [] + + pbuilder.register_parent(self, self.get_master_control_output, name='Torso', tags={'torso', 'child'}) + pbuilder.build_child( self, master_name, exclude_self=True, + extra_parents=parents, select_parent=org_parent, prop_id='torso_parent', prop_name='Torso Parent', controls=lambda: self.bones.flatten('ctrl'), ) @@ -78,8 +111,8 @@ class BaseSpineRig(TweakChainRig): self.register_parent_bones(pbuilder) def register_parent_bones(self, pbuilder): - pbuilder.register_parent(self, self.bones.org[0], name='Hips', exclude_self=True) - pbuilder.register_parent(self, self.bones.org[-1], name='Chest', exclude_self=True) + pbuilder.register_parent(self, self.bones.org[0], name='Hips', exclude_self=True, tags={'hips'}) + pbuilder.register_parent(self, self.bones.org[-1], name='Chest', exclude_self=True, tags={'chest'}) @stage.parent_bones def parent_master_control(self): @@ -91,10 +124,9 @@ class BaseSpineRig(TweakChainRig): @stage.generate_widgets def make_master_control_widget(self): - create_cube_widget( - self.obj, self.bones.ctrl.master, - radius=0.5, - ) + master = self.bones.ctrl.master + set_bone_widget_transform(self.obj, master, self.get_master_control_output()) + create_cube_widget(self.obj, master, radius=0.5) #################################################### # Tweak bones @@ -117,11 +149,19 @@ class BaseSpineRig(TweakChainRig): @classmethod def add_parameters(self, params): + params.make_custom_pivot = bpy.props.BoolProperty( + name = "Custom Pivot Control", + default = False, + description = "Create a rotation pivot control that can be repositioned arbitrarily" + ) + # Setting up extra layers for the FK and tweak ControlLayersOption.TWEAK.add_parameters(params) @classmethod def parameters_ui(self, layout, params): + layout.prop(params, 'make_custom_pivot') + ControlLayersOption.TWEAK.parameters_ui(layout, params) diff --git a/rigify/rigs/spines/super_head.py b/rigify/rigs/spines/super_head.py index 9b85e5b5..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) + 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/rigs/spines/super_spine.py b/rigify/rigs/spines/super_spine.py index 5ed1588e..86021c84 100644 --- a/rigify/rigs/spines/super_spine.py +++ b/rigify/rigs/spines/super_spine.py @@ -81,7 +81,7 @@ class Rig(SubstitutionRig, BoneUtilityMixin): bpy.ops.object.mode_set(mode='OBJECT') # Create the parts - self.assign_params(spine_orgs[0], params_copy, pivot_pos=pivot_pos) + self.assign_params(spine_orgs[0], params_copy, pivot_pos=pivot_pos, make_fk_controls=False) result = [ self.instantiate_rig(basic_spine.Rig, spine_orgs[0]) ] @@ -133,6 +133,8 @@ def add_parameters(params): def parameters_ui(layout, params): """ Create the ui for the rig parameters.""" + layout.label(text="Note: this combined rig is deprecated.", icon='INFO') + r = layout.row(align=True) r.prop(params, "use_head", toggle=True, text="Head") r.prop(params, "use_tail", toggle=True, text="Tail") |