Merge branch 'blender-v3.6-release'
[blender-addons.git] / animation_animall / __init__.py
blobd679c97011613ad79de771030557faebca5c4b90
1 # SPDX-License-Identifier: GPL-2.0-or-later
3 bl_info = {
4 "name": "AnimAll",
5 "author": "Daniel Salazar (ZanQdo), Damien Picard (pioverfour)",
6 "version": (0, 9, 6),
7 "blender": (3, 3, 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 import bpy
16 from bpy.types import (Operator, Panel, AddonPreferences)
17 from bpy.props import (BoolProperty, StringProperty)
18 from bpy.app.handlers import persistent
19 from bpy.app.translations import (pgettext_iface as iface_,
20 pgettext_data as data_)
21 from . import translations
24 # Property Definitions
25 class AnimallProperties(bpy.types.PropertyGroup):
26 key_selected: BoolProperty(
27 name="Key Selected Only",
28 description="Insert keyframes only on selected elements",
29 default=False)
31 # Generic attributes
32 key_point_location: BoolProperty(
33 name="Location",
34 description="Insert keyframes on point locations",
35 default=False)
36 key_shape_key: BoolProperty(
37 name="Shape Key",
38 description="Insert keyframes on active Shape Key layer",
39 default=False)
40 key_material_index: BoolProperty(
41 name="Material Index",
42 description="Insert keyframes on face material indices",
43 default=False)
45 # Mesh attributes
46 key_vertex_bevel: BoolProperty(
47 name="Vertex Bevel",
48 description="Insert keyframes on vertex bevel weight",
49 default=False)
50 # key_vertex_crease: BoolProperty(
51 # name="Vertex Crease",
52 # description="Insert keyframes on vertex crease weight",
53 # default=False)
54 key_vertex_group: BoolProperty(
55 name="Vertex Group",
56 description="Insert keyframes on active vertex group values",
57 default=False)
59 key_edge_bevel: BoolProperty(
60 name="Edge Bevel",
61 description="Insert keyframes on edge bevel weight",
62 default=False)
63 key_edge_crease: BoolProperty(
64 name="Edge Crease",
65 description="Insert keyframes on edge creases",
66 default=False)
68 key_attribute: BoolProperty(
69 name="Attribute",
70 description="Insert keyframes on active attribute values",
71 default=False)
72 key_uvs: BoolProperty(
73 name="UV Map",
74 description="Insert keyframes on active UV coordinates",
75 default=False)
77 # Curve and surface attributes
78 key_radius: BoolProperty(
79 name="Radius",
80 description="Insert keyframes on point radius (Shrink/Fatten)",
81 default=False)
82 key_tilt: BoolProperty(
83 name="Tilt",
84 description="Insert keyframes on point tilt",
85 default=False)
88 # Utility functions
90 def refresh_ui_keyframes():
91 try:
92 for area in bpy.context.screen.areas:
93 if area.type in ('TIMELINE', 'GRAPH_EDITOR', 'DOPESHEET_EDITOR'):
94 area.tag_redraw()
95 except:
96 pass
99 def insert_key(data, key, group=None):
100 try:
101 if group is not None:
102 data.keyframe_insert(key, group=group)
103 else:
104 data.keyframe_insert(key)
105 except:
106 pass
109 def delete_key(data, key):
110 try:
111 data.keyframe_delete(key)
112 except:
113 pass
116 def is_selected_vert_loop(data, loop_i):
117 """Get selection status of vertex corresponding to a loop"""
118 vertex_index = data.loops[loop_i].vertex_index
119 return data.vertices[vertex_index].select
122 # GUI (Panel)
124 class VIEW3D_PT_animall(Panel):
125 bl_space_type = 'VIEW_3D'
126 bl_region_type = 'UI'
127 bl_category = "Animate"
128 bl_label = ''
130 @classmethod
131 def poll(self, context):
132 return context.active_object and context.active_object.type in {'MESH', 'LATTICE', 'CURVE', 'SURFACE'}
134 def draw_header(self, context):
136 layout = self.layout
137 row = layout.row()
138 row.label (text = 'AnimAll', icon = 'ARMATURE_DATA')
140 def draw(self, context):
141 obj = context.active_object
142 animall_properties = context.scene.animall_properties
144 layout = self.layout
146 layout.label(text='Key:')
148 layout.use_property_split = True
149 layout.use_property_decorate = False
151 if obj.type == 'LATTICE':
152 col = layout.column(heading="Points", align=True)
153 col.prop(animall_properties, "key_point_location")
155 col = layout.column(heading="Others", align=True)
156 col.prop(animall_properties, "key_shape_key")
158 elif obj.type == 'MESH':
159 col = layout.column(heading="Points", align=True)
160 col.prop(animall_properties, "key_point_location")
161 col.prop(animall_properties, "key_vertex_bevel", text="Bevel")
162 col.prop(animall_properties, "key_vertex_group")
164 col = layout.column(heading="Edges", align=True)
165 col.prop(animall_properties, "key_edge_bevel", text="Bevel")
166 col.prop(animall_properties, "key_edge_crease", text="Crease")
168 col = layout.column(heading="Faces", align=True)
169 col.prop(animall_properties, "key_material_index")
171 col = layout.column(heading="Others", align=True)
172 col.prop(animall_properties, "key_attribute")
173 col.prop(animall_properties, "key_uvs")
174 col.prop(animall_properties, "key_shape_key")
176 # Vertex group update operator
177 if (obj.data.animation_data is not None
178 and obj.data.animation_data.action is not None):
179 for fcurve in context.active_object.data.animation_data.action.fcurves:
180 if fcurve.data_path.startswith("vertex_colors"):
181 col = layout.column(align=True)
182 col.label(text="Object includes old-style vertex colors. Consider updating them.", icon="ERROR")
183 col.operator("anim.update_vertex_color_animation_animall", icon="FILE_REFRESH")
184 break
186 elif obj.type in {'CURVE', 'SURFACE'}:
187 col = layout.column(align=True)
188 col = layout.column(heading="Points", align=True)
189 col.prop(animall_properties, "key_point_location")
190 col.prop(animall_properties, "key_radius")
191 col.prop(animall_properties, "key_tilt")
193 col = layout.column(heading="Splines", align=True)
194 col.prop(animall_properties, "key_material_index")
196 col = layout.column(heading="Others", align=True)
197 col.prop(animall_properties, "key_shape_key")
199 if animall_properties.key_shape_key:
200 shape_key = obj.active_shape_key
201 shape_key_index = obj.active_shape_key_index
203 if shape_key_index > 0:
204 col = layout.column(align=True)
205 row = col.row(align=True)
206 row.prop(shape_key, "value", text=shape_key.name, icon="SHAPEKEY_DATA")
207 row.prop(obj, "show_only_shape_key", text="")
208 if shape_key.value < 1:
209 col.label(text=iface_('Maybe set "%s" to 1.0?') % shape_key.name, icon="INFO")
210 elif shape_key is not None:
211 col = layout.column(align=True)
212 col.label(text="Cannot key on Basis Shape", icon="ERROR")
213 else:
214 col = layout.column(align=True)
215 col.label(text="No active Shape Key", icon="ERROR")
217 if animall_properties.key_point_location:
218 col.label(text='"Location" and "Shape Key" are redundant?', icon="INFO")
220 layout.use_property_split = False
221 layout.separator()
222 row = layout.row()
223 row.prop(animall_properties, "key_selected")
225 row = layout.row(align=True)
226 row.operator("anim.insert_keyframe_animall", icon="KEY_HLT")
227 row.operator("anim.delete_keyframe_animall", icon="KEY_DEHLT")
228 row = layout.row()
229 row.operator("anim.clear_animation_animall", icon="CANCEL")
232 class ANIM_OT_insert_keyframe_animall(Operator):
233 bl_label = "Insert Key"
234 bl_idname = "anim.insert_keyframe_animall"
235 bl_description = "Insert a Keyframe"
236 bl_options = {'REGISTER', 'UNDO'}
238 def execute(self, context):
239 animall_properties = context.scene.animall_properties
241 if context.mode == 'OBJECT':
242 objects = context.selected_objects
243 else:
244 objects = context.objects_in_mode_unique_data[:]
246 mode = context.object.mode
248 # Separate loop for lattices, curves and surfaces, since keyframe insertion
249 # has to happen in Edit Mode, otherwise points move back upon mode switch...
250 # (except for curve shape keys)
251 for obj in [o for o in objects if o.type in {'CURVE', 'SURFACE', 'LATTICE'}]:
252 data = obj.data
254 if obj.type == 'LATTICE':
255 if animall_properties.key_shape_key:
256 if obj.active_shape_key_index > 0:
257 sk_name = obj.active_shape_key.name
258 for p_i, point in enumerate(obj.active_shape_key.data):
259 if not animall_properties.key_selected or data.points[p_i].select:
260 insert_key(point, 'co', group=data_("%s Point %s") % (sk_name, p_i))
262 if animall_properties.key_point_location:
263 for p_i, point in enumerate(data.points):
264 if not animall_properties.key_selected or point.select:
265 insert_key(point, 'co_deform', group=data_("Point %s") % p_i)
267 else:
268 if animall_properties.key_material_index:
269 for s_i, spline in enumerate(data.splines):
270 if (not animall_properties.key_selected
271 or any(point.select for point in spline.points)
272 or any(point.select_control_point for point in spline.bezier_points)):
273 insert_key(spline, 'material_index', group=data_("Spline %s") % s_i)
275 for s_i, spline in enumerate(data.splines):
276 if spline.type == 'BEZIER':
277 for v_i, CV in enumerate(spline.bezier_points):
278 if (not animall_properties.key_selected
279 or CV.select_control_point
280 or CV.select_left_handle
281 or CV.select_right_handle):
282 if animall_properties.key_point_location:
283 insert_key(CV, 'co', group=data_("Spline %s CV %s") % (s_i, v_i))
284 insert_key(CV, 'handle_left', group=data_("Spline %s CV %s") % (s_i, v_i))
285 insert_key(CV, 'handle_right', group=data_("Spline %s CV %s") % (s_i, v_i))
287 if animall_properties.key_radius:
288 insert_key(CV, 'radius', group=data_("Spline %s CV %s") % (s_i, v_i))
290 if animall_properties.key_tilt:
291 insert_key(CV, 'tilt', group=data_("Spline %s CV %s") % (s_i, v_i))
293 elif spline.type in ('POLY', 'NURBS'):
294 for v_i, CV in enumerate(spline.points):
295 if not animall_properties.key_selected or CV.select:
296 if animall_properties.key_point_location:
297 insert_key(CV, 'co', group=data_("Spline %s CV %s") % (s_i, v_i))
299 if animall_properties.key_radius:
300 insert_key(CV, 'radius', group=data_("Spline %s CV %s") % (s_i, v_i))
302 if animall_properties.key_tilt:
303 insert_key(CV, 'tilt', group=data_("Spline %s CV %s") % (s_i, v_i))
305 bpy.ops.object.mode_set(mode='OBJECT')
307 for obj in [o for o in objects if o.type in {'MESH', 'CURVE', 'SURFACE'}]:
308 data = obj.data
309 if obj.type == 'MESH':
310 if animall_properties.key_point_location:
311 for v_i, vert in enumerate(data.vertices):
312 if not animall_properties.key_selected or vert.select:
313 insert_key(vert, 'co', group=data_("Vertex %s") % v_i)
315 if animall_properties.key_vertex_bevel:
316 for v_i, vert in enumerate(data.vertices):
317 if not animall_properties.key_selected or vert.select:
318 insert_key(vert, 'bevel_weight', group=data_("Vertex %s") % v_i)
319 # if animall_properties.key_vertex_crease:
320 # for v_i, vert in enumerate(data.vertices):
321 # if not animall_properties.key_selected or vert.select:
322 # insert_key(vert, 'crease', group=data_("Vertex %s") % v_i)
324 if animall_properties.key_vertex_group:
325 for v_i, vert in enumerate(data.vertices):
326 if not animall_properties.key_selected or vert.select:
327 for group in vert.groups:
328 insert_key(group, 'weight', group=data_("Vertex %s") % v_i)
330 if animall_properties.key_edge_bevel:
331 for e_i, edge in enumerate(data.edges):
332 if not animall_properties.key_selected or edge.select:
333 insert_key(edge, 'bevel_weight', group=data_("Edge %s") % e_i)
335 if animall_properties.key_edge_crease:
336 for e_i, edge in enumerate(data.edges):
337 if not animall_properties.key_selected or edge.select:
338 insert_key(edge, 'crease', group=data_("Edge %s") % e_i)
340 if animall_properties.key_material_index:
341 for p_i, polygon in enumerate(data.polygons):
342 if not animall_properties.key_selected or polygon.select:
343 insert_key(polygon, 'material_index', group=data_("Face %s") % p_i)
345 if animall_properties.key_attribute:
346 if data.attributes.active is not None:
347 attribute = data.attributes.active
348 if attribute.data_type != 'STRING':
349 # Cannot animate string attributes?
350 if attribute.data_type in {'FLOAT', 'INT', 'BOOLEAN', 'INT8'}:
351 attribute_key = "value"
352 elif attribute.data_type in {'FLOAT_COLOR', 'BYTE_COLOR'}:
353 attribute_key = "color"
354 elif attribute.data_type in {'FLOAT_VECTOR', 'FLOAT2'}:
355 attribute_key = "vector"
357 if attribute.domain == 'POINT':
358 group = data_("Vertex %s")
359 elif attribute.domain == 'EDGE':
360 group = data_("Edge %s")
361 elif attribute.domain == 'FACE':
362 group = data_("Face %s")
363 elif attribute.domain == 'CORNER':
364 group = data_("Loop %s")
366 for e_i, _attribute_data in enumerate(attribute.data):
367 if (not animall_properties.key_selected
368 or attribute.domain == 'POINT' and data.vertices[e_i].select
369 or attribute.domain == 'EDGE' and data.edges[e_i].select
370 or attribute.domain == 'FACE' and data.polygons[e_i].select
371 or attribute.domain == 'CORNER' and is_selected_vert_loop(data, e_i)):
372 insert_key(data, f'attributes["{attribute.name}"].data[{e_i}].{attribute_key}',
373 group=group % e_i)
375 if animall_properties.key_uvs:
376 if data.uv_layers.active is not None:
377 for uv_i, uv in enumerate(data.uv_layers.active.data):
378 if not animall_properties.key_selected or uv.select:
379 insert_key(uv, 'uv', group=data_("UV layer %s") % uv_i)
381 if animall_properties.key_shape_key:
382 if obj.active_shape_key_index > 0:
383 sk_name = obj.active_shape_key.name
384 for v_i, vert in enumerate(obj.active_shape_key.data):
385 if not animall_properties.key_selected or data.vertices[v_i].select:
386 insert_key(vert, 'co', group=data_("%s Vertex %s") % (sk_name, v_i))
388 elif obj.type in {'CURVE', 'SURFACE'}:
389 # Shape key keys have to be inserted in object mode for curves...
390 if animall_properties.key_shape_key:
391 sk_name = obj.active_shape_key.name
392 global_spline_index = 0 # numbering for shape keys, which have flattened indices
393 for s_i, spline in enumerate(data.splines):
394 if spline.type == 'BEZIER':
395 for v_i, CV in enumerate(spline.bezier_points):
396 if (not animall_properties.key_selected
397 or CV.select_control_point
398 or CV.select_left_handle
399 or CV.select_right_handle):
400 if obj.active_shape_key_index > 0:
401 CV = obj.active_shape_key.data[global_spline_index]
402 insert_key(CV, 'co', group=data_("%s Spline %s CV %s") % (sk_name, s_i, v_i))
403 insert_key(CV, 'handle_left', group=data_("%s Spline %s CV %s") % (sk_name, s_i, v_i))
404 insert_key(CV, 'handle_right', group=data_("%s Spline %s CV %s") % (sk_name, s_i, v_i))
405 insert_key(CV, 'radius', group=data_("%s Spline %s CV %s") % (sk_name, s_i, v_i))
406 insert_key(CV, 'tilt', group=data_("%s Spline %s CV %s") % (sk_name, s_i, v_i))
407 global_spline_index += 1
409 elif spline.type in ('POLY', 'NURBS'):
410 for v_i, CV in enumerate(spline.points):
411 if not animall_properties.key_selected or CV.select:
412 if obj.active_shape_key_index > 0:
413 CV = obj.active_shape_key.data[global_spline_index]
414 insert_key(CV, 'co', group=data_("%s Spline %s CV %s") % (sk_name, s_i, v_i))
415 insert_key(CV, 'radius', group=data_("%s Spline %s CV %s") % (sk_name, s_i, v_i))
416 insert_key(CV, 'tilt', group=data_("%s Spline %s CV %s") % (sk_name, s_i, v_i))
417 global_spline_index += 1
419 bpy.ops.object.mode_set(mode=mode)
420 refresh_ui_keyframes()
422 return {'FINISHED'}
425 class ANIM_OT_delete_keyframe_animall(Operator):
426 bl_label = "Delete Key"
427 bl_idname = "anim.delete_keyframe_animall"
428 bl_description = "Delete a Keyframe"
429 bl_options = {'REGISTER', 'UNDO'}
431 def execute(self, context):
432 animall_properties = context.scene.animall_properties
434 if context.mode == 'OBJECT':
435 objects = context.selected_objects
436 else:
437 objects = context.objects_in_mode_unique_data[:]
439 mode = context.object.mode
441 for obj in objects:
442 data = obj.data
443 if obj.type == 'MESH':
444 if animall_properties.key_point_location:
445 for vert in data.vertices:
446 if not animall_properties.key_selected or vert.select:
447 delete_key(vert, 'co')
449 if animall_properties.key_vertex_bevel:
450 for vert in data.vertices:
451 if not animall_properties.key_selected or vert.select:
452 delete_key(vert, 'bevel_weight')
454 if animall_properties.key_vertex_group:
455 for vert in data.vertices:
456 if not animall_properties.key_selected or vert.select:
457 for group in vert.groups:
458 delete_key(group, 'weight')
460 # if animall_properties.key_vertex_crease:
461 # for vert in data.vertices:
462 # if not animall_properties.key_selected or vert.select:
463 # delete_key(vert, 'crease')
465 if animall_properties.key_edge_bevel:
466 for edge in data.edges:
467 if not animall_properties.key_selected or edge.select:
468 delete_key(edge, 'bevel_weight')
470 if animall_properties.key_edge_crease:
471 for edge in data.edges:
472 if not animall_properties.key_selected or vert.select:
473 delete_key(edge, 'crease')
475 if animall_properties.key_shape_key:
476 if obj.active_shape_key:
477 for v_i, vert in enumerate(obj.active_shape_key.data):
478 if not animall_properties.key_selected or data.vertices[v_i].select:
479 delete_key(vert, 'co')
481 if animall_properties.key_uvs:
482 if data.uv_layers.active is not None:
483 for uv in data.uv_layers.active.data:
484 if not animall_properties.key_selected or uv.select:
485 delete_key(uv, 'uv')
487 if animall_properties.key_attribute:
488 if data.attributes.active is not None:
489 attribute = data.attributes.active
490 if attribute.data_type != 'STRING':
491 # Cannot animate string attributes?
492 if attribute.data_type in {'FLOAT', 'INT', 'BOOLEAN', 'INT8'}:
493 attribute_key = "value"
494 elif attribute.data_type in {'FLOAT_COLOR', 'BYTE_COLOR'}:
495 attribute_key = "color"
496 elif attribute.data_type in {'FLOAT_VECTOR', 'FLOAT2'}:
497 attribute_key = "vector"
499 for e_i, _attribute_data in enumerate(attribute.data):
500 if (not animall_properties.key_selected
501 or attribute.domain == 'POINT' and data.vertices[e_i].select
502 or attribute.domain == 'EDGE' and data.edges[e_i].select
503 or attribute.domain == 'FACE' and data.polygons[e_i].select
504 or attribute.domain == 'CORNER' and is_selected_vert_loop(data, e_i)):
505 delete_key(data, f'attributes["{attribute.name}"].data[{e_i}].{attribute_key}')
507 elif obj.type == 'LATTICE':
508 if animall_properties.key_shape_key:
509 if obj.active_shape_key:
510 for point in obj.active_shape_key.data:
511 delete_key(point, 'co')
513 if animall_properties.key_point_location:
514 for point in data.points:
515 if not animall_properties.key_selected or point.select:
516 delete_key(point, 'co_deform')
518 elif obj.type in {'CURVE', 'SURFACE'}:
519 # Run this outside the splines loop (only once)
520 if animall_properties.key_shape_key:
521 if obj.active_shape_key_index > 0:
522 for CV in obj.active_shape_key.data:
523 delete_key(CV, 'co')
524 delete_key(CV, 'handle_left')
525 delete_key(CV, 'handle_right')
527 for spline in data.splines:
528 if spline.type == 'BEZIER':
529 for CV in spline.bezier_points:
530 if (not animall_properties.key_selected
531 or CV.select_control_point
532 or CV.select_left_handle
533 or CV.select_right_handle):
534 if animall_properties.key_point_location:
535 delete_key(CV, 'co')
536 delete_key(CV, 'handle_left')
537 delete_key(CV, 'handle_right')
538 if animall_properties.key_radius:
539 delete_key(CV, 'radius')
540 if animall_properties.key_tilt:
541 delete_key(CV, 'tilt')
543 elif spline.type in ('POLY', 'NURBS'):
544 for CV in spline.points:
545 if not animall_properties.key_selected or CV.select:
546 if animall_properties.key_point_location:
547 delete_key(CV, 'co')
548 if animall_properties.key_radius:
549 delete_key(CV, 'radius')
550 if animall_properties.key_tilt:
551 delete_key(CV, 'tilt')
553 refresh_ui_keyframes()
555 return {'FINISHED'}
558 class ANIM_OT_clear_animation_animall(Operator):
559 bl_label = "Clear Animation"
560 bl_idname = "anim.clear_animation_animall"
561 bl_description = ("Delete all keyframes for this object\n"
562 "If in a specific case it doesn't work\n"
563 "try to delete the keys manually")
564 bl_options = {'REGISTER', 'UNDO'}
566 def invoke(self, context, event):
567 wm = context.window_manager
568 return wm.invoke_confirm(self, event)
570 def execute(self, context):
571 if context.mode == 'OBJECT':
572 objects = context.selected_objects
573 else:
574 objects = context.objects_in_mode_unique_data
576 for obj in objects:
577 try:
578 data = obj.data
579 data.animation_data_clear()
580 except:
581 self.report({'WARNING'}, "Clear Animation could not be performed")
582 return {'CANCELLED'}
584 refresh_ui_keyframes()
586 return {'FINISHED'}
589 class ANIM_OT_update_vertex_color_animation_animall(Operator):
590 bl_label = "Update Vertex Color Animation"
591 bl_idname = "anim.update_vertex_color_animation_animall"
592 bl_description = "Update old vertex color channel formats from pre-3.3 versions"
593 bl_options = {'REGISTER', 'UNDO'}
595 @classmethod
596 def poll(self, context):
597 if (context.active_object is None
598 or context.active_object.type != 'MESH'
599 or context.active_object.data.animation_data is None
600 or context.active_object.data.animation_data.action is None):
601 return False
602 for fcurve in context.active_object.data.animation_data.action.fcurves:
603 if fcurve.data_path.startswith("vertex_colors"):
604 return True
606 def execute(self, context):
607 for fcurve in context.active_object.data.animation_data.action.fcurves:
608 if fcurve.data_path.startswith("vertex_colors"):
609 fcurve.data_path = fcurve.data_path.replace("vertex_colors", "attributes")
610 return {'FINISHED'}
612 # Add-ons Preferences Update Panel
614 # Define Panel classes for updating
615 panels = [
616 VIEW3D_PT_animall
620 def update_panel(self, context):
621 message = "AnimAll: Updating Panel locations has failed"
622 try:
623 for panel in panels:
624 if "bl_rna" in panel.__dict__:
625 bpy.utils.unregister_class(panel)
627 for panel in panels:
628 panel.bl_category = context.preferences.addons[__name__].preferences.category
629 bpy.utils.register_class(panel)
631 except Exception as e:
632 print("\n[{}]\n{}\n\nError:\n{}".format(__name__, message, e))
633 pass
636 class AnimallAddonPreferences(AddonPreferences):
637 # this must match the addon name, use '__package__'
638 # when defining this in a submodule of a python package.
639 bl_idname = __name__
641 category: StringProperty(
642 name="Tab Category",
643 description="Choose a name for the category of the panel",
644 default="Animate",
645 update=update_panel
648 def draw(self, context):
649 layout = self.layout
650 row = layout.row()
651 col = row.column()
653 col.label(text="Tab Category:")
654 col.prop(self, "category", text="")
656 register_classes, unregister_classes = bpy.utils.register_classes_factory(
657 (AnimallProperties, VIEW3D_PT_animall, ANIM_OT_insert_keyframe_animall,
658 ANIM_OT_delete_keyframe_animall, ANIM_OT_clear_animation_animall,
659 ANIM_OT_update_vertex_color_animation_animall, AnimallAddonPreferences))
661 def register():
662 register_classes()
663 bpy.types.Scene.animall_properties = bpy.props.PointerProperty(type=AnimallProperties)
664 update_panel(None, bpy.context)
665 bpy.app.translations.register(__name__, translations.translations_dict)
667 def unregister():
668 bpy.app.translations.unregister(__name__)
669 del bpy.types.Scene.animall_properties
670 unregister_classes()
672 if __name__ == "__main__":
673 register()