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 #####
21 "author": "Daniel Salazar <zanqdo@gmail.com>",
23 "blender": (2, 80, 0),
24 "location": "3D View > Toolbox > Animation tab > AnimAll",
25 "description": "Allows animation of mesh, lattice, curve and surface data",
27 "doc_url": "{BLENDER_MANUAL_URL}/addons/animation/animall.html",
28 "category": "Animation",
32 Thanks to Campbell Barton and Joshua Leung for hes API additions and fixes
33 Daniel 'ZanQdo' Salazar
37 from bpy
.types
import (
42 from bpy
.props
import (
46 from bpy
.app
.handlers
import persistent
49 # Property Definitions
50 class AnimallProperties(bpy
.types
.PropertyGroup
):
51 key_selected
: BoolProperty(
53 description
="Insert keyframes only on selected elements",
56 key_shape
: BoolProperty(
58 description
="Insert keyframes on active Shape Key layer",
61 key_uvs
: BoolProperty(
63 description
="Insert keyframes on active UV coordinates",
66 key_ebevel
: BoolProperty(
68 description
="Insert keyframes on edge bevel weight",
71 key_vbevel
: BoolProperty(
73 description
="Insert keyframes on vertex bevel weight",
76 key_crease
: BoolProperty(
78 description
="Insert keyframes on edge creases",
81 key_vcols
: BoolProperty(
83 description
="Insert keyframes on active Vertex Color values",
86 key_vgroups
: BoolProperty(
88 description
="Insert keyframes on active Vertex group values",
91 key_points
: BoolProperty(
93 description
="Insert keyframes on point locations",
96 key_radius
: BoolProperty(
98 description
="Insert keyframes on point radius (Shrink/Fatten)",
101 key_tilt
: BoolProperty(
103 description
="Insert keyframes on point tilt",
110 def refresh_ui_keyframes():
112 for area
in bpy
.context
.screen
.areas
:
113 if area
.type in ('TIMELINE', 'GRAPH_EDITOR', 'DOPESHEET_EDITOR'):
119 def insert_key(data
, key
, group
=None):
121 if group
is not None:
122 data
.keyframe_insert(key
, group
=group
)
124 data
.keyframe_insert(key
)
129 def delete_key(data
, key
):
131 data
.keyframe_delete(key
)
138 class VIEW3D_PT_animall(Panel
):
139 bl_space_type
= 'VIEW_3D'
140 bl_region_type
= 'UI'
141 bl_category
= "Animate"
143 bl_options
= {'DEFAULT_CLOSED'}
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
154 col
= layout
.column(align
=True)
156 row
.prop(animall_properties
, "key_selected")
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")
169 row
.prop(animall_properties
, "key_ebevel")
170 row
.prop(animall_properties
, "key_vbevel")
172 row
.prop(animall_properties
, "key_crease")
173 row
.prop(animall_properties
, "key_uvs")
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")
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")
189 row
.prop(animall_properties
, "key_radius")
190 row
.prop(animall_properties
, "key_tilt")
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")
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()
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:
212 row
.label(text
='Maybe set "%s" to 1.0?' % shape_key
.name
, icon
="INFO")
214 row
.label(text
="Cannot key on Basis Shape", icon
="ERROR")
216 row
.label(text
="No active Shape Key", icon
="ERROR")
218 if animall_properties
.key_points
and animall_properties
.key_shape
:
220 row
.label(text
='"Points" and "Shape" are redundant?', icon
="INFO")
223 class ANIM_OT_insert_keyframe_animall(Operator
):
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
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'}]:
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
)
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'}]:
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()
376 class ANIM_OT_delete_keyframe_animall(Operator
):
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
389 objects
= context
.objects_in_mode_unique_data
[:]
391 mode
= context
.object.mode
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
:
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
:
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
:
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
:
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()
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
507 objects
= context
.objects_in_mode_unique_data
512 data
.animation_data_clear()
514 self
.report({'WARNING'}, "Clear Animation could not be performed")
517 refresh_ui_keyframes()
522 # Add-ons Preferences Update Panel
524 # Define Panel classes for updating
530 def update_panel(self
, context
):
531 message
= "AnimAll: Updating Panel locations has failed"
534 if "bl_rna" in panel
.__dict
__:
535 bpy
.utils
.unregister_class(panel
)
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
))
546 class AnimallAddonPreferences(AddonPreferences
):
547 # this must match the addon name, use '__package__'
548 # when defining this in a submodule of a python package.
551 category
: StringProperty(
553 description
="Choose a name for the category of the panel",
558 def draw(self
, context
):
563 col
.label(text
="Tab Category:")
564 col
.prop(self
, "category", text
="")
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
)
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__":