ed656cf224341cad5d30301a96457ce8a2592005
[blender-staging.git] / release / scripts / modules / rigify / palm_curl.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_utils import copy_bone_simple, get_side_name
23 from rna_prop_ui import rna_idprop_ui_prop_get
24
25 # not used, defined for completeness
26 METARIG_NAMES = tuple()
27
28
29 def metarig_template():
30     # generated by rigify.write_meta_rig
31     bpy.ops.object.mode_set(mode='EDIT')
32     obj = bpy.context.active_object
33     arm = obj.data
34     bone = arm.edit_bones.new('hand')
35     bone.head[:] = 0.0004, -0.0629, 0.0000
36     bone.tail[:] = 0.0021, -0.0209, 0.0000
37     bone.roll = 0.0000
38     bone.connected = False
39     bone = arm.edit_bones.new('palm.03')
40     bone.head[:] = -0.0000, 0.0000, 0.0000
41     bone.tail[:] = 0.0025, 0.0644, -0.0065
42     bone.roll = -3.1396
43     bone.connected = False
44     bone.parent = arm.edit_bones['hand']
45     bone = arm.edit_bones.new('palm.02')
46     bone.head[:] = 0.0252, -0.0000, 0.0000
47     bone.tail[:] = 0.0324, 0.0627, -0.0065
48     bone.roll = -3.1357
49     bone.connected = False
50     bone.parent = arm.edit_bones['hand']
51     bone = arm.edit_bones.new('palm.01')
52     bone.head[:] = 0.0504, 0.0000, 0.0000
53     bone.tail[:] = 0.0703, 0.0508, -0.0065
54     bone.roll = -3.1190
55     bone.connected = False
56     bone.parent = arm.edit_bones['hand']
57     bone = arm.edit_bones.new('palm.04')
58     bone.head[:] = -0.0252, 0.0000, 0.0000
59     bone.tail[:] = -0.0286, 0.0606, -0.0065
60     bone.roll = 3.1386
61     bone.connected = False
62     bone.parent = arm.edit_bones['hand']
63     bone = arm.edit_bones.new('palm.05')
64     bone.head[:] = -0.0504, 0.0000, 0.0000
65     bone.tail[:] = -0.0669, 0.0534, -0.0065
66     bone.roll = 3.1239
67     bone.connected = False
68     bone.parent = arm.edit_bones['hand']
69     bone = arm.edit_bones.new('thumb')
70     bone.head[:] = 0.0682, -0.0148, 0.0000
71     bone.tail[:] = 0.1063, 0.0242, -0.0065
72     bone.roll = -3.0929
73     bone.connected = False
74     bone.parent = arm.edit_bones['hand']
75
76     bpy.ops.object.mode_set(mode='OBJECT')
77     pbone = obj.pose.bones['palm.01']
78     pbone['type'] = 'palm_curl'
79
80
81 def metarig_definition(obj, orig_bone_name):
82     '''
83     The bone given is the first in an array of siblings with a matching basename
84     sorted with pointer first, little finger last.
85     eg.
86         [pointer, middle, ring, pinky... ] # any number of fingers
87     '''
88     arm = obj.data
89
90     palm_bone = arm.bones[orig_bone_name]
91     palm_parent = palm_bone.parent
92     palm_base = palm_bone.basename
93     bone_definition = [bone.name for bone in palm_parent.children if bone.basename == palm_base]
94     bone_definition.sort()
95     bone_definition.reverse()
96
97     return [palm_parent.name] + bone_definition
98
99
100 def deform(obj, definitions, base_names, options):
101     for org_bone_name in definitions[1:]:
102         bpy.ops.object.mode_set(mode='EDIT')
103
104         # Create deform bone.
105         bone = copy_bone_simple(obj.data, org_bone_name, "DEF-%s" % base_names[org_bone_name], parent=True)
106
107         # Store name before leaving edit mode
108         bone_name = bone.name
109
110         # Leave edit mode
111         bpy.ops.object.mode_set(mode='OBJECT')
112
113         # Get the pose bone
114         bone = obj.pose.bones[bone_name]
115
116         # Constrain to the original bone
117         # XXX. Todo, is this needed if the bone is connected to its parent?
118         con = bone.constraints.new('COPY_TRANSFORMS')
119         con.name = "copy_loc"
120         con.target = obj
121         con.subtarget = org_bone_name
122
123
124 def main(obj, bone_definition, base_names, options):
125     arm = obj.data
126
127     children = bone_definition[1:]
128
129     # Make a copy of the pinky
130     # simply assume the pinky has the lowest name
131     pinky_ebone = arm.edit_bones[children[0]]
132     ring_ebone = arm.edit_bones[children[1]]
133
134     # FIXME, why split the second one?
135     base_name = base_names[pinky_ebone.name].rsplit('.', 2)[0]
136
137     control_ebone = copy_bone_simple(arm, pinky_ebone.name, base_name + get_side_name(base_names[pinky_ebone.name]), parent=True)
138     control_name = control_ebone.name
139
140     offset = (pinky_ebone.head - ring_ebone.head)
141
142     control_ebone.translate(offset)
143
144     deform(obj, bone_definition, base_names, options)
145
146     bpy.ops.object.mode_set(mode='OBJECT')
147
148     arm = obj.data
149     control_pbone = obj.pose.bones[control_name]
150     pinky_pbone = obj.pose.bones[children[0]]
151
152     control_pbone.rotation_mode = 'YZX'
153     control_pbone.lock_rotation = False, True, True
154     control_pbone.lock_location = True, True, True
155
156     driver_fcurves = pinky_pbone.driver_add("rotation_euler")
157
158
159     controller_path = control_pbone.path_from_id()
160
161     # add custom prop
162     control_pbone["spread"] = 0.0
163     prop = rna_idprop_ui_prop_get(control_pbone, "spread", create=True)
164     prop["soft_min"] = -1.0
165     prop["soft_max"] = 1.0
166     prop["min"] = -1.0
167     prop["max"] = 1.0
168
169
170     # *****
171     driver = driver_fcurves[0].driver
172     driver.type = 'AVERAGE'
173
174     var = driver.variables.new()
175     var.name = "x"
176     var.targets[0].id_type = 'OBJECT'
177     var.targets[0].id = obj
178     var.targets[0].data_path = controller_path + ".rotation_euler[0]"
179
180
181     # *****
182     driver = driver_fcurves[1].driver
183     driver.expression = "-x/4.0"
184
185     var = driver.variables.new()
186     var.name = "x"
187     var.targets[0].id_type = 'OBJECT'
188     var.targets[0].id = obj
189     var.targets[0].data_path = controller_path + ".rotation_euler[0]"
190
191
192     # *****
193     driver = driver_fcurves[2].driver
194     driver.expression = "(1.0-cos(x))-s"
195
196     for fcurve in driver_fcurves:
197         fcurve.modifiers.remove(0) # grr dont need a modifier
198
199     var = driver.variables.new()
200     var.name = "x"
201     var.targets[0].id_type = 'OBJECT'
202     var.targets[0].id = obj
203     var.targets[0].data_path = controller_path + ".rotation_euler[0]"
204
205     var = driver.variables.new()
206     var.name = "s"
207     var.targets[0].id_type = 'OBJECT'
208     var.targets[0].id = obj
209     var.targets[0].data_path = controller_path + '["spread"]'
210
211
212     for i, child_name in enumerate(children):
213         child_pbone = obj.pose.bones[child_name]
214         child_pbone.rotation_mode = 'YZX'
215
216         if child_name != children[-1] and child_name != children[0]:
217
218             # this is somewhat arbitrary but seems to look good
219             inf = i / (len(children) + 1)
220             inf = 1.0 - inf
221             inf = ((inf * inf) + inf) / 2.0
222
223             # used for X/Y constraint
224             inf_minor = inf * inf
225
226             con = child_pbone.constraints.new('COPY_ROTATION')
227             con.name = "Copy Z Rot"
228             con.target = obj
229             con.subtarget = children[0] # also pinky_pbone
230             con.owner_space = con.target_space = 'LOCAL'
231             con.use_x, con.use_y, con.use_z = False, False, True
232             con.influence = inf
233
234             con = child_pbone.constraints.new('COPY_ROTATION')
235             con.name = "Copy XY Rot"
236             con.target = obj
237             con.subtarget = children[0] # also pinky_pbone
238             con.owner_space = con.target_space = 'LOCAL'
239             con.use_x, con.use_y, con.use_z = True, True, False
240             con.influence = inf_minor
241
242
243     child_pbone = obj.pose.bones[children[-1]]
244     child_pbone.rotation_mode = 'QUATERNION'
245
246     # fix at the end since there is some trouble with tx info not being updated otherwise
247     def x_direction():
248         # NOTE: the direction of the Z rotation depends on which side the palm is on.
249         # we could do a simple side-of-x test but better to work out the direction
250         # the hand is facing.
251         from mathutils import Vector
252         from math import degrees
253         child_pbone_01 = obj.pose.bones[children[0]].bone
254         child_pbone_02 = obj.pose.bones[children[1]].bone
255
256         rel_vec = child_pbone_01.head - child_pbone_02.head
257         x_vec = child_pbone_01.matrix.rotation_part() * Vector(1.0, 0.0, 0.0)
258
259         return degrees(rel_vec.angle(x_vec)) > 90.0
260
261     if x_direction(): # flip
262         driver.expression = "-(%s)" % driver.expression
263
264
265     # last step setup layers
266     arm.bones[control_name].layer = list(arm.bones[bone_definition[1]].layer)
267
268
269     # no blending the result of this
270     return None