File headers: use SPDX license identifiers
[blender-addons.git] / rigify / rigs / limbs / front_paw.py
blobd5c36f966ad1798028e402b3dbe6f9161e3c399b
1 # SPDX-License-Identifier: GPL-2.0-or-later
3 # <pep8 compliant>
5 import bpy
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
19 class Rig(pawRig):
20 """Front paw rig with special IK automation."""
22 ####################################################
23 # EXTRA BONES
25 # mch:
26 # ik2_chain[2]
27 # Second IK system (pre-driving heel)
28 # heel_track
29 # Bone tracking IK2 to rotate heel
30 # heel_parent
31 # Parent of the heel control
33 ####################################################
35 ####################################################
36 # IK controls
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()
43 assert not tail_chain
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
64 @stage.generate_bones
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
70 if self.use_heel2:
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'))
83 @stage.parent_bones
84 def parent_ik2_mch_chain(self):
85 mch = self.bones.mch
86 if self.use_heel2:
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)
98 bone.ik_stretch = 0.1
99 if i == 1:
100 bone.lock_ik_x = bone.lock_ik_y = bone.lock_ik_z = True
101 setattr(bone, 'lock_ik_' + self.main_axis, False)
103 @stage.rig_bones
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
115 mch = self.bones.mch
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)
126 @stage.parent_bones
127 def parent_heel_control_bone(self):
128 self.set_bone_parent(self.bones.ctrl.heel, self.bones.mch.heel_parent)
130 @stage.parent_bones
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
150 @stage.apply_bones
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 ####################################################
157 # Settings
159 @classmethod
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',
165 default = 0.8,
166 min = 0,
167 max = 1,
168 description = 'Influence of the secondary IK on the heel control rotation'
171 @classmethod
172 def parameters_ui(self, layout, params):
173 r = layout.row()
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')
182 arm = obj.data
184 bones = {}
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
189 bone.roll = 0.0000
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
195 bone.roll = 0.0000
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
202 bone.roll = 0.0000
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
209 bone.roll = 3.1416
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'
222 try:
223 pbone.rigify_parameters.limb_type = "paw"
224 except AttributeError:
225 pass
226 try:
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:
229 pass
230 try:
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:
233 pass
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'
255 try:
256 pbone.rigify_parameters.limb_type = "paw"
257 except AttributeError:
258 pass
260 bpy.ops.object.mode_set(mode='EDIT')
261 for bone in arm.edit_bones:
262 bone.select = False
263 bone.select_head = False
264 bone.select_tail = False
265 for b in bones:
266 bone = arm.edit_bones[bones[b]]
267 bone.select = True
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
273 return bones