Cleanup: quiet character escape warnings
[blender-addons.git] / rigify / rigs / limbs / super_palm.py
blob7573e4a71d962441eb610a4d13d115482bd1e10a
1 # ##### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # ##### END GPL LICENSE BLOCK #####
19 # <pep8 compliant>
21 import bpy
22 import re
24 from math import cos, pi
25 from itertools import count, repeat
27 from rigify.utils.rig import is_rig_base_bone
28 from rigify.utils.naming import strip_org, make_derived_name, choose_derived_bone
29 from rigify.utils.widgets import widget_generator, register_widget
30 from rigify.utils.widgets_basic import create_bone_widget
31 from rigify.utils.misc import map_list
33 from rigify.base_rig import BaseRig, stage
36 def bone_siblings(obj, bone):
37 """ Returns a list of the siblings of the given bone.
38 This requires that the bones has a parent.
39 """
40 parent = obj.data.bones[bone].parent
42 if parent is None:
43 return []
45 bones = []
47 for b in parent.children:
48 if b.name != bone and not is_rig_base_bone(obj, b.name):
49 bones += [b.name]
51 return bones
54 class Rig(BaseRig):
55 """ A "palm" rig. A set of sibling bones that bend with each other.
56 This is a control and deformation rig.
57 """
59 def find_org_bones(self, bone):
60 base_head = bone.bone.head
61 siblings = bone_siblings(self.obj, bone.name)
63 # Sort list by name and distance
64 siblings.sort()
65 siblings.sort(key=lambda b: (self.get_bone(b).bone.head - base_head).length)
67 return [bone.name] + siblings
69 def initialize(self):
70 if len(self.bones.org) <= 1:
71 self.raise_error('The palm rig must have a parent and at least one sibling')
73 self.palm_rotation_axis = self.params.palm_rotation_axis
74 self.make_secondary = self.params.palm_both_sides
75 self.make_fk = self.params.make_extra_control
77 self.order = 'YXZ' if 'X' in self.palm_rotation_axis else 'YZX'
79 # Figure out the name for the control bone (remove the last .##)
80 self.ctrl_name = re.sub(r"([0-9]+\.)", "", strip_org(self.bones.org[-1])[::-1], count=1)[::-1]
82 def parent_bones(self):
83 self.rig_parent_bone = self.get_bone_parent(self.bones.org[0])
85 # Parent to the deform bone of the parent if exists
86 def_bone = choose_derived_bone(self.generator, self.rig_parent_bone, 'def')
88 if def_bone:
89 self.rig_parent_bone = def_bone
91 ####################################################
92 # BONES
94 # org[]:
95 # Original bones in order of distance.
96 # ctrl:
97 # master:
98 # Main control.
99 # secondary:
100 # Control for the other side.
101 # fk[]:
102 # Optional individual FK controls
103 # mch:
104 # fk_parents[]:
105 # Parents for the individual FK controls
106 # deform[]:
107 # DEF bones
109 ####################################################
111 ####################################################
112 # Master control
114 @stage.generate_bones
115 def make_master_control(self):
116 orgs = self.bones.org
118 self.bones.ctrl.master = self.copy_bone(orgs[-1], self.ctrl_name, parent=True)
120 if self.make_secondary:
121 second_name = make_derived_name(orgs[0], 'ctrl')
122 self.bones.ctrl.secondary = self.copy_bone(orgs[0], second_name, parent=True)
124 @stage.parent_bones
125 def parent_master_control(self):
126 self.set_bone_parent(self.bones.ctrl.master, self.rig_parent_bone, inherit_scale='AVERAGE')
128 if self.make_secondary:
129 self.set_bone_parent(self.bones.ctrl.secondary, self.rig_parent_bone, inherit_scale='AVERAGE')
131 @stage.configure_bones
132 def configure_master_control(self):
133 self.configure_control_bone(self.bones.ctrl.master, self.bones.org[-1])
135 if self.make_secondary:
136 self.configure_control_bone(self.bones.ctrl.secondary, self.bones.org[0])
138 def configure_control_bone(self, ctrl, org):
139 self.copy_bone_properties(org, ctrl)
140 self.get_bone(ctrl).lock_scale = (True, True, True)
142 @stage.generate_widgets
143 def make_master_control_widgets(self):
144 self.make_control_widget(self.bones.ctrl.master)
146 if self.make_secondary:
147 self.make_control_widget(self.bones.ctrl.secondary)
149 def make_control_widget(self, ctrl):
150 make_palm_widget(self.obj, ctrl, axis=self.palm_rotation_axis, radius=0.4)
152 ####################################################
153 # FK controls
155 @stage.generate_bones
156 def make_fk_controls(self):
157 if self.make_fk:
158 self.bones.ctrl.fk = map_list(self.make_fk_control_bone, count(0), self.bones.org)
160 def make_fk_control_bone(self, i, org):
161 return self.copy_bone(org, make_derived_name(org, 'ctrl', '_fk'))
163 @stage.parent_bones
164 def parent_fk_controls(self):
165 if self.make_fk:
166 for fk, mch in zip(self.bones.ctrl.fk, self.bones.mch.fk_parents):
167 self.set_bone_parent(fk, mch, inherit_scale='ALIGNED')
169 @stage.configure_bones
170 def configure_fk_controls(self):
171 if self.make_fk:
172 for fk, org in zip(self.bones.ctrl.fk, self.bones.org):
173 self.copy_bone_properties(org, fk)
175 @stage.generate_widgets
176 def make_fk_control_widgets(self):
177 if self.make_fk:
178 for fk in self.bones.ctrl.fk:
179 create_bone_widget(self.obj, fk)
181 ####################################################
182 # FK parent bones
184 @stage.generate_bones
185 def make_mch_fk_parents(self):
186 if self.make_fk:
187 self.bones.mch.fk_parents = map_list(self.make_fk_parent_bone, count(0), self.bones.org)
189 def make_fk_parent_bone(self, i, org):
190 return self.copy_bone(org, make_derived_name(org, 'mch', '_fk_parent'))
192 @stage.parent_bones
193 def parent_mch_fk_parents(self):
194 if self.make_fk:
195 for i, mch in enumerate(self.bones.mch.fk_parents):
196 self.parent_mch_fk_parent_bone(i, mch)
198 def parent_mch_fk_parent_bone(self, i, mch):
199 self.set_bone_parent(mch, self.rig_parent_bone, inherit_scale='NONE')
201 @stage.rig_bones
202 def rig_mch_fk_parents(self):
203 if self.make_fk:
204 for i, mch in enumerate(self.bones.mch.fk_parents):
205 self.rig_mch_fk_parent_bone(i, mch)
207 def rig_mch_fk_parent_bone(self, i, org):
208 num_orgs = len(self.bones.org)
209 ctrl = self.bones.ctrl
210 fac = i / (num_orgs - 1)
212 if fac > 0:
213 self.make_constraint(
214 org, 'COPY_TRANSFORMS', ctrl.master, space='LOCAL',
215 influence=fac
218 if self.make_secondary and fac < 1:
219 self.make_constraint(
220 org, 'COPY_LOCATION', ctrl.secondary, space='LOCAL',
221 use_offset=True, influence=1-fac
223 self.make_constraint(
224 org, 'COPY_ROTATION', ctrl.secondary, space='LOCAL',
225 euler_order=self.order, mix_mode='ADD', influence=1-fac
228 elif self.make_secondary:
229 self.make_constraint(
230 org, 'COPY_TRANSFORMS', ctrl.secondary, space='LOCAL'
233 self.make_constraint(org, 'COPY_SCALE', self.rig_parent_bone)
235 self.rig_mch_back_rotation(org, ctrl.master, fac)
237 if self.make_secondary:
238 self.rig_mch_back_rotation(org, ctrl.secondary, 1 - fac)
240 def rig_mch_back_rotation(self, org, ctrl, fac):
241 if 0 < fac < 1:
242 inf = (fac + 1) * (fac + cos(fac * pi / 2) - 1)
244 if 'X' in self.palm_rotation_axis:
245 self.make_constraint(
246 org, 'COPY_ROTATION', ctrl, space='LOCAL',
247 invert_x=True, use_xyz=(True,False,False),
248 euler_order=self.order, mix_mode='ADD', influence=inf
250 else:
251 self.make_constraint(
252 org, 'COPY_ROTATION', ctrl, space='LOCAL',
253 invert_z=True, use_xyz=(False,False,True),
254 euler_order=self.order, mix_mode='ADD', influence=inf
257 ####################################################
258 # ORG bones
260 @stage.parent_bones
261 def parent_org_chain(self):
262 if self.make_fk:
263 for org, fk in zip(self.bones.org, self.bones.ctrl.fk):
264 self.set_bone_parent(org, fk)
265 else:
266 for i, org in enumerate(self.bones.org):
267 self.parent_mch_fk_parent_bone(i, org)
269 @stage.rig_bones
270 def rig_org_chain(self):
271 if not self.make_fk:
272 for i, org in enumerate(self.bones.org):
273 self.rig_mch_fk_parent_bone(i, org)
275 ####################################################
276 # DEF bones
278 @stage.generate_bones
279 def make_def_chain(self):
280 self.bones.deform = map_list(self.make_deform_bone, self.bones.org)
282 def make_deform_bone(self, org):
283 return self.copy_bone(org, make_derived_name(org, 'def'))
285 @stage.parent_bones
286 def parent_deform_chain(self):
287 for deform, org in zip(self.bones.deform, self.bones.org):
288 self.set_bone_parent(deform, org, use_connect=False)
290 ####################################################
291 # Settings
293 @classmethod
294 def add_parameters(cls, params):
295 items = [('X', 'X', ''), ('Z', 'Z', '')]
296 params.palm_rotation_axis = bpy.props.EnumProperty(
297 items=items,
298 name="Palm Rotation Axis",
299 default='X',
301 params.palm_both_sides = bpy.props.BoolProperty(
302 name="Both Sides",
303 default=False,
304 description="Create controls for both sides of the palm"
306 params.make_extra_control = bpy.props.BoolProperty(
307 name = "Extra Control",
308 default = False,
309 description = "Create an optional control"
312 @classmethod
313 def parameters_ui(cls, layout, params):
314 r = layout.row()
315 r.label(text="Primary rotation axis:")
316 r.prop(params, "palm_rotation_axis", text="")
317 layout.prop(params, "palm_both_sides")
318 layout.prop(params, "make_extra_control", text="Extra FK Controls")
321 @widget_generator(register="palm", subsurf=2)
322 def make_palm_widget(geom, axis='X', radius=0.5):
323 sx = radius / 0.4
324 sz = radius / 0.3
325 v = [(0.1578, 0.0, -0.3), (0.1578, 1.0, -0.2), (-0.1578, 1.0, -0.2), (-0.1578, -0.0, -0.3),
326 (-0.1578, -0.0, 0.3), (-0.1578, 1.0, 0.2), (0.1578, 1.0, 0.2), (0.1578, 0.0, 0.3),
327 (0.1578, 0.25, -0.275), (-0.1578, 0.25, -0.275), (0.1578, 0.75, -0.225), (-0.1578, 0.75, -0.225),
328 (0.1578, 0.75, 0.225), (0.1578, 0.25, 0.275), (-0.1578, 0.25, 0.275), (-0.1578, 0.75, 0.225)]
330 geom.verts = [(x*sx, y, z*sz) for x,y,z in v]
332 if 'Z' in axis:
333 # Flip x/z coordinates
334 geom.verts = [v[::-1] for v in geom.verts]
336 geom.edges = [(1, 2), (0, 3), (4, 7), (5, 6), (8, 0), (9, 3), (10, 1), (11, 2), (12, 6),
337 (13, 7), (4, 14), (15, 5), (10, 8), (11, 9), (15, 14), (12, 13)]
339 register_widget("palm_z", make_palm_widget, axis='Z')
342 def create_sample(obj):
343 # generated by rigify.utils.write_metarig
344 bpy.ops.object.mode_set(mode='EDIT')
345 arm = obj.data
347 bones = {}
349 bone = arm.edit_bones.new('palm.parent')
350 bone.head[:] = 0.0000, 0.0000, 0.0000
351 bone.tail[:] = 0.0577, 0.0000, -0.0000
352 bone.roll = 3.1416
353 bone.use_connect = False
354 bones['palm.parent'] = bone.name
355 bone = arm.edit_bones.new('palm.04')
356 bone.head[:] = 0.0577, 0.0315, -0.0000
357 bone.tail[:] = 0.1627, 0.0315, -0.0000
358 bone.roll = 3.1416
359 bone.use_connect = False
360 bone.parent = arm.edit_bones[bones['palm.parent']]
361 bones['palm.04'] = bone.name
362 bone = arm.edit_bones.new('palm.03')
363 bone.head[:] = 0.0577, 0.0105, -0.0000
364 bone.tail[:] = 0.1627, 0.0105, -0.0000
365 bone.roll = 3.1416
366 bone.use_connect = False
367 bone.parent = arm.edit_bones[bones['palm.parent']]
368 bones['palm.03'] = bone.name
369 bone = arm.edit_bones.new('palm.02')
370 bone.head[:] = 0.0577, -0.0105, -0.0000
371 bone.tail[:] = 0.1627, -0.0105, -0.0000
372 bone.roll = 3.1416
373 bone.use_connect = False
374 bone.parent = arm.edit_bones[bones['palm.parent']]
375 bones['palm.02'] = bone.name
376 bone = arm.edit_bones.new('palm.01')
377 bone.head[:] = 0.0577, -0.0315, -0.0000
378 bone.tail[:] = 0.1627, -0.0315, -0.0000
379 bone.roll = 3.1416
380 bone.use_connect = False
381 bone.parent = arm.edit_bones[bones['palm.parent']]
382 bones['palm.01'] = bone.name
384 bpy.ops.object.mode_set(mode='OBJECT')
385 pbone = obj.pose.bones[bones['palm.parent']]
386 pbone.rigify_type = ''
387 pbone.lock_location = (False, False, False)
388 pbone.lock_rotation = (False, False, False)
389 pbone.lock_rotation_w = False
390 pbone.lock_scale = (False, False, False)
391 pbone.rotation_mode = 'QUATERNION'
392 pbone = obj.pose.bones[bones['palm.04']]
393 pbone.rigify_type = ''
394 pbone.lock_location = (False, False, False)
395 pbone.lock_rotation = (False, True, True)
396 pbone.lock_rotation_w = False
397 pbone.lock_scale = (False, False, False)
398 pbone.rotation_mode = 'YXZ'
399 pbone = obj.pose.bones[bones['palm.03']]
400 pbone.rigify_type = ''
401 pbone.lock_location = (False, False, False)
402 pbone.lock_rotation = (False, True, True)
403 pbone.lock_rotation_w = False
404 pbone.lock_scale = (False, False, False)
405 pbone.rotation_mode = 'YXZ'
406 pbone = obj.pose.bones[bones['palm.02']]
407 pbone.rigify_type = ''
408 pbone.lock_location = (False, False, False)
409 pbone.lock_rotation = (False, True, True)
410 pbone.lock_rotation_w = False
411 pbone.lock_scale = (False, False, False)
412 pbone.rotation_mode = 'YXZ'
413 pbone = obj.pose.bones[bones['palm.01']]
414 pbone.rigify_type = 'limbs.super_palm'
415 pbone.lock_location = (False, False, False)
416 pbone.lock_rotation = (False, True, True)
417 pbone.lock_rotation_w = False
418 pbone.lock_scale = (False, False, False)
419 pbone.rotation_mode = 'YXZ'
421 bpy.ops.object.mode_set(mode='EDIT')
422 for bone in arm.edit_bones:
423 bone.select = False
424 bone.select_head = False
425 bone.select_tail = False
426 for b in bones:
427 bone = arm.edit_bones[bones[b]]
428 bone.select = True
429 bone.select_head = True
430 bone.select_tail = True
431 arm.edit_bones.active = bone