Import_3ds: Improved distance cue node setup
[blender-addons.git] / io_scene_fbx / __init__.py
blob9a657d85b5dbd639e83f07f60d55c5c79249cfe1
1 # SPDX-FileCopyrightText: 2011-2023 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 bl_info = {
6 "name": "FBX format",
7 "author": "Campbell Barton, Bastien Montagne, Jens Restemeier, @Mysteryem",
8 "version": (5, 12, 3),
9 "blender": (4, 2, 0),
10 "location": "File > Import-Export",
11 "description": "FBX IO meshes, UVs, vertex colors, materials, textures, cameras, lamps and actions",
12 "warning": "",
13 "doc_url": "{BLENDER_MANUAL_URL}/addons/import_export/scene_fbx.html",
14 "support": 'OFFICIAL',
15 "category": "Import-Export",
19 if "bpy" in locals():
20 import importlib
21 if "import_fbx" in locals():
22 importlib.reload(import_fbx)
23 if "export_fbx_bin" in locals():
24 importlib.reload(export_fbx_bin)
25 if "export_fbx" in locals():
26 importlib.reload(export_fbx)
29 import bpy
30 from bpy.props import (
31 StringProperty,
32 BoolProperty,
33 FloatProperty,
34 EnumProperty,
35 CollectionProperty,
37 from bpy_extras.io_utils import (
38 ImportHelper,
39 ExportHelper,
40 orientation_helper,
41 path_reference_mode,
42 axis_conversion,
43 poll_file_object_drop,
47 @orientation_helper(axis_forward='-Z', axis_up='Y')
48 class ImportFBX(bpy.types.Operator, ImportHelper):
49 """Load a FBX file"""
50 bl_idname = "import_scene.fbx"
51 bl_label = "Import FBX"
52 bl_options = {'UNDO', 'PRESET'}
54 directory: StringProperty()
56 filename_ext = ".fbx"
57 filter_glob: StringProperty(default="*.fbx", options={'HIDDEN'})
59 files: CollectionProperty(
60 name="File Path",
61 type=bpy.types.OperatorFileListElement,
64 ui_tab: EnumProperty(
65 items=(('MAIN', "Main", "Main basic settings"),
66 ('ARMATURE', "Armatures", "Armature-related settings"),
68 name="ui_tab",
69 description="Import options categories",
72 use_manual_orientation: BoolProperty(
73 name="Manual Orientation",
74 description="Specify orientation and scale, instead of using embedded data in FBX file",
75 default=False,
77 global_scale: FloatProperty(
78 name="Scale",
79 min=0.001, max=1000.0,
80 default=1.0,
82 bake_space_transform: BoolProperty(
83 name="Apply Transform",
84 description="Bake space transform into object data, avoids getting unwanted rotations to objects when "
85 "target space is not aligned with Blender's space "
86 "(WARNING! experimental option, use at own risk, known to be broken with armatures/animations)",
87 default=False,
90 use_custom_normals: BoolProperty(
91 name="Custom Normals",
92 description="Import custom normals, if available (otherwise Blender will recompute them)",
93 default=True,
95 colors_type: EnumProperty(
96 name="Vertex Colors",
97 items=(('NONE', "None", "Do not import color attributes"),
98 ('SRGB', "sRGB", "Expect file colors in sRGB color space"),
99 ('LINEAR', "Linear", "Expect file colors in linear color space"),
101 description="Import vertex color attributes",
102 default='SRGB',
105 use_image_search: BoolProperty(
106 name="Image Search",
107 description="Search subdirs for any associated images (WARNING: may be slow)",
108 default=True,
111 use_alpha_decals: BoolProperty(
112 name="Alpha Decals",
113 description="Treat materials with alpha as decals (no shadow casting)",
114 default=False,
116 decal_offset: FloatProperty(
117 name="Decal Offset",
118 description="Displace geometry of alpha meshes",
119 min=0.0, max=1.0,
120 default=0.0,
123 use_anim: BoolProperty(
124 name="Import Animation",
125 description="Import FBX animation",
126 default=True,
128 anim_offset: FloatProperty(
129 name="Animation Offset",
130 description="Offset to apply to animation during import, in frames",
131 default=1.0,
134 use_subsurf: BoolProperty(
135 name="Subdivision Data",
136 description="Import FBX subdivision information as subdivision surface modifiers",
137 default=False,
140 use_custom_props: BoolProperty(
141 name="Custom Properties",
142 description="Import user properties as custom properties",
143 default=True,
145 use_custom_props_enum_as_string: BoolProperty(
146 name="Import Enums As Strings",
147 description="Store enumeration values as strings",
148 default=True,
151 ignore_leaf_bones: BoolProperty(
152 name="Ignore Leaf Bones",
153 description="Ignore the last bone at the end of each chain (used to mark the length of the previous bone)",
154 default=False,
156 force_connect_children: BoolProperty(
157 name="Force Connect Children",
158 description="Force connection of children bones to their parent, even if their computed head/tail "
159 "positions do not match (can be useful with pure-joints-type armatures)",
160 default=False,
162 automatic_bone_orientation: BoolProperty(
163 name="Automatic Bone Orientation",
164 description="Try to align the major bone axis with the bone children",
165 default=False,
167 primary_bone_axis: EnumProperty(
168 name="Primary Bone Axis",
169 items=(('X', "X Axis", ""),
170 ('Y', "Y Axis", ""),
171 ('Z', "Z Axis", ""),
172 ('-X', "-X Axis", ""),
173 ('-Y', "-Y Axis", ""),
174 ('-Z', "-Z Axis", ""),
176 default='Y',
178 secondary_bone_axis: EnumProperty(
179 name="Secondary Bone Axis",
180 items=(('X', "X Axis", ""),
181 ('Y', "Y Axis", ""),
182 ('Z', "Z Axis", ""),
183 ('-X', "-X Axis", ""),
184 ('-Y', "-Y Axis", ""),
185 ('-Z', "-Z Axis", ""),
187 default='X',
190 use_prepost_rot: BoolProperty(
191 name="Use Pre/Post Rotation",
192 description="Use pre/post rotation from FBX transform (you may have to disable that in some cases)",
193 default=True,
196 def draw(self, context):
197 layout = self.layout
198 layout.use_property_split = True
199 layout.use_property_decorate = False # No animation.
201 import_panel_include(layout, self)
202 import_panel_transform(layout, self)
203 import_panel_animation(layout, self)
204 import_panel_armature(layout, self)
206 def execute(self, context):
207 keywords = self.as_keywords(ignore=("filter_glob", "directory", "ui_tab", "filepath", "files"))
209 from . import import_fbx
210 import os
212 if self.files:
213 ret = {'CANCELLED'}
214 dirname = os.path.dirname(self.filepath)
215 for file in self.files:
216 path = os.path.join(dirname, file.name)
217 if import_fbx.load(self, context, filepath=path, **keywords) == {'FINISHED'}:
218 ret = {'FINISHED'}
219 return ret
220 else:
221 return import_fbx.load(self, context, filepath=self.filepath, **keywords)
223 def invoke(self, context, event):
224 return self.invoke_popup(context)
226 def import_panel_include(layout, operator):
227 header, body = layout.panel("FBX_import_include", default_closed=False)
228 header.label(text="Include")
229 if body:
230 body.prop(operator, "use_custom_normals")
231 body.prop(operator, "use_subsurf")
232 body.prop(operator, "use_custom_props")
233 sub = body.row()
234 sub.enabled = operator.use_custom_props
235 sub.prop(operator, "use_custom_props_enum_as_string")
236 body.prop(operator, "use_image_search")
237 body.prop(operator, "colors_type")
240 def import_panel_transform(layout, operator):
241 header, body = layout.panel("FBX_import_transform", default_closed=False)
242 header.label(text="Transform")
243 if body:
244 body.prop(operator, "global_scale")
245 body.prop(operator, "decal_offset")
246 row = body.row()
247 row.prop(operator, "bake_space_transform")
248 row.label(text="", icon='ERROR')
249 body.prop(operator, "use_prepost_rot")
251 import_panel_transform_orientation(body, operator)
254 def import_panel_transform_orientation(layout, operator):
255 header, body = layout.panel("FBX_import_transform_manual_orientation", default_closed=False)
256 header.use_property_split = False
257 header.prop(operator, "use_manual_orientation", text="")
258 header.label(text="Manual Orientation")
259 if body:
260 body.enabled = operator.use_manual_orientation
261 body.prop(operator, "axis_forward")
262 body.prop(operator, "axis_up")
266 def import_panel_animation(layout, operator):
267 header, body = layout.panel("FBX_import_animation", default_closed=True)
268 header.use_property_split = False
269 header.prop(operator, "use_anim", text="")
270 header.label(text="Animation")
271 if body:
272 body.enabled = operator.use_anim
273 body.prop(operator, "anim_offset")
277 def import_panel_armature(layout, operator):
278 header, body = layout.panel("FBX_import_armature", default_closed=True)
279 header.label(text="Armature")
280 if body:
281 body.prop(operator, "ignore_leaf_bones")
282 body.prop(operator, "force_connect_children"),
283 body.prop(operator, "automatic_bone_orientation"),
284 sub = body.column()
285 sub.enabled = not operator.automatic_bone_orientation
286 sub.prop(operator, "primary_bone_axis")
287 sub.prop(operator, "secondary_bone_axis")
290 @orientation_helper(axis_forward='-Z', axis_up='Y')
291 class ExportFBX(bpy.types.Operator, ExportHelper):
292 """Write a FBX file"""
293 bl_idname = "export_scene.fbx"
294 bl_label = "Export FBX"
295 bl_options = {'UNDO', 'PRESET'}
297 filename_ext = ".fbx"
298 filter_glob: StringProperty(default="*.fbx", options={'HIDDEN'})
300 # List of operator properties, the attributes will be assigned
301 # to the class instance from the operator settings before calling.
303 use_selection: BoolProperty(
304 name="Selected Objects",
305 description="Export selected and visible objects only",
306 default=False,
308 use_visible: BoolProperty(
309 name='Visible Objects',
310 description='Export visible objects only',
311 default=False
313 use_active_collection: BoolProperty(
314 name="Active Collection",
315 description="Export only objects from the active collection (and its children)",
316 default=False,
318 collection: StringProperty(
319 name="Source Collection",
320 description="Export only objects from this collection (and its children)",
321 default="",
323 global_scale: FloatProperty(
324 name="Scale",
325 description="Scale all data (Some importers do not support scaled armatures!)",
326 min=0.001, max=1000.0,
327 soft_min=0.01, soft_max=1000.0,
328 default=1.0,
330 apply_unit_scale: BoolProperty(
331 name="Apply Unit",
332 description="Take into account current Blender units settings (if unset, raw Blender Units values are used as-is)",
333 default=True,
335 apply_scale_options: EnumProperty(
336 items=(('FBX_SCALE_NONE', "All Local",
337 "Apply custom scaling and units scaling to each object transformation, FBX scale remains at 1.0"),
338 ('FBX_SCALE_UNITS', "FBX Units Scale",
339 "Apply custom scaling to each object transformation, and units scaling to FBX scale"),
340 ('FBX_SCALE_CUSTOM', "FBX Custom Scale",
341 "Apply custom scaling to FBX scale, and units scaling to each object transformation"),
342 ('FBX_SCALE_ALL', "FBX All",
343 "Apply custom scaling and units scaling to FBX scale"),
345 name="Apply Scalings",
346 description="How to apply custom and units scalings in generated FBX file "
347 "(Blender uses FBX scale to detect units on import, "
348 "but many other applications do not handle the same way)",
351 use_space_transform: BoolProperty(
352 name="Use Space Transform",
353 description="Apply global space transform to the object rotations. When disabled "
354 "only the axis space is written to the file and all object transforms are left as-is",
355 default=True,
357 bake_space_transform: BoolProperty(
358 name="Apply Transform",
359 description="Bake space transform into object data, avoids getting unwanted rotations to objects when "
360 "target space is not aligned with Blender's space "
361 "(WARNING! experimental option, use at own risk, known to be broken with armatures/animations)",
362 default=False,
365 object_types: EnumProperty(
366 name="Object Types",
367 options={'ENUM_FLAG'},
368 items=(('EMPTY', "Empty", ""),
369 ('CAMERA', "Camera", ""),
370 ('LIGHT', "Lamp", ""),
371 ('ARMATURE', "Armature", "WARNING: not supported in dupli/group instances"),
372 ('MESH', "Mesh", ""),
373 ('OTHER', "Other", "Other geometry types, like curve, metaball, etc. (converted to meshes)"),
375 description="Which kind of object to export",
376 default={'EMPTY', 'CAMERA', 'LIGHT', 'ARMATURE', 'MESH', 'OTHER'},
379 use_mesh_modifiers: BoolProperty(
380 name="Apply Modifiers",
381 description="Apply modifiers to mesh objects (except Armature ones) - "
382 "WARNING: prevents exporting shape keys",
383 default=True,
385 use_mesh_modifiers_render: BoolProperty(
386 name="Use Modifiers Render Setting",
387 description="Use render settings when applying modifiers to mesh objects (DISABLED in Blender 2.8)",
388 default=True,
390 mesh_smooth_type: EnumProperty(
391 name="Smoothing",
392 items=(('OFF', "Normals Only", "Export only normals instead of writing edge or face smoothing data"),
393 ('FACE', "Face", "Write face smoothing"),
394 ('EDGE', "Edge", "Write edge smoothing"),
396 description="Export smoothing information "
397 "(prefer 'Normals Only' option if your target importer understand split normals)",
398 default='OFF',
400 colors_type: EnumProperty(
401 name="Vertex Colors",
402 items=(('NONE', "None", "Do not export color attributes"),
403 ('SRGB', "sRGB", "Export colors in sRGB color space"),
404 ('LINEAR', "Linear", "Export colors in linear color space"),
406 description="Export vertex color attributes",
407 default='SRGB',
409 prioritize_active_color: BoolProperty(
410 name="Prioritize Active Color",
411 description="Make sure active color will be exported first. Could be important "
412 "since some other software can discard other color attributes besides the first one",
413 default=False,
415 use_subsurf: BoolProperty(
416 name="Export Subdivision Surface",
417 description="Export the last Catmull-Rom subdivision modifier as FBX subdivision "
418 "(does not apply the modifier even if 'Apply Modifiers' is enabled)",
419 default=False,
421 use_mesh_edges: BoolProperty(
422 name="Loose Edges",
423 description="Export loose edges (as two-vertices polygons)",
424 default=False,
426 use_tspace: BoolProperty(
427 name="Tangent Space",
428 description="Add binormal and tangent vectors, together with normal they form the tangent space "
429 "(will only work correctly with tris/quads only meshes!)",
430 default=False,
432 use_triangles: BoolProperty(
433 name="Triangulate Faces",
434 description="Convert all faces to triangles",
435 default=False,
437 use_custom_props: BoolProperty(
438 name="Custom Properties",
439 description="Export custom properties",
440 default=False,
442 add_leaf_bones: BoolProperty(
443 name="Add Leaf Bones",
444 description="Append a final bone to the end of each chain to specify last bone length "
445 "(use this when you intend to edit the armature from exported data)",
446 default=True # False for commit!
448 primary_bone_axis: EnumProperty(
449 name="Primary Bone Axis",
450 items=(('X', "X Axis", ""),
451 ('Y', "Y Axis", ""),
452 ('Z', "Z Axis", ""),
453 ('-X', "-X Axis", ""),
454 ('-Y', "-Y Axis", ""),
455 ('-Z', "-Z Axis", ""),
457 default='Y',
459 secondary_bone_axis: EnumProperty(
460 name="Secondary Bone Axis",
461 items=(('X', "X Axis", ""),
462 ('Y', "Y Axis", ""),
463 ('Z', "Z Axis", ""),
464 ('-X', "-X Axis", ""),
465 ('-Y', "-Y Axis", ""),
466 ('-Z', "-Z Axis", ""),
468 default='X',
470 use_armature_deform_only: BoolProperty(
471 name="Only Deform Bones",
472 description="Only write deforming bones (and non-deforming ones when they have deforming children)",
473 default=False,
475 armature_nodetype: EnumProperty(
476 name="Armature FBXNode Type",
477 items=(('NULL', "Null", "'Null' FBX node, similar to Blender's Empty (default)"),
478 ('ROOT', "Root", "'Root' FBX node, supposed to be the root of chains of bones..."),
479 ('LIMBNODE', "LimbNode", "'LimbNode' FBX node, a regular joint between two bones..."),
481 description="FBX type of node (object) used to represent Blender's armatures "
482 "(use the Null type unless you experience issues with the other app, "
483 "as other choices may not import back perfectly into Blender...)",
484 default='NULL',
486 bake_anim: BoolProperty(
487 name="Baked Animation",
488 description="Export baked keyframe animation",
489 default=True,
491 bake_anim_use_all_bones: BoolProperty(
492 name="Key All Bones",
493 description="Force exporting at least one key of animation for all bones "
494 "(needed with some target applications, like UE4)",
495 default=True,
497 bake_anim_use_nla_strips: BoolProperty(
498 name="NLA Strips",
499 description="Export each non-muted NLA strip as a separated FBX's AnimStack, if any, "
500 "instead of global scene animation",
501 default=True,
503 bake_anim_use_all_actions: BoolProperty(
504 name="All Actions",
505 description="Export each action as a separated FBX's AnimStack, instead of global scene animation "
506 "(note that animated objects will get all actions compatible with them, "
507 "others will get no animation at all)",
508 default=True,
510 bake_anim_force_startend_keying: BoolProperty(
511 name="Force Start/End Keying",
512 description="Always add a keyframe at start and end of actions for animated channels",
513 default=True,
515 bake_anim_step: FloatProperty(
516 name="Sampling Rate",
517 description="How often to evaluate animated values (in frames)",
518 min=0.01, max=100.0,
519 soft_min=0.1, soft_max=10.0,
520 default=1.0,
522 bake_anim_simplify_factor: FloatProperty(
523 name="Simplify",
524 description="How much to simplify baked values (0.0 to disable, the higher the more simplified)",
525 min=0.0, max=100.0, # No simplification to up to 10% of current magnitude tolerance.
526 soft_min=0.0, soft_max=10.0,
527 default=1.0, # default: min slope: 0.005, max frame step: 10.
529 path_mode: path_reference_mode
530 embed_textures: BoolProperty(
531 name="Embed Textures",
532 description="Embed textures in FBX binary file (only for \"Copy\" path mode!)",
533 default=False,
535 batch_mode: EnumProperty(
536 name="Batch Mode",
537 items=(('OFF', "Off", "Active scene to file"),
538 ('SCENE', "Scene", "Each scene as a file"),
539 ('COLLECTION', "Collection",
540 "Each collection (data-block ones) as a file, does not include content of children collections"),
541 ('SCENE_COLLECTION', "Scene Collections",
542 "Each collection (including master, non-data-block ones) of each scene as a file, "
543 "including content from children collections"),
544 ('ACTIVE_SCENE_COLLECTION', "Active Scene Collections",
545 "Each collection (including master, non-data-block one) of the active scene as a file, "
546 "including content from children collections"),
549 use_batch_own_dir: BoolProperty(
550 name="Batch Own Dir",
551 description="Create a dir for each exported file",
552 default=True,
554 use_metadata: BoolProperty(
555 name="Use Metadata",
556 default=True,
557 options={'HIDDEN'},
560 def draw(self, context):
561 layout = self.layout
562 layout.use_property_split = True
563 layout.use_property_decorate = False # No animation.
565 # Are we inside the File browser
566 is_file_browser = context.space_data.type == 'FILE_BROWSER'
568 export_main(layout, self, is_file_browser)
569 export_panel_include(layout, self, is_file_browser)
570 export_panel_transform(layout, self)
571 export_panel_geometry(layout, self)
572 export_panel_armature(layout, self)
573 export_panel_animation(layout, self)
575 @property
576 def check_extension(self):
577 return self.batch_mode == 'OFF'
579 def execute(self, context):
580 from mathutils import Matrix
581 if not self.filepath:
582 raise Exception("filepath not set")
584 global_matrix = (axis_conversion(to_forward=self.axis_forward,
585 to_up=self.axis_up,
586 ).to_4x4()
587 if self.use_space_transform else Matrix())
589 keywords = self.as_keywords(ignore=("check_existing",
590 "filter_glob",
591 "ui_tab",
594 keywords["global_matrix"] = global_matrix
596 from . import export_fbx_bin
597 return export_fbx_bin.save(self, context, **keywords)
600 def export_main(layout, operator, is_file_browser):
601 row = layout.row(align=True)
602 row.prop(operator, "path_mode")
603 sub = row.row(align=True)
604 sub.enabled = (operator.path_mode == 'COPY')
605 sub.prop(operator, "embed_textures", text="", icon='PACKAGE' if operator.embed_textures else 'UGLYPACKAGE')
606 if is_file_browser:
607 row = layout.row(align=True)
608 row.prop(operator, "batch_mode")
609 sub = row.row(align=True)
610 sub.prop(operator, "use_batch_own_dir", text="", icon='NEWFOLDER')
613 def export_panel_include(layout, operator, is_file_browser):
614 header, body = layout.panel("FBX_export_include", default_closed=False)
615 header.label(text="Include")
616 if body:
617 sublayout = body.column(heading="Limit to")
618 sublayout.enabled = (operator.batch_mode == 'OFF')
619 if is_file_browser:
620 sublayout.prop(operator, "use_selection")
621 sublayout.prop(operator, "use_visible")
622 sublayout.prop(operator, "use_active_collection")
624 body.column().prop(operator, "object_types")
625 body.prop(operator, "use_custom_props")
628 def export_panel_transform(layout, operator):
629 header, body = layout.panel("FBX_export_transform", default_closed=False)
630 header.label(text="Transform")
631 if body:
632 body.prop(operator, "global_scale")
633 body.prop(operator, "apply_scale_options")
635 body.prop(operator, "axis_forward")
636 body.prop(operator, "axis_up")
638 body.prop(operator, "apply_unit_scale")
639 body.prop(operator, "use_space_transform")
640 row = body.row()
641 row.prop(operator, "bake_space_transform")
642 row.label(text="", icon='ERROR')
645 def export_panel_geometry(layout, operator):
646 header, body = layout.panel("FBX_export_geometry", default_closed=True)
647 header.label(text="Geometry")
648 if body:
649 body.prop(operator, "mesh_smooth_type")
650 body.prop(operator, "use_subsurf")
651 body.prop(operator, "use_mesh_modifiers")
652 #sub = body.row()
653 #sub.enabled = operator.use_mesh_modifiers and False # disabled in 2.8...
654 #sub.prop(operator, "use_mesh_modifiers_render")
655 body.prop(operator, "use_mesh_edges")
656 body.prop(operator, "use_triangles")
657 sub = body.row()
658 #~ sub.enabled = operator.mesh_smooth_type in {'OFF'}
659 sub.prop(operator, "use_tspace")
660 body.prop(operator, "colors_type")
661 body.prop(operator, "prioritize_active_color")
664 def export_panel_armature(layout, operator):
665 header, body = layout.panel("FBX_export_armature", default_closed=True)
666 header.label(text="Armature")
667 if body:
668 body.prop(operator, "primary_bone_axis")
669 body.prop(operator, "secondary_bone_axis")
670 body.prop(operator, "armature_nodetype")
671 body.prop(operator, "use_armature_deform_only")
672 body.prop(operator, "add_leaf_bones")
675 def export_panel_animation(layout, operator):
676 header, body = layout.panel("FBX_export_bake_animation", default_closed=True)
677 header.use_property_split = False
678 header.prop(operator, "bake_anim", text="")
679 header.label(text="Animation")
680 if body:
681 body.enabled = operator.bake_anim
682 body.prop(operator, "bake_anim_use_all_bones")
683 body.prop(operator, "bake_anim_use_nla_strips")
684 body.prop(operator, "bake_anim_use_all_actions")
685 body.prop(operator, "bake_anim_force_startend_keying")
686 body.prop(operator, "bake_anim_step")
687 body.prop(operator, "bake_anim_simplify_factor")
690 class IO_FH_fbx(bpy.types.FileHandler):
691 bl_idname = "IO_FH_fbx"
692 bl_label = "FBX"
693 bl_import_operator = "import_scene.fbx"
694 bl_export_operator = "export_scene.fbx"
695 bl_file_extensions = ".fbx"
697 @classmethod
698 def poll_drop(cls, context):
699 return poll_file_object_drop(context)
702 def menu_func_import(self, context):
703 self.layout.operator(ImportFBX.bl_idname, text="FBX (.fbx)")
706 def menu_func_export(self, context):
707 self.layout.operator(ExportFBX.bl_idname, text="FBX (.fbx)")
710 classes = (
711 ImportFBX,
712 ExportFBX,
713 IO_FH_fbx,
717 def register():
718 for cls in classes:
719 bpy.utils.register_class(cls)
721 bpy.types.TOPBAR_MT_file_import.append(menu_func_import)
722 bpy.types.TOPBAR_MT_file_export.append(menu_func_export)
725 def unregister():
726 bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)
727 bpy.types.TOPBAR_MT_file_export.remove(menu_func_export)
729 for cls in classes:
730 bpy.utils.unregister_class(cls)
733 if __name__ == "__main__":
734 register()