1 # SPDX-License-Identifier: Apache-2.0
2 # Copyright 2018-2021 The glTF-Blender-IO authors.
5 'name': 'glTF 2.0 format',
6 'author': 'Julien Duroure, Scurest, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors',
9 'location': 'File > Import-Export',
10 'description': 'Import-Export as glTF 2.0',
12 'doc_url': "{BLENDER_MANUAL_URL}/addons/import_export/scene_gltf2.html",
13 'tracker_url': "https://github.com/KhronosGroup/glTF-Blender-IO/issues/",
14 'support': 'OFFICIAL',
15 'category': 'Import-Export',
18 def get_version_string():
19 return str(bl_info
['version'][0]) + '.' + str(bl_info
['version'][1]) + '.' + str(bl_info
['version'][2])
22 # Script reloading (if the user calls 'Reload Scripts' from Blender)
25 def reload_package(module_dict_main
):
27 from pathlib
import Path
29 def reload_package_recursive(current_dir
, module_dict
):
30 for path
in current_dir
.iterdir():
31 if "__init__" in str(path
) or path
.stem
not in module_dict
:
34 if path
.is_file() and path
.suffix
== ".py":
35 importlib
.reload(module_dict
[path
.stem
])
37 reload_package_recursive(path
, module_dict
[path
.stem
].__dict
__)
39 reload_package_recursive(Path(__file__
).parent
, module_dict_main
)
43 reload_package(locals())
46 from bpy
.props
import (StringProperty
,
51 from bpy
.types
import Operator
52 from bpy_extras
.io_utils
import ImportHelper
, ExportHelper
56 # Functions / Classes.
59 exporter_extension_panel_unregister_functors
= []
60 importer_extension_panel_unregister_functors
= []
63 def ensure_filepath_matches_export_format(filepath
, export_format
):
65 filename
= os
.path
.basename(filepath
)
69 stem
, ext
= os
.path
.splitext(filename
)
70 if stem
.startswith('.') and not ext
:
73 desired_ext
= '.glb' if export_format
== 'GLB' else '.gltf'
74 ext_lower
= ext
.lower()
75 if ext_lower
not in ['.glb', '.gltf']:
76 return filepath
+ desired_ext
77 elif ext_lower
!= desired_ext
:
78 filepath
= filepath
[:-len(ext
)] # strip off ext
79 return filepath
+ desired_ext
84 def on_export_format_changed(self
, context
):
85 # Update the filename in the file browser when the format (.glb/.gltf)
87 sfile
= context
.space_data
88 if not isinstance(sfile
, bpy
.types
.SpaceFileBrowser
):
90 if not sfile
.active_operator
:
92 if sfile
.active_operator
.bl_idname
!= "EXPORT_SCENE_OT_gltf":
95 sfile
.params
.filename
= ensure_filepath_matches_export_format(
96 sfile
.params
.filename
,
101 class ExportGLTF2_Base
:
102 # TODO: refactor to avoid boilerplate
105 from io_scene_gltf2
.io
.com
import gltf2_io_draco_compression_extension
106 self
.is_draco_available
= gltf2_io_draco_compression_extension
.dll_exists()
108 bl_options
= {'PRESET'}
110 export_format
: EnumProperty(
112 items
=(('GLB', 'glTF Binary (.glb)',
113 'Exports a single file, with all data packed in binary form. '
114 'Most efficient and portable, but more difficult to edit later'),
115 ('GLTF_SEPARATE', 'glTF Separate (.gltf + .bin + textures)',
116 'Exports multiple files, with separate JSON, binary and texture data. '
117 'Easiest to edit later'),
118 ('GLTF_EMBEDDED', 'glTF Embedded (.gltf)',
119 'Exports a single file, with all data packed in JSON. '
120 'Less efficient than binary, but easier to edit later')),
122 'Output format and embedding options. Binary is most efficient, '
123 'but JSON (embedded or separate) may be easier to edit later'
126 update
=on_export_format_changed
,
129 ui_tab
: EnumProperty(
130 items
=(('GENERAL', "General", "General settings"),
131 ('MESHES', "Meshes", "Mesh settings"),
132 ('OBJECTS', "Objects", "Object settings"),
133 ('ANIMATION', "Animation", "Animation settings")),
135 description
="Export setting categories",
138 export_copyright
: StringProperty(
140 description
='Legal rights and conditions for the model',
144 export_image_format
: EnumProperty(
146 items
=(('AUTO', 'Automatic',
147 'Save PNGs as PNGs and JPEGs as JPEGs. '
148 'If neither one, use PNG'),
149 ('JPEG', 'JPEG Format (.jpg)',
150 'Save images as JPEGs. (Images that need alpha are saved as PNGs though.) '
151 'Be aware of a possible loss in quality'),
154 'Output format for images. PNG is lossless and generally preferred, but JPEG might be preferable for web '
155 'applications due to the smaller file size'
160 export_texture_dir
: StringProperty(
162 description
='Folder to place texture files in. Relative to the .gltf file',
166 export_keep_originals
: BoolProperty(
167 name
='Keep original',
168 description
=('Keep original textures files if possible. '
169 'WARNING: if you use more than one texture, '
170 'where pbr standard requires only one, only one texture will be used. '
171 'This can lead to unexpected results'
176 export_texcoords
: BoolProperty(
178 description
='Export UVs (texture coordinates) with meshes',
182 export_normals
: BoolProperty(
184 description
='Export vertex normals with meshes',
188 export_draco_mesh_compression_enable
: BoolProperty(
189 name
='Draco mesh compression',
190 description
='Compress mesh using Draco',
194 export_draco_mesh_compression_level
: IntProperty(
195 name
='Compression level',
196 description
='Compression level (0 = most speed, 6 = most compression, higher values currently not supported)',
202 export_draco_position_quantization
: IntProperty(
203 name
='Position quantization bits',
204 description
='Quantization bits for position values (0 = no quantization)',
210 export_draco_normal_quantization
: IntProperty(
211 name
='Normal quantization bits',
212 description
='Quantization bits for normal values (0 = no quantization)',
218 export_draco_texcoord_quantization
: IntProperty(
219 name
='Texcoord quantization bits',
220 description
='Quantization bits for texture coordinate values (0 = no quantization)',
226 export_draco_color_quantization
: IntProperty(
227 name
='Color quantization bits',
228 description
='Quantization bits for color values (0 = no quantization)',
234 export_draco_generic_quantization
: IntProperty(
235 name
='Generic quantization bits',
236 description
='Quantization bits for generic coordinate values like weights or joints (0 = no quantization)',
242 export_tangents
: BoolProperty(
244 description
='Export vertex tangents with meshes',
248 export_materials
: EnumProperty(
250 items
=(('EXPORT', 'Export',
251 'Export all materials used by included objects'),
252 ('PLACEHOLDER', 'Placeholder',
253 'Do not export materials, but write multiple primitive groups per mesh, keeping material slot information'),
254 ('NONE', 'No export',
255 'Do not export materials, and combine mesh primitive groups, losing material slot information')),
256 description
='Export materials ',
260 export_colors
: BoolProperty(
261 name
='Vertex Colors',
262 description
='Export vertex colors with meshes',
266 use_mesh_edges
: BoolProperty(
269 'Export loose edges as lines, using the material from the first material slot'
274 use_mesh_vertices
: BoolProperty(
277 'Export loose points as glTF points, using the material from the first material slot'
282 export_cameras
: BoolProperty(
284 description
='Export cameras',
288 # keep it for compatibility (for now)
289 export_selected
: BoolProperty(
290 name
='Selected Objects',
291 description
='Export selected objects only',
295 use_selection
: BoolProperty(
296 name
='Selected Objects',
297 description
='Export selected objects only',
301 use_visible
: BoolProperty(
302 name
='Visible Objects',
303 description
='Export visible objects only',
307 use_renderable
: BoolProperty(
308 name
='Renderable Objects',
309 description
='Export renderable objects only',
313 use_active_collection
: BoolProperty(
314 name
='Active Collection',
315 description
='Export objects in the active collection only',
319 export_extras
: BoolProperty(
320 name
='Custom Properties',
321 description
='Export custom properties as glTF extras',
325 export_yup
: BoolProperty(
327 description
='Export using glTF convention, +Y up',
331 export_apply
: BoolProperty(
332 name
='Apply Modifiers',
333 description
='Apply modifiers (excluding Armatures) to mesh objects -'
334 'WARNING: prevents exporting shape keys',
338 export_animations
: BoolProperty(
340 description
='Exports active actions and NLA tracks as glTF animations',
344 export_frame_range
: BoolProperty(
345 name
='Limit to Playback Range',
346 description
='Clips animations to selected playback range',
350 export_frame_step
: IntProperty(
351 name
='Sampling Rate',
352 description
='How often to evaluate animated values (in frames)',
358 export_force_sampling
: BoolProperty(
359 name
='Always Sample Animations',
360 description
='Apply sampling to all animations',
364 export_nla_strips
: BoolProperty(
365 name
='Group by NLA Track',
367 "When on, multiple actions become part of the same glTF animation if "
368 "they're pushed onto NLA tracks with the same name. "
369 "When off, all the currently assigned actions become one glTF animation"
374 export_def_bones
: BoolProperty(
375 name
='Export Deformation Bones Only',
376 description
='Export Deformation bones only (and needed bones for hierarchy)',
380 export_current_frame
: BoolProperty(
381 name
='Use Current Frame',
382 description
='Export the scene in the current animation frame',
386 export_skins
: BoolProperty(
388 description
='Export skinning (armature) data',
392 export_all_influences
: BoolProperty(
393 name
='Include All Bone Influences',
394 description
='Allow >4 joint vertex influences. Models may appear incorrectly in many viewers',
398 export_morph
: BoolProperty(
400 description
='Export shape keys (morph targets)',
404 export_morph_normal
: BoolProperty(
405 name
='Shape Key Normals',
406 description
='Export vertex normals with shape keys (morph targets)',
410 export_morph_tangent
: BoolProperty(
411 name
='Shape Key Tangents',
412 description
='Export vertex tangents with shape keys (morph targets)',
416 export_lights
: BoolProperty(
417 name
='Punctual Lights',
418 description
='Export directional, point, and spot lights. '
419 'Uses "KHR_lights_punctual" glTF extension',
423 export_displacement
: BoolProperty(
424 name
='Displacement Textures (EXPERIMENTAL)',
425 description
='EXPERIMENTAL: Export displacement textures. '
426 'Uses incomplete "KHR_materials_displacement" glTF extension',
430 will_save_settings
: BoolProperty(
431 name
='Remember Export Settings',
432 description
='Store glTF export settings in the Blender project',
435 # Custom scene property for saving settings
436 scene_key
= "glTF2ExportSettings"
440 def check(self
, _context
):
441 # Ensure file extension matches format
442 old_filepath
= self
.filepath
443 self
.filepath
= ensure_filepath_matches_export_format(
447 return self
.filepath
!= old_filepath
449 def invoke(self
, context
, event
):
450 settings
= context
.scene
.get(self
.scene_key
)
451 self
.will_save_settings
= False
454 if 'export_selected' in settings
.keys(): # Back compatibility for export_selected --> use_selection
455 setattr(self
, "use_selection", settings
['export_selected'])
456 settings
["use_selection"] = settings
['export_selected']
457 del settings
['export_selected']
458 print("export_selected is now renamed use_selection, and will be deleted in a few release")
459 for (k
, v
) in settings
.items():
461 self
.will_save_settings
= True
463 except (AttributeError, TypeError):
464 self
.report({"ERROR"}, "Loading export settings failed. Removed corrupted settings")
465 del context
.scene
[self
.scene_key
]
468 preferences
= bpy
.context
.preferences
469 for addon_name
in preferences
.addons
.keys():
471 if hasattr(sys
.modules
[addon_name
], 'glTF2ExportUserExtension') or hasattr(sys
.modules
[addon_name
], 'glTF2ExportUserExtensions'):
472 exporter_extension_panel_unregister_functors
.append(sys
.modules
[addon_name
].register_panel())
476 self
.has_active_exporter_extensions
= len(exporter_extension_panel_unregister_functors
) > 0
477 return ExportHelper
.invoke(self
, context
, event
)
479 def save_settings(self
, context
):
480 # find all props to save
482 # options that don't start with 'export_'
486 'use_active_collection',
490 all_props
= self
.properties
492 x
: getattr(self
, x
) for x
in dir(all_props
)
493 if (x
.startswith("export_") or x
in exceptional
) and all_props
.get(x
) is not None
495 if 'export_selected' in export_props
.keys():
496 del export_props
['export_selected'] # Do not save this property, only here for backward compatibility
497 context
.scene
[self
.scene_key
] = export_props
499 def execute(self
, context
):
502 from .blender
.exp
import gltf2_blender_export
504 if self
.will_save_settings
:
505 self
.save_settings(context
)
507 self
.check(context
) # ensure filepath has the right extension
509 # All custom export settings are stored in this container.
512 export_settings
['timestamp'] = datetime
.datetime
.now()
514 export_settings
['gltf_filepath'] = self
.filepath
515 export_settings
['gltf_filedirectory'] = os
.path
.dirname(export_settings
['gltf_filepath']) + '/'
516 export_settings
['gltf_texturedirectory'] = os
.path
.join(
517 export_settings
['gltf_filedirectory'],
518 self
.export_texture_dir
,
520 export_settings
['gltf_keep_original_textures'] = self
.export_keep_originals
522 export_settings
['gltf_format'] = self
.export_format
523 export_settings
['gltf_image_format'] = self
.export_image_format
524 export_settings
['gltf_copyright'] = self
.export_copyright
525 export_settings
['gltf_texcoords'] = self
.export_texcoords
526 export_settings
['gltf_normals'] = self
.export_normals
527 export_settings
['gltf_tangents'] = self
.export_tangents
and self
.export_normals
528 export_settings
['gltf_loose_edges'] = self
.use_mesh_edges
529 export_settings
['gltf_loose_points'] = self
.use_mesh_vertices
531 if self
.is_draco_available
:
532 export_settings
['gltf_draco_mesh_compression'] = self
.export_draco_mesh_compression_enable
533 export_settings
['gltf_draco_mesh_compression_level'] = self
.export_draco_mesh_compression_level
534 export_settings
['gltf_draco_position_quantization'] = self
.export_draco_position_quantization
535 export_settings
['gltf_draco_normal_quantization'] = self
.export_draco_normal_quantization
536 export_settings
['gltf_draco_texcoord_quantization'] = self
.export_draco_texcoord_quantization
537 export_settings
['gltf_draco_color_quantization'] = self
.export_draco_color_quantization
538 export_settings
['gltf_draco_generic_quantization'] = self
.export_draco_generic_quantization
540 export_settings
['gltf_draco_mesh_compression'] = False
542 export_settings
['gltf_materials'] = self
.export_materials
543 export_settings
['gltf_colors'] = self
.export_colors
544 export_settings
['gltf_cameras'] = self
.export_cameras
546 # compatibility after renaming export_selected to use_selection
547 if self
.export_selected
is True:
548 self
.report({"WARNING"}, "export_selected is now renamed use_selection, and will be deleted in a few release")
549 export_settings
['gltf_selected'] = self
.export_selected
551 export_settings
['gltf_selected'] = self
.use_selection
553 export_settings
['gltf_visible'] = self
.use_visible
554 export_settings
['gltf_renderable'] = self
.use_renderable
555 export_settings
['gltf_active_collection'] = self
.use_active_collection
557 # export_settings['gltf_selected'] = self.use_selection This can be uncomment when removing compatibility of export_selected
558 export_settings
['gltf_layers'] = True # self.export_layers
559 export_settings
['gltf_extras'] = self
.export_extras
560 export_settings
['gltf_yup'] = self
.export_yup
561 export_settings
['gltf_apply'] = self
.export_apply
562 export_settings
['gltf_current_frame'] = self
.export_current_frame
563 export_settings
['gltf_animations'] = self
.export_animations
564 if self
.export_animations
:
565 export_settings
['gltf_frame_range'] = self
.export_frame_range
566 export_settings
['gltf_force_sampling'] = self
.export_force_sampling
567 if self
.export_force_sampling
:
568 export_settings
['gltf_def_bones'] = self
.export_def_bones
570 export_settings
['gltf_def_bones'] = False
571 export_settings
['gltf_nla_strips'] = self
.export_nla_strips
573 export_settings
['gltf_frame_range'] = False
574 export_settings
['gltf_move_keyframes'] = False
575 export_settings
['gltf_force_sampling'] = False
576 export_settings
['gltf_def_bones'] = False
577 export_settings
['gltf_skins'] = self
.export_skins
578 if self
.export_skins
:
579 export_settings
['gltf_all_vertex_influences'] = self
.export_all_influences
581 export_settings
['gltf_all_vertex_influences'] = False
582 export_settings
['gltf_frame_step'] = self
.export_frame_step
583 export_settings
['gltf_morph'] = self
.export_morph
584 if self
.export_morph
:
585 export_settings
['gltf_morph_normal'] = self
.export_morph_normal
587 export_settings
['gltf_morph_normal'] = False
588 if self
.export_morph
and self
.export_morph_normal
:
589 export_settings
['gltf_morph_tangent'] = self
.export_morph_tangent
591 export_settings
['gltf_morph_tangent'] = False
593 export_settings
['gltf_lights'] = self
.export_lights
594 export_settings
['gltf_displacement'] = self
.export_displacement
596 export_settings
['gltf_binary'] = bytearray()
597 export_settings
['gltf_binaryfilename'] = (
598 os
.path
.splitext(os
.path
.basename(self
.filepath
))[0] + '.bin'
602 pre_export_callbacks
= []
603 post_export_callbacks
= []
606 preferences
= bpy
.context
.preferences
607 for addon_name
in preferences
.addons
.keys():
609 module
= sys
.modules
[addon_name
]
612 if hasattr(module
, 'glTF2ExportUserExtension'):
613 extension_ctor
= module
.glTF2ExportUserExtension
614 user_extensions
.append(extension_ctor())
615 if hasattr(module
, 'glTF2ExportUserExtensions'):
616 extension_ctors
= module
.glTF2ExportUserExtensions
617 for extension_ctor
in extension_ctors
:
618 user_extensions
.append(extension_ctor())
619 if hasattr(module
, 'glTF2_pre_export_callback'):
620 pre_export_callbacks
.append(module
.glTF2_pre_export_callback
)
621 if hasattr(module
, 'glTF2_post_export_callback'):
622 post_export_callbacks
.append(module
.glTF2_post_export_callback
)
623 export_settings
['gltf_user_extensions'] = user_extensions
624 export_settings
['pre_export_callbacks'] = pre_export_callbacks
625 export_settings
['post_export_callbacks'] = post_export_callbacks
627 return gltf2_blender_export
.save(context
, export_settings
)
629 def draw(self
, context
):
630 pass # Is needed to get panels available
633 class GLTF_PT_export_main(bpy
.types
.Panel
):
634 bl_space_type
= 'FILE_BROWSER'
635 bl_region_type
= 'TOOL_PROPS'
637 bl_parent_id
= "FILE_PT_operator"
638 bl_options
= {'HIDE_HEADER'}
641 def poll(cls
, context
):
642 sfile
= context
.space_data
643 operator
= sfile
.active_operator
645 return operator
.bl_idname
== "EXPORT_SCENE_OT_gltf"
647 def draw(self
, context
):
649 layout
.use_property_split
= True
650 layout
.use_property_decorate
= False # No animation.
652 sfile
= context
.space_data
653 operator
= sfile
.active_operator
655 layout
.prop(operator
, 'export_format')
656 if operator
.export_format
== 'GLTF_SEPARATE':
657 layout
.prop(operator
, 'export_keep_originals')
658 if operator
.export_keep_originals
is False:
659 layout
.prop(operator
, 'export_texture_dir', icon
='FILE_FOLDER')
661 layout
.prop(operator
, 'export_copyright')
662 layout
.prop(operator
, 'will_save_settings')
665 class GLTF_PT_export_include(bpy
.types
.Panel
):
666 bl_space_type
= 'FILE_BROWSER'
667 bl_region_type
= 'TOOL_PROPS'
669 bl_parent_id
= "FILE_PT_operator"
670 bl_options
= {'DEFAULT_CLOSED'}
673 def poll(cls
, context
):
674 sfile
= context
.space_data
675 operator
= sfile
.active_operator
677 return operator
.bl_idname
== "EXPORT_SCENE_OT_gltf"
679 def draw(self
, context
):
681 layout
.use_property_split
= True
682 layout
.use_property_decorate
= False # No animation.
684 sfile
= context
.space_data
685 operator
= sfile
.active_operator
687 col
= layout
.column(heading
= "Limit to", align
= True)
688 col
.prop(operator
, 'use_selection')
689 col
.prop(operator
, 'use_visible')
690 col
.prop(operator
, 'use_renderable')
691 col
.prop(operator
, 'use_active_collection')
693 col
= layout
.column(heading
= "Data", align
= True)
694 col
.prop(operator
, 'export_extras')
695 col
.prop(operator
, 'export_cameras')
696 col
.prop(operator
, 'export_lights')
699 class GLTF_PT_export_transform(bpy
.types
.Panel
):
700 bl_space_type
= 'FILE_BROWSER'
701 bl_region_type
= 'TOOL_PROPS'
702 bl_label
= "Transform"
703 bl_parent_id
= "FILE_PT_operator"
704 bl_options
= {'DEFAULT_CLOSED'}
707 def poll(cls
, context
):
708 sfile
= context
.space_data
709 operator
= sfile
.active_operator
711 return operator
.bl_idname
== "EXPORT_SCENE_OT_gltf"
713 def draw(self
, context
):
715 layout
.use_property_split
= True
716 layout
.use_property_decorate
= False # No animation.
718 sfile
= context
.space_data
719 operator
= sfile
.active_operator
721 layout
.prop(operator
, 'export_yup')
724 class GLTF_PT_export_geometry(bpy
.types
.Panel
):
725 bl_space_type
= 'FILE_BROWSER'
726 bl_region_type
= 'TOOL_PROPS'
727 bl_label
= "Geometry"
728 bl_parent_id
= "FILE_PT_operator"
729 bl_options
= {'DEFAULT_CLOSED'}
732 def poll(cls
, context
):
733 sfile
= context
.space_data
734 operator
= sfile
.active_operator
736 return operator
.bl_idname
== "EXPORT_SCENE_OT_gltf"
738 def draw(self
, context
):
740 layout
.use_property_split
= True
741 layout
.use_property_decorate
= False # No animation.
743 sfile
= context
.space_data
744 operator
= sfile
.active_operator
746 layout
.prop(operator
, 'export_apply')
747 layout
.prop(operator
, 'export_texcoords')
748 layout
.prop(operator
, 'export_normals')
749 col
= layout
.column()
750 col
.active
= operator
.export_normals
751 col
.prop(operator
, 'export_tangents')
752 layout
.prop(operator
, 'export_colors')
754 col
= layout
.column()
755 col
.prop(operator
, 'use_mesh_edges')
756 col
.prop(operator
, 'use_mesh_vertices')
758 layout
.prop(operator
, 'export_materials')
759 col
= layout
.column()
760 col
.active
= operator
.export_materials
== "EXPORT"
761 col
.prop(operator
, 'export_image_format')
764 class GLTF_PT_export_geometry_compression(bpy
.types
.Panel
):
765 bl_space_type
= 'FILE_BROWSER'
766 bl_region_type
= 'TOOL_PROPS'
767 bl_label
= "Compression"
768 bl_parent_id
= "GLTF_PT_export_geometry"
769 bl_options
= {'DEFAULT_CLOSED'}
772 from io_scene_gltf2
.io
.com
import gltf2_io_draco_compression_extension
773 self
.is_draco_available
= gltf2_io_draco_compression_extension
.dll_exists(quiet
=True)
776 def poll(cls
, context
):
777 sfile
= context
.space_data
778 operator
= sfile
.active_operator
779 if operator
.is_draco_available
:
780 return operator
.bl_idname
== "EXPORT_SCENE_OT_gltf"
782 def draw_header(self
, context
):
783 sfile
= context
.space_data
784 operator
= sfile
.active_operator
785 self
.layout
.prop(operator
, "export_draco_mesh_compression_enable", text
="")
787 def draw(self
, context
):
789 layout
.use_property_split
= True
790 layout
.use_property_decorate
= False # No animation.
792 sfile
= context
.space_data
793 operator
= sfile
.active_operator
795 layout
.active
= operator
.export_draco_mesh_compression_enable
796 layout
.prop(operator
, 'export_draco_mesh_compression_level')
798 col
= layout
.column(align
=True)
799 col
.prop(operator
, 'export_draco_position_quantization', text
="Quantize Position")
800 col
.prop(operator
, 'export_draco_normal_quantization', text
="Normal")
801 col
.prop(operator
, 'export_draco_texcoord_quantization', text
="Tex Coord")
802 col
.prop(operator
, 'export_draco_color_quantization', text
="Color")
803 col
.prop(operator
, 'export_draco_generic_quantization', text
="Generic")
806 class GLTF_PT_export_animation(bpy
.types
.Panel
):
807 bl_space_type
= 'FILE_BROWSER'
808 bl_region_type
= 'TOOL_PROPS'
809 bl_label
= "Animation"
810 bl_parent_id
= "FILE_PT_operator"
811 bl_options
= {'DEFAULT_CLOSED'}
814 def poll(cls
, context
):
815 sfile
= context
.space_data
816 operator
= sfile
.active_operator
818 return operator
.bl_idname
== "EXPORT_SCENE_OT_gltf"
820 def draw(self
, context
):
822 layout
.use_property_split
= True
823 layout
.use_property_decorate
= False # No animation.
825 sfile
= context
.space_data
826 operator
= sfile
.active_operator
828 layout
.prop(operator
, 'export_current_frame')
831 class GLTF_PT_export_animation_export(bpy
.types
.Panel
):
832 bl_space_type
= 'FILE_BROWSER'
833 bl_region_type
= 'TOOL_PROPS'
834 bl_label
= "Animation"
835 bl_parent_id
= "GLTF_PT_export_animation"
836 bl_options
= {'DEFAULT_CLOSED'}
839 def poll(cls
, context
):
840 sfile
= context
.space_data
841 operator
= sfile
.active_operator
843 return operator
.bl_idname
== "EXPORT_SCENE_OT_gltf"
845 def draw_header(self
, context
):
846 sfile
= context
.space_data
847 operator
= sfile
.active_operator
848 self
.layout
.prop(operator
, "export_animations", text
="")
850 def draw(self
, context
):
852 layout
.use_property_split
= True
853 layout
.use_property_decorate
= False # No animation.
855 sfile
= context
.space_data
856 operator
= sfile
.active_operator
858 layout
.active
= operator
.export_animations
860 layout
.prop(operator
, 'export_frame_range')
861 layout
.prop(operator
, 'export_frame_step')
862 layout
.prop(operator
, 'export_force_sampling')
863 layout
.prop(operator
, 'export_nla_strips')
866 row
.active
= operator
.export_force_sampling
867 row
.prop(operator
, 'export_def_bones')
870 class GLTF_PT_export_animation_shapekeys(bpy
.types
.Panel
):
871 bl_space_type
= 'FILE_BROWSER'
872 bl_region_type
= 'TOOL_PROPS'
873 bl_label
= "Shape Keys"
874 bl_parent_id
= "GLTF_PT_export_animation"
875 bl_options
= {'DEFAULT_CLOSED'}
878 def poll(cls
, context
):
879 sfile
= context
.space_data
880 operator
= sfile
.active_operator
882 return operator
.bl_idname
== "EXPORT_SCENE_OT_gltf"
884 def draw_header(self
, context
):
885 sfile
= context
.space_data
886 operator
= sfile
.active_operator
887 self
.layout
.prop(operator
, "export_morph", text
="")
889 def draw(self
, context
):
891 layout
.use_property_split
= True
892 layout
.use_property_decorate
= False # No animation.
894 sfile
= context
.space_data
895 operator
= sfile
.active_operator
897 layout
.active
= operator
.export_morph
899 layout
.prop(operator
, 'export_morph_normal')
900 col
= layout
.column()
901 col
.active
= operator
.export_morph_normal
902 col
.prop(operator
, 'export_morph_tangent')
905 class GLTF_PT_export_animation_skinning(bpy
.types
.Panel
):
906 bl_space_type
= 'FILE_BROWSER'
907 bl_region_type
= 'TOOL_PROPS'
908 bl_label
= "Skinning"
909 bl_parent_id
= "GLTF_PT_export_animation"
910 bl_options
= {'DEFAULT_CLOSED'}
913 def poll(cls
, context
):
914 sfile
= context
.space_data
915 operator
= sfile
.active_operator
917 return operator
.bl_idname
== "EXPORT_SCENE_OT_gltf"
919 def draw_header(self
, context
):
920 sfile
= context
.space_data
921 operator
= sfile
.active_operator
922 self
.layout
.prop(operator
, "export_skins", text
="")
924 def draw(self
, context
):
926 layout
.use_property_split
= True
927 layout
.use_property_decorate
= False # No animation.
929 sfile
= context
.space_data
930 operator
= sfile
.active_operator
932 layout
.active
= operator
.export_skins
933 layout
.prop(operator
, 'export_all_influences')
935 class GLTF_PT_export_user_extensions(bpy
.types
.Panel
):
936 bl_space_type
= 'FILE_BROWSER'
937 bl_region_type
= 'TOOL_PROPS'
938 bl_label
= "Exporter Extensions"
939 bl_parent_id
= "FILE_PT_operator"
940 bl_options
= {'DEFAULT_CLOSED'}
943 def poll(cls
, context
):
944 sfile
= context
.space_data
945 operator
= sfile
.active_operator
947 return operator
.bl_idname
== "EXPORT_SCENE_OT_gltf" and operator
.has_active_exporter_extensions
949 def draw(self
, context
):
951 layout
.use_property_split
= True
952 layout
.use_property_decorate
= False # No animation.
954 class GLTF_PT_import_user_extensions(bpy
.types
.Panel
):
955 bl_space_type
= 'FILE_BROWSER'
956 bl_region_type
= 'TOOL_PROPS'
957 bl_label
= "Importer Extensions"
958 bl_parent_id
= "FILE_PT_operator"
959 bl_options
= {'DEFAULT_CLOSED'}
962 def poll(cls
, context
):
963 sfile
= context
.space_data
964 operator
= sfile
.active_operator
965 return operator
.bl_idname
== "IMPORT_SCENE_OT_gltf" and operator
.has_active_importer_extensions
967 def draw(self
, context
):
969 layout
.use_property_split
= True
970 layout
.use_property_decorate
= False # No animation.
972 class ExportGLTF2(bpy
.types
.Operator
, ExportGLTF2_Base
, ExportHelper
):
973 """Export scene as glTF 2.0 file"""
974 bl_idname
= 'export_scene.gltf'
975 bl_label
= 'Export glTF 2.0'
979 filter_glob
: StringProperty(default
='*.glb;*.gltf', options
={'HIDDEN'})
982 def menu_func_export(self
, context
):
983 self
.layout
.operator(ExportGLTF2
.bl_idname
, text
='glTF 2.0 (.glb/.gltf)')
986 class ImportGLTF2(Operator
, ImportHelper
):
987 """Load a glTF 2.0 file"""
988 bl_idname
= 'import_scene.gltf'
989 bl_label
= 'Import glTF 2.0'
990 bl_options
= {'REGISTER', 'UNDO'}
992 filter_glob
: StringProperty(default
="*.glb;*.gltf", options
={'HIDDEN'})
994 files
: CollectionProperty(
996 type=bpy
.types
.OperatorFileListElement
,
999 loglevel
: IntProperty(
1001 description
="Log Level")
1003 import_pack_images
: BoolProperty(
1005 description
='Pack all images into .blend file',
1009 merge_vertices
: BoolProperty(
1010 name
='Merge Vertices',
1012 'The glTF format requires discontinuous normals, UVs, and '
1013 'other vertex attributes to be stored as separate vertices, '
1014 'as required for rendering on typical graphics hardware. '
1015 'This option attempts to combine co-located vertices where possible. '
1016 'Currently cannot combine verts with different normals'
1021 import_shading
: EnumProperty(
1023 items
=(("NORMALS", "Use Normal Data", ""),
1024 ("FLAT", "Flat Shading", ""),
1025 ("SMOOTH", "Smooth Shading", "")),
1026 description
="How normals are computed during import",
1029 bone_heuristic
: EnumProperty(
1032 ("BLENDER", "Blender (best for re-importing)",
1033 "Good for re-importing glTFs exported from Blender. "
1034 "Bone tips are placed on their local +Y axis (in glTF space)"),
1035 ("TEMPERANCE", "Temperance (average)",
1036 "Decent all-around strategy. "
1037 "A bone with one child has its tip placed on the local axis "
1038 "closest to its child"),
1039 ("FORTUNE", "Fortune (may look better, less accurate)",
1040 "Might look better than Temperance, but also might have errors. "
1041 "A bone with one child has its tip placed at its child's root. "
1042 "Non-uniform scalings may get messed up though, so beware"),
1044 description
="Heuristic for placing bones. Tries to make bones pretty",
1045 default
="TEMPERANCE",
1048 guess_original_bind_pose
: BoolProperty(
1049 name
='Guess Original Bind Pose',
1051 'Try to guess the original bind pose for skinned meshes from '
1052 'the inverse bind matrices. '
1053 'When off, use default/rest pose as bind pose'
1058 def draw(self
, context
):
1059 layout
= self
.layout
1061 layout
.use_property_split
= True
1062 layout
.use_property_decorate
= False # No animation.
1064 layout
.prop(self
, 'import_pack_images')
1065 layout
.prop(self
, 'merge_vertices')
1066 layout
.prop(self
, 'import_shading')
1067 layout
.prop(self
, 'guess_original_bind_pose')
1068 layout
.prop(self
, 'bone_heuristic')
1070 def invoke(self
, context
, event
):
1072 preferences
= bpy
.context
.preferences
1073 for addon_name
in preferences
.addons
.keys():
1075 if hasattr(sys
.modules
[addon_name
], 'glTF2ImportUserExtension') or hasattr(sys
.modules
[addon_name
], 'glTF2ImportUserExtensions'):
1076 importer_extension_panel_unregister_functors
.append(sys
.modules
[addon_name
].register_panel())
1080 self
.has_active_importer_extensions
= len(importer_extension_panel_unregister_functors
) > 0
1081 return ImportHelper
.invoke(self
, context
, event
)
1083 def execute(self
, context
):
1084 return self
.import_gltf2(context
)
1086 def import_gltf2(self
, context
):
1089 self
.set_debug_log()
1090 import_settings
= self
.as_keywords()
1092 user_extensions
= []
1095 preferences
= bpy
.context
.preferences
1096 for addon_name
in preferences
.addons
.keys():
1098 module
= sys
.modules
[addon_name
]
1101 if hasattr(module
, 'glTF2ImportUserExtension'):
1102 extension_ctor
= module
.glTF2ImportUserExtension
1103 user_extensions
.append(extension_ctor())
1104 import_settings
['import_user_extensions'] = user_extensions
1107 # Multiple file import
1109 dirname
= os
.path
.dirname(self
.filepath
)
1110 for file in self
.files
:
1111 path
= os
.path
.join(dirname
, file.name
)
1112 if self
.unit_import(path
, import_settings
) == {'FINISHED'}:
1116 # Single file import
1117 return self
.unit_import(self
.filepath
, import_settings
)
1119 def unit_import(self
, filename
, import_settings
):
1121 from .io
.imp
.gltf2_io_gltf
import glTFImporter
, ImportError
1122 from .blender
.imp
.gltf2_blender_gltf
import BlenderGlTF
1125 gltf_importer
= glTFImporter(filename
, import_settings
)
1126 gltf_importer
.read()
1127 gltf_importer
.checks()
1129 print("Data are loaded, start creating Blender stuff")
1131 start_time
= time
.time()
1132 BlenderGlTF
.create(gltf_importer
)
1133 elapsed_s
= "{:.2f}s".format(time
.time() - start_time
)
1134 print("glTF import finished in " + elapsed_s
)
1136 gltf_importer
.log
.removeHandler(gltf_importer
.log_handler
)
1140 except ImportError as e
:
1141 self
.report({'ERROR'}, e
.args
[0])
1142 return {'CANCELLED'}
1144 def set_debug_log(self
):
1146 if bpy
.app
.debug_value
== 0:
1147 self
.loglevel
= logging
.CRITICAL
1148 elif bpy
.app
.debug_value
== 1:
1149 self
.loglevel
= logging
.ERROR
1150 elif bpy
.app
.debug_value
== 2:
1151 self
.loglevel
= logging
.WARNING
1152 elif bpy
.app
.debug_value
== 3:
1153 self
.loglevel
= logging
.INFO
1155 self
.loglevel
= logging
.NOTSET
1158 def menu_func_import(self
, context
):
1159 self
.layout
.operator(ImportGLTF2
.bl_idname
, text
='glTF 2.0 (.glb/.gltf)')
1164 GLTF_PT_export_main
,
1165 GLTF_PT_export_include
,
1166 GLTF_PT_export_transform
,
1167 GLTF_PT_export_geometry
,
1168 GLTF_PT_export_geometry_compression
,
1169 GLTF_PT_export_animation
,
1170 GLTF_PT_export_animation_export
,
1171 GLTF_PT_export_animation_shapekeys
,
1172 GLTF_PT_export_animation_skinning
,
1173 GLTF_PT_export_user_extensions
,
1175 GLTF_PT_import_user_extensions
1181 bpy
.utils
.register_class(c
)
1182 # bpy.utils.register_module(__name__)
1184 # add to the export / import menu
1185 bpy
.types
.TOPBAR_MT_file_export
.append(menu_func_export
)
1186 bpy
.types
.TOPBAR_MT_file_import
.append(menu_func_import
)
1191 bpy
.utils
.unregister_class(c
)
1192 for f
in exporter_extension_panel_unregister_functors
:
1194 exporter_extension_panel_unregister_functors
.clear()
1196 for f
in importer_extension_panel_unregister_functors
:
1198 importer_extension_panel_unregister_functors
.clear()
1200 # bpy.utils.unregister_module(__name__)
1202 # remove from the export / import menu
1203 bpy
.types
.TOPBAR_MT_file_export
.remove(menu_func_export
)
1204 bpy
.types
.TOPBAR_MT_file_import
.remove(menu_func_import
)