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 #####
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.
40 parent
= obj
.data
.bones
[bone
].parent
47 for b
in parent
.children
:
48 if b
.name
!= bone
and not is_rig_base_bone(obj
, b
.name
):
55 """ A "palm" rig. A set of sibling bones that bend with each other.
56 This is a control and deformation rig.
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
65 siblings
.sort(key
=lambda b
: (self
.get_bone(b
).bone
.head
- base_head
).length
)
67 return [bone
.name
] + siblings
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')
89 self
.rig_parent_bone
= def_bone
91 ####################################################
95 # Original bones in order of distance.
100 # Control for the other side.
102 # Optional individual FK controls
105 # Parents for the individual FK controls
109 ####################################################
111 ####################################################
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)
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 ####################################################
155 @stage.generate_bones
156 def make_fk_controls(self
):
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'))
164 def parent_fk_controls(self
):
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
):
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
):
178 for fk
in self
.bones
.ctrl
.fk
:
179 create_bone_widget(self
.obj
, fk
)
181 ####################################################
184 @stage.generate_bones
185 def make_mch_fk_parents(self
):
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'))
193 def parent_mch_fk_parents(self
):
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')
202 def rig_mch_fk_parents(self
):
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)
213 self
.make_constraint(
214 org
, 'COPY_TRANSFORMS', ctrl
.master
, space
='LOCAL',
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
):
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
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 ####################################################
261 def parent_org_chain(self
):
263 for org
, fk
in zip(self
.bones
.org
, self
.bones
.ctrl
.fk
):
264 self
.set_bone_parent(org
, fk
)
266 for i
, org
in enumerate(self
.bones
.org
):
267 self
.parent_mch_fk_parent_bone(i
, org
)
270 def rig_org_chain(self
):
272 for i
, org
in enumerate(self
.bones
.org
):
273 self
.rig_mch_fk_parent_bone(i
, org
)
275 ####################################################
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'))
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 ####################################################
294 def add_parameters(cls
, params
):
295 items
= [('X', 'X', ''), ('Z', 'Z', '')]
296 params
.palm_rotation_axis
= bpy
.props
.EnumProperty(
298 name
="Palm Rotation Axis",
301 params
.palm_both_sides
= bpy
.props
.BoolProperty(
304 description
="Create controls for both sides of the palm"
306 params
.make_extra_control
= bpy
.props
.BoolProperty(
307 name
= "Extra Control",
309 description
= "Create an optional control"
313 def parameters_ui(cls
, layout
, params
):
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):
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
]
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')
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
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
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
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
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
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
:
424 bone
.select_head
= False
425 bone
.select_tail
= False
427 bone
= arm
.edit_bones
[bones
[b
]]
429 bone
.select_head
= True
430 bone
.select_tail
= True
431 arm
.edit_bones
.active
= bone