Merge with trunk, revision 28528 - 28976.
[blender-staging.git] / release / scripts / modules / rigify / spine_pivot_flex.py
1 # ##### BEGIN GPL LICENSE BLOCK #####
2 #
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.
7 #
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.
12 #
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.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 # <pep8 compliant>
20
21 import bpy
22 from rigify import RigifyError
23 from rigify_utils import bone_class_instance, copy_bone_simple
24 from rna_prop_ui import rna_idprop_ui_prop_get
25
26 # not used, defined for completeness
27 METARIG_NAMES = ("pelvis", "ribcage")
28
29
30 def metarig_template():
31     # generated by rigify.write_meta_rig
32     bpy.ops.object.mode_set(mode='EDIT')
33     obj = bpy.context.active_object
34     arm = obj.data
35     bone = arm.edit_bones.new('pelvis')
36     bone.head[:] = 0.0000, -0.0306, 0.1039
37     bone.tail[:] = 0.0000, -0.0306, -0.0159
38     bone.roll = 0.0000
39     bone.connected = False
40     bone = arm.edit_bones.new('rib_cage')
41     bone.head[:] = 0.0000, -0.0306, 0.1039
42     bone.tail[:] = 0.0000, -0.0306, 0.2236
43     bone.roll = -0.0000
44     bone.connected = False
45     bone.parent = arm.edit_bones['pelvis']
46     bone = arm.edit_bones.new('spine.01')
47     bone.head[:] = 0.0000, 0.0000, -0.0000
48     bone.tail[:] = 0.0000, -0.0306, 0.1039
49     bone.roll = -0.0000
50     bone.connected = False
51     bone.parent = arm.edit_bones['rib_cage']
52     bone = arm.edit_bones.new('spine.02')
53     bone.head[:] = 0.0000, -0.0306, 0.1039
54     bone.tail[:] = -0.0000, -0.0398, 0.2045
55     bone.roll = -0.0000
56     bone.connected = True
57     bone.parent = arm.edit_bones['spine.01']
58     bone = arm.edit_bones.new('spine.03')
59     bone.head[:] = -0.0000, -0.0398, 0.2045
60     bone.tail[:] = -0.0000, -0.0094, 0.2893
61     bone.roll = -0.0000
62     bone.connected = True
63     bone.parent = arm.edit_bones['spine.02']
64     bone = arm.edit_bones.new('spine.04')
65     bone.head[:] = -0.0000, -0.0094, 0.2893
66     bone.tail[:] = -0.0000, 0.0335, 0.3595
67     bone.roll = -0.0000
68     bone.connected = True
69     bone.parent = arm.edit_bones['spine.03']
70     bone = arm.edit_bones.new('spine.05')
71     bone.head[:] = -0.0000, 0.0335, 0.3595
72     bone.tail[:] = -0.0000, 0.0555, 0.4327
73     bone.roll = -0.0000
74     bone.connected = True
75     bone.parent = arm.edit_bones['spine.04']
76     bone = arm.edit_bones.new('spine.06')
77     bone.head[:] = -0.0000, 0.0555, 0.4327
78     bone.tail[:] = -0.0000, 0.0440, 0.5207
79     bone.roll = -0.0000
80     bone.connected = True
81     bone.parent = arm.edit_bones['spine.05']
82     bone = arm.edit_bones.new('spine.07')
83     bone.head[:] = -0.0000, 0.0440, 0.5207
84     bone.tail[:] = -0.0000, 0.0021, 0.5992
85     bone.roll = -0.0000
86     bone.connected = True
87     bone.parent = arm.edit_bones['spine.06']
88
89     bpy.ops.object.mode_set(mode='OBJECT')
90     pbone = obj.pose.bones['rib_cage']
91     pbone['type'] = 'spine_pivot_flex'
92
93
94 def metarig_definition(obj, orig_bone_name):
95     '''
96     The bone given is the second in a chain.
97     Expects at least 1 parent and a chain of children withe the same basename
98     eg.
99         pelvis -> rib_cage -> spine.01 -> spine.02 -> spine.03
100
101     note: same as neck.
102     '''
103     arm = obj.data
104     ribcage = arm.bones[orig_bone_name]
105     pelvis = ribcage.parent
106
107     if pelvis is None:
108         raise RigifyError("expected the ribcage bone:'%s' to have a parent (ribcage)." % ribcage.name)
109
110     children = ribcage.children
111     if len(children) != 1:
112         raise RigifyError("expected the ribcage to have only 1 child.")
113
114     child = children[0]
115
116     bone_definition = [pelvis.name, ribcage.name, child.name]
117     bone_definition.extend([child.name for child in child.children_recursive_basename])
118     return bone_definition
119
120
121 def fk(*args):
122     main(*args)
123
124
125 def deform(obj, definitions, base_names, options):
126     for org_bone_name in definitions[2:]:
127         bpy.ops.object.mode_set(mode='EDIT')
128
129         # Create deform bone.
130         bone = copy_bone_simple(obj.data, org_bone_name, "DEF-%s" % base_names[org_bone_name], parent=True)
131
132         # Store name before leaving edit mode
133         bone_name = bone.name
134
135         # Leave edit mode
136         bpy.ops.object.mode_set(mode='OBJECT')
137
138         # Get the pose bone
139         bone = obj.pose.bones[bone_name]
140
141         # Constrain to the original bone
142         # XXX. Todo, is this needed if the bone is connected to its parent?
143         con = bone.constraints.new('COPY_TRANSFORMS')
144         con.name = "copy_loc"
145         con.target = obj
146         con.subtarget = org_bone_name
147
148
149 def main(obj, bone_definition, base_names, options):
150     from mathutils import Vector, RotationMatrix
151     from math import radians, pi
152
153     arm = obj.data
154
155     # Initialize container classes for convenience
156     mt = bone_class_instance(obj, ["pelvis", "ribcage"]) # meta
157     mt.pelvis = bone_definition[0]
158     mt.ribcage = bone_definition[1]
159     mt.update()
160
161     spine_chain_orig = tuple(bone_definition[2:])
162     spine_chain = [arm.edit_bones[child_name] for child_name in spine_chain_orig]
163     spine_chain_basename = base_names[spine_chain[0].name].rsplit(".", 1)[0] # probably 'ORG-spine.01' -> 'spine'
164     spine_chain_len = len(spine_chain_orig)
165
166     child = spine_chain[0]
167     spine_chain_segment_length = child.length
168     #child.parent = mt.pelvis_e # was mt.ribcage
169
170     # The first bone in the chain happens to be the basis of others, create them now
171     ex = bone_class_instance(obj, ["pelvis_copy", "ribcage_hinge", "ribcage_copy", "spine_rotate"])
172
173     ex.pelvis_copy_e = copy_bone_simple(arm, mt.pelvis, base_names[mt.pelvis]) # no parent
174     ex.pelvis_copy = ex.pelvis_copy_e.name
175     ex.pelvis_copy_e.local_location = False
176
177     # copy the pelvis, offset to make MCH-spine_rotate and MCH-ribcage_hinge
178     ex.ribcage_hinge_e = copy_bone_simple(arm, mt.pelvis, "MCH-%s_hinge" % base_names[mt.ribcage])
179     ex.ribcage_hinge = ex.ribcage_hinge_e.name
180     ex.ribcage_hinge_e.translate(Vector((0.0, spine_chain_segment_length / 4.0, 0.0)))
181
182     ex.spine_rotate_e = copy_bone_simple(arm, mt.ribcage, "MCH-%s_rotate" % spine_chain_basename)
183     ex.spine_rotate = ex.spine_rotate_e.name
184     ex.spine_rotate_e.translate(Vector((0.0, spine_chain_segment_length / 2.0, 0.0)))
185     ex.spine_rotate_e.connected = False
186     ex.spine_rotate_e.parent = ex.pelvis_copy_e
187
188
189     # Copy the last bone now
190     child = spine_chain[-1]
191
192     ex.ribcage_copy_e = copy_bone_simple(arm, mt.ribcage, base_names[mt.ribcage])
193     ex.ribcage_copy = ex.ribcage_copy_e.name
194     ex.ribcage_copy_e.connected = False
195     ex.ribcage_copy_e.parent = ex.ribcage_hinge_e
196
197     spine_chain = [child.name for child in spine_chain]
198
199     # We have 3 spine chains
200     # - original (ORG_*)
201     # - copy (*use original name*)
202     # - reverse (MCH-rev_*)
203     spine_chain_attrs = [("spine_%.2d" % (i + 1)) for i in range(spine_chain_len)]
204
205     mt_chain = bone_class_instance(obj, spine_chain_attrs) # ORG_*
206     rv_chain = bone_class_instance(obj, spine_chain_attrs) # *
207     ex_chain = bone_class_instance(obj, spine_chain_attrs) # MCH-rev_*
208     del spine_chain_attrs
209
210     for i, child_name in enumerate(spine_chain):
211         child_name_orig = base_names[spine_chain_orig[i]]
212
213         attr = mt_chain.attr_names[i] # eg. spine_04
214
215         setattr(mt_chain, attr, spine_chain_orig[i]) # the original bone
216
217         ebone = copy_bone_simple(arm, child_name, child_name_orig) # use the original name
218         setattr(ex_chain, attr, ebone.name)
219
220         ebone = copy_bone_simple(arm, child_name, "MCH-rev_%s" % child_name_orig)
221         setattr(rv_chain, attr, ebone.name)
222         ebone.connected = False
223
224     mt_chain.update()
225     ex_chain.update()
226     rv_chain.update()
227
228     # Now we need to re-parent these chains
229     for i, child_name in enumerate(spine_chain_orig):
230         attr = ex_chain.attr_names[i] + "_e"
231         ebone = getattr(ex_chain, attr)
232         if i == 0:
233             ebone.connected = False
234             ebone.parent = ex.pelvis_copy_e
235         else:
236             attr_parent = ex_chain.attr_names[i - 1] + "_e"
237             ebone.parent = getattr(ex_chain, attr_parent)
238
239         # intentional! get the parent from the other paralelle chain member
240         getattr(rv_chain, attr).parent = ebone
241
242
243     # ex_chain needs to interlace bones!
244     # Note, skip the first bone
245     for i in range(1, spine_chain_len): # similar to neck
246         child_name_orig = base_names[spine_chain_orig[i]]
247         spine_e = getattr(mt_chain, mt_chain.attr_names[i] + "_e")
248
249         # dont store parent names, re-reference as each chain bones parent.
250         spine_e_parent = arm.edit_bones.new("MCH-rot_%s" % child_name_orig)
251         spine_e_parent.head = spine_e.head
252         spine_e_parent.tail = spine_e.head + (mt.ribcage_e.vector.normalize() * spine_chain_segment_length / 2.0)
253         spine_e_parent.roll = mt.ribcage_e.roll
254
255
256         spine_e = getattr(ex_chain, ex_chain.attr_names[i] + "_e")
257         orig_parent = spine_e.parent
258         spine_e.connected = False
259         spine_e.parent = spine_e_parent
260         spine_e_parent.connected = False
261
262         spine_e_parent.parent = orig_parent
263
264
265     # Rotate the rev chain 180 about the by the first bones center point
266     pivot = (rv_chain.spine_01_e.head + rv_chain.spine_01_e.tail) * 0.5
267     matrix = RotationMatrix(radians(180), 3, 'X')
268     for i, attr in enumerate(rv_chain.attr_names): # similar to neck
269         spine_e = getattr(rv_chain, attr + "_e")
270         # use the first bone as the pivot
271
272         spine_e.head = ((spine_e.head - pivot) * matrix) + pivot
273         spine_e.tail = ((spine_e.tail - pivot) * matrix) + pivot
274         spine_e.roll += pi # 180d roll
275         del spine_e
276
277     deform(obj, bone_definition, base_names, options)
278
279     bpy.ops.object.mode_set(mode='OBJECT')
280
281     # refresh pose bones
282     mt.update()
283     ex.update()
284     mt_chain.update()
285     ex_chain.update()
286     rv_chain.update()
287
288     # Axis locks
289     ex.ribcage_copy_p.lock_location = True, True, True
290
291     con = ex.ribcage_hinge_p.constraints.new('COPY_ROTATION')
292     con.name = "hinge"
293     con.target = obj
294     con.subtarget = ex.pelvis_copy
295
296     # add driver
297     fcurve = con.driver_add("influence")
298     driver = fcurve.driver
299     var = driver.variables.new()
300     driver.type = 'AVERAGE'
301     var.name = "var"
302     var.targets[0].id_type = 'OBJECT'
303     var.targets[0].id = obj
304     var.targets[0].data_path = ex.ribcage_copy_p.path_from_id() + '["hinge"]'
305
306     mod = fcurve.modifiers[0]
307     mod.poly_order = 1
308     mod.coefficients[0] = 1.0
309     mod.coefficients[1] = -1.0
310
311     con = ex.spine_rotate_p.constraints.new('COPY_ROTATION')
312     con.target = obj
313     con.subtarget = ex.ribcage_copy
314
315     # ex.pelvis_copy_p / rib_cage
316     con = ex.ribcage_copy_p.constraints.new('COPY_LOCATION')
317     con.target = obj
318     con.subtarget = ex.pelvis_copy
319     con.head_tail = 0.0
320
321     # This stores all important ID props
322     prop = rna_idprop_ui_prop_get(ex.ribcage_copy_p, "hinge", create=True)
323     ex.ribcage_copy_p["hinge"] = 1.0
324     prop["soft_min"] = 0.0
325     prop["soft_max"] = 1.0
326
327     prop = rna_idprop_ui_prop_get(ex.ribcage_copy_p, "pivot_slide", create=True)
328     ex.ribcage_copy_p["pivot_slide"] = 1.0 / spine_chain_len
329     prop["soft_min"] = 1.0 / spine_chain_len
330     prop["soft_max"] = 1.0
331
332
333     # Create a fake connected parent/child relationship with bone location constraints
334     # positioned at the tip.
335
336     # reverse bones / MCH-rev_spine.##
337     for i in range(1, spine_chain_len):
338         spine_p = getattr(rv_chain, rv_chain.attr_names[i] + "_p")
339         spine_fake_parent_name = getattr(rv_chain, rv_chain.attr_names[i - 1])
340
341         con = spine_p.constraints.new('COPY_LOCATION')
342         con.target = obj
343         con.subtarget = spine_fake_parent_name
344         con.head_tail = 1.0
345         del spine_p, spine_fake_parent_name, con
346
347
348     # Constrain 'inbetween' bones
349     target_names = [("b%.2d" % (i + 1)) for i in range(spine_chain_len - 1)]
350     rib_driver_path = ex.ribcage_copy_p.path_from_id()
351
352     ex.ribcage_copy_p["bend_tot"] = 0.0
353     fcurve = ex.ribcage_copy_p.driver_add('["bend_tot"]')
354     driver = fcurve.driver
355     driver.type = 'SUM'
356     fcurve.modifiers.remove(0) # grr dont need a modifier
357
358     for i in range(spine_chain_len - 1):
359         var = driver.variables.new()
360         var.name = target_names[i]
361         var.targets[0].id_type = 'OBJECT'
362         var.targets[0].id = obj
363         var.targets[0].data_path = rib_driver_path + ('["bend_%.2d"]' % (i + 1))
364
365     for i in range(1, spine_chain_len):
366
367         # Add bend prop
368         prop_name = "bend_%.2d" % i
369         prop = rna_idprop_ui_prop_get(ex.ribcage_copy_p, prop_name, create=True)
370         if ("bend_%.2d" % i) in options:
371             ex.ribcage_copy_p[prop_name] = options["bend_%.2d" % i]
372         else:
373             ex.ribcage_copy_p[prop_name] = 1.0
374         prop["soft_min"] = 0.0
375         prop["soft_max"] = 1.0
376
377         spine_p = getattr(ex_chain, ex_chain.attr_names[i] + "_p")
378         spine_p_parent = spine_p.parent # interlaced bone
379
380         con = spine_p_parent.constraints.new('COPY_ROTATION')
381         con.target = obj
382         con.subtarget = ex.spine_rotate
383         con.owner_space = 'LOCAL'
384         con.target_space = 'LOCAL'
385         del spine_p
386
387         # add driver
388         fcurve = con.driver_add("influence")
389         driver = fcurve.driver
390         driver.type = 'SCRIPTED'
391         driver.expression = "bend/bend_tot"
392
393         fcurve.modifiers.remove(0) # grr dont need a modifier
394
395
396         # add target
397         var = driver.variables.new()
398         var.name = "bend_tot"
399         var.targets[0].id_type = 'OBJECT'
400         var.targets[0].id = obj
401         var.targets[0].data_path = rib_driver_path + ('["bend_tot"]')
402
403         var = driver.variables.new()
404         var.name = "bend"
405         var.targets[0].id_type = 'OBJECT'
406         var.targets[0].id = obj
407         var.targets[0].data_path = rib_driver_path + ('["%s"]' % prop_name)
408
409
410
411     # original bone drivers
412     # note: the first bone has a lot more constraints, but also this simple one is first.
413     for i, attr in enumerate(mt_chain.attr_names):
414         spine_p = getattr(mt_chain, attr + "_p")
415
416         con = spine_p.constraints.new('COPY_ROTATION')
417         con.target = obj
418         con.subtarget = getattr(ex_chain, attr) # lock to the copy's rotation
419         del spine_p
420
421     # pivot slide: - lots of copy location constraints.
422
423     con = mt_chain.spine_01_p.constraints.new('COPY_LOCATION')
424     con.name = "base"
425     con.target = obj
426     con.subtarget = rv_chain.spine_01 # lock to the reverse location
427
428     for i in range(1, spine_chain_len + 1):
429         con = mt_chain.spine_01_p.constraints.new('COPY_LOCATION')
430         con.name = "slide_%d" % i
431         con.target = obj
432
433         if i == spine_chain_len:
434             attr = mt_chain.attr_names[i - 1]
435         else:
436             attr = mt_chain.attr_names[i]
437
438         con.subtarget = getattr(rv_chain, attr) # lock to the reverse location
439
440         if i == spine_chain_len:
441             con.head_tail = 1.0
442
443         fcurve = con.driver_add("influence")
444         driver = fcurve.driver
445         var = driver.variables.new()
446         driver.type = 'AVERAGE'
447         var.name = "var"
448         var.targets[0].id_type = 'OBJECT'
449         var.targets[0].id = obj
450         var.targets[0].data_path = rib_driver_path + '["pivot_slide"]'
451
452         mod = fcurve.modifiers[0]
453         mod.poly_order = 1
454         mod.coefficients[0] = - (i - 1)
455         mod.coefficients[1] = spine_chain_len
456
457
458     # Set pelvis and ribcage controls to use the first and last bone in the
459     # spine respectively for their custom shape transform
460     ex.ribcage_copy_p.custom_shape_transform = obj.pose.bones[bone_definition[len(bone_definition)-1]]
461     ex.pelvis_copy_p.custom_shape_transform = obj.pose.bones[bone_definition[2]]
462
463
464     # last step setup layers
465     if "ex_layer" in options:
466         layer = [n == options["ex_layer"] for n in range(0, 32)]
467     else:
468         layer = list(arm.bones[bone_definition[1]].layer)
469     for attr in ex.attr_names:
470         getattr(ex, attr + "_b").layer = layer
471     for attr in ex_chain.attr_names:
472         getattr(ex_chain, attr + "_b").layer = layer
473     for attr in rv_chain.attr_names:
474         getattr(rv_chain, attr + "_b").layer = layer
475
476     layer = list(arm.bones[bone_definition[1]].layer)
477     arm.bones[ex.pelvis_copy].layer = layer
478     arm.bones[ex.ribcage_copy].layer = layer
479
480     # no support for blending chains
481     return None