1 # SPDX-License-Identifier: GPL-2.0-or-later
5 "author": "Daniel Salazar <zanqdo@gmail.com>",
8 "location": "3D View > Toolbox > Animation tab > AnimAll",
9 "description": "Allows animation of mesh, lattice, curve and surface data",
11 "doc_url": "{BLENDER_MANUAL_URL}/addons/animation/animall.html",
12 "category": "Animation",
16 Thanks to Campbell Barton and Joshua Leung for hes API additions and fixes
17 Daniel 'ZanQdo' Salazar
21 from bpy
.types
import (
26 from bpy
.props
import (
30 from bpy
.app
.handlers
import persistent
33 # Property Definitions
34 class AnimallProperties(bpy
.types
.PropertyGroup
):
35 key_selected
: BoolProperty(
37 description
="Insert keyframes only on selected elements",
40 key_shape
: BoolProperty(
42 description
="Insert keyframes on active Shape Key layer",
45 key_uvs
: BoolProperty(
47 description
="Insert keyframes on active UV coordinates",
50 key_ebevel
: BoolProperty(
52 description
="Insert keyframes on edge bevel weight",
55 key_vbevel
: BoolProperty(
57 description
="Insert keyframes on vertex bevel weight",
60 key_crease
: BoolProperty(
62 description
="Insert keyframes on edge creases",
65 key_vcols
: BoolProperty(
67 description
="Insert keyframes on active Vertex Color values",
70 key_vgroups
: BoolProperty(
72 description
="Insert keyframes on active Vertex group values",
75 key_points
: BoolProperty(
77 description
="Insert keyframes on point locations",
80 key_handle_type
: BoolProperty(
82 description
="Insert keyframes on Bezier point types",
85 key_radius
: BoolProperty(
87 description
="Insert keyframes on point radius (Shrink/Fatten)",
90 key_tilt
: BoolProperty(
92 description
="Insert keyframes on point tilt",
99 def refresh_ui_keyframes():
101 for area
in bpy
.context
.screen
.areas
:
102 if area
.type in ('TIMELINE', 'GRAPH_EDITOR', 'DOPESHEET_EDITOR'):
108 def insert_key(data
, key
, group
=None):
110 if group
is not None:
111 data
.keyframe_insert(key
, group
=group
)
113 data
.keyframe_insert(key
)
118 def delete_key(data
, key
):
120 data
.keyframe_delete(key
)
127 class VIEW3D_PT_animall(Panel
):
128 bl_space_type
= 'VIEW_3D'
129 bl_region_type
= 'UI'
130 bl_category
= "Animate"
132 bl_options
= {'DEFAULT_CLOSED'}
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
143 col
= layout
.column(align
=True)
145 row
.prop(animall_properties
, "key_selected")
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")
158 row
.prop(animall_properties
, "key_ebevel")
159 row
.prop(animall_properties
, "key_vbevel")
161 row
.prop(animall_properties
, "key_crease")
162 row
.prop(animall_properties
, "key_uvs")
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")
171 row
.prop(animall_properties
, "key_radius")
172 row
.prop(animall_properties
, "key_tilt")
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")
180 row
.prop(animall_properties
, "key_radius")
181 row
.prop(animall_properties
, "key_tilt")
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")
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()
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:
203 row
.label(text
='Maybe set "%s" to 1.0?' % shape_key
.name
, icon
="INFO")
205 row
.label(text
="Cannot key on Basis Shape", icon
="ERROR")
207 row
.label(text
="No active Shape Key", icon
="ERROR")
209 if animall_properties
.key_points
and animall_properties
.key_shape
:
211 row
.label(text
='"Points" and "Shape" are redundant?', icon
="INFO")
214 class ANIM_OT_insert_keyframe_animall(Operator
):
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
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'}]:
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
)
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'}]:
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()
371 class ANIM_OT_delete_keyframe_animall(Operator
):
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
384 objects
= context
.objects_in_mode_unique_data
[:]
386 mode
= context
.object.mode
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
:
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
:
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
:
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
:
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()
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
505 objects
= context
.objects_in_mode_unique_data
510 data
.animation_data_clear()
512 self
.report({'WARNING'}, "Clear Animation could not be performed")
515 refresh_ui_keyframes()
520 # Add-ons Preferences Update Panel
522 # Define Panel classes for updating
528 def update_panel(self
, context
):
529 message
= "AnimAll: Updating Panel locations has failed"
532 if "bl_rna" in panel
.__dict
__:
533 bpy
.utils
.unregister_class(panel
)
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
))
544 class AnimallAddonPreferences(AddonPreferences
):
545 # this must match the addon name, use '__package__'
546 # when defining this in a submodule of a python package.
549 category
: StringProperty(
551 description
="Choose a name for the category of the panel",
556 def draw(self
, context
):
561 col
.label(text
="Tab Category:")
562 col
.prop(self
, "category", text
="")
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
)
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__":