Cleanup: trailing space
[blender-addons.git] / animation_animall.py
blob309141a95101fb30d31f19aa8ab3d0b5a7ed21b2
1 # SPDX-License-Identifier: GPL-2.0-or-later
3 bl_info = {
4 "name": "AnimAll",
5 "author": "Daniel Salazar <zanqdo@gmail.com>",
6 "version": (0, 8, 3),
7 "blender": (2, 80, 0),
8 "location": "3D View > Toolbox > Animation tab > AnimAll",
9 "description": "Allows animation of mesh, lattice, curve and surface data",
10 "warning": "",
11 "doc_url": "{BLENDER_MANUAL_URL}/addons/animation/animall.html",
12 "category": "Animation",
15 """
16 Thanks to Campbell Barton and Joshua Leung for hes API additions and fixes
17 Daniel 'ZanQdo' Salazar
18 """
20 import bpy
21 from bpy.types import (
22 Operator,
23 Panel,
24 AddonPreferences,
26 from bpy.props import (
27 BoolProperty,
28 StringProperty,
30 from bpy.app.handlers import persistent
33 # Property Definitions
34 class AnimallProperties(bpy.types.PropertyGroup):
35 key_selected: BoolProperty(
36 name="Selected Only",
37 description="Insert keyframes only on selected elements",
38 default=True
40 key_shape: BoolProperty(
41 name="Shape",
42 description="Insert keyframes on active Shape Key layer",
43 default=False
45 key_uvs: BoolProperty(
46 name="UVs",
47 description="Insert keyframes on active UV coordinates",
48 default=False
50 key_ebevel: BoolProperty(
51 name="E-Bevel",
52 description="Insert keyframes on edge bevel weight",
53 default=False
55 key_vbevel: BoolProperty(
56 name="V-Bevel",
57 description="Insert keyframes on vertex bevel weight",
58 default=False
60 key_crease: BoolProperty(
61 name="Crease",
62 description="Insert keyframes on edge creases",
63 default=False
65 key_vcols: BoolProperty(
66 name="V-Cols",
67 description="Insert keyframes on active Vertex Color values",
68 default=False
70 key_vgroups: BoolProperty(
71 name="V-groups",
72 description="Insert keyframes on active Vertex group values",
73 default=False
75 key_points: BoolProperty(
76 name="Points",
77 description="Insert keyframes on point locations",
78 default=False
80 key_handle_type: BoolProperty(
81 name="Handle Types",
82 description="Insert keyframes on Bezier point types",
83 default=False
85 key_radius: BoolProperty(
86 name="Radius",
87 description="Insert keyframes on point radius (Shrink/Fatten)",
88 default=False
90 key_tilt: BoolProperty(
91 name="Tilt",
92 description="Insert keyframes on point tilt",
93 default=False
97 # Utility functions
99 def refresh_ui_keyframes():
100 try:
101 for area in bpy.context.screen.areas:
102 if area.type in ('TIMELINE', 'GRAPH_EDITOR', 'DOPESHEET_EDITOR'):
103 area.tag_redraw()
104 except:
105 pass
108 def insert_key(data, key, group=None):
109 try:
110 if group is not None:
111 data.keyframe_insert(key, group=group)
112 else:
113 data.keyframe_insert(key)
114 except:
115 pass
118 def delete_key(data, key):
119 try:
120 data.keyframe_delete(key)
121 except:
122 pass
125 # GUI (Panel)
127 class VIEW3D_PT_animall(Panel):
128 bl_space_type = 'VIEW_3D'
129 bl_region_type = 'UI'
130 bl_category = "Animate"
131 bl_label = 'AnimAll'
132 bl_options = {'DEFAULT_CLOSED'}
134 @classmethod
135 def poll(self, context):
136 return context.active_object and context.active_object.type in {'MESH', 'LATTICE', 'CURVE', 'SURFACE'}
138 def draw(self, context):
139 obj = context.active_object
140 animall_properties = context.window_manager.animall_properties
142 layout = self.layout
143 col = layout.column(align=True)
144 row = col.row()
145 row.prop(animall_properties, "key_selected")
146 col.separator()
148 row = col.row()
150 if obj.type == 'LATTICE':
151 row.prop(animall_properties, "key_points")
152 row.prop(animall_properties, "key_shape")
154 elif obj.type == 'MESH':
155 row.prop(animall_properties, "key_points")
156 row.prop(animall_properties, "key_shape")
157 row = col.row()
158 row.prop(animall_properties, "key_ebevel")
159 row.prop(animall_properties, "key_vbevel")
160 row = col.row()
161 row.prop(animall_properties, "key_crease")
162 row.prop(animall_properties, "key_uvs")
163 row = col.row()
164 row.prop(animall_properties, "key_vcols")
165 row.prop(animall_properties, "key_vgroups")
167 elif obj.type == 'CURVE':
168 row.prop(animall_properties, "key_points")
169 row.prop(animall_properties, "key_shape")
170 row = col.row()
171 row.prop(animall_properties, "key_radius")
172 row.prop(animall_properties, "key_tilt")
173 row = col.row()
174 row.prop(animall_properties, "key_handle_type")
176 elif obj.type == 'SURFACE':
177 row.prop(animall_properties, "key_points")
178 row.prop(animall_properties, "key_shape")
179 row = col.row()
180 row.prop(animall_properties, "key_radius")
181 row.prop(animall_properties, "key_tilt")
183 layout.separator()
184 row = layout.row(align=True)
185 row.operator("anim.insert_keyframe_animall", icon="KEY_HLT")
186 row.operator("anim.delete_keyframe_animall", icon="KEY_DEHLT")
187 row = layout.row()
188 row.operator("anim.clear_animation_animall", icon="X")
190 if animall_properties.key_shape:
191 shape_key = obj.active_shape_key
192 shape_key_index = obj.active_shape_key_index
194 split = layout.split()
195 row = split.row()
197 if shape_key_index > 0:
198 row.label(text=shape_key.name, icon="SHAPEKEY_DATA")
199 row.prop(shape_key, "value", text="")
200 row.prop(obj, "show_only_shape_key", text="")
201 if shape_key.value < 1:
202 row = layout.row()
203 row.label(text='Maybe set "%s" to 1.0?' % shape_key.name, icon="INFO")
204 elif shape_key:
205 row.label(text="Cannot key on Basis Shape", icon="ERROR")
206 else:
207 row.label(text="No active Shape Key", icon="ERROR")
209 if animall_properties.key_points and animall_properties.key_shape:
210 row = layout.row()
211 row.label(text='"Points" and "Shape" are redundant?', icon="INFO")
214 class ANIM_OT_insert_keyframe_animall(Operator):
215 bl_label = "Insert"
216 bl_idname = "anim.insert_keyframe_animall"
217 bl_description = "Insert a Keyframe"
218 bl_options = {'REGISTER', 'UNDO'}
220 def execute(op, context):
221 animall_properties = context.window_manager.animall_properties
223 if context.mode == 'OBJECT':
224 objects = context.selected_objects
225 else:
226 objects = context.objects_in_mode_unique_data[:]
228 mode = context.object.mode
230 # Separate loop for lattices, curves and surfaces, since keyframe insertion
231 # has to happen in Edit Mode, otherwise points move back upon mode switch...
232 # (except for curve shape keys)
233 for obj in [o for o in objects if o.type in {'CURVE', 'SURFACE', 'LATTICE'}]:
234 data = obj.data
236 if obj.type == 'LATTICE':
237 if animall_properties.key_shape:
238 if obj.active_shape_key_index > 0:
239 sk_name = obj.active_shape_key.name
240 for p_i, point in enumerate(obj.active_shape_key.data):
241 if not animall_properties.key_selected or data.points[p_i].select:
242 insert_key(point, 'co', group="%s Point %s" % (sk_name, p_i))
244 if animall_properties.key_points:
245 for p_i, point in enumerate(data.points):
246 if not animall_properties.key_selected or point.select:
247 insert_key(point, 'co_deform', group="Point %s" % p_i)
249 else:
250 for s_i, spline in enumerate(data.splines):
251 if spline.type == 'BEZIER':
252 for v_i, CV in enumerate(spline.bezier_points):
253 if (not animall_properties.key_selected
254 or CV.select_control_point
255 or CV.select_left_handle
256 or CV.select_right_handle):
257 if animall_properties.key_points:
258 insert_key(CV, 'co', group="Spline %s CV %s" % (s_i, v_i))
259 insert_key(CV, 'handle_left', group="Spline %s CV %s" % (s_i, v_i))
260 insert_key(CV, 'handle_right', group="Spline %s CV %s" % (s_i, v_i))
262 if animall_properties.key_radius:
263 insert_key(CV, 'radius', group="Spline %s CV %s" % (s_i, v_i))
265 if animall_properties.key_handle_type:
266 insert_key(CV, 'handle_left_type', group="spline %s CV %s" % (s_i, v_i))
267 insert_key(CV, 'handle_right_type', group="spline %s CV %s" % (s_i, v_i))
269 if animall_properties.key_tilt:
270 insert_key(CV, 'tilt', group="Spline %s CV %s" % (s_i, v_i))
272 elif spline.type in ('POLY', 'NURBS'):
273 for v_i, CV in enumerate(spline.points):
274 if not animall_properties.key_selected or CV.select:
275 if animall_properties.key_points:
276 insert_key(CV, 'co', group="Spline %s CV %s" % (s_i, v_i))
278 if animall_properties.key_radius:
279 insert_key(CV, 'radius', group="Spline %s CV %s" % (s_i, v_i))
281 if animall_properties.key_tilt:
282 insert_key(CV, 'tilt', group="Spline %s CV %s" % (s_i, v_i))
284 bpy.ops.object.mode_set(mode='OBJECT')
286 for obj in [o for o in objects if o.type in {'MESH', 'CURVE', 'SURFACE'}]:
287 data = obj.data
288 if obj.type == 'MESH':
289 if animall_properties.key_points:
290 for v_i, vert in enumerate(data.vertices):
291 if not animall_properties.key_selected or vert.select:
292 insert_key(vert, 'co', group="Vertex %s" % v_i)
294 if animall_properties.key_vbevel:
295 for v_i, vert in enumerate(data.vertices):
296 if not animall_properties.key_selected or vert.select:
297 insert_key(vert, 'bevel_weight', group="Vertex %s" % v_i)
299 if animall_properties.key_vgroups:
300 for v_i, vert in enumerate(data.vertices):
301 if not animall_properties.key_selected or vert.select:
302 for group in vert.groups:
303 insert_key(group, 'weight', group="Vertex %s" % v_i)
305 if animall_properties.key_ebevel:
306 for e_i, edge in enumerate(data.edges):
307 if not animall_properties.key_selected or edge.select:
308 insert_key(edge, 'bevel_weight', group="Edge %s" % e_i)
310 if animall_properties.key_crease:
311 for e_i, edge in enumerate(data.edges):
312 if not animall_properties.key_selected or edge.select:
313 insert_key(edge, 'crease', group="Edge %s" % e_i)
315 if animall_properties.key_shape:
316 if obj.active_shape_key_index > 0:
317 sk_name = obj.active_shape_key.name
318 for v_i, vert in enumerate(obj.active_shape_key.data):
319 if not animall_properties.key_selected or data.vertices[v_i].select:
320 insert_key(vert, 'co', group="%s Vertex %s" % (sk_name, v_i))
322 if animall_properties.key_uvs:
323 if data.uv_layers.active is not None:
324 for uv_i, uv in enumerate(data.uv_layers.active.data):
325 if not animall_properties.key_selected or uv.select:
326 insert_key(uv, 'uv', group="UV layer %s" % uv_i)
328 if animall_properties.key_vcols:
329 for v_col_layer in data.vertex_colors:
330 if v_col_layer.active: # only insert in active VCol layer
331 for v_i, data in enumerate(v_col_layer.data):
332 insert_key(data, 'color', group="Loop %s" % v_i)
334 elif obj.type in {'CURVE', 'SURFACE'}:
335 # Shape key keys have to be inserted in object mode for curves...
336 if animall_properties.key_shape:
337 sk_name = obj.active_shape_key.name
338 global_spline_index = 0 # numbering for shape keys, which have flattened indices
339 for s_i, spline in enumerate(data.splines):
340 if spline.type == 'BEZIER':
341 for v_i, CV in enumerate(spline.bezier_points):
342 if (not animall_properties.key_selected
343 or CV.select_control_point
344 or CV.select_left_handle
345 or CV.select_right_handle):
346 if obj.active_shape_key_index > 0:
347 CV = obj.active_shape_key.data[global_spline_index]
348 insert_key(CV, 'co', group="%s Spline %s CV %s" % (sk_name, s_i, v_i))
349 insert_key(CV, 'handle_left', group="%s Spline %s CV %s" % (sk_name, s_i, v_i))
350 insert_key(CV, 'handle_right', group="%s Spline %s CV %s" % (sk_name, s_i, v_i))
351 insert_key(CV, 'radius', group="%s Spline %s CV %s" % (sk_name, s_i, v_i))
352 insert_key(CV, 'tilt', group="%s Spline %s CV %s" % (sk_name, s_i, v_i))
353 global_spline_index += 1
355 elif spline.type in ('POLY', 'NURBS'):
356 for v_i, CV in enumerate(spline.points):
357 if not animall_properties.key_selected or CV.select:
358 if obj.active_shape_key_index > 0:
359 CV = obj.active_shape_key.data[global_spline_index]
360 insert_key(CV, 'co', group="%s Spline %s CV %s" % (sk_name, s_i, v_i))
361 insert_key(CV, 'radius', group="%s Spline %s CV %s" % (sk_name, s_i, v_i))
362 insert_key(CV, 'tilt', group="%s Spline %s CV %s" % (sk_name, s_i, v_i))
363 global_spline_index += 1
365 bpy.ops.object.mode_set(mode=mode)
366 refresh_ui_keyframes()
368 return {'FINISHED'}
371 class ANIM_OT_delete_keyframe_animall(Operator):
372 bl_label = "Delete"
373 bl_idname = "anim.delete_keyframe_animall"
374 bl_description = "Delete a Keyframe"
375 bl_options = {'REGISTER', 'UNDO'}
378 def execute(op, context):
379 animall_properties = context.window_manager.animall_properties
381 if context.mode == 'OBJECT':
382 objects = context.selected_objects
383 else:
384 objects = context.objects_in_mode_unique_data[:]
386 mode = context.object.mode
388 for obj in objects:
389 data = obj.data
390 if obj.type == 'MESH':
391 if animall_properties.key_points:
392 for vert in data.vertices:
393 if not animall_properties.key_selected or vert.select:
394 delete_key(vert, 'co')
396 if animall_properties.key_vbevel:
397 for vert in data.vertices:
398 if not animall_properties.key_selected or vert.select:
399 delete_key(vert, 'bevel_weight')
401 if animall_properties.key_vgroups:
402 for vert in data.vertices:
403 if not animall_properties.key_selected or vert.select:
404 for group in vert.groups:
405 delete_key(group, 'weight')
407 if animall_properties.key_ebevel:
408 for edge in data.edges:
409 if not animall_properties.key_selected or edge.select:
410 delete_key(edge, 'bevel_weight')
412 if animall_properties.key_crease:
413 for edge in data.edges:
414 if not animall_properties.key_selected or vert.select:
415 delete_key(edge, 'crease')
417 if animall_properties.key_shape:
418 if obj.active_shape_key:
419 for v_i, vert in enumerate(obj.active_shape_key.data):
420 if not animall_properties.key_selected or data.vertices[v_i].select:
421 delete_key(vert, 'co')
423 if animall_properties.key_uvs:
424 if data.uv_layers.active is not None:
425 for uv in data.uv_layers.active.data:
426 if not animall_properties.key_selected or uv.select:
427 delete_key(uv, 'uv')
429 if animall_properties.key_vcols:
430 for v_col_layer in data.vertex_colors:
431 if v_col_layer.active: # only delete in active VCol layer
432 for data in v_col_layer.data:
433 delete_key(data, 'color')
435 elif obj.type == 'LATTICE':
436 if animall_properties.key_shape:
437 if obj.active_shape_key:
438 for point in obj.active_shape_key.data:
439 delete_key(point, 'co')
441 if animall_properties.key_points:
442 for point in data.points:
443 if not animall_properties.key_selected or point.select:
444 delete_key(point, 'co_deform')
446 elif obj.type in {'CURVE', 'SURFACE'}:
447 # run this outside the splines loop (only once)
448 if animall_properties.key_shape:
449 if obj.active_shape_key_index > 0:
450 for CV in obj.active_shape_key.data:
451 delete_key(CV, 'co')
452 delete_key(CV, 'handle_left')
453 delete_key(CV, 'handle_right')
455 for spline in data.splines:
456 if spline.type == 'BEZIER':
457 for CV in spline.bezier_points:
458 if (not animall_properties.key_selected
459 or CV.select_control_point
460 or CV.select_left_handle
461 or CV.select_right_handle):
462 if animall_properties.key_points:
463 delete_key(CV, 'co')
464 delete_key(CV, 'handle_left')
465 delete_key(CV, 'handle_right')
466 if animall_properties.key_handle_type:
467 delete_key(CV, 'handle_left_type')
468 delete_key(CV, 'handle_right_type')
469 if animall_properties.key_radius:
470 delete_key(CV, 'radius')
471 if animall_properties.key_tilt:
472 delete_key(CV, 'tilt')
474 elif spline.type in ('POLY', 'NURBS'):
475 for CV in spline.points:
476 if not animall_properties.key_selected or CV.select:
477 if animall_properties.key_points:
478 delete_key(CV, 'co')
479 if animall_properties.key_radius:
480 delete_key(CV, 'radius')
481 if animall_properties.key_tilt:
482 delete_key(CV, 'tilt')
484 refresh_ui_keyframes()
486 return {'FINISHED'}
489 class ANIM_OT_clear_animation_animall(Operator):
490 bl_label = "Clear Animation"
491 bl_idname = "anim.clear_animation_animall"
492 bl_description = ("Delete all keyframes for this object\n"
493 "If in a specific case it doesn't work\n"
494 "try to delete the keys manually")
495 bl_options = {'REGISTER', 'UNDO'}
497 def invoke(self, context, event):
498 wm = context.window_manager
499 return wm.invoke_confirm(self, event)
501 def execute(self, context):
502 if context.mode == 'OBJECT':
503 objects = context.selected_objects
504 else:
505 objects = context.objects_in_mode_unique_data
507 for obj in objects:
508 try:
509 data = obj.data
510 data.animation_data_clear()
511 except:
512 self.report({'WARNING'}, "Clear Animation could not be performed")
513 return {'CANCELLED'}
515 refresh_ui_keyframes()
517 return {'FINISHED'}
520 # Add-ons Preferences Update Panel
522 # Define Panel classes for updating
523 panels = [
524 VIEW3D_PT_animall
528 def update_panel(self, context):
529 message = "AnimAll: Updating Panel locations has failed"
530 try:
531 for panel in panels:
532 if "bl_rna" in panel.__dict__:
533 bpy.utils.unregister_class(panel)
535 for panel in panels:
536 panel.bl_category = context.preferences.addons[__name__].preferences.category
537 bpy.utils.register_class(panel)
539 except Exception as e:
540 print("\n[{}]\n{}\n\nError:\n{}".format(__name__, message, e))
541 pass
544 class AnimallAddonPreferences(AddonPreferences):
545 # this must match the addon name, use '__package__'
546 # when defining this in a submodule of a python package.
547 bl_idname = __name__
549 category: StringProperty(
550 name="Tab Category",
551 description="Choose a name for the category of the panel",
552 default="Animate",
553 update=update_panel
556 def draw(self, context):
557 layout = self.layout
558 row = layout.row()
559 col = row.column()
561 col.label(text="Tab Category:")
562 col.prop(self, "category", text="")
565 def register():
566 bpy.utils.register_class(AnimallProperties)
567 bpy.types.WindowManager.animall_properties = bpy.props.PointerProperty(type=AnimallProperties)
568 bpy.utils.register_class(VIEW3D_PT_animall)
569 bpy.utils.register_class(ANIM_OT_insert_keyframe_animall)
570 bpy.utils.register_class(ANIM_OT_delete_keyframe_animall)
571 bpy.utils.register_class(ANIM_OT_clear_animation_animall)
572 bpy.utils.register_class(AnimallAddonPreferences)
573 update_panel(None, bpy.context)
576 def unregister():
577 del bpy.types.WindowManager.animall_properties
578 bpy.utils.unregister_class(AnimallProperties)
579 bpy.utils.unregister_class(VIEW3D_PT_animall)
580 bpy.utils.unregister_class(ANIM_OT_insert_keyframe_animall)
581 bpy.utils.unregister_class(ANIM_OT_delete_keyframe_animall)
582 bpy.utils.unregister_class(ANIM_OT_clear_animation_animall)
583 bpy.utils.unregister_class(AnimallAddonPreferences)
585 if __name__ == "__main__":
586 register()