diff options
author | Alexander Gavrilov <angavrilov@gmail.com> | 2019-10-15 16:50:44 +0300 |
---|---|---|
committer | Alexander Gavrilov <angavrilov@gmail.com> | 2019-10-15 17:02:38 +0300 |
commit | dd723151763d8ee3b5c2c94f6c4b5c1ac43b8b59 (patch) | |
tree | 5a15f3067134c18a249df434b1feb6587080900e /rigify/rigs/spines/basic_spine.py | |
parent | f4c101d692eee45484025762d77df751538d0da4 (diff) |
Rigify: implement FK controls for basic_spine with an option to disable.
All metarigs are updated to use split spine components, as super_spine
is deprecated and won't generate FK controls for backward compatibility.
Diffstat (limited to 'rigify/rigs/spines/basic_spine.py')
-rw-r--r-- | rigify/rigs/spines/basic_spine.py | 129 |
1 files changed, 105 insertions, 24 deletions
diff --git a/rigify/rigs/spines/basic_spine.py b/rigify/rigs/spines/basic_spine.py index 269889cf..08255bb4 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 +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: @@ -85,7 +92,7 @@ class Rig(BaseSpineRig): # 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 +110,89 @@ 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 - @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_control_widget(self, ctrl, wgt_mch): - self.get_bone(ctrl).custom_shape_transform = self.get_bone(wgt_mch) + 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 - create_circle_widget( + self.get_bone(ctrl).custom_shape_transform = shape_bone + + 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 +216,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 +237,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) @@ -184,8 +249,11 @@ class Rig(BaseSpineRig): def parent_mch_chain(self): master = self.bones.ctrl.master 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 +273,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 +292,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 +306,12 @@ class Rig(BaseSpineRig): super().parameters_ui(layout, params) + layout.prop(params, 'make_fk_controls') + + col = layout.column() + col.active = params.make_fk_controls + ControlLayersOption.FK.parameters_ui(col, params) + def create_sample(obj): # generated by rigify.utils.write_metarig |