add_camera_rigs: refactor and cleanup
[blender-addons.git] / animation_animall.py
blob3867155d9391ebdf5ad6c34a29707232bd327083
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 "wiki_url": "https://docs.blender.org/manual/en/dev/addons/"
28 "animation/animall.html",
29 "category": "Animation",
32 """
33 Thanks to Campbell Barton and Joshua Leung for hes API additions and fixes
34 Daniel 'ZanQdo' Salazar
35 """
37 import bpy
38 from bpy.types import (
39 Operator,
40 Panel,
41 AddonPreferences,
43 from bpy.props import (
44 BoolProperty,
45 StringProperty,
47 from bpy.app.handlers import persistent
50 # Property Definitions
51 class AnimallProperties(bpy.types.PropertyGroup):
52 key_selected: BoolProperty(
53 name="Selected Only",
54 description="Insert keyframes only on selected elements",
55 default=True
57 key_shape: BoolProperty(
58 name="Shape",
59 description="Insert keyframes on active Shape Key layer",
60 default=False
62 key_uvs: BoolProperty(
63 name="UVs",
64 description="Insert keyframes on active UV coordinates",
65 default=False
67 key_ebevel: BoolProperty(
68 name="E-Bevel",
69 description="Insert keyframes on edge bevel weight",
70 default=False
72 key_vbevel: BoolProperty(
73 name="V-Bevel",
74 description="Insert keyframes on vertex bevel weight",
75 default=False
77 key_crease: BoolProperty(
78 name="Crease",
79 description="Insert keyframes on edge creases",
80 default=False
82 key_vcols: BoolProperty(
83 name="V-Cols",
84 description="Insert keyframes on active Vertex Color values",
85 default=False
87 key_vgroups: BoolProperty(
88 name="V-groups",
89 description="Insert keyframes on active Vertex group values",
90 default=False
92 key_points: BoolProperty(
93 name="Points",
94 description="Insert keyframes on point locations",
95 default=False
97 key_radius: BoolProperty(
98 name="Radius",
99 description="Insert keyframes on point radius (Shrink/Fatten)",
100 default=False
102 key_tilt: BoolProperty(
103 name="Tilt",
104 description="Insert keyframes on point tilt",
105 default=False
109 # Utility functions
111 def refresh_ui_keyframes():
112 try:
113 for area in bpy.context.screen.areas:
114 if area.type in ('TIMELINE', 'GRAPH_EDITOR', 'DOPESHEET_EDITOR'):
115 area.tag_redraw()
116 except:
117 pass
120 def insert_key(data, key, group=None):
121 try:
122 if group is not None:
123 data.keyframe_insert(key, group=group)
124 else:
125 data.keyframe_insert(key)
126 except:
127 pass
130 def delete_key(data, key):
131 try:
132 data.keyframe_delete(key)
133 except:
134 pass
137 # GUI (Panel)
139 class VIEW3D_PT_animall(Panel):
140 bl_space_type = 'VIEW_3D'
141 bl_region_type = 'UI'
142 bl_category = "Animate"
143 bl_label = 'AnimAll'
144 bl_options = {'DEFAULT_CLOSED'}
146 @classmethod
147 def poll(self, context):
148 return context.active_object and context.active_object.type in {'MESH', 'LATTICE', 'CURVE', 'SURFACE'}
150 def draw(self, context):
151 obj = context.active_object
152 animall_properties = context.window_manager.animall_properties
154 layout = self.layout
155 col = layout.column(align=True)
156 row = col.row()
157 row.prop(animall_properties, "key_selected")
158 col.separator()
160 row = col.row()
162 if obj.type == 'LATTICE':
163 row.prop(animall_properties, "key_points")
164 row.prop(animall_properties, "key_shape")
166 elif obj.type == 'MESH':
167 row.prop(animall_properties, "key_points")
168 row.prop(animall_properties, "key_shape")
169 row = col.row()
170 row.prop(animall_properties, "key_ebevel")
171 row.prop(animall_properties, "key_vbevel")
172 row = col.row()
173 row.prop(animall_properties, "key_crease")
174 row.prop(animall_properties, "key_uvs")
175 row = col.row()
176 row.prop(animall_properties, "key_vcols")
177 row.prop(animall_properties, "key_vgroups")
179 elif obj.type == 'CURVE':
180 row.prop(animall_properties, "key_points")
181 row.prop(animall_properties, "key_shape")
182 row = col.row()
183 row.prop(animall_properties, "key_radius")
184 row.prop(animall_properties, "key_tilt")
186 elif obj.type == 'SURFACE':
187 row.prop(animall_properties, "key_points")
188 row.prop(animall_properties, "key_shape")
189 row = col.row()
190 row.prop(animall_properties, "key_radius")
191 row.prop(animall_properties, "key_tilt")
193 layout.separator()
194 row = layout.row(align=True)
195 row.operator("anim.insert_keyframe_animall", icon="KEY_HLT")
196 row.operator("anim.delete_keyframe_animall", icon="KEY_DEHLT")
197 row = layout.row()
198 row.operator("anim.clear_animation_animall", icon="X")
200 if animall_properties.key_shape:
201 shape_key = obj.active_shape_key
202 shape_key_index = obj.active_shape_key_index
204 split = layout.split()
205 row = split.row()
207 if shape_key_index > 0:
208 row.label(text=shape_key.name, icon="SHAPEKEY_DATA")
209 row.prop(shape_key, "value", text="")
210 row.prop(obj, "show_only_shape_key", text="")
211 if shape_key.value < 1:
212 row = layout.row()
213 row.label(text='Maybe set "%s" to 1.0?' % shape_key.name, icon="INFO")
214 elif shape_key:
215 row.label(text="Cannot key on Basis Shape", icon="ERROR")
216 else:
217 row.label(text="No active Shape Key", icon="ERROR")
219 if animall_properties.key_points and animall_properties.key_shape:
220 row = layout.row()
221 row.label(text='"Points" and "Shape" are redundant?', icon="INFO")
224 class ANIM_OT_insert_keyframe_animall(Operator):
225 bl_label = "Insert"
226 bl_idname = "anim.insert_keyframe_animall"
227 bl_description = "Insert a Keyframe"
228 bl_options = {'REGISTER', 'UNDO'}
230 def execute(op, context):
231 animall_properties = context.window_manager.animall_properties
233 if context.mode == 'OBJECT':
234 objects = context.selected_objects
235 else:
236 objects = context.objects_in_mode_unique_data[:]
238 mode = context.object.mode
240 # Separate loop for lattices, curves and surfaces, since keyframe insertion
241 # has to happen in Edit Mode, otherwise points move back upon mode switch...
242 # (except for curve shape keys)
243 for obj in [o for o in objects if o.type in {'CURVE', 'SURFACE', 'LATTICE'}]:
244 data = obj.data
246 if obj.type == 'LATTICE':
247 if animall_properties.key_shape:
248 if obj.active_shape_key_index > 0:
249 sk_name = obj.active_shape_key.name
250 for p_i, point in enumerate(obj.active_shape_key.data):
251 if not animall_properties.key_selected or data.points[p_i].select:
252 insert_key(point, 'co', group="%s Point %s" % (sk_name, p_i))
254 if animall_properties.key_points:
255 for p_i, point in enumerate(data.points):
256 if not animall_properties.key_selected or point.select:
257 insert_key(point, 'co_deform', group="Point %s" % p_i)
259 else:
260 for s_i, spline in enumerate(data.splines):
261 if spline.type == 'BEZIER':
262 for v_i, CV in enumerate(spline.bezier_points):
263 if (not animall_properties.key_selected
264 or CV.select_control_point
265 or CV.select_left_handle
266 or CV.select_right_handle):
267 if animall_properties.key_points:
268 insert_key(CV, 'co', group="Spline %s CV %s" % (s_i, v_i))
269 insert_key(CV, 'handle_left', group="Spline %s CV %s" % (s_i, v_i))
270 insert_key(CV, 'handle_right', group="Spline %s CV %s" % (s_i, v_i))
272 if animall_properties.key_radius:
273 insert_key(CV, 'radius', group="Spline %s CV %s" % (s_i, v_i))
275 if animall_properties.key_tilt:
276 insert_key(CV, 'tilt', group="Spline %s CV %s" % (s_i, v_i))
278 elif spline.type in ('POLY', 'NURBS'):
279 for v_i, CV in enumerate(spline.points):
280 if not animall_properties.key_selected or CV.select:
281 if animall_properties.key_points:
282 insert_key(CV, 'co', group="Spline %s CV %s" % (s_i, v_i))
284 if animall_properties.key_radius:
285 insert_key(CV, 'radius', group="Spline %s CV %s" % (s_i, v_i))
287 if animall_properties.key_tilt:
288 insert_key(CV, 'tilt', group="Spline %s CV %s" % (s_i, v_i))
290 bpy.ops.object.mode_set(mode='OBJECT')
292 for obj in [o for o in objects if o.type in {'MESH', 'CURVE', 'SURFACE'}]:
293 data = obj.data
294 if obj.type == 'MESH':
295 if animall_properties.key_points:
296 for v_i, vert in enumerate(data.vertices):
297 if not animall_properties.key_selected or vert.select:
298 insert_key(vert, 'co', group="Vertex %s" % v_i)
300 if animall_properties.key_vbevel:
301 for v_i, vert in enumerate(data.vertices):
302 if not animall_properties.key_selected or vert.select:
303 insert_key(vert, 'bevel_weight', group="Vertex %s" % v_i)
305 if animall_properties.key_vgroups:
306 for v_i, vert in enumerate(data.vertices):
307 if not animall_properties.key_selected or vert.select:
308 for group in vert.groups:
309 insert_key(group, 'weight', group="Vertex %s" % v_i)
311 if animall_properties.key_ebevel:
312 for e_i, edge in enumerate(data.edges):
313 if not animall_properties.key_selected or edge.select:
314 insert_key(edge, 'bevel_weight', group="Edge %s" % e_i)
316 if animall_properties.key_crease:
317 for e_i, edge in enumerate(data.edges):
318 if not animall_properties.key_selected or edge.select:
319 insert_key(edge, 'crease', group="Edge %s" % e_i)
321 if animall_properties.key_shape:
322 if obj.active_shape_key_index > 0:
323 sk_name = obj.active_shape_key.name
324 for v_i, vert in enumerate(obj.active_shape_key.data):
325 if not animall_properties.key_selected or data.vertices[v_i].select:
326 insert_key(vert, 'co', group="%s Vertex %s" % (sk_name, v_i))
328 if animall_properties.key_uvs:
329 if data.uv_layers.active is not None:
330 for uv_i, uv in enumerate(data.uv_layers.active.data):
331 if not animall_properties.key_selected or uv.select:
332 insert_key(uv, 'uv', group="UV layer %s" % uv_i)
334 if animall_properties.key_vcols:
335 for v_col_layer in data.vertex_colors:
336 if v_col_layer.active: # only insert in active VCol layer
337 for v_i, data in enumerate(v_col_layer.data):
338 insert_key(data, 'color', group="Loop %s" % v_i)
340 elif obj.type in {'CURVE', 'SURFACE'}:
341 # Shape key keys have to be inserted in object mode for curves...
342 if animall_properties.key_shape:
343 sk_name = obj.active_shape_key.name
344 global_spline_index = 0 # numbering for shape keys, which have flattened indices
345 for s_i, spline in enumerate(data.splines):
346 if spline.type == 'BEZIER':
347 for v_i, CV in enumerate(spline.bezier_points):
348 if (not animall_properties.key_selected
349 or CV.select_control_point
350 or CV.select_left_handle
351 or CV.select_right_handle):
352 if obj.active_shape_key_index > 0:
353 CV = obj.active_shape_key.data[global_spline_index]
354 insert_key(CV, 'co', group="%s Spline %s CV %s" % (sk_name, s_i, v_i))
355 insert_key(CV, 'handle_left', group="%s Spline %s CV %s" % (sk_name, s_i, v_i))
356 insert_key(CV, 'handle_right', group="%s Spline %s CV %s" % (sk_name, s_i, v_i))
357 insert_key(CV, 'radius', group="%s Spline %s CV %s" % (sk_name, s_i, v_i))
358 insert_key(CV, 'tilt', group="%s Spline %s CV %s" % (sk_name, s_i, v_i))
359 global_spline_index += 1
361 elif spline.type in ('POLY', 'NURBS'):
362 for v_i, CV in enumerate(spline.points):
363 if not animall_properties.key_selected or CV.select:
364 if obj.active_shape_key_index > 0:
365 CV = obj.active_shape_key.data[global_spline_index]
366 insert_key(CV, 'co', group="%s Spline %s CV %s" % (sk_name, s_i, v_i))
367 insert_key(CV, 'radius', group="%s Spline %s CV %s" % (sk_name, s_i, v_i))
368 insert_key(CV, 'tilt', group="%s Spline %s CV %s" % (sk_name, s_i, v_i))
369 global_spline_index += 1
371 bpy.ops.object.mode_set(mode=mode)
372 refresh_ui_keyframes()
374 return {'FINISHED'}
377 class ANIM_OT_delete_keyframe_animall(Operator):
378 bl_label = "Delete"
379 bl_idname = "anim.delete_keyframe_animall"
380 bl_description = "Delete a Keyframe"
381 bl_options = {'REGISTER', 'UNDO'}
384 def execute(op, context):
385 animall_properties = context.window_manager.animall_properties
387 if context.mode == 'OBJECT':
388 objects = context.selected_objects
389 else:
390 objects = context.objects_in_mode_unique_data[:]
392 mode = context.object.mode
394 for obj in objects:
395 data = obj.data
396 if obj.type == 'MESH':
397 if animall_properties.key_points:
398 for vert in data.vertices:
399 if not animall_properties.key_selected or vert.select:
400 delete_key(vert, 'co')
402 if animall_properties.key_vbevel:
403 for vert in data.vertices:
404 if not animall_properties.key_selected or vert.select:
405 delete_key(vert, 'bevel_weight')
407 if animall_properties.key_vgroups:
408 for vert in data.vertices:
409 if not animall_properties.key_selected or vert.select:
410 for group in vert.groups:
411 delete_key(group, 'weight')
413 if animall_properties.key_ebevel:
414 for edge in data.edges:
415 if not animall_properties.key_selected or edge.select:
416 delete_key(edge, 'bevel_weight')
418 if animall_properties.key_crease:
419 for edge in data.edges:
420 if not animall_properties.key_selected or vert.select:
421 delete_key(edge, 'crease')
423 if animall_properties.key_shape:
424 if obj.active_shape_key:
425 for v_i, vert in enumerate(obj.active_shape_key.data):
426 if not animall_properties.key_selected or data.vertices[v_i].select:
427 delete_key(vert, 'co')
429 if animall_properties.key_uvs:
430 if data.uv_layers.active is not None:
431 for uv in data.uv_layers.active.data:
432 if not animall_properties.key_selected or uv.select:
433 delete_key(uv, 'uv')
435 if animall_properties.key_vcols:
436 for v_col_layer in data.vertex_colors:
437 if v_col_layer.active: # only delete in active VCol layer
438 for data in v_col_layer.data:
439 delete_key(data, 'color')
441 elif obj.type == 'LATTICE':
442 if animall_properties.key_shape:
443 if obj.active_shape_key:
444 for point in obj.active_shape_key.data:
445 delete_key(point, 'co')
447 if animall_properties.key_points:
448 for point in data.points:
449 if not animall_properties.key_selected or point.select:
450 delete_key(point, 'co_deform')
452 elif obj.type in {'CURVE', 'SURFACE'}:
453 # run this outside the splines loop (only once)
454 if animall_properties.key_shape:
455 if obj.active_shape_key_index > 0:
456 for CV in obj.active_shape_key.data:
457 delete_key(CV, 'co')
458 delete_key(CV, 'handle_left')
459 delete_key(CV, 'handle_right')
461 for spline in data.splines:
462 if spline.type == 'BEZIER':
463 for CV in spline.bezier_points:
464 if (not animall_properties.key_selected
465 or CV.select_control_point
466 or CV.select_left_handle
467 or CV.select_right_handle):
468 if animall_properties.key_points:
469 delete_key(CV, 'co')
470 delete_key(CV, 'handle_left')
471 delete_key(CV, 'handle_right')
472 if animall_properties.key_radius:
473 delete_key(CV, 'radius')
474 if animall_properties.key_tilt:
475 delete_key(CV, 'tilt')
477 elif spline.type in ('POLY', 'NURBS'):
478 for CV in spline.points:
479 if not animall_properties.key_selected or CV.select:
480 if animall_properties.key_points:
481 delete_key(CV, 'co')
482 if animall_properties.key_radius:
483 delete_key(CV, 'radius')
484 if animall_properties.key_tilt:
485 delete_key(CV, 'tilt')
487 refresh_ui_keyframes()
489 return {'FINISHED'}
492 class ANIM_OT_clear_animation_animall(Operator):
493 bl_label = "Clear Animation"
494 bl_idname = "anim.clear_animation_animall"
495 bl_description = ("Delete all keyframes for this object\n"
496 "If in a specific case it doesn't work\n"
497 "try to delete the keys manually")
498 bl_options = {'REGISTER', 'UNDO'}
500 def invoke(self, context, event):
501 wm = context.window_manager
502 return wm.invoke_confirm(self, event)
504 def execute(self, context):
505 if context.mode == 'OBJECT':
506 objects = context.selected_objects
507 else:
508 objects = context.objects_in_mode_unique_data
510 for obj in objects:
511 try:
512 data = obj.data
513 data.animation_data_clear()
514 except:
515 self.report({'WARNING'}, "Clear Animation could not be performed")
516 return {'CANCELLED'}
518 refresh_ui_keyframes()
520 return {'FINISHED'}
523 # Add-ons Preferences Update Panel
525 # Define Panel classes for updating
526 panels = [
527 VIEW3D_PT_animall
531 def update_panel(self, context):
532 message = "AnimAll: Updating Panel locations has failed"
533 try:
534 for panel in panels:
535 if "bl_rna" in panel.__dict__:
536 bpy.utils.unregister_class(panel)
538 for panel in panels:
539 panel.bl_category = context.preferences.addons[__name__].preferences.category
540 bpy.utils.register_class(panel)
542 except Exception as e:
543 print("\n[{}]\n{}\n\nError:\n{}".format(__name__, message, e))
544 pass
547 class AnimallAddonPreferences(AddonPreferences):
548 # this must match the addon name, use '__package__'
549 # when defining this in a submodule of a python package.
550 bl_idname = __name__
552 category: StringProperty(
553 name="Tab Category",
554 description="Choose a name for the category of the panel",
555 default="Animate",
556 update=update_panel
559 def draw(self, context):
560 layout = self.layout
561 row = layout.row()
562 col = row.column()
564 col.label(text="Tab Category:")
565 col.prop(self, "category", text="")
568 def register():
569 bpy.utils.register_class(AnimallProperties)
570 bpy.types.WindowManager.animall_properties = bpy.props.PointerProperty(type=AnimallProperties)
571 bpy.utils.register_class(VIEW3D_PT_animall)
572 bpy.utils.register_class(ANIM_OT_insert_keyframe_animall)
573 bpy.utils.register_class(ANIM_OT_delete_keyframe_animall)
574 bpy.utils.register_class(ANIM_OT_clear_animation_animall)
575 bpy.utils.register_class(AnimallAddonPreferences)
576 update_panel(None, bpy.context)
579 def unregister():
580 del bpy.types.WindowManager.animall_properties
581 bpy.utils.unregister_class(AnimallProperties)
582 bpy.utils.unregister_class(VIEW3D_PT_animall)
583 bpy.utils.unregister_class(ANIM_OT_insert_keyframe_animall)
584 bpy.utils.unregister_class(ANIM_OT_delete_keyframe_animall)
585 bpy.utils.unregister_class(ANIM_OT_clear_animation_animall)
586 bpy.utils.unregister_class(AnimallAddonPreferences)
588 if __name__ == "__main__":
589 register()