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 "wiki_url": "https://docs.blender.org/manual/en/dev/addons/"
28 "animation/animall.html",
29 "category": "Animation",
33 Thanks to Campbell Barton and Joshua Leung for hes API additions and fixes
34 Daniel 'ZanQdo' Salazar
38 from bpy
.types
import (
43 from bpy
.props
import (
47 from bpy
.app
.handlers
import persistent
50 # Property Definitions
51 class AnimallProperties(bpy
.types
.PropertyGroup
):
52 key_selected
: BoolProperty(
54 description
="Insert keyframes only on selected elements",
57 key_shape
: BoolProperty(
59 description
="Insert keyframes on active Shape Key layer",
62 key_uvs
: BoolProperty(
64 description
="Insert keyframes on active UV coordinates",
67 key_ebevel
: BoolProperty(
69 description
="Insert keyframes on edge bevel weight",
72 key_vbevel
: BoolProperty(
74 description
="Insert keyframes on vertex bevel weight",
77 key_crease
: BoolProperty(
79 description
="Insert keyframes on edge creases",
82 key_vcols
: BoolProperty(
84 description
="Insert keyframes on active Vertex Color values",
87 key_vgroups
: BoolProperty(
89 description
="Insert keyframes on active Vertex group values",
92 key_points
: BoolProperty(
94 description
="Insert keyframes on point locations",
97 key_radius
: BoolProperty(
99 description
="Insert keyframes on point radius (Shrink/Fatten)",
102 key_tilt
: BoolProperty(
104 description
="Insert keyframes on point tilt",
111 def refresh_ui_keyframes():
113 for area
in bpy
.context
.screen
.areas
:
114 if area
.type in ('TIMELINE', 'GRAPH_EDITOR', 'DOPESHEET_EDITOR'):
120 def insert_key(data
, key
, group
=None):
122 if group
is not None:
123 data
.keyframe_insert(key
, group
=group
)
125 data
.keyframe_insert(key
)
130 def delete_key(data
, key
):
132 data
.keyframe_delete(key
)
139 class VIEW3D_PT_animall(Panel
):
140 bl_space_type
= 'VIEW_3D'
141 bl_region_type
= 'UI'
142 bl_category
= "Animate"
144 bl_options
= {'DEFAULT_CLOSED'}
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
155 col
= layout
.column(align
=True)
157 row
.prop(animall_properties
, "key_selected")
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")
170 row
.prop(animall_properties
, "key_ebevel")
171 row
.prop(animall_properties
, "key_vbevel")
173 row
.prop(animall_properties
, "key_crease")
174 row
.prop(animall_properties
, "key_uvs")
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")
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")
190 row
.prop(animall_properties
, "key_radius")
191 row
.prop(animall_properties
, "key_tilt")
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")
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()
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:
213 row
.label(text
='Maybe set "%s" to 1.0?' % shape_key
.name
, icon
="INFO")
215 row
.label(text
="Cannot key on Basis Shape", icon
="ERROR")
217 row
.label(text
="No active Shape Key", icon
="ERROR")
219 if animall_properties
.key_points
and animall_properties
.key_shape
:
221 row
.label(text
='"Points" and "Shape" are redundant?', icon
="INFO")
224 class ANIM_OT_insert_keyframe_animall(Operator
):
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
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'}]:
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
)
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'}]:
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()
377 class ANIM_OT_delete_keyframe_animall(Operator
):
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
390 objects
= context
.objects_in_mode_unique_data
[:]
392 mode
= context
.object.mode
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
:
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
:
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
:
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
:
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()
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
508 objects
= context
.objects_in_mode_unique_data
513 data
.animation_data_clear()
515 self
.report({'WARNING'}, "Clear Animation could not be performed")
518 refresh_ui_keyframes()
523 # Add-ons Preferences Update Panel
525 # Define Panel classes for updating
531 def update_panel(self
, context
):
532 message
= "AnimAll: Updating Panel locations has failed"
535 if "bl_rna" in panel
.__dict
__:
536 bpy
.utils
.unregister_class(panel
)
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
))
547 class AnimallAddonPreferences(AddonPreferences
):
548 # this must match the addon name, use '__package__'
549 # when defining this in a submodule of a python package.
552 category
: StringProperty(
554 description
="Choose a name for the category of the panel",
559 def draw(self
, context
):
564 col
.label(text
="Tab Category:")
565 col
.prop(self
, "category", text
="")
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
)
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__":