1 # SPDX-License-Identifier: GPL-2.0-or-later
5 from ...utils
.bones
import compute_chain_x_axis
, align_bone_x_axis
, align_bone_z_axis
6 from ...utils
.bones
import align_bone_to_axis
, flip_bone
7 from ...utils
.naming
import make_derived_name
8 from ...utils
.widgets_basic
import create_circle_widget
, create_limb_widget
10 from ..widgets
import create_foot_widget
, create_ball_socket_widget
12 from ...base_rig
import stage
14 from .limb_rigs
import BaseLimbRig
17 class Rig(BaseLimbRig
):
18 """Paw rig with an optional second heel control."""
28 self
.use_heel2
= len(self
.bones
.org
.main
) > 4
31 self
.toe_bone_index
= 4
32 self
.fk_name_suffix_cutoff
= 3
33 self
.fk_ik_layer_cutoff
= 4
37 def prepare_bones(self
):
38 orgs
= self
.bones
.org
.main
40 foot_x
= self
.vector_without_z(self
.get_bone(orgs
[2]).y_axis
).cross((0, 0, 1))
42 if self
.params
.rotation_axis
== 'automatic':
43 axis
= compute_chain_x_axis(self
.obj
, orgs
[0:2])
46 align_bone_x_axis(self
.obj
, bone
, axis
)
48 elif self
.params
.auto_align_extremity
:
49 if self
.main_axis
== 'x':
50 align_bone_x_axis(self
.obj
, orgs
[2], foot_x
)
51 align_bone_x_axis(self
.obj
, orgs
[3], -foot_x
)
53 align_bone_z_axis(self
.obj
, orgs
[2], foot_x
)
54 align_bone_z_axis(self
.obj
, orgs
[3], -foot_x
)
56 ####################################################
59 def align_ik_control_bone(self
, name
: str):
60 if self
.params
.rotation_axis
== 'automatic' or self
.params
.auto_align_extremity
:
61 align_bone_to_axis(self
.obj
, name
, 'y', flip
=True)
64 flip_bone(self
.obj
, name
)
66 bone
= self
.get_bone(name
)
67 bone
.tail
[2] = bone
.head
[2]
70 ####################################################
73 class CtrlBones(BaseLimbRig
.CtrlBones
):
74 heel
: str # Foot heel control
75 heel2
: str # Second foot heel control (optional)
77 class MchBones(BaseLimbRig
.MchBones
):
78 toe_socket
: str # IK toe orientation bone
79 ik_heel2
: str # Final position of heel2 in the IK output
81 bones
: BaseLimbRig
.ToplevelBones
[
88 ####################################################
91 def get_middle_ik_controls(self
):
92 return [self
.bones
.ctrl
.heel
] if self
.use_heel2
else []
94 def get_extra_ik_controls(self
):
95 extra
= [self
.bones
.ctrl
.heel2
] if self
.use_heel2
else [self
.bones
.ctrl
.heel
]
96 return super().get_extra_ik_controls() + extra
98 def make_ik_control_bone(self
, orgs
: list[str]):
99 return self
.make_paw_ik_control_bone(orgs
[-2], orgs
[-1], orgs
[2])
101 def make_paw_ik_control_bone(self
, org_one
: str, org_two
: str, org_name
: str):
102 name
= self
.copy_bone(org_two
, make_derived_name(org_name
, 'ctrl', '_ik'))
104 self
.align_ik_control_bone(name
)
106 vec
= self
.get_bone(org_two
).tail
- self
.get_bone(org_one
).head
107 self
.get_bone(name
).length
= self
.vector_without_z(vec
).length
111 def register_switch_parents(self
, pbuilder
):
112 super().register_switch_parents(pbuilder
)
114 pbuilder
.register_parent(self
, self
.bones
.org
.main
[3], exclude_self
=True, tags
={'limb_end'})
116 def make_ik_ctrl_widget(self
, ctrl
):
117 create_foot_widget(self
.obj
, ctrl
)
119 ####################################################
122 @stage.generate_bones
123 def make_heel_control_bone(self
):
124 org
= self
.bones
.org
.main
[2]
125 name
= self
.copy_bone(org
, make_derived_name(org
, 'ctrl', '_heel_ik'))
126 self
.bones
.ctrl
.heel
= name
128 flip_bone(self
.obj
, name
)
131 def parent_heel_control_bone(self
):
133 self
.set_bone_parent(self
.bones
.ctrl
.heel
, self
.bones
.ctrl
.heel2
)
135 self
.set_bone_parent(self
.bones
.ctrl
.heel
, self
.get_ik_control_output())
137 @stage.configure_bones
138 def configure_heel_control_bone(self
):
139 bone
= self
.get_bone(self
.bones
.ctrl
.heel
)
140 bone
.lock_location
= True, True, True
142 @stage.generate_widgets
143 def generate_heel_control_widget(self
):
144 create_ball_socket_widget(self
.obj
, self
.bones
.ctrl
.heel
)
146 ####################################################
147 # Second Heel control
149 @stage.generate_bones
150 def make_heel2_control_bone(self
):
152 org
= self
.bones
.org
.main
[3]
153 name
= self
.copy_bone(org
, make_derived_name(org
, 'ctrl', '_ik'))
154 self
.bones
.ctrl
.heel2
= name
156 flip_bone(self
.obj
, name
)
159 def parent_heel2_control_bone(self
):
161 self
.set_bone_parent(self
.bones
.ctrl
.heel2
, self
.get_ik_control_output())
163 @stage.configure_bones
164 def configure_heel2_control_bone(self
):
166 bone
= self
.get_bone(self
.bones
.ctrl
.heel2
)
167 bone
.lock_location
= True, True, True
169 @stage.generate_widgets
170 def generate_heel2_control_widget(self
):
172 create_ball_socket_widget(self
.obj
, self
.bones
.ctrl
.heel2
)
174 ####################################################
177 def make_fk_control_widget(self
, i
, ctrl
):
178 if i
< self
.toe_bone_index
- 1:
179 create_limb_widget(self
.obj
, ctrl
)
180 elif i
== self
.toe_bone_index
- 1:
181 create_circle_widget(self
.obj
, ctrl
, radius
=0.4, head_tail
=0.0)
183 create_circle_widget(self
.obj
, ctrl
, radius
=0.4, head_tail
=0.5)
185 ####################################################
186 # FK parents MCH chain
188 @stage.generate_bones
189 def make_toe_socket_bone(self
):
190 org
= self
.bones
.org
.main
[self
.toe_bone_index
]
191 self
.bones
.mch
.toe_socket
= self
.copy_bone(org
, make_derived_name(org
, 'mch', '_ik_socket'))
194 def parent_toe_socket_bone(self
):
195 self
.set_bone_parent(self
.bones
.mch
.toe_socket
, self
.get_ik_control_output())
197 def parent_fk_parent_bone(self
, i
, parent_mch
, prev_ctrl
, org
, prev_org
):
198 if i
== self
.toe_bone_index
:
199 self
.set_bone_parent(parent_mch
, prev_org
, use_connect
=True, inherit_scale
='ALIGNED')
202 super().parent_fk_parent_bone(i
, parent_mch
, prev_ctrl
, org
, prev_org
)
204 def rig_fk_parent_bone(self
, i
, parent_mch
, org
):
205 if i
== self
.toe_bone_index
:
206 con
= self
.make_constraint(parent_mch
, 'COPY_TRANSFORMS', self
.bones
.mch
.toe_socket
)
208 self
.make_driver(con
, 'influence', variables
=[(self
.prop_bone
, 'IK_FK')], polynomial
=[1.0, -1.0])
211 super().rig_fk_parent_bone(i
, parent_mch
, org
)
213 ####################################################
216 ik_input_head_tail
= 1.0
218 def get_ik_input_bone(self
):
219 return self
.bones
.ctrl
.heel
222 def parent_ik_mch_chain(self
):
223 super().parent_ik_mch_chain()
225 self
.set_bone_parent(self
.bones
.mch
.ik_target
, self
.bones
.ctrl
.heel
)
227 ####################################################
230 def get_ik_output_chain(self
):
231 tail
= [self
.bones
.mch
.ik_heel2
] if self
.use_heel2
else []
232 return super().get_ik_output_chain() + tail
234 @stage.generate_bones
235 def make_ik_heel2_bone(self
):
237 orgs
= self
.bones
.org
.main
238 self
.bones
.mch
.ik_heel2
= self
.copy_bone(orgs
[3], make_derived_name(orgs
[3], 'mch', '_ik_out'))
241 def parent_ik_heel2_bone(self
):
243 self
.set_bone_parent(self
.bones
.mch
.ik_heel2
, self
.bones
.ctrl
.heel2
)
246 def rig_ik_heel2_bone(self
):
248 self
.make_constraint(self
.bones
.mch
.ik_heel2
, 'COPY_LOCATION', self
.bones
.mch
.ik_target
, head_tail
=1)
250 ####################################################
253 def rig_deform_bone(self
, i
, deform
, entry
, next_entry
, tweak
, next_tweak
):
254 super().rig_deform_bone(i
, deform
, entry
, next_entry
, tweak
, next_tweak
)
256 if tweak
and not (next_tweak
or next_entry
):
257 self
.make_constraint(deform
, 'STRETCH_TO', entry
.org
, head_tail
=1.0, keep_axis
='SWING_Y')
259 ####################################################
263 def parameters_ui(cls
, layout
, params
, end
='Claw'):
264 super().parameters_ui(layout
, params
, end
)
267 def create_sample(obj
):
268 # generated by rigify.utils.write_metarig
269 bpy
.ops
.object.mode_set(mode
='EDIT')
274 bone
= arm
.edit_bones
.new('thigh.L')
275 bone
.head
[:] = 0.0000, 0.0017, 0.7379
276 bone
.tail
[:] = 0.0000, -0.0690, 0.4731
278 bone
.use_connect
= False
279 bones
['thigh.L'] = bone
.name
280 bone
= arm
.edit_bones
.new('shin.L')
281 bone
.head
[:] = 0.0000, -0.0690, 0.4731
282 bone
.tail
[:] = 0.0000, 0.1364, 0.2473
284 bone
.use_connect
= True
285 bone
.parent
= arm
.edit_bones
[bones
['thigh.L']]
286 bones
['shin.L'] = bone
.name
287 bone
= arm
.edit_bones
.new('foot.L')
288 bone
.head
[:] = 0.0000, 0.1364, 0.2473
289 bone
.tail
[:] = 0.0000, 0.0736, 0.0411
291 bone
.use_connect
= True
292 bone
.parent
= arm
.edit_bones
[bones
['shin.L']]
293 bones
['foot.L'] = bone
.name
294 bone
= arm
.edit_bones
.new('toe.L')
295 bone
.head
[:] = 0.0000, 0.0736, 0.0411
296 bone
.tail
[:] = 0.0000, -0.0594, 0.0000
298 bone
.use_connect
= True
299 bone
.parent
= arm
.edit_bones
[bones
['foot.L']]
300 bones
['toe.L'] = bone
.name
302 bpy
.ops
.object.mode_set(mode
='OBJECT')
303 pbone
= obj
.pose
.bones
[bones
['thigh.L']]
304 pbone
.rigify_type
= 'limbs.paw'
305 pbone
.lock_location
= (False, False, False)
306 pbone
.lock_rotation
= (False, False, False)
307 pbone
.lock_rotation_w
= False
308 pbone
.lock_scale
= (False, False, False)
309 pbone
.rotation_mode
= 'QUATERNION'
311 pbone
.rigify_parameters
.fk_layers
= [
312 False, False, False, False, False, False, False, False, False, False, False, False,
313 False, False, False, False, False, True, False, False, False, False, False, False,
314 False, False, False, False, False, False, False, False]
315 except AttributeError:
318 pbone
.rigify_parameters
.tweak_layers
= [
319 False, False, False, False, False, False, False, False, False, False, False, False,
320 False, False, False, False, False, False, True, False, False, False, False, False,
321 False, False, False, False, False, False, False, False]
322 except AttributeError:
325 pbone
.rigify_parameters
.limb_type
= "paw"
326 except AttributeError:
329 pbone
.rigify_parameters
.ik_local_location
= False
330 except AttributeError:
332 pbone
= obj
.pose
.bones
[bones
['shin.L']]
333 pbone
.rigify_type
= ''
334 pbone
.lock_location
= (False, False, False)
335 pbone
.lock_rotation
= (False, False, False)
336 pbone
.lock_rotation_w
= False
337 pbone
.lock_scale
= (False, False, False)
338 pbone
.rotation_mode
= 'QUATERNION'
339 pbone
= obj
.pose
.bones
[bones
['foot.L']]
340 pbone
.rigify_type
= ''
341 pbone
.lock_location
= (False, False, False)
342 pbone
.lock_rotation
= (False, False, False)
343 pbone
.lock_rotation_w
= False
344 pbone
.lock_scale
= (False, False, False)
345 pbone
.rotation_mode
= 'QUATERNION'
346 pbone
= obj
.pose
.bones
[bones
['toe.L']]
347 pbone
.rigify_type
= ''
348 pbone
.lock_location
= (False, False, False)
349 pbone
.lock_rotation
= (False, False, False)
350 pbone
.lock_rotation_w
= False
351 pbone
.lock_scale
= (False, False, False)
352 pbone
.rotation_mode
= 'QUATERNION'
354 pbone
.rigify_parameters
.limb_type
= "paw"
355 except AttributeError:
358 bpy
.ops
.object.mode_set(mode
='EDIT')
359 for bone
in arm
.edit_bones
:
361 bone
.select_head
= False
362 bone
.select_tail
= False
364 bone
= arm
.edit_bones
[bones
[b
]]
366 bone
.select_head
= True
367 bone
.select_tail
= True
368 arm
.edit_bones
.active
= bone