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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 # ##### END GPL LICENSE BLOCK #####
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
23 METARIG_NAMES = "hips", "thigh", "shin", "foot", "toe", "heel"
25 def metarig_template():
26 # generated by rigify.write_meta_rig
27 bpy.ops.object.mode_set(mode='EDIT')
28 obj = bpy.context.object
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
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
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
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
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
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
63 bone.connected = False
64 bone.parent = arm.edit_bones['foot']
66 bpy.ops.object.mode_set(mode='OBJECT')
67 pbone = obj.pose.bones['thigh']
70 def metarig_definition(obj, orig_bone_name):
72 The bone given is the first in a chain
73 Expects a chain of at least 3 children.
75 thigh -> shin -> foot -> [toe, heel]
80 orig_bone = obj.data.bones[orig_bone_name]
81 orig_bone_parent = orig_bone.parent
83 if orig_bone_parent is None:
84 raise Exception("expected the thigh bone to have a parent hip bone")
86 bone_definition.append(orig_bone_parent.name)
87 bone_definition.append(orig_bone.name)
92 while chain < 2: # first 2 bones only have 1 child
93 children = bone.children
95 if len(children) != 1:
96 raise Exception("expected the thigh bone to have 3 children without a fork")
98 bone_definition.append(bone.name) # shin, foot
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")
106 if children[0].connected == children[1].connected:
107 raise Exception("expected one bone to be connected")
111 toe, heel = heel, toe
114 bone_definition.append(toe.name)
115 bone_definition.append(heel.name)
117 if len(bone_definition) != len(METARIG_NAMES):
118 raise Exception("internal problem, expected %d bones" % len(METARIG_NAMES))
120 return bone_definition
123 def ik(obj, bone_definition, base_names):
124 from Mathutils import Vector
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"])
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)
145 # Make a new chain, ORG are the original bones renamed.
146 ik_chain = mt_chain.copy(to_prefix="MCH-")
149 ik_chain.rename("thigh", ik_chain.thigh + "_ik")
150 ik_chain.rename("shin", ik_chain.shin + "_ik")
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
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
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
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
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
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
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
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
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))
206 bpy.ops.object.mode_set(mode='OBJECT')
214 con = ik_chain.shin_p.constraints.new('IK')
217 con.pole_angle = -90.0 # XXX - in deg!
219 con.use_stretch = True
220 con.use_target = True
221 con.use_rotation = False
225 con.subtarget = ik.foot
227 con.pole_target = obj
228 con.pole_subtarget = ik.knee_target
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'))
236 for con, con_l in cons:
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'
243 con.use_limit_x, con.use_limit_y, con.use_limit_z = True, False, False
244 con.owner_space = 'LOCAL'
246 if con_l is cons[-1][-1]:
248 con.maximum_x = 180.0 # XXX -deg
250 con.minimum_x = -180.0 # XXX -deg
253 return None, ik_chain.thigh, ik_chain.shin, ik_chain.foot, ik_chain.toe, None
256 def fk(obj, bone_definition, base_names):
257 from Mathutils import Vector
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"])
265 ex = bone_class_instance(obj, ["thigh_socket", "thigh_hinge"])
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)
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)
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
284 fk_chain = mt_chain.copy() # fk has no prefix!
286 fk_chain.thigh_e.connected = False
287 fk_chain.thigh_e.parent = ex.thigh_hinge_e
289 bpy.ops.object.mode_set(mode='OBJECT')
295 con = fk_chain.thigh_p.constraints.new('COPY_LOCATION')
297 con.subtarget = ex.thigh_socket
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
305 con = ex.thigh_hinge_p.constraints.new('COPY_ROTATION')
307 con.subtarget = mt.hips
310 hinge_driver_path = fk_chain.thigh_p.path_to_id() + '["hinge"]'
312 fcurve = con.driver_add("influence", 0)
313 driver = fcurve.driver
314 tar = driver.targets.new()
315 driver.type = 'AVERAGE'
317 tar.id_type = 'OBJECT'
319 tar.rna_path = hinge_driver_path
321 mod = fcurve.modifiers[0]
323 mod.coefficients[0] = 1.0
324 mod.coefficients[1] = -1.0
326 # dont blend the hips or heel
327 return None, fk_chain.thigh, fk_chain.shin, fk_chain.foot, fk_chain.toe, None