Fix T77568: turnaround camera crashes undoing
[blender-addons.git] / animation_animall.py
blob141c032a1b2f615abbe3ad4d2bd303558d03a6ba
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # ##### END GPL LICENSE BLOCK #####
19 bl_info = {
20 "name": "AnimAll",
21 "author": "Daniel Salazar <zanqdo@gmail.com>",
22 "version": (0, 8, 3),
23 "blender": (2, 80, 0),
24 "location": "3D View > Toolbox > Animation tab > AnimAll",
25 "description": "Allows animation of mesh, lattice, curve and surface data",
26 "warning": "",
27 "doc_url": "{BLENDER_MANUAL_URL}/addons/animation/animall.html",
28 "category": "Animation",
31 """
32 Thanks to Campbell Barton and Joshua Leung for hes API additions and fixes
33 Daniel 'ZanQdo' Salazar
34 """
36 import bpy
37 from bpy.types import (
38 Operator,
39 Panel,
40 AddonPreferences,
42 from bpy.props import (
43 BoolProperty,
44 StringProperty,
46 from bpy.app.handlers import persistent
49 # Property Definitions
50 class AnimallProperties(bpy.types.PropertyGroup):
51 key_selected: BoolProperty(
52 name="Selected Only",
53 description="Insert keyframes only on selected elements",
54 default=True
56 key_shape: BoolProperty(
57 name="Shape",
58 description="Insert keyframes on active Shape Key layer",
59 default=False
61 key_uvs: BoolProperty(
62 name="UVs",
63 description="Insert keyframes on active UV coordinates",
64 default=False
66 key_ebevel: BoolProperty(
67 name="E-Bevel",
68 description="Insert keyframes on edge bevel weight",
69 default=False
71 key_vbevel: BoolProperty(
72 name="V-Bevel",
73 description="Insert keyframes on vertex bevel weight",
74 default=False
76 key_crease: BoolProperty(
77 name="Crease",
78 description="Insert keyframes on edge creases",
79 default=False
81 key_vcols: BoolProperty(
82 name="V-Cols",
83 description="Insert keyframes on active Vertex Color values",
84 default=False
86 key_vgroups: BoolProperty(
87 name="V-groups",
88 description="Insert keyframes on active Vertex group values",
89 default=False
91 key_points: BoolProperty(
92 name="Points",
93 description="Insert keyframes on point locations",
94 default=False
96 key_radius: BoolProperty(
97 name="Radius",
98 description="Insert keyframes on point radius (Shrink/Fatten)",
99 default=False
101 key_tilt: BoolProperty(
102 name="Tilt",
103 description="Insert keyframes on point tilt",
104 default=False
108 # Utility functions
110 def refresh_ui_keyframes():
111 try:
112 for area in bpy.context.screen.areas:
113 if area.type in ('TIMELINE', 'GRAPH_EDITOR', 'DOPESHEET_EDITOR'):
114 area.tag_redraw()
115 except:
116 pass
119 def insert_key(data, key, group=None):
120 try:
121 if group is not None:
122 data.keyframe_insert(key, group=group)
123 else:
124 data.keyframe_insert(key)
125 except:
126 pass
129 def delete_key(data, key):
130 try:
131 data.keyframe_delete(key)
132 except:
133 pass
136 # GUI (Panel)
138 class VIEW3D_PT_animall(Panel):
139 bl_space_type = 'VIEW_3D'
140 bl_region_type = 'UI'
141 bl_category = "Animate"
142 bl_label = 'AnimAll'
143 bl_options = {'DEFAULT_CLOSED'}
145 @classmethod
146 def poll(self, context):
147 return context.active_object and context.active_object.type in {'MESH', 'LATTICE', 'CURVE', 'SURFACE'}
149 def draw(self, context):
150 obj = context.active_object
151 animall_properties = context.window_manager.animall_properties
153 layout = self.layout
154 col = layout.column(align=True)
155 row = col.row()
156 row.prop(animall_properties, "key_selected")
157 col.separator()
159 row = col.row()
161 if obj.type == 'LATTICE':
162 row.prop(animall_properties, "key_points")
163 row.prop(animall_properties, "key_shape")
165 elif obj.type == 'MESH':
166 row.prop(animall_properties, "key_points")
167 row.prop(animall_properties, "key_shape")
168 row = col.row()
169 row.prop(animall_properties, "key_ebevel")
170 row.prop(animall_properties, "key_vbevel")
171 row = col.row()
172 row.prop(animall_properties, "key_crease")
173 row.prop(animall_properties, "key_uvs")
174 row = col.row()
175 row.prop(animall_properties, "key_vcols")
176 row.prop(animall_properties, "key_vgroups")
178 elif obj.type == 'CURVE':
179 row.prop(animall_properties, "key_points")
180 row.prop(animall_properties, "key_shape")
181 row = col.row()
182 row.prop(animall_properties, "key_radius")
183 row.prop(animall_properties, "key_tilt")
185 elif obj.type == 'SURFACE':
186 row.prop(animall_properties, "key_points")
187 row.prop(animall_properties, "key_shape")
188 row = col.row()
189 row.prop(animall_properties, "key_radius")
190 row.prop(animall_properties, "key_tilt")
192 layout.separator()
193 row = layout.row(align=True)
194 row.operator("anim.insert_keyframe_animall", icon="KEY_HLT")
195 row.operator("anim.delete_keyframe_animall", icon="KEY_DEHLT")
196 row = layout.row()
197 row.operator("anim.clear_animation_animall", icon="X")
199 if animall_properties.key_shape:
200 shape_key = obj.active_shape_key
201 shape_key_index = obj.active_shape_key_index
203 split = layout.split()
204 row = split.row()
206 if shape_key_index > 0:
207 row.label(text=shape_key.name, icon="SHAPEKEY_DATA")
208 row.prop(shape_key, "value", text="")
209 row.prop(obj, "show_only_shape_key", text="")
210 if shape_key.value < 1:
211 row = layout.row()
212 row.label(text='Maybe set "%s" to 1.0?' % shape_key.name, icon="INFO")
213 elif shape_key:
214 row.label(text="Cannot key on Basis Shape", icon="ERROR")
215 else:
216 row.label(text="No active Shape Key", icon="ERROR")
218 if animall_properties.key_points and animall_properties.key_shape:
219 row = layout.row()
220 row.label(text='"Points" and "Shape" are redundant?', icon="INFO")
223 class ANIM_OT_insert_keyframe_animall(Operator):
224 bl_label = "Insert"
225 bl_idname = "anim.insert_keyframe_animall"
226 bl_description = "Insert a Keyframe"
227 bl_options = {'REGISTER', 'UNDO'}
229 def execute(op, context):
230 animall_properties = context.window_manager.animall_properties
232 if context.mode == 'OBJECT':
233 objects = context.selected_objects
234 else:
235 objects = context.objects_in_mode_unique_data[:]
237 mode = context.object.mode
239 # Separate loop for lattices, curves and surfaces, since keyframe insertion
240 # has to happen in Edit Mode, otherwise points move back upon mode switch...
241 # (except for curve shape keys)
242 for obj in [o for o in objects if o.type in {'CURVE', 'SURFACE', 'LATTICE'}]:
243 data = obj.data
245 if obj.type == 'LATTICE':
246 if animall_properties.key_shape:
247 if obj.active_shape_key_index > 0:
248 sk_name = obj.active_shape_key.name
249 for p_i, point in enumerate(obj.active_shape_key.data):
250 if not animall_properties.key_selected or data.points[p_i].select:
251 insert_key(point, 'co', group="%s Point %s" % (sk_name, p_i))
253 if animall_properties.key_points:
254 for p_i, point in enumerate(data.points):
255 if not animall_properties.key_selected or point.select:
256 insert_key(point, 'co_deform', group="Point %s" % p_i)
258 else:
259 for s_i, spline in enumerate(data.splines):
260 if spline.type == 'BEZIER':
261 for v_i, CV in enumerate(spline.bezier_points):
262 if (not animall_properties.key_selected
263 or CV.select_control_point
264 or CV.select_left_handle
265 or CV.select_right_handle):
266 if animall_properties.key_points:
267 insert_key(CV, 'co', group="Spline %s CV %s" % (s_i, v_i))
268 insert_key(CV, 'handle_left', group="Spline %s CV %s" % (s_i, v_i))
269 insert_key(CV, 'handle_right', group="Spline %s CV %s" % (s_i, v_i))
271 if animall_properties.key_radius:
272 insert_key(CV, 'radius', group="Spline %s CV %s" % (s_i, v_i))
274 if animall_properties.key_tilt:
275 insert_key(CV, 'tilt', group="Spline %s CV %s" % (s_i, v_i))
277 elif spline.type in ('POLY', 'NURBS'):
278 for v_i, CV in enumerate(spline.points):
279 if not animall_properties.key_selected or CV.select:
280 if animall_properties.key_points:
281 insert_key(CV, 'co', group="Spline %s CV %s" % (s_i, v_i))
283 if animall_properties.key_radius:
284 insert_key(CV, 'radius', group="Spline %s CV %s" % (s_i, v_i))
286 if animall_properties.key_tilt:
287 insert_key(CV, 'tilt', group="Spline %s CV %s" % (s_i, v_i))
289 bpy.ops.object.mode_set(mode='OBJECT')
291 for obj in [o for o in objects if o.type in {'MESH', 'CURVE', 'SURFACE'}]:
292 data = obj.data
293 if obj.type == 'MESH':
294 if animall_properties.key_points:
295 for v_i, vert in enumerate(data.vertices):
296 if not animall_properties.key_selected or vert.select:
297 insert_key(vert, 'co', group="Vertex %s" % v_i)
299 if animall_properties.key_vbevel:
300 for v_i, vert in enumerate(data.vertices):
301 if not animall_properties.key_selected or vert.select:
302 insert_key(vert, 'bevel_weight', group="Vertex %s" % v_i)
304 if animall_properties.key_vgroups:
305 for v_i, vert in enumerate(data.vertices):
306 if not animall_properties.key_selected or vert.select:
307 for group in vert.groups:
308 insert_key(group, 'weight', group="Vertex %s" % v_i)
310 if animall_properties.key_ebevel:
311 for e_i, edge in enumerate(data.edges):
312 if not animall_properties.key_selected or edge.select:
313 insert_key(edge, 'bevel_weight', group="Edge %s" % e_i)
315 if animall_properties.key_crease:
316 for e_i, edge in enumerate(data.edges):
317 if not animall_properties.key_selected or edge.select:
318 insert_key(edge, 'crease', group="Edge %s" % e_i)
320 if animall_properties.key_shape:
321 if obj.active_shape_key_index > 0:
322 sk_name = obj.active_shape_key.name
323 for v_i, vert in enumerate(obj.active_shape_key.data):
324 if not animall_properties.key_selected or data.vertices[v_i].select:
325 insert_key(vert, 'co', group="%s Vertex %s" % (sk_name, v_i))
327 if animall_properties.key_uvs:
328 if data.uv_layers.active is not None:
329 for uv_i, uv in enumerate(data.uv_layers.active.data):
330 if not animall_properties.key_selected or uv.select:
331 insert_key(uv, 'uv', group="UV layer %s" % uv_i)
333 if animall_properties.key_vcols:
334 for v_col_layer in data.vertex_colors:
335 if v_col_layer.active: # only insert in active VCol layer
336 for v_i, data in enumerate(v_col_layer.data):
337 insert_key(data, 'color', group="Loop %s" % v_i)
339 elif obj.type in {'CURVE', 'SURFACE'}:
340 # Shape key keys have to be inserted in object mode for curves...
341 if animall_properties.key_shape:
342 sk_name = obj.active_shape_key.name
343 global_spline_index = 0 # numbering for shape keys, which have flattened indices
344 for s_i, spline in enumerate(data.splines):
345 if spline.type == 'BEZIER':
346 for v_i, CV in enumerate(spline.bezier_points):
347 if (not animall_properties.key_selected
348 or CV.select_control_point
349 or CV.select_left_handle
350 or CV.select_right_handle):
351 if obj.active_shape_key_index > 0:
352 CV = obj.active_shape_key.data[global_spline_index]
353 insert_key(CV, 'co', group="%s Spline %s CV %s" % (sk_name, s_i, v_i))
354 insert_key(CV, 'handle_left', group="%s Spline %s CV %s" % (sk_name, s_i, v_i))
355 insert_key(CV, 'handle_right', group="%s Spline %s CV %s" % (sk_name, s_i, v_i))
356 insert_key(CV, 'radius', group="%s Spline %s CV %s" % (sk_name, s_i, v_i))
357 insert_key(CV, 'tilt', group="%s Spline %s CV %s" % (sk_name, s_i, v_i))
358 global_spline_index += 1
360 elif spline.type in ('POLY', 'NURBS'):
361 for v_i, CV in enumerate(spline.points):
362 if not animall_properties.key_selected or CV.select:
363 if obj.active_shape_key_index > 0:
364 CV = obj.active_shape_key.data[global_spline_index]
365 insert_key(CV, 'co', group="%s Spline %s CV %s" % (sk_name, s_i, v_i))
366 insert_key(CV, 'radius', group="%s Spline %s CV %s" % (sk_name, s_i, v_i))
367 insert_key(CV, 'tilt', group="%s Spline %s CV %s" % (sk_name, s_i, v_i))
368 global_spline_index += 1
370 bpy.ops.object.mode_set(mode=mode)
371 refresh_ui_keyframes()
373 return {'FINISHED'}
376 class ANIM_OT_delete_keyframe_animall(Operator):
377 bl_label = "Delete"
378 bl_idname = "anim.delete_keyframe_animall"
379 bl_description = "Delete a Keyframe"
380 bl_options = {'REGISTER', 'UNDO'}
383 def execute(op, context):
384 animall_properties = context.window_manager.animall_properties
386 if context.mode == 'OBJECT':
387 objects = context.selected_objects
388 else:
389 objects = context.objects_in_mode_unique_data[:]
391 mode = context.object.mode
393 for obj in objects:
394 data = obj.data
395 if obj.type == 'MESH':
396 if animall_properties.key_points:
397 for vert in data.vertices:
398 if not animall_properties.key_selected or vert.select:
399 delete_key(vert, 'co')
401 if animall_properties.key_vbevel:
402 for vert in data.vertices:
403 if not animall_properties.key_selected or vert.select:
404 delete_key(vert, 'bevel_weight')
406 if animall_properties.key_vgroups:
407 for vert in data.vertices:
408 if not animall_properties.key_selected or vert.select:
409 for group in vert.groups:
410 delete_key(group, 'weight')
412 if animall_properties.key_ebevel:
413 for edge in data.edges:
414 if not animall_properties.key_selected or edge.select:
415 delete_key(edge, 'bevel_weight')
417 if animall_properties.key_crease:
418 for edge in data.edges:
419 if not animall_properties.key_selected or vert.select:
420 delete_key(edge, 'crease')
422 if animall_properties.key_shape:
423 if obj.active_shape_key:
424 for v_i, vert in enumerate(obj.active_shape_key.data):
425 if not animall_properties.key_selected or data.vertices[v_i].select:
426 delete_key(vert, 'co')
428 if animall_properties.key_uvs:
429 if data.uv_layers.active is not None:
430 for uv in data.uv_layers.active.data:
431 if not animall_properties.key_selected or uv.select:
432 delete_key(uv, 'uv')
434 if animall_properties.key_vcols:
435 for v_col_layer in data.vertex_colors:
436 if v_col_layer.active: # only delete in active VCol layer
437 for data in v_col_layer.data:
438 delete_key(data, 'color')
440 elif obj.type == 'LATTICE':
441 if animall_properties.key_shape:
442 if obj.active_shape_key:
443 for point in obj.active_shape_key.data:
444 delete_key(point, 'co')
446 if animall_properties.key_points:
447 for point in data.points:
448 if not animall_properties.key_selected or point.select:
449 delete_key(point, 'co_deform')
451 elif obj.type in {'CURVE', 'SURFACE'}:
452 # run this outside the splines loop (only once)
453 if animall_properties.key_shape:
454 if obj.active_shape_key_index > 0:
455 for CV in obj.active_shape_key.data:
456 delete_key(CV, 'co')
457 delete_key(CV, 'handle_left')
458 delete_key(CV, 'handle_right')
460 for spline in data.splines:
461 if spline.type == 'BEZIER':
462 for CV in spline.bezier_points:
463 if (not animall_properties.key_selected
464 or CV.select_control_point
465 or CV.select_left_handle
466 or CV.select_right_handle):
467 if animall_properties.key_points:
468 delete_key(CV, 'co')
469 delete_key(CV, 'handle_left')
470 delete_key(CV, 'handle_right')
471 if animall_properties.key_radius:
472 delete_key(CV, 'radius')
473 if animall_properties.key_tilt:
474 delete_key(CV, 'tilt')
476 elif spline.type in ('POLY', 'NURBS'):
477 for CV in spline.points:
478 if not animall_properties.key_selected or CV.select:
479 if animall_properties.key_points:
480 delete_key(CV, 'co')
481 if animall_properties.key_radius:
482 delete_key(CV, 'radius')
483 if animall_properties.key_tilt:
484 delete_key(CV, 'tilt')
486 refresh_ui_keyframes()
488 return {'FINISHED'}
491 class ANIM_OT_clear_animation_animall(Operator):
492 bl_label = "Clear Animation"
493 bl_idname = "anim.clear_animation_animall"
494 bl_description = ("Delete all keyframes for this object\n"
495 "If in a specific case it doesn't work\n"
496 "try to delete the keys manually")
497 bl_options = {'REGISTER', 'UNDO'}
499 def invoke(self, context, event):
500 wm = context.window_manager
501 return wm.invoke_confirm(self, event)
503 def execute(self, context):
504 if context.mode == 'OBJECT':
505 objects = context.selected_objects
506 else:
507 objects = context.objects_in_mode_unique_data
509 for obj in objects:
510 try:
511 data = obj.data
512 data.animation_data_clear()
513 except:
514 self.report({'WARNING'}, "Clear Animation could not be performed")
515 return {'CANCELLED'}
517 refresh_ui_keyframes()
519 return {'FINISHED'}
522 # Add-ons Preferences Update Panel
524 # Define Panel classes for updating
525 panels = [
526 VIEW3D_PT_animall
530 def update_panel(self, context):
531 message = "AnimAll: Updating Panel locations has failed"
532 try:
533 for panel in panels:
534 if "bl_rna" in panel.__dict__:
535 bpy.utils.unregister_class(panel)
537 for panel in panels:
538 panel.bl_category = context.preferences.addons[__name__].preferences.category
539 bpy.utils.register_class(panel)
541 except Exception as e:
542 print("\n[{}]\n{}\n\nError:\n{}".format(__name__, message, e))
543 pass
546 class AnimallAddonPreferences(AddonPreferences):
547 # this must match the addon name, use '__package__'
548 # when defining this in a submodule of a python package.
549 bl_idname = __name__
551 category: StringProperty(
552 name="Tab Category",
553 description="Choose a name for the category of the panel",
554 default="Animate",
555 update=update_panel
558 def draw(self, context):
559 layout = self.layout
560 row = layout.row()
561 col = row.column()
563 col.label(text="Tab Category:")
564 col.prop(self, "category", text="")
567 def register():
568 bpy.utils.register_class(AnimallProperties)
569 bpy.types.WindowManager.animall_properties = bpy.props.PointerProperty(type=AnimallProperties)
570 bpy.utils.register_class(VIEW3D_PT_animall)
571 bpy.utils.register_class(ANIM_OT_insert_keyframe_animall)
572 bpy.utils.register_class(ANIM_OT_delete_keyframe_animall)
573 bpy.utils.register_class(ANIM_OT_clear_animation_animall)
574 bpy.utils.register_class(AnimallAddonPreferences)
575 update_panel(None, bpy.context)
578 def unregister():
579 del bpy.types.WindowManager.animall_properties
580 bpy.utils.unregister_class(AnimallProperties)
581 bpy.utils.unregister_class(VIEW3D_PT_animall)
582 bpy.utils.unregister_class(ANIM_OT_insert_keyframe_animall)
583 bpy.utils.unregister_class(ANIM_OT_delete_keyframe_animall)
584 bpy.utils.unregister_class(ANIM_OT_clear_animation_animall)
585 bpy.utils.unregister_class(AnimallAddonPreferences)
587 if __name__ == "__main__":
588 register()