minor edits, pep8 - also correct float -> double promotion for blf.
[blender.git] / release / scripts / startup / keyingsets_builtins.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 """
22 Built-In Keying Sets
23 None of these Keying Sets should be removed, as these
24 are needed by various parts of Blender in order for them
25 to work correctly.
26
27 Beware also about changing the order that these are defined
28 here, since this can result in old files referring to the
29 wrong Keying Set as the active one, potentially resulting
30 in lost (i.e. unkeyed) animation.
31 """
32
33 import bpy
34 import keyingsets_utils
35 from bpy.types import KeyingSetInfo
36
37 ###############################
38 # Built-In KeyingSets
39
40
41 # Location
42 class BUILTIN_KSI_Location(KeyingSetInfo):
43     bl_label = "Location"
44
45     # poll - use predefined callback for selected bones/objects
46     poll = keyingsets_utils.RKS_POLL_selected_items
47
48     # iterator - use callback for selected bones/objects
49     iterator = keyingsets_utils.RKS_ITER_selected_item
50
51     # generator - use callback for location
52     generate = keyingsets_utils.RKS_GEN_location
53
54
55 # Rotation
56 class BUILTIN_KSI_Rotation(KeyingSetInfo):
57     bl_label = "Rotation"
58
59     # poll - use predefined callback for selected bones/objects
60     poll = keyingsets_utils.RKS_POLL_selected_items
61
62     # iterator - use callback for selected bones/objects
63     iterator = keyingsets_utils.RKS_ITER_selected_item
64
65     # generator - use callback for location
66     generate = keyingsets_utils.RKS_GEN_rotation
67
68
69 # Scale
70 class BUILTIN_KSI_Scaling(KeyingSetInfo):
71     bl_label = "Scaling"
72
73     # poll - use predefined callback for selected bones/objects
74     poll = keyingsets_utils.RKS_POLL_selected_items
75
76     # iterator - use callback for selected bones/objects
77     iterator = keyingsets_utils.RKS_ITER_selected_item
78
79     # generator - use callback for location
80     generate = keyingsets_utils.RKS_GEN_scaling
81
82 # ------------
83
84
85 # LocRot
86 class BUILTIN_KSI_LocRot(KeyingSetInfo):
87     bl_label = "LocRot"
88
89     # poll - use predefined callback for selected bones/objects
90     poll = keyingsets_utils.RKS_POLL_selected_items
91
92     # iterator - use callback for selected bones/objects
93     iterator = keyingsets_utils.RKS_ITER_selected_item
94
95     # generator
96     def generate(self, context, ks, data):
97         # location
98         keyingsets_utils.RKS_GEN_location(self, context, ks, data)
99         # rotation
100         keyingsets_utils.RKS_GEN_rotation(self, context, ks, data)
101
102
103 # LocScale
104 class BUILTIN_KSI_LocScale(KeyingSetInfo):
105     bl_label = "LocScale"
106
107     # poll - use predefined callback for selected bones/objects
108     poll = keyingsets_utils.RKS_POLL_selected_items
109
110     # iterator - use callback for selected bones/objects
111     iterator = keyingsets_utils.RKS_ITER_selected_item
112
113     # generator
114     def generate(self, context, ks, data):
115         # location
116         keyingsets_utils.RKS_GEN_location(self, context, ks, data)
117         # scale
118         keyingsets_utils.RKS_GEN_scaling(self, context, ks, data)
119
120
121 # LocRotScale
122 class BUILTIN_KSI_LocRotScale(KeyingSetInfo):
123     bl_label = "LocRotScale"
124
125     # poll - use predefined callback for selected bones/objects
126     poll = keyingsets_utils.RKS_POLL_selected_items
127
128     # iterator - use callback for selected bones/objects
129     iterator = keyingsets_utils.RKS_ITER_selected_item
130
131     # generator
132     def generate(self, context, ks, data):
133         # location
134         keyingsets_utils.RKS_GEN_location(self, context, ks, data)
135         # rotation
136         keyingsets_utils.RKS_GEN_rotation(self, context, ks, data)
137         # scale
138         keyingsets_utils.RKS_GEN_scaling(self, context, ks, data)
139
140
141 # RotScale
142 class BUILTIN_KSI_RotScale(KeyingSetInfo):
143     bl_label = "RotScale"
144
145     # poll - use predefined callback for selected bones/objects
146     poll = keyingsets_utils.RKS_POLL_selected_items
147
148     # iterator - use callback for selected bones/objects
149     iterator = keyingsets_utils.RKS_ITER_selected_item
150
151     # generator
152     def generate(self, context, ks, data):
153         # rotation
154         keyingsets_utils.RKS_GEN_rotation(self, context, ks, data)
155         # scaling
156         keyingsets_utils.RKS_GEN_scaling(self, context, ks, data)
157
158 # ------------
159
160
161 # Location
162 class BUILTIN_KSI_VisualLoc(KeyingSetInfo):
163     bl_label = "Visual Location"
164
165     bl_options = {'INSERTKEY_VISUAL'}
166
167     # poll - use predefined callback for selected bones/objects
168     poll = keyingsets_utils.RKS_POLL_selected_items
169
170     # iterator - use callback for selected bones/objects
171     iterator = keyingsets_utils.RKS_ITER_selected_item
172
173     # generator - use callback for location
174     generate = keyingsets_utils.RKS_GEN_location
175
176
177 # Rotation
178 class BUILTIN_KSI_VisualRot(KeyingSetInfo):
179     bl_label = "Visual Rotation"
180
181     bl_options = {'INSERTKEY_VISUAL'}
182
183     # poll - use predefined callback for selected bones/objects
184     poll = keyingsets_utils.RKS_POLL_selected_items
185
186     # iterator - use callback for selected bones/objects
187     iterator = keyingsets_utils.RKS_ITER_selected_item
188
189     # generator - use callback for rotation
190     generate = keyingsets_utils.RKS_GEN_rotation
191
192
193 # VisualLocRot
194 class BUILTIN_KSI_VisualLocRot(KeyingSetInfo):
195     bl_label = "Visual LocRot"
196
197     bl_options = {'INSERTKEY_VISUAL'}
198
199     # poll - use predefined callback for selected bones/objects
200     poll = keyingsets_utils.RKS_POLL_selected_items
201
202     # iterator - use callback for selected bones/objects
203     iterator = keyingsets_utils.RKS_ITER_selected_item
204
205     # generator
206     def generate(self, context, ks, data):
207         # location
208         keyingsets_utils.RKS_GEN_location(self, context, ks, data)
209         # rotation
210         keyingsets_utils.RKS_GEN_rotation(self, context, ks, data)
211
212 # ------------
213
214
215 # Available
216 class BUILTIN_KSI_Available(KeyingSetInfo):
217     bl_label = "Available"
218
219     # poll - selected objects or selected object with animation data
220     def poll(ksi, context):
221         ob = context.active_object
222         if ob:
223             # TODO: this fails if one animation-less object is active, but many others are selected
224             return ob.animation_data and ob.animation_data.action
225         else:
226             return bool(context.selected_objects)
227
228     # iterator - use callback for selected bones/objects
229     iterator = keyingsets_utils.RKS_ITER_selected_item
230
231     # generator - use callback for doing this
232     generate = keyingsets_utils.RKS_GEN_available
233
234 ###############################
235
236
237 # All properties that are likely to get animated in a character rig
238 class BUILTIN_KSI_WholeCharacter(KeyingSetInfo):
239     bl_label = "Whole Character"
240
241     # these prefixes should be avoided, as they are not really bones
242     # that animators should be touching (or need to touch)
243     badBonePrefixes = (
244         'DEF',
245         'GEO',
246         'MCH',
247         'ORG',
248         'COR',
249         'VIS',
250         # ... more can be added here as you need in your own rigs ...
251     )
252
253     # poll - pose-mode on active object only
254     def poll(ksi, context):
255         return ((context.active_object) and (context.active_object.pose) and
256                 (context.active_object.mode == 'POSE'))
257
258     # iterator - all bones regardless of selection
259     def iterator(ksi, context, ks):
260         for bone in context.active_object.pose.bones:
261             if not bone.name.startswith(BUILTIN_KSI_WholeCharacter.badBonePrefixes):
262                 ksi.generate(context, ks, bone)
263
264     # generator - all unlocked bone transforms + custom properties
265     def generate(ksi, context, ks, bone):
266         # loc, rot, scale - only include unlocked ones
267         ksi.doLoc(ks, bone)
268
269         if bone.rotation_mode in {'QUATERNION', 'AXIS_ANGLE'}:
270             ksi.doRot4d(ks, bone)
271         else:
272             ksi.doRot3d(ks, bone)
273         ksi.doScale(ks, bone)
274
275         # custom props?
276         ksi.doCustomProps(ks, bone)
277
278     # ----------------
279
280     # helper to add some bone's property to the Keying Set
281     def addProp(ksi, ks, bone, prop, index=-1, use_groups=True):
282         # add the property name to the base path
283         id_path = bone.path_from_id()
284         id_block = bone.id_data
285
286         if prop.startswith('['):
287             # custom properties
288             path = id_path + prop
289         else:
290             # standard transforms/properties
291             path = keyingsets_utils.path_add_property(id_path, prop)
292
293         # add Keying Set entry for this...
294         if use_groups:
295             ks.paths.add(id_block, path, index, group_method='NAMED', group_name=bone.name)
296         else:
297             ks.paths.add(id_block, path, index)
298
299     # ----------------
300
301     # location properties
302     def doLoc(ksi, ks, bone):
303         if bone.lock_location == (False, False, False):
304             ksi.addProp(ks, bone, "location")
305         else:
306             for i in range(3):
307                 if not bone.lock_location[i]:
308                     ksi.addProp(ks, bone, "location", i)
309
310     # rotation properties
311     def doRot4d(ksi, ks, bone):
312         # rotation mode affects the property used
313         if bone.rotation_mode == 'QUATERNION':
314             prop = "rotation_quaternion"
315         elif bone.rotation_mode == 'AXIS_ANGLE':
316             prop = "rotation_axis_angle"
317
318         # add rotation properties if they will
319         if bone.lock_rotations_4d:
320             # can check individually
321             if (bone.lock_rotation == (False, False, False)) and (bone.lock_rotation_w == False):
322                 ksi.addProp(ks, bone, prop)
323             else:
324                 if bone.lock_rotation_w == False:
325                     ksi.addProp(ks, bone, prop, 0)  # w = 0
326
327                 for i in range(3):
328                     if not bone.lock_rotation[i]:
329                         ksi.addProp(ks, bone, prop, i + 1)  # i + 1, since here x,y,z = 1,2,3, and w=0
330         elif True not in bone.lock_rotation:
331             # if axis-angle rotations get locked as eulers, then it's too messy to allow anything
332             # other than all open unless we keyframe the whole lot
333             ksi.addProp(ks, bone, prop)
334
335     def doRot3d(ksi, ks, bone):
336         if bone.lock_rotation == (False, False, False):
337             ksi.addProp(ks, bone, "rotation_euler")
338         else:
339             for i in range(3):
340                 if not bone.lock_rotation[i]:
341                     ksi.addProp(ks, bone, "rotation_euler", i)
342
343     # scale properties
344     def doScale(ksi, ks, bone):
345         if bone.lock_scale == (0, 0, 0):
346             ksi.addProp(ks, bone, "scale")
347         else:
348             for i in range(3):
349                 if not bone.lock_scale[i]:
350                     ksi.addProp(ks, bone, "scale", i)
351
352     # ----------------
353
354     # custom properties
355     def doCustomProps(ksi, ks, bone):
356         # go over all custom properties for bone
357         for prop, val in bone.items():
358             # ignore special "_RNA_UI" used for UI editing
359             if prop == "_RNA_UI":
360                 continue
361
362             # for now, just add all of 'em
363             ksi.addProp(ks, bone, '["%s"]' % (prop))
364
365 ###############################
366
367
368 # Delta Location
369 class BUILTIN_KSI_DeltaLocation(KeyingSetInfo):
370     bl_label = "Delta Location"
371
372     # poll - selected objects only (and only if active object in object mode)
373     poll = keyingsets_utils.RKS_POLL_selected_objects
374
375     # iterator - selected objects only
376     iterator = keyingsets_utils.RKS_ITER_selected_objects
377
378     # generator - delta location channels only
379     def generate(ksi, context, ks, data):
380         # get id-block and path info
381         id_block, base_path, grouping = keyingsets_utils.get_transform_generators_base_info(data)
382
383         # add the property name to the base path
384         path = keyingsets_utils.path_add_property(base_path, "delta_location")
385
386         # add Keying Set entry for this...
387         if grouping:
388             ks.paths.add(id_block, path, group_method='NAMED', group_name=grouping)
389         else:
390             ks.paths.add(id_block, path)
391
392
393 # Delta Rotation
394 class BUILTIN_KSI_DeltaRotation(KeyingSetInfo):
395     bl_label = "Delta Rotation"
396
397     # poll - selected objects only (and only if active object in object mode)
398     poll = keyingsets_utils.RKS_POLL_selected_objects
399
400     # iterator - selected objects only
401     iterator = keyingsets_utils.RKS_ITER_selected_objects
402
403     # generator - delta location channels only
404     def generate(ksi, context, ks, data):
405         # get id-block and path info
406         id_block, base_path, grouping = keyingsets_utils.get_transform_generators_base_info(data)
407
408         # add the property name to the base path
409         #   rotation mode affects the property used
410         if data.rotation_mode == 'QUATERNION':
411             path = keyingsets_utils.path_add_property(base_path, "delta_rotation_quaternion")
412         elif data.rotation_mode == 'AXIS_ANGLE':
413             # XXX: for now, this is not available yet
414             #path = path_add_property(base_path, "delta_rotation_axis_angle")
415             return
416         else:
417             path = keyingsets_utils.path_add_property(base_path, "delta_rotation_euler")
418
419         # add Keying Set entry for this...
420         if grouping:
421             ks.paths.add(id_block, path, group_method='NAMED', group_name=grouping)
422         else:
423             ks.paths.add(id_block, path)
424
425
426 # Delta Scale
427 class BUILTIN_KSI_DeltaScale(KeyingSetInfo):
428     bl_label = "Delta Scale"
429
430     # poll - selected objects only (and only if active object in object mode)
431     poll = keyingsets_utils.RKS_POLL_selected_objects
432
433     # iterator - selected objects only
434     iterator = keyingsets_utils.RKS_ITER_selected_objects
435
436     # generator - delta location channels only
437     def generate(ksi, context, ks, data):
438         # get id-block and path info
439         id_block, base_path, grouping = keyingsets_utils.get_transform_generators_base_info(data)
440
441         # add the property name to the base path
442         path = keyingsets_utils.path_add_property(base_path, "delta_scale")
443
444         # add Keying Set entry for this...
445         if grouping:
446             ks.paths.add(id_block, path, group_method='NAMED', group_name=grouping)
447         else:
448             ks.paths.add(id_block, path)
449
450 ###############################
451
452
453 def register():
454     bpy.utils.register_module(__name__)
455
456
457 def unregister():
458     bpy.utils.unregister_module(__name__)
459
460
461 if __name__ == "__main__":
462     register()