1 # SPDX-License-Identifier: GPL-2.0-or-later
7 from ...utils
.bones
import align_bone_roll
, put_bone
, copy_bone_position
, flip_bone
8 from ...utils
.naming
import make_derived_name
9 from ...utils
.misc
import map_list
11 from itertools
import count
13 from ...base_rig
import stage
15 from .limb_rigs
import BaseLimbRig
16 from .paw
import Rig
as pawRig
20 """Front paw rig with special IK automation."""
22 ####################################################
27 # Second IK system (pre-driving heel)
29 # Bone tracking IK2 to rotate heel
31 # Parent of the heel control
33 ####################################################
35 ####################################################
38 def get_middle_ik_controls(self
):
39 return [self
.bones
.ctrl
.heel
]
41 def get_ik_fk_position_chains(self
):
42 ik_chain
, tail_chain
, fk_chain
= super().get_ik_fk_position_chains()
44 if not self
.use_heel2
:
45 return [*ik_chain
, ik_chain
[-1]], [*fk_chain
, fk_chain
[-1]]
46 return ik_chain
, tail_chain
, fk_chain
48 def get_extra_ik_controls(self
):
49 extra
= [self
.bones
.ctrl
.heel2
] if self
.use_heel2
else []
50 return BaseLimbRig
.get_extra_ik_controls(self
) + extra
52 def get_ik_pole_parents(self
):
53 return [(self
.get_ik2_target_bone(), self
.bones
.ctrl
.ik
)]
56 ####################################################
57 # Second IK system (pre-driving heel)
59 use_mch_ik_base
= True
61 def get_ik2_target_bone(self
):
62 return self
.bones
.mch
.ik2_target
if self
.use_heel2
else self
.bones
.mch
.toe_socket
65 def make_ik2_mch_chain(self
):
66 orgs
= self
.bones
.org
.main
67 chain
= map_list(self
.make_ik2_mch_bone
, count(0), orgs
[0:2])
68 self
.bones
.mch
.ik2_chain
= chain
71 self
.bones
.mch
.ik2_target
= self
.make_ik2_mch_target_bone(orgs
)
73 # Connect the chain end to the target
74 self
.get_bone(chain
[1]).tail
= self
.get_bone(orgs
[2]).tail
75 align_bone_roll(self
.obj
, chain
[1], orgs
[1])
77 def make_ik2_mch_target_bone(self
, orgs
):
78 return self
.copy_bone(orgs
[3], make_derived_name(orgs
[0], 'mch', '_ik2_target'), scale
=1/2)
80 def make_ik2_mch_bone(self
, i
, org
):
81 return self
.copy_bone(org
, make_derived_name(org
, 'mch', '_ik2'))
84 def parent_ik2_mch_chain(self
):
87 self
.set_bone_parent(mch
.ik2_target
, self
.bones
.ctrl
.heel2
)
88 self
.set_bone_parent(mch
.ik2_chain
[0], self
.bones
.ctrl
.ik_base
, inherit_scale
='AVERAGE')
89 self
.parent_bone_chain(mch
.ik2_chain
, use_connect
=True)
91 @stage.configure_bones
92 def configure_ik2_mch_chain(self
):
93 for i
, mch
in enumerate(self
.bones
.mch
.ik2_chain
):
94 self
.configure_ik2_mch_bone(i
, mch
)
96 def configure_ik2_mch_bone(self
, i
, mch
):
97 bone
= self
.get_bone(mch
)
100 bone
.lock_ik_x
= bone
.lock_ik_y
= bone
.lock_ik_z
= True
101 setattr(bone
, 'lock_ik_' + self
.main_axis
, False)
104 def rig_ik2_mch_chain(self
):
105 target_bone
= self
.get_ik2_target_bone()
106 self
.rig_ik_mch_end_bone(self
.bones
.mch
.ik2_chain
[-1], target_bone
, self
.bones
.ctrl
.ik_pole
)
109 ####################################################
110 # Heel tracking from IK2
112 @stage.generate_bones
113 def make_heel_track_bones(self
):
114 orgs
= self
.bones
.org
.main
116 mch
.heel_track
= self
.copy_bone(orgs
[2], make_derived_name(orgs
[2], 'mch', '_track'))
117 mch
.heel_parent
= self
.copy_bone(orgs
[2], make_derived_name(orgs
[2], 'mch', '_parent'))
119 # This two bone setup is used to move the damped track singularity out
120 # of the way to a forbidden zone of the rig, and thus avoid flipping.
121 # The bones are aligned to the center of the valid transformation zone.
122 self
.align_ik_control_bone(mch
.heel_track
)
123 put_bone(self
.obj
, mch
.heel_track
, self
.get_bone(orgs
[2]).tail
, scale
=1/3)
124 copy_bone_position(self
.obj
, mch
.heel_track
, mch
.heel_parent
, scale
=3/4)
127 def parent_heel_control_bone(self
):
128 self
.set_bone_parent(self
.bones
.ctrl
.heel
, self
.bones
.mch
.heel_parent
)
131 def parent_heel_track_bones(self
):
132 # Parenting heel_parent deferred to apply_bones.
133 self
.set_bone_parent(self
.bones
.mch
.heel_track
, self
.get_ik2_target_bone())
135 @stage.configure_bones
136 def prerig_heel_track_bones(self
):
137 # Assign the constraint before the apply stage.
138 self
.make_constraint(
139 self
.bones
.mch
.heel_track
, 'DAMPED_TRACK', self
.bones
.mch
.ik2_chain
[1],
140 influence
=self
.params
.front_paw_heel_influence
143 @stage.preapply_bones
144 def preapply_heel_track_bones(self
):
145 # Assign local transform negating the effect of the constraint at rest.
146 track_bone
= self
.get_bone(self
.bones
.mch
.heel_track
)
147 bone
= self
.get_bone(self
.bones
.mch
.heel_parent
)
148 bone
.matrix_basis
= track_bone
.matrix
.inverted() @ bone
.matrix
151 def apply_heel_track_bones(self
):
152 # Complete the parent chain.
153 self
.set_bone_parent(self
.bones
.mch
.heel_parent
, self
.bones
.mch
.heel_track
)
156 ####################################################
160 def add_parameters(self
, params
):
161 super().add_parameters(params
)
163 params
.front_paw_heel_influence
= bpy
.props
.FloatProperty(
164 name
= 'Heel IK Influence',
168 description
= 'Influence of the secondary IK on the heel control rotation'
172 def parameters_ui(self
, layout
, params
):
174 r
.prop(params
, "front_paw_heel_influence", slider
=True)
176 super().parameters_ui(layout
, params
)
179 def create_sample(obj
):
180 # generated by rigify.utils.write_metarig
181 bpy
.ops
.object.mode_set(mode
='EDIT')
186 bone
= arm
.edit_bones
.new('front_thigh.L')
187 bone
.head
= 0.0000, 0.0000, 0.6902
188 bone
.tail
= 0.0000, 0.0916, 0.4418
190 bone
.use_connect
= False
191 bones
['front_thigh.L'] = bone
.name
192 bone
= arm
.edit_bones
.new('front_shin.L')
193 bone
.head
= 0.0000, 0.0916, 0.4418
194 bone
.tail
= 0.0000, 0.1014, 0.1698
196 bone
.use_connect
= True
197 bone
.parent
= arm
.edit_bones
[bones
['front_thigh.L']]
198 bones
['front_shin.L'] = bone
.name
199 bone
= arm
.edit_bones
.new('front_foot.L')
200 bone
.head
= 0.0000, 0.1014, 0.1698
201 bone
.tail
= 0.0000, 0.0699, 0.0411
203 bone
.use_connect
= True
204 bone
.parent
= arm
.edit_bones
[bones
['front_shin.L']]
205 bones
['front_foot.L'] = bone
.name
206 bone
= arm
.edit_bones
.new('front_toe.L')
207 bone
.head
= 0.0000, 0.0699, 0.0411
208 bone
.tail
= 0.0000, -0.0540, 0.0000
210 bone
.use_connect
= True
211 bone
.parent
= arm
.edit_bones
[bones
['front_foot.L']]
212 bones
['front_toe.L'] = bone
.name
214 bpy
.ops
.object.mode_set(mode
='OBJECT')
215 pbone
= obj
.pose
.bones
[bones
['front_thigh.L']]
216 pbone
.rigify_type
= 'limbs.front_paw'
217 pbone
.lock_location
= (False, False, False)
218 pbone
.lock_rotation
= (False, False, False)
219 pbone
.lock_rotation_w
= False
220 pbone
.lock_scale
= (False, False, False)
221 pbone
.rotation_mode
= 'QUATERNION'
223 pbone
.rigify_parameters
.limb_type
= "paw"
224 except AttributeError:
227 pbone
.rigify_parameters
.fk_layers
= [False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
228 except AttributeError:
231 pbone
.rigify_parameters
.tweak_layers
= [False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]
232 except AttributeError:
234 pbone
= obj
.pose
.bones
[bones
['front_shin.L']]
235 pbone
.rigify_type
= ''
236 pbone
.lock_location
= (False, False, False)
237 pbone
.lock_rotation
= (False, False, False)
238 pbone
.lock_rotation_w
= False
239 pbone
.lock_scale
= (False, False, False)
240 pbone
.rotation_mode
= 'QUATERNION'
241 pbone
= obj
.pose
.bones
[bones
['front_foot.L']]
242 pbone
.rigify_type
= ''
243 pbone
.lock_location
= (False, False, False)
244 pbone
.lock_rotation
= (False, False, False)
245 pbone
.lock_rotation_w
= False
246 pbone
.lock_scale
= (False, False, False)
247 pbone
.rotation_mode
= 'QUATERNION'
248 pbone
= obj
.pose
.bones
[bones
['front_toe.L']]
249 pbone
.rigify_type
= ''
250 pbone
.lock_location
= (False, False, False)
251 pbone
.lock_rotation
= (False, False, False)
252 pbone
.lock_rotation_w
= False
253 pbone
.lock_scale
= (False, False, False)
254 pbone
.rotation_mode
= 'QUATERNION'
256 pbone
.rigify_parameters
.limb_type
= "paw"
257 except AttributeError:
260 bpy
.ops
.object.mode_set(mode
='EDIT')
261 for bone
in arm
.edit_bones
:
263 bone
.select_head
= False
264 bone
.select_tail
= False
266 bone
= arm
.edit_bones
[bones
[b
]]
268 bone
.select_head
= True
269 bone
.select_tail
= True
270 bone
.bbone_x
= bone
.bbone_z
= bone
.length
* 0.05
271 arm
.edit_bones
.active
= bone