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