split up metarig hierarchy evaluation and modifying the metarig into 2 steps,
[blender.git] / release / scripts / modules / rigify / leg.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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 import bpy
20 from rigify import bone_class_instance, copy_bone_simple, copy_bone_simple_list, add_pole_target_bone, add_stretch_to
21 from rna_prop_ui import rna_idprop_ui_get, rna_idprop_ui_prop_get
22
23 METARIG_NAMES = "hips", "thigh", "shin", "foot", "toe", "heel"
24
25 def metarig_template():
26     # generated by rigify.write_meta_rig
27     bpy.ops.object.mode_set(mode='EDIT')
28     obj = bpy.context.object
29     arm = obj.data
30     bone = arm.edit_bones.new('hips')
31     bone.head[:] = 0.0000, 0.0000, 0.0000
32     bone.tail[:] = 0.0000, 0.0000, 1.0000
33     bone.roll = 0.0000
34     bone.connected = False
35     bone = arm.edit_bones.new('thigh')
36     bone.head[:] = 0.5000, 0.0000, 0.0000
37     bone.tail[:] = 0.3000, -0.1000, -1.7000
38     bone.roll = 0.1171
39     bone.connected = False
40     bone.parent = arm.edit_bones['hips']
41     bone = arm.edit_bones.new('shin')
42     bone.head[:] = 0.3000, -0.1000, -1.7000
43     bone.tail[:] = 0.3000, 0.0000, -3.5000
44     bone.roll = -0.0000
45     bone.connected = True
46     bone.parent = arm.edit_bones['thigh']
47     bone = arm.edit_bones.new('foot')
48     bone.head[:] = 0.3000, 0.0000, -3.5000
49     bone.tail[:] = 0.4042, -0.5909, -3.9000
50     bone.roll = -0.4662
51     bone.connected = True
52     bone.parent = arm.edit_bones['shin']
53     bone = arm.edit_bones.new('toe')
54     bone.head[:] = 0.4042, -0.5909, -3.9000
55     bone.tail[:] = 0.4391, -0.9894, -3.9000
56     bone.roll = -3.1416
57     bone.connected = True
58     bone.parent = arm.edit_bones['foot']
59     bone = arm.edit_bones.new('heel')
60     bone.head[:] = 0.2600, 0.2000, -4.0000
61     bone.tail[:] = 0.3700, -0.4000, -4.0000
62     bone.roll = 0.0000
63     bone.connected = False
64     bone.parent = arm.edit_bones['foot']
65
66     bpy.ops.object.mode_set(mode='OBJECT')
67     pbone = obj.pose.bones['thigh']
68     pbone['type'] = 'leg'
69
70 def metarig_definition(obj, orig_bone_name):
71     '''
72     The bone given is the first in a chain
73     Expects a chain of at least 3 children.
74     eg.
75         thigh -> shin -> foot -> [toe, heel]
76     '''
77     
78     bone_definition = []
79
80     orig_bone = obj.data.bones[orig_bone_name]
81     orig_bone_parent = orig_bone.parent
82
83     if orig_bone_parent is None:
84         raise Exception("expected the thigh bone to have a parent hip bone")
85
86     bone_definition.append(orig_bone_parent.name)
87     bone_definition.append(orig_bone.name)
88     
89     
90     bone = orig_bone
91     chain = 0
92     while chain < 2: # first 2 bones only have 1 child
93         children = bone.children
94
95         if len(children) != 1:
96             raise Exception("expected the thigh bone to have 3 children without a fork")
97         bone = children[0]
98         bone_definition.append(bone.name) # shin, foot
99         chain += 1
100         
101     children = bone.children
102     # Now there must be 2 children, only one connected
103     if len(children) != 2:
104         raise Exception("expected the foot to have 2 children")
105
106     if children[0].connected == children[1].connected:
107         raise Exception("expected one bone to be connected")
108     
109     toe, heel = children
110     if heel.connected:
111         toe, heel = heel, toe
112     
113     
114     bone_definition.append(toe.name)
115     bone_definition.append(heel.name)
116     
117     if len(bone_definition) != len(METARIG_NAMES):
118         raise Exception("internal problem, expected %d bones" % len(METARIG_NAMES))
119     
120     return bone_definition
121
122
123 def ik(obj, bone_definition, base_names):
124     from Mathutils import Vector
125     arm = obj.data
126     
127     # setup the existing bones
128     mt_chain = bone_class_instance(obj, ["thigh", "shin", "foot", "toe"])
129     mt = bone_class_instance(obj, ["hips", "heel"])
130     #ex = bone_class_instance(obj, [""])
131     ex = bone_class_instance(obj, ["thigh_socket", "thigh_hinge", "foot_roll_1", "foot_roll_2", "foot_roll_3"])
132     # children of ik_foot
133     ik = bone_class_instance(obj, ["foot", "foot_roll", "foot_roll_01", "foot_roll_02", "knee_target"])
134     
135     # XXX - duplicate below
136     for bone_class in (mt, mt_chain):
137         for attr in bone_class.attr_names:
138             i = METARIG_NAMES.index(attr)
139             ebone = arm.edit_bones[bone_definition[i]]
140             setattr(bone_class, attr, ebone.name)
141         bone_class.update()
142     # XXX - end dupe
143
144
145     # Make a new chain, ORG are the original bones renamed.
146     ik_chain = mt_chain.copy(to_prefix="MCH-")
147
148     # simple rename
149     ik_chain.rename("thigh", ik_chain.thigh + "_ik")
150     ik_chain.rename("shin", ik_chain.shin + "_ik")
151
152     # ik foot, no parents
153     base_foot_name = base_names[mt_chain.foot] # whatever the foot is called, use that!, XXX - ORG!
154     ik.foot_e = copy_bone_simple(arm, mt_chain.foot, "%s_ik" % base_foot_name)
155     ik.foot = ik.foot_e.name
156     ik.foot_e.tail.z = ik.foot_e.head.z
157     ik.foot_e.roll = 0.0
158     
159     # heel pointing backwards, half length
160     ik.foot_roll_e = copy_bone_simple(arm, mt.heel, "%s_roll" % base_foot_name)
161     ik.foot_roll = ik.foot_roll_e.name
162     ik.foot_roll_e.tail = ik.foot_roll_e.head + (ik.foot_roll_e.head - ik.foot_roll_e.tail) / 2.0
163     ik.foot_roll_e.parent = ik.foot_e # heel is disconnected
164     
165     # heel pointing forwards to the toe base, parent of the following 2 bones
166     ik.foot_roll_01_e = copy_bone_simple(arm, mt.heel, "MCH-%s_roll.01" % base_foot_name)
167     ik.foot_roll_01 = ik.foot_roll_01_e.name
168     ik.foot_roll_01_e.tail = mt_chain.foot_e.tail
169     ik.foot_roll_01_e.parent = ik.foot_e # heel is disconnected
170
171     # same as above but reverse direction
172     ik.foot_roll_02_e = copy_bone_simple(arm, mt.heel, "MCH-%s_roll.02" % base_foot_name)
173     ik.foot_roll_02 = ik.foot_roll_02_e.name
174     ik.foot_roll_02_e.parent = ik.foot_roll_01_e # heel is disconnected
175     ik.foot_roll_02_e.head = mt_chain.foot_e.tail
176     ik.foot_roll_02_e.tail = mt.heel_e.head
177     
178     del base_foot_name
179     
180     # rename 'MCH-toe' --> to 'toe_ik' and make the child of ik.foot_roll_01
181     # ------------------ FK or IK?
182     ik_chain.rename("toe", base_names[mt_chain.toe] + "_ik")
183     ik_chain.toe_e.connected = False
184     ik_chain.toe_e.parent = ik.foot_roll_01_e
185     
186     # re-parent ik_chain.foot to the 
187     ik_chain.foot_e.connected = False
188     ik_chain.foot_e.parent = ik.foot_roll_02_e
189     
190     
191     # knee target is the heel moved up and forward on its local axis
192     ik.knee_target_e = copy_bone_simple(arm, mt.heel, "knee_target")
193     ik.knee_target = ik.knee_target_e.name
194     offset = ik.knee_target_e.tail - ik.knee_target_e.head
195     offset.z = 0
196     offset.length = mt_chain.shin_e.head.z - mt.heel_e.head.z
197     offset.z += offset.length
198     ik.knee_target_e.translate(offset)
199     ik.knee_target_e.length *= 0.5
200     ik.knee_target_e.parent = ik.foot_e
201
202     # roll the bone to point up... could also point in the same direction as ik.foot_roll
203     # ik.foot_roll_02_e.matrix * Vector(0.0, 0.0, 1.0) # ACK!, no rest matrix in editmode
204     ik.foot_roll_01_e.align((0.0, 0.0, -1.0))
205
206     bpy.ops.object.mode_set(mode='OBJECT')
207
208     ik.update()
209     ex.update()
210     mt_chain.update()
211     ik_chain.update()
212     
213     # IK
214     con = ik_chain.shin_p.constraints.new('IK')
215     con.chain_length = 2
216     con.iterations = 500
217     con.pole_angle = -90.0 # XXX - in deg!
218     con.use_tail = True
219     con.use_stretch = True
220     con.use_target = True
221     con.use_rotation = False
222     con.weight = 1.0
223     
224     con.target = obj
225     con.subtarget = ik.foot
226     
227     con.pole_target = obj
228     con.pole_subtarget = ik.knee_target
229     
230     # foot roll
231     cons = [ \
232         (ik.foot_roll_01_p.constraints.new('COPY_ROTATION'), ik.foot_roll_01_p.constraints.new('LIMIT_ROTATION')), \
233         (ik.foot_roll_02_p.constraints.new('COPY_ROTATION'), ik.foot_roll_02_p.constraints.new('LIMIT_ROTATION'))
234     ]
235     
236     for con, con_l in cons:
237         con.target = obj
238         con.subtarget = ik.foot_roll
239         con.use_x, con.use_y, con.use_z = True, False, False
240         con.target_space = con.owner_space = 'LOCAL'
241         
242         con = con_l
243         con.use_limit_x, con.use_limit_y, con.use_limit_z = True, False, False
244         con.owner_space = 'LOCAL'
245         
246         if con_l is cons[-1][-1]:
247             con.minimum_x = 0.0
248             con.maximum_x = 180.0 # XXX -deg
249         else:
250             con.minimum_x = -180.0 # XXX -deg
251             con.maximum_x = 0.0
252
253     return None, ik_chain.thigh, ik_chain.shin, ik_chain.foot, ik_chain.toe, None
254
255
256 def fk(obj, bone_definition, base_names):
257     from Mathutils import Vector
258     arm = obj.data
259     
260     # these account for all bones in METARIG_NAMES
261     mt_chain = bone_class_instance(obj, ["thigh", "shin", "foot", "toe"])
262     mt = bone_class_instance(obj, ["hips", "heel"])
263     
264     # new bones
265     ex = bone_class_instance(obj, ["thigh_socket", "thigh_hinge"])
266     
267     for bone_class in (mt, mt_chain):
268         for attr in bone_class.attr_names:
269             i = METARIG_NAMES.index(attr)
270             ebone = arm.edit_bones[bone_definition[i]]
271             setattr(bone_class, attr, ebone.name)
272         bone_class.update()
273
274     ex.thigh_socket_e = copy_bone_simple(arm, mt_chain.thigh, "MCH-%s_socket" % base_names[mt_chain.thigh], parent=True)
275     ex.thigh_socket = ex.thigh_socket_e.name
276     ex.thigh_socket_e.tail = ex.thigh_socket_e.head + Vector(0.0, 0.0, ex.thigh_socket_e.length / 4.0)
277
278     ex.thigh_hinge_e = copy_bone_simple(arm, mt_chain.thigh, "MCH-%s_hinge" % base_names[mt_chain.thigh], parent=True)
279     ex.thigh_hinge = ex.thigh_hinge_e.name
280     ex.thigh_hinge_e.tail = ex.thigh_hinge_e.head + Vector(0.0, 0.0, mt_chain.thigh_e.head.length)
281     ex.thigh_hinge_e.translate(Vector(-(mt.hips_e.head.x - mt_chain.thigh_e.head.x), 0.0, 0.0))
282     ex.thigh_hinge_e.length = mt.hips_e.length
283     
284     fk_chain = mt_chain.copy() # fk has no prefix!
285     
286     fk_chain.thigh_e.connected = False
287     fk_chain.thigh_e.parent = ex.thigh_hinge_e
288
289     bpy.ops.object.mode_set(mode='OBJECT')
290
291     ex.update()
292     mt_chain.update()
293     fk_chain.update()
294
295     con = fk_chain.thigh_p.constraints.new('COPY_LOCATION')
296     con.target = obj
297     con.subtarget = ex.thigh_socket
298
299     # hinge
300     prop = rna_idprop_ui_prop_get(fk_chain.thigh_p, "hinge", create=True)
301     fk_chain.thigh_p["hinge"] = 0.5
302     prop["soft_min"] = 0.0
303     prop["soft_max"] = 1.0
304     
305     con = ex.thigh_hinge_p.constraints.new('COPY_ROTATION')
306     con.target = obj
307     con.subtarget = mt.hips
308     
309     # add driver
310     hinge_driver_path = fk_chain.thigh_p.path_to_id() + '["hinge"]'
311     
312     fcurve = con.driver_add("influence", 0)
313     driver = fcurve.driver
314     tar = driver.targets.new()
315     driver.type = 'AVERAGE'
316     tar.name = "var"
317     tar.id_type = 'OBJECT'
318     tar.id = obj
319     tar.rna_path = hinge_driver_path
320
321     mod = fcurve.modifiers[0]
322     mod.poly_order = 1
323     mod.coefficients[0] = 1.0
324     mod.coefficients[1] = -1.0
325     
326     # dont blend the hips or heel
327     return None, fk_chain.thigh, fk_chain.shin, fk_chain.foot, fk_chain.toe, None