animation_animall: workaround for data refresh: T68332 T68666
[blender-addons.git] / animation_animall.py
blobf919bbb959b552d8f75151062458442791a4076b
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://wiki.blender.org/index.php/Extensions:2.6/Py/"
28 "Scripts/Animation/AnimAll",
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 = "Animation"
143 bl_label = 'AnimAll'
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="Animation",
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 @persistent
568 def animall_update_handler(scene):
569 '''Force data refresh on frame change.
570 To be removed when T68666 is fixed, probably.'''
571 for obj in scene.objects:
572 if obj.data is not None:
573 obj.update_tag(refresh={'DATA'})
576 def register():
577 bpy.utils.register_class(AnimallProperties)
578 bpy.types.WindowManager.animall_properties = bpy.props.PointerProperty(type=AnimallProperties)
579 bpy.utils.register_class(VIEW3D_PT_animall)
580 bpy.utils.register_class(ANIM_OT_insert_keyframe_animall)
581 bpy.utils.register_class(ANIM_OT_delete_keyframe_animall)
582 bpy.utils.register_class(ANIM_OT_clear_animation_animall)
583 bpy.utils.register_class(AnimallAddonPreferences)
584 update_panel(None, bpy.context)
585 bpy.app.handlers.frame_change_post.append(animall_update_handler)
588 def unregister():
589 del bpy.types.WindowManager.animall_properties
590 bpy.utils.unregister_class(AnimallProperties)
591 bpy.utils.unregister_class(VIEW3D_PT_animall)
592 bpy.utils.unregister_class(ANIM_OT_insert_keyframe_animall)
593 bpy.utils.unregister_class(ANIM_OT_delete_keyframe_animall)
594 bpy.utils.unregister_class(ANIM_OT_clear_animation_animall)
595 bpy.utils.unregister_class(AnimallAddonPreferences)
596 bpy.app.handlers.frame_change_post.remove(animall_update_handler)
598 if __name__ == "__main__":
599 register()