enable playing with blenders internal anim player again.
[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 # "Defines"
42 # Keep these in sync with those in ED_keyframing.h!
43 ANIM_KS_LOCATION_ID = "Location"
44 ANIM_KS_ROTATION_ID = "Rotation"
45 ANIM_KS_SCALING_ID = "Scaling"
46 ANIM_KS_LOC_ROT_SCALE_ID = "LocRotScale"
47 ANIM_KS_AVAILABLE_ID = "Available"
48 ANIM_KS_WHOLE_CHARACTER_ID = "WholeCharacter"
49
50
51 # Location
52 class BUILTIN_KSI_Location(KeyingSetInfo):
53     """Insert a keyframe on each of the location channels"""
54     bl_idname = ANIM_KS_LOCATION_ID
55     bl_label = "Location"
56
57     # poll - use predefined callback for selected bones/objects
58     poll = keyingsets_utils.RKS_POLL_selected_items
59
60     # iterator - use callback for selected bones/objects
61     iterator = keyingsets_utils.RKS_ITER_selected_item
62
63     # generator - use callback for location
64     generate = keyingsets_utils.RKS_GEN_location
65
66
67 # Rotation
68 class BUILTIN_KSI_Rotation(KeyingSetInfo):
69     """Insert a keyframe on each of the rotation channels"""
70     bl_idname = ANIM_KS_ROTATION_ID
71     bl_label = "Rotation"
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 rotation
80     generate = keyingsets_utils.RKS_GEN_rotation
81
82
83 # Scale
84 class BUILTIN_KSI_Scaling(KeyingSetInfo):
85     """Insert a keyframe on each of the scale channels"""
86     bl_idname = ANIM_KS_SCALING_ID
87     bl_label = "Scaling"
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 - use callback for scaling
96     generate = keyingsets_utils.RKS_GEN_scaling
97
98 # ------------
99
100
101 # LocRot
102 class BUILTIN_KSI_LocRot(KeyingSetInfo):
103     """Insert a keyframe on each of the location and rotation channels"""
104     bl_label = "LocRot"
105
106     # poll - use predefined callback for selected bones/objects
107     poll = keyingsets_utils.RKS_POLL_selected_items
108
109     # iterator - use callback for selected bones/objects
110     iterator = keyingsets_utils.RKS_ITER_selected_item
111
112     # generator
113     def generate(self, context, ks, data):
114         # location
115         keyingsets_utils.RKS_GEN_location(self, context, ks, data)
116         # rotation
117         keyingsets_utils.RKS_GEN_rotation(self, context, ks, data)
118
119
120 # LocScale
121 class BUILTIN_KSI_LocScale(KeyingSetInfo):
122     """Insert a keyframe on each of the location and scale channels"""
123     bl_label = "LocScale"
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         # scale
136         keyingsets_utils.RKS_GEN_scaling(self, context, ks, data)
137
138
139 # LocRotScale
140 class BUILTIN_KSI_LocRotScale(KeyingSetInfo):
141     """
142     Insert a keyframe on each of the location, rotation, and scale channels
143     """
144     bl_idname = ANIM_KS_LOC_ROT_SCALE_ID
145     bl_label = "LocRotScale"
146
147     # poll - use predefined callback for selected bones/objects
148     poll = keyingsets_utils.RKS_POLL_selected_items
149
150     # iterator - use callback for selected bones/objects
151     iterator = keyingsets_utils.RKS_ITER_selected_item
152
153     # generator
154     def generate(self, context, ks, data):
155         # location
156         keyingsets_utils.RKS_GEN_location(self, context, ks, data)
157         # rotation
158         keyingsets_utils.RKS_GEN_rotation(self, context, ks, data)
159         # scale
160         keyingsets_utils.RKS_GEN_scaling(self, context, ks, data)
161
162
163 # RotScale
164 class BUILTIN_KSI_RotScale(KeyingSetInfo):
165     """Insert a keyframe on each of the rotation and scale channels"""
166     bl_label = "RotScale"
167
168     # poll - use predefined callback for selected bones/objects
169     poll = keyingsets_utils.RKS_POLL_selected_items
170
171     # iterator - use callback for selected bones/objects
172     iterator = keyingsets_utils.RKS_ITER_selected_item
173
174     # generator
175     def generate(self, context, ks, data):
176         # rotation
177         keyingsets_utils.RKS_GEN_rotation(self, context, ks, data)
178         # scaling
179         keyingsets_utils.RKS_GEN_scaling(self, context, ks, data)
180
181 # ------------
182
183
184 # Location
185 class BUILTIN_KSI_VisualLoc(KeyingSetInfo):
186     """
187     Insert a keyframe on each of the location channels, taking into account
188     effects of constraints and relationships
189     """
190     bl_label = "Visual Location"
191
192     bl_options = {'INSERTKEY_VISUAL'}
193
194     # poll - use predefined callback for selected bones/objects
195     poll = keyingsets_utils.RKS_POLL_selected_items
196
197     # iterator - use callback for selected bones/objects
198     iterator = keyingsets_utils.RKS_ITER_selected_item
199
200     # generator - use callback for location
201     generate = keyingsets_utils.RKS_GEN_location
202
203
204 # Rotation
205 class BUILTIN_KSI_VisualRot(KeyingSetInfo):
206     """
207     Insert a keyframe on each of the rotation channels, taking into account
208     effects of constraints and relationships
209     """
210     bl_label = "Visual Rotation"
211
212     bl_options = {'INSERTKEY_VISUAL'}
213
214     # poll - use predefined callback for selected bones/objects
215     poll = keyingsets_utils.RKS_POLL_selected_items
216
217     # iterator - use callback for selected bones/objects
218     iterator = keyingsets_utils.RKS_ITER_selected_item
219
220     # generator - use callback for rotation
221     generate = keyingsets_utils.RKS_GEN_rotation
222
223
224 # VisualLocRot
225 class BUILTIN_KSI_VisualLocRot(KeyingSetInfo):
226     """
227     Insert a keyframe on each of the location and rotation channels,
228     taking into account effects of constraints and relationships
229     """
230     bl_label = "Visual LocRot"
231
232     bl_options = {'INSERTKEY_VISUAL'}
233
234     # poll - use predefined callback for selected bones/objects
235     poll = keyingsets_utils.RKS_POLL_selected_items
236
237     # iterator - use callback for selected bones/objects
238     iterator = keyingsets_utils.RKS_ITER_selected_item
239
240     # generator
241     def generate(self, context, ks, data):
242         # location
243         keyingsets_utils.RKS_GEN_location(self, context, ks, data)
244         # rotation
245         keyingsets_utils.RKS_GEN_rotation(self, context, ks, data)
246
247 # ------------
248
249
250 # Available
251 class BUILTIN_KSI_Available(KeyingSetInfo):
252     """Insert a keyframe on each of the already existing F-Curves"""
253     bl_idname = ANIM_KS_AVAILABLE_ID
254     bl_label = "Available"
255
256     # poll - selected objects or selected object with animation data
257     def poll(ksi, context):
258         ob = context.active_object
259         if ob:
260             # TODO: this fails if one animation-less object is active, but many others are selected
261             return ob.animation_data and ob.animation_data.action
262         else:
263             return bool(context.selected_objects)
264
265     # iterator - use callback for selected bones/objects
266     iterator = keyingsets_utils.RKS_ITER_selected_item
267
268     # generator - use callback for doing this
269     generate = keyingsets_utils.RKS_GEN_available
270
271 ###############################
272
273
274 # All properties that are likely to get animated in a character rig
275 class BUILTIN_KSI_WholeCharacter(KeyingSetInfo):
276     """
277     Insert a keyframe for all properties that are likely to get animated in a
278     character rig (useful when blocking out a shot)
279     """
280     bl_idname = ANIM_KS_WHOLE_CHARACTER_ID
281     bl_label = "Whole Character"
282
283     # these prefixes should be avoided, as they are not really bones
284     # that animators should be touching (or need to touch)
285     badBonePrefixes = (
286         'DEF',
287         'GEO',
288         'MCH',
289         'ORG',
290         'COR',
291         'VIS',
292         # ... more can be added here as you need in your own rigs ...
293     )
294
295     # poll - pose-mode on active object only
296     def poll(ksi, context):
297         return ((context.active_object) and (context.active_object.pose) and
298                 (context.active_object.mode == 'POSE'))
299
300     # iterator - all bones regardless of selection
301     def iterator(ksi, context, ks):
302         for bone in context.active_object.pose.bones:
303             if not bone.name.startswith(BUILTIN_KSI_WholeCharacter.badBonePrefixes):
304                 ksi.generate(context, ks, bone)
305
306     # generator - all unlocked bone transforms + custom properties
307     def generate(ksi, context, ks, bone):
308         # loc, rot, scale - only include unlocked ones
309         ksi.doLoc(ks, bone)
310
311         if bone.rotation_mode in {'QUATERNION', 'AXIS_ANGLE'}:
312             ksi.doRot4d(ks, bone)
313         else:
314             ksi.doRot3d(ks, bone)
315         ksi.doScale(ks, bone)
316
317         # custom props?
318         ksi.doCustomProps(ks, bone)
319
320     # ----------------
321
322     # helper to add some bone's property to the Keying Set
323     def addProp(ksi, ks, bone, prop, index=-1, use_groups=True):
324         # add the property name to the base path
325         id_path = bone.path_from_id()
326         id_block = bone.id_data
327
328         if prop.startswith('['):
329             # custom properties
330             path = id_path + prop
331         else:
332             # standard transforms/properties
333             path = keyingsets_utils.path_add_property(id_path, prop)
334
335         # add Keying Set entry for this...
336         if use_groups:
337             ks.paths.add(id_block, path, index, group_method='NAMED', group_name=bone.name)
338         else:
339             ks.paths.add(id_block, path, index)
340
341     # ----------------
342
343     # location properties
344     def doLoc(ksi, ks, bone):
345         if bone.lock_location == (False, False, False):
346             ksi.addProp(ks, bone, "location")
347         else:
348             for i in range(3):
349                 if not bone.lock_location[i]:
350                     ksi.addProp(ks, bone, "location", i)
351
352     # rotation properties
353     def doRot4d(ksi, ks, bone):
354         # rotation mode affects the property used
355         if bone.rotation_mode == 'QUATERNION':
356             prop = "rotation_quaternion"
357         elif bone.rotation_mode == 'AXIS_ANGLE':
358             prop = "rotation_axis_angle"
359
360         # add rotation properties if they will
361         if bone.lock_rotations_4d:
362             # can check individually
363             if (bone.lock_rotation == (False, False, False)) and (bone.lock_rotation_w == False):
364                 ksi.addProp(ks, bone, prop)
365             else:
366                 if bone.lock_rotation_w == False:
367                     ksi.addProp(ks, bone, prop, 0)  # w = 0
368
369                 for i in range(3):
370                     if not bone.lock_rotation[i]:
371                         ksi.addProp(ks, bone, prop, i + 1)  # i + 1, since here x/y/z = 1,2,3, and w=0
372         elif True not in bone.lock_rotation:
373             # if axis-angle rotations get locked as eulers, then it's too messy to allow anything
374             # other than all open unless we keyframe the whole lot
375             ksi.addProp(ks, bone, prop)
376
377     def doRot3d(ksi, ks, bone):
378         if bone.lock_rotation == (False, False, False):
379             ksi.addProp(ks, bone, "rotation_euler")
380         else:
381             for i in range(3):
382                 if not bone.lock_rotation[i]:
383                     ksi.addProp(ks, bone, "rotation_euler", i)
384
385     # scale properties
386     def doScale(ksi, ks, bone):
387         if bone.lock_scale == (0, 0, 0):
388             ksi.addProp(ks, bone, "scale")
389         else:
390             for i in range(3):
391                 if not bone.lock_scale[i]:
392                     ksi.addProp(ks, bone, "scale", i)
393
394     # ----------------
395
396     # custom properties
397     def doCustomProps(ksi, ks, bone):
398
399         prop_type_compat = {bpy.types.BoolProperty,
400                             bpy.types.IntProperty,
401                             bpy.types.FloatProperty}
402
403         # go over all custom properties for bone
404         for prop in bone.keys():
405             # ignore special "_RNA_UI" used for UI editing
406             if prop == "_RNA_UI":
407                 continue
408
409             # for now, just add all of 'em
410             prop_rna = type(bone).bl_rna.properties.get(prop, None)
411             if prop_rna is None:
412                 prop_path = '["%s"]' % prop
413                 if bone.path_resolve(prop_path, False).rna_type in prop_type_compat:
414                     ksi.addProp(ks, bone, prop_path)
415             elif prop_rna.is_animatable:
416                 ksi.addProp(ks, bone, prop)
417
418
419 ###############################
420
421
422 # Delta Location
423 class BUILTIN_KSI_DeltaLocation(KeyingSetInfo):
424     """Insert keyframes for additional location offset"""
425     bl_label = "Delta Location"
426
427     # poll - selected objects only (and only if active object in object mode)
428     poll = keyingsets_utils.RKS_POLL_selected_objects
429
430     # iterator - selected objects only
431     iterator = keyingsets_utils.RKS_ITER_selected_objects
432
433     # generator - delta location channels only
434     def generate(ksi, context, ks, data):
435         # get id-block and path info
436         id_block, base_path, grouping = keyingsets_utils.get_transform_generators_base_info(data)
437
438         # add the property name to the base path
439         path = keyingsets_utils.path_add_property(base_path, "delta_location")
440
441         # add Keying Set entry for this...
442         if grouping:
443             ks.paths.add(id_block, path, group_method='NAMED', group_name=grouping)
444         else:
445             ks.paths.add(id_block, path)
446
447
448 # Delta Rotation
449 class BUILTIN_KSI_DeltaRotation(KeyingSetInfo):
450     """Insert keyframes for additional rotation offset"""
451     bl_label = "Delta Rotation"
452
453     # poll - selected objects only (and only if active object in object mode)
454     poll = keyingsets_utils.RKS_POLL_selected_objects
455
456     # iterator - selected objects only
457     iterator = keyingsets_utils.RKS_ITER_selected_objects
458
459     # generator - delta location channels only
460     def generate(ksi, context, ks, data):
461         # get id-block and path info
462         id_block, base_path, grouping = keyingsets_utils.get_transform_generators_base_info(data)
463
464         # add the property name to the base path
465         #   rotation mode affects the property used
466         if data.rotation_mode == 'QUATERNION':
467             path = keyingsets_utils.path_add_property(base_path, "delta_rotation_quaternion")
468         elif data.rotation_mode == 'AXIS_ANGLE':
469             # XXX: for now, this is not available yet
470             #path = path_add_property(base_path, "delta_rotation_axis_angle")
471             return
472         else:
473             path = keyingsets_utils.path_add_property(base_path, "delta_rotation_euler")
474
475         # add Keying Set entry for this...
476         if grouping:
477             ks.paths.add(id_block, path, group_method='NAMED', group_name=grouping)
478         else:
479             ks.paths.add(id_block, path)
480
481
482 # Delta Scale
483 class BUILTIN_KSI_DeltaScale(KeyingSetInfo):
484     """Insert keyframes for additional scaling factor"""
485     bl_label = "Delta Scale"
486
487     # poll - selected objects only (and only if active object in object mode)
488     poll = keyingsets_utils.RKS_POLL_selected_objects
489
490     # iterator - selected objects only
491     iterator = keyingsets_utils.RKS_ITER_selected_objects
492
493     # generator - delta location channels only
494     def generate(ksi, context, ks, data):
495         # get id-block and path info
496         id_block, base_path, grouping = keyingsets_utils.get_transform_generators_base_info(data)
497
498         # add the property name to the base path
499         path = keyingsets_utils.path_add_property(base_path, "delta_scale")
500
501         # add Keying Set entry for this...
502         if grouping:
503             ks.paths.add(id_block, path, group_method='NAMED', group_name=grouping)
504         else:
505             ks.paths.add(id_block, path)
506
507 ###############################
508
509
510 def register():
511     bpy.utils.register_module(__name__)
512
513
514 def unregister():
515     bpy.utils.unregister_module(__name__)
516
517
518 if __name__ == "__main__":
519     register()