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'),
153 'Don\'t export images'),
156 'Output format for images. PNG is lossless and generally preferred, but JPEG might be preferable for web '
157 'applications due to the smaller file size. Alternatively they can be omitted if they are not needed'
162 export_texture_dir
: StringProperty(
164 description
='Folder to place texture files in. Relative to the .gltf file',
168 export_keep_originals
: BoolProperty(
169 name
='Keep original',
170 description
=('Keep original textures files if possible. '
171 'WARNING: if you use more than one texture, '
172 'where pbr standard requires only one, only one texture will be used. '
173 'This can lead to unexpected results'
178 export_texcoords
: BoolProperty(
180 description
='Export UVs (texture coordinates) with meshes',
184 export_normals
: BoolProperty(
186 description
='Export vertex normals with meshes',
190 export_draco_mesh_compression_enable
: BoolProperty(
191 name
='Draco mesh compression',
192 description
='Compress mesh using Draco',
196 export_draco_mesh_compression_level
: IntProperty(
197 name
='Compression level',
198 description
='Compression level (0 = most speed, 6 = most compression, higher values currently not supported)',
204 export_draco_position_quantization
: IntProperty(
205 name
='Position quantization bits',
206 description
='Quantization bits for position values (0 = no quantization)',
212 export_draco_normal_quantization
: IntProperty(
213 name
='Normal quantization bits',
214 description
='Quantization bits for normal values (0 = no quantization)',
220 export_draco_texcoord_quantization
: IntProperty(
221 name
='Texcoord quantization bits',
222 description
='Quantization bits for texture coordinate values (0 = no quantization)',
228 export_draco_color_quantization
: IntProperty(
229 name
='Color quantization bits',
230 description
='Quantization bits for color values (0 = no quantization)',
236 export_draco_generic_quantization
: IntProperty(
237 name
='Generic quantization bits',
238 description
='Quantization bits for generic coordinate values like weights or joints (0 = no quantization)',
244 export_tangents
: BoolProperty(
246 description
='Export vertex tangents with meshes',
250 export_materials
: EnumProperty(
252 items
=(('EXPORT', 'Export',
253 'Export all materials used by included objects'),
254 ('PLACEHOLDER', 'Placeholder',
255 'Do not export materials, but write multiple primitive groups per mesh, keeping material slot information'),
256 ('NONE', 'No export',
257 'Do not export materials, and combine mesh primitive groups, losing material slot information')),
258 description
='Export materials ',
262 export_original_specular
: BoolProperty(
263 name
='Export original PBR Specular',
265 'Export original glTF PBR Specular, instead of Blender Principled Shader Specular'
270 export_colors
: BoolProperty(
271 name
='Vertex Colors',
272 description
='Export vertex colors with meshes',
276 use_mesh_edges
: BoolProperty(
279 'Export loose edges as lines, using the material from the first material slot'
284 use_mesh_vertices
: BoolProperty(
287 'Export loose points as glTF points, using the material from the first material slot'
292 export_cameras
: BoolProperty(
294 description
='Export cameras',
298 use_selection
: BoolProperty(
299 name
='Selected Objects',
300 description
='Export selected objects only',
304 use_visible
: BoolProperty(
305 name
='Visible Objects',
306 description
='Export visible objects only',
310 use_renderable
: BoolProperty(
311 name
='Renderable Objects',
312 description
='Export renderable objects only',
316 use_active_collection
: BoolProperty(
317 name
='Active Collection',
318 description
='Export objects in the active collection only',
322 use_active_scene
: BoolProperty(
324 description
='Export active scene only',
328 export_extras
: BoolProperty(
329 name
='Custom Properties',
330 description
='Export custom properties as glTF extras',
334 export_yup
: BoolProperty(
336 description
='Export using glTF convention, +Y up',
340 export_apply
: BoolProperty(
341 name
='Apply Modifiers',
342 description
='Apply modifiers (excluding Armatures) to mesh objects -'
343 'WARNING: prevents exporting shape keys',
347 export_animations
: BoolProperty(
349 description
='Exports active actions and NLA tracks as glTF animations',
353 export_frame_range
: BoolProperty(
354 name
='Limit to Playback Range',
355 description
='Clips animations to selected playback range',
359 export_frame_step
: IntProperty(
360 name
='Sampling Rate',
361 description
='How often to evaluate animated values (in frames)',
367 export_force_sampling
: BoolProperty(
368 name
='Always Sample Animations',
369 description
='Apply sampling to all animations',
373 export_nla_strips
: BoolProperty(
374 name
='Group by NLA Track',
376 "When on, multiple actions become part of the same glTF animation if "
377 "they're pushed onto NLA tracks with the same name. "
378 "When off, all the currently assigned actions become one glTF animation"
383 export_nla_strips_merged_animation_name
: StringProperty(
384 name
='Merged Animation Name',
386 "Name of single glTF animation to be exported"
391 export_def_bones
: BoolProperty(
392 name
='Export Deformation Bones Only',
393 description
='Export Deformation bones only',
397 export_optimize_animation_size
: BoolProperty(
398 name
='Optimize Animation Size',
400 "Reduce exported file-size by removing duplicate keyframes"
401 "(can cause problems with stepped animation)"
406 export_anim_single_armature
: BoolProperty(
407 name
='Export all Armature Actions',
409 "Export all actions, bound to a single armature. "
410 "WARNING: Option does not support exports including multiple armatures"
415 export_current_frame
: BoolProperty(
416 name
='Use Current Frame',
417 description
='Export the scene in the current animation frame',
421 export_skins
: BoolProperty(
423 description
='Export skinning (armature) data',
427 export_all_influences
: BoolProperty(
428 name
='Include All Bone Influences',
429 description
='Allow >4 joint vertex influences. Models may appear incorrectly in many viewers',
433 export_morph
: BoolProperty(
435 description
='Export shape keys (morph targets)',
439 export_morph_normal
: BoolProperty(
440 name
='Shape Key Normals',
441 description
='Export vertex normals with shape keys (morph targets)',
445 export_morph_tangent
: BoolProperty(
446 name
='Shape Key Tangents',
447 description
='Export vertex tangents with shape keys (morph targets)',
451 export_lights
: BoolProperty(
452 name
='Punctual Lights',
453 description
='Export directional, point, and spot lights. '
454 'Uses "KHR_lights_punctual" glTF extension',
458 will_save_settings
: BoolProperty(
459 name
='Remember Export Settings',
460 description
='Store glTF export settings in the Blender project',
463 # Custom scene property for saving settings
464 scene_key
= "glTF2ExportSettings"
468 def check(self
, _context
):
469 # Ensure file extension matches format
470 old_filepath
= self
.filepath
471 self
.filepath
= ensure_filepath_matches_export_format(
475 return self
.filepath
!= old_filepath
477 def invoke(self
, context
, event
):
478 settings
= context
.scene
.get(self
.scene_key
)
479 self
.will_save_settings
= False
482 for (k
, v
) in settings
.items():
484 self
.will_save_settings
= True
486 except (AttributeError, TypeError):
487 self
.report({"ERROR"}, "Loading export settings failed. Removed corrupted settings")
488 del context
.scene
[self
.scene_key
]
491 preferences
= bpy
.context
.preferences
492 for addon_name
in preferences
.addons
.keys():
494 if hasattr(sys
.modules
[addon_name
], 'glTF2ExportUserExtension') or hasattr(sys
.modules
[addon_name
], 'glTF2ExportUserExtensions'):
495 exporter_extension_panel_unregister_functors
.append(sys
.modules
[addon_name
].register_panel())
499 self
.has_active_exporter_extensions
= len(exporter_extension_panel_unregister_functors
) > 0
500 return ExportHelper
.invoke(self
, context
, event
)
502 def save_settings(self
, context
):
503 # find all props to save
505 # options that don't start with 'export_'
509 'use_active_collection',
514 all_props
= self
.properties
516 x
: getattr(self
, x
) for x
in dir(all_props
)
517 if (x
.startswith("export_") or x
in exceptional
) and all_props
.get(x
) is not None
519 context
.scene
[self
.scene_key
] = export_props
521 def execute(self
, context
):
524 from .blender
.exp
import gltf2_blender_export
526 if self
.will_save_settings
:
527 self
.save_settings(context
)
529 self
.check(context
) # ensure filepath has the right extension
531 # All custom export settings are stored in this container.
534 export_settings
['timestamp'] = datetime
.datetime
.now()
536 export_settings
['gltf_filepath'] = self
.filepath
537 export_settings
['gltf_filedirectory'] = os
.path
.dirname(export_settings
['gltf_filepath']) + '/'
538 export_settings
['gltf_texturedirectory'] = os
.path
.join(
539 export_settings
['gltf_filedirectory'],
540 self
.export_texture_dir
,
542 export_settings
['gltf_keep_original_textures'] = self
.export_keep_originals
544 export_settings
['gltf_format'] = self
.export_format
545 export_settings
['gltf_image_format'] = self
.export_image_format
546 export_settings
['gltf_copyright'] = self
.export_copyright
547 export_settings
['gltf_texcoords'] = self
.export_texcoords
548 export_settings
['gltf_normals'] = self
.export_normals
549 export_settings
['gltf_tangents'] = self
.export_tangents
and self
.export_normals
550 export_settings
['gltf_loose_edges'] = self
.use_mesh_edges
551 export_settings
['gltf_loose_points'] = self
.use_mesh_vertices
553 if self
.is_draco_available
:
554 export_settings
['gltf_draco_mesh_compression'] = self
.export_draco_mesh_compression_enable
555 export_settings
['gltf_draco_mesh_compression_level'] = self
.export_draco_mesh_compression_level
556 export_settings
['gltf_draco_position_quantization'] = self
.export_draco_position_quantization
557 export_settings
['gltf_draco_normal_quantization'] = self
.export_draco_normal_quantization
558 export_settings
['gltf_draco_texcoord_quantization'] = self
.export_draco_texcoord_quantization
559 export_settings
['gltf_draco_color_quantization'] = self
.export_draco_color_quantization
560 export_settings
['gltf_draco_generic_quantization'] = self
.export_draco_generic_quantization
562 export_settings
['gltf_draco_mesh_compression'] = False
564 export_settings
['gltf_materials'] = self
.export_materials
565 export_settings
['gltf_colors'] = self
.export_colors
566 export_settings
['gltf_cameras'] = self
.export_cameras
568 export_settings
['gltf_original_specular'] = self
.export_original_specular
570 export_settings
['gltf_visible'] = self
.use_visible
571 export_settings
['gltf_renderable'] = self
.use_renderable
572 export_settings
['gltf_active_collection'] = self
.use_active_collection
573 export_settings
['gltf_active_scene'] = self
.use_active_scene
575 export_settings
['gltf_selected'] = self
.use_selection
576 export_settings
['gltf_layers'] = True # self.export_layers
577 export_settings
['gltf_extras'] = self
.export_extras
578 export_settings
['gltf_yup'] = self
.export_yup
579 export_settings
['gltf_apply'] = self
.export_apply
580 export_settings
['gltf_current_frame'] = self
.export_current_frame
581 export_settings
['gltf_animations'] = self
.export_animations
582 export_settings
['gltf_def_bones'] = self
.export_def_bones
583 if self
.export_animations
:
584 export_settings
['gltf_frame_range'] = self
.export_frame_range
585 export_settings
['gltf_force_sampling'] = self
.export_force_sampling
586 if not self
.export_force_sampling
:
587 export_settings
['gltf_def_bones'] = False
588 export_settings
['gltf_nla_strips'] = self
.export_nla_strips
589 export_settings
['gltf_nla_strips_merged_animation_name'] = self
.export_nla_strips_merged_animation_name
590 export_settings
['gltf_optimize_animation'] = self
.export_optimize_animation_size
591 export_settings
['gltf_export_anim_single_armature'] = self
.export_anim_single_armature
593 export_settings
['gltf_frame_range'] = False
594 export_settings
['gltf_move_keyframes'] = False
595 export_settings
['gltf_force_sampling'] = False
596 export_settings
['gltf_optimize_animation'] = False
597 export_settings
['gltf_export_anim_single_armature'] = False
598 export_settings
['gltf_skins'] = self
.export_skins
599 if self
.export_skins
:
600 export_settings
['gltf_all_vertex_influences'] = self
.export_all_influences
602 export_settings
['gltf_all_vertex_influences'] = False
603 export_settings
['gltf_def_bones'] = False
604 export_settings
['gltf_frame_step'] = self
.export_frame_step
605 export_settings
['gltf_morph'] = self
.export_morph
606 if self
.export_morph
:
607 export_settings
['gltf_morph_normal'] = self
.export_morph_normal
609 export_settings
['gltf_morph_normal'] = False
610 if self
.export_morph
and self
.export_morph_normal
:
611 export_settings
['gltf_morph_tangent'] = self
.export_morph_tangent
613 export_settings
['gltf_morph_tangent'] = False
615 export_settings
['gltf_lights'] = self
.export_lights
617 export_settings
['gltf_binary'] = bytearray()
618 export_settings
['gltf_binaryfilename'] = (
619 os
.path
.splitext(os
.path
.basename(self
.filepath
))[0] + '.bin'
623 pre_export_callbacks
= []
624 post_export_callbacks
= []
627 preferences
= bpy
.context
.preferences
628 for addon_name
in preferences
.addons
.keys():
630 module
= sys
.modules
[addon_name
]
633 if hasattr(module
, 'glTF2ExportUserExtension'):
634 extension_ctor
= module
.glTF2ExportUserExtension
635 user_extensions
.append(extension_ctor())
636 if hasattr(module
, 'glTF2ExportUserExtensions'):
637 extension_ctors
= module
.glTF2ExportUserExtensions
638 for extension_ctor
in extension_ctors
:
639 user_extensions
.append(extension_ctor())
640 if hasattr(module
, 'glTF2_pre_export_callback'):
641 pre_export_callbacks
.append(module
.glTF2_pre_export_callback
)
642 if hasattr(module
, 'glTF2_post_export_callback'):
643 post_export_callbacks
.append(module
.glTF2_post_export_callback
)
644 export_settings
['gltf_user_extensions'] = user_extensions
645 export_settings
['pre_export_callbacks'] = pre_export_callbacks
646 export_settings
['post_export_callbacks'] = post_export_callbacks
648 return gltf2_blender_export
.save(context
, export_settings
)
650 def draw(self
, context
):
651 pass # Is needed to get panels available
654 class GLTF_PT_export_main(bpy
.types
.Panel
):
655 bl_space_type
= 'FILE_BROWSER'
656 bl_region_type
= 'TOOL_PROPS'
658 bl_parent_id
= "FILE_PT_operator"
659 bl_options
= {'HIDE_HEADER'}
662 def poll(cls
, context
):
663 sfile
= context
.space_data
664 operator
= sfile
.active_operator
666 return operator
.bl_idname
== "EXPORT_SCENE_OT_gltf"
668 def draw(self
, context
):
670 layout
.use_property_split
= True
671 layout
.use_property_decorate
= False # No animation.
673 sfile
= context
.space_data
674 operator
= sfile
.active_operator
676 layout
.prop(operator
, 'export_format')
677 if operator
.export_format
== 'GLTF_SEPARATE':
678 layout
.prop(operator
, 'export_keep_originals')
679 if operator
.export_keep_originals
is False:
680 layout
.prop(operator
, 'export_texture_dir', icon
='FILE_FOLDER')
682 layout
.prop(operator
, 'export_copyright')
683 layout
.prop(operator
, 'will_save_settings')
686 class GLTF_PT_export_include(bpy
.types
.Panel
):
687 bl_space_type
= 'FILE_BROWSER'
688 bl_region_type
= 'TOOL_PROPS'
690 bl_parent_id
= "FILE_PT_operator"
691 bl_options
= {'DEFAULT_CLOSED'}
694 def poll(cls
, context
):
695 sfile
= context
.space_data
696 operator
= sfile
.active_operator
698 return operator
.bl_idname
== "EXPORT_SCENE_OT_gltf"
700 def draw(self
, context
):
702 layout
.use_property_split
= True
703 layout
.use_property_decorate
= False # No animation.
705 sfile
= context
.space_data
706 operator
= sfile
.active_operator
708 col
= layout
.column(heading
= "Limit to", align
= True)
709 col
.prop(operator
, 'use_selection')
710 col
.prop(operator
, 'use_visible')
711 col
.prop(operator
, 'use_renderable')
712 col
.prop(operator
, 'use_active_collection')
713 col
.prop(operator
, 'use_active_scene')
715 col
= layout
.column(heading
= "Data", align
= True)
716 col
.prop(operator
, 'export_extras')
717 col
.prop(operator
, 'export_cameras')
718 col
.prop(operator
, 'export_lights')
721 class GLTF_PT_export_transform(bpy
.types
.Panel
):
722 bl_space_type
= 'FILE_BROWSER'
723 bl_region_type
= 'TOOL_PROPS'
724 bl_label
= "Transform"
725 bl_parent_id
= "FILE_PT_operator"
726 bl_options
= {'DEFAULT_CLOSED'}
729 def poll(cls
, context
):
730 sfile
= context
.space_data
731 operator
= sfile
.active_operator
733 return operator
.bl_idname
== "EXPORT_SCENE_OT_gltf"
735 def draw(self
, context
):
737 layout
.use_property_split
= True
738 layout
.use_property_decorate
= False # No animation.
740 sfile
= context
.space_data
741 operator
= sfile
.active_operator
743 layout
.prop(operator
, 'export_yup')
746 class GLTF_PT_export_geometry(bpy
.types
.Panel
):
747 bl_space_type
= 'FILE_BROWSER'
748 bl_region_type
= 'TOOL_PROPS'
749 bl_label
= "Geometry"
750 bl_parent_id
= "FILE_PT_operator"
751 bl_options
= {'DEFAULT_CLOSED'}
754 def poll(cls
, context
):
755 sfile
= context
.space_data
756 operator
= sfile
.active_operator
758 return operator
.bl_idname
== "EXPORT_SCENE_OT_gltf"
760 def draw(self
, context
):
763 class GLTF_PT_export_geometry_mesh(bpy
.types
.Panel
):
764 bl_space_type
= 'FILE_BROWSER'
765 bl_region_type
= 'TOOL_PROPS'
767 bl_parent_id
= "GLTF_PT_export_geometry"
768 bl_options
= {'DEFAULT_CLOSED'}
771 def poll(cls
, context
):
772 sfile
= context
.space_data
773 operator
= sfile
.active_operator
774 return operator
.bl_idname
== "EXPORT_SCENE_OT_gltf"
776 def draw(self
, context
):
778 layout
.use_property_split
= True
779 layout
.use_property_decorate
= False # No animation.
781 sfile
= context
.space_data
782 operator
= sfile
.active_operator
784 layout
.prop(operator
, 'export_apply')
785 layout
.prop(operator
, 'export_texcoords')
786 layout
.prop(operator
, 'export_normals')
787 col
= layout
.column()
788 col
.active
= operator
.export_normals
789 col
.prop(operator
, 'export_tangents')
790 layout
.prop(operator
, 'export_colors')
792 col
= layout
.column()
793 col
.prop(operator
, 'use_mesh_edges')
794 col
.prop(operator
, 'use_mesh_vertices')
797 class GLTF_PT_export_geometry_material(bpy
.types
.Panel
):
798 bl_space_type
= 'FILE_BROWSER'
799 bl_region_type
= 'TOOL_PROPS'
800 bl_label
= "Material"
801 bl_parent_id
= "GLTF_PT_export_geometry"
802 bl_options
= {'DEFAULT_CLOSED'}
805 def poll(cls
, context
):
806 sfile
= context
.space_data
807 operator
= sfile
.active_operator
808 return operator
.bl_idname
== "EXPORT_SCENE_OT_gltf"
810 def draw(self
, context
):
812 layout
.use_property_split
= True
813 layout
.use_property_decorate
= False # No animation.
815 sfile
= context
.space_data
816 operator
= sfile
.active_operator
818 layout
.prop(operator
, 'export_materials')
819 col
= layout
.column()
820 col
.active
= operator
.export_materials
== "EXPORT"
821 col
.prop(operator
, 'export_image_format')
823 class GLTF_PT_export_geometry_original_pbr(bpy
.types
.Panel
):
824 bl_space_type
= 'FILE_BROWSER'
825 bl_region_type
= 'TOOL_PROPS'
826 bl_label
= "PBR Extensions"
827 bl_parent_id
= "GLTF_PT_export_geometry_material"
828 bl_options
= {'DEFAULT_CLOSED'}
831 def poll(cls
, context
):
832 sfile
= context
.space_data
833 operator
= sfile
.active_operator
834 return operator
.bl_idname
== "EXPORT_SCENE_OT_gltf"
836 def draw(self
, context
):
838 layout
.use_property_split
= True
839 layout
.use_property_decorate
= False # No animation.
841 sfile
= context
.space_data
842 operator
= sfile
.active_operator
844 layout
.prop(operator
, 'export_original_specular')
847 class GLTF_PT_export_geometry_compression(bpy
.types
.Panel
):
848 bl_space_type
= 'FILE_BROWSER'
849 bl_region_type
= 'TOOL_PROPS'
850 bl_label
= "Compression"
851 bl_parent_id
= "GLTF_PT_export_geometry"
852 bl_options
= {'DEFAULT_CLOSED'}
855 from io_scene_gltf2
.io
.com
import gltf2_io_draco_compression_extension
856 self
.is_draco_available
= gltf2_io_draco_compression_extension
.dll_exists(quiet
=True)
859 def poll(cls
, context
):
860 sfile
= context
.space_data
861 operator
= sfile
.active_operator
862 if operator
.is_draco_available
:
863 return operator
.bl_idname
== "EXPORT_SCENE_OT_gltf"
865 def draw_header(self
, context
):
866 sfile
= context
.space_data
867 operator
= sfile
.active_operator
868 self
.layout
.prop(operator
, "export_draco_mesh_compression_enable", text
="")
870 def draw(self
, context
):
872 layout
.use_property_split
= True
873 layout
.use_property_decorate
= False # No animation.
875 sfile
= context
.space_data
876 operator
= sfile
.active_operator
878 layout
.active
= operator
.export_draco_mesh_compression_enable
879 layout
.prop(operator
, 'export_draco_mesh_compression_level')
881 col
= layout
.column(align
=True)
882 col
.prop(operator
, 'export_draco_position_quantization', text
="Quantize Position")
883 col
.prop(operator
, 'export_draco_normal_quantization', text
="Normal")
884 col
.prop(operator
, 'export_draco_texcoord_quantization', text
="Tex Coord")
885 col
.prop(operator
, 'export_draco_color_quantization', text
="Color")
886 col
.prop(operator
, 'export_draco_generic_quantization', text
="Generic")
889 class GLTF_PT_export_animation(bpy
.types
.Panel
):
890 bl_space_type
= 'FILE_BROWSER'
891 bl_region_type
= 'TOOL_PROPS'
892 bl_label
= "Animation"
893 bl_parent_id
= "FILE_PT_operator"
894 bl_options
= {'DEFAULT_CLOSED'}
897 def poll(cls
, context
):
898 sfile
= context
.space_data
899 operator
= sfile
.active_operator
901 return operator
.bl_idname
== "EXPORT_SCENE_OT_gltf"
903 def draw(self
, context
):
905 layout
.use_property_split
= True
906 layout
.use_property_decorate
= False # No animation.
908 sfile
= context
.space_data
909 operator
= sfile
.active_operator
911 layout
.prop(operator
, 'export_current_frame')
914 class GLTF_PT_export_animation_export(bpy
.types
.Panel
):
915 bl_space_type
= 'FILE_BROWSER'
916 bl_region_type
= 'TOOL_PROPS'
917 bl_label
= "Animation"
918 bl_parent_id
= "GLTF_PT_export_animation"
919 bl_options
= {'DEFAULT_CLOSED'}
922 def poll(cls
, context
):
923 sfile
= context
.space_data
924 operator
= sfile
.active_operator
926 return operator
.bl_idname
== "EXPORT_SCENE_OT_gltf"
928 def draw_header(self
, context
):
929 sfile
= context
.space_data
930 operator
= sfile
.active_operator
931 self
.layout
.prop(operator
, "export_animations", text
="")
933 def draw(self
, context
):
935 layout
.use_property_split
= True
936 layout
.use_property_decorate
= False # No animation.
938 sfile
= context
.space_data
939 operator
= sfile
.active_operator
941 layout
.active
= operator
.export_animations
943 layout
.prop(operator
, 'export_frame_range')
944 layout
.prop(operator
, 'export_frame_step')
945 layout
.prop(operator
, 'export_force_sampling')
946 layout
.prop(operator
, 'export_nla_strips')
947 if operator
.export_nla_strips
is False:
948 layout
.prop(operator
, 'export_nla_strips_merged_animation_name')
949 layout
.prop(operator
, 'export_optimize_animation_size')
950 layout
.prop(operator
, 'export_anim_single_armature')
953 class GLTF_PT_export_animation_shapekeys(bpy
.types
.Panel
):
954 bl_space_type
= 'FILE_BROWSER'
955 bl_region_type
= 'TOOL_PROPS'
956 bl_label
= "Shape Keys"
957 bl_parent_id
= "GLTF_PT_export_animation"
958 bl_options
= {'DEFAULT_CLOSED'}
961 def poll(cls
, context
):
962 sfile
= context
.space_data
963 operator
= sfile
.active_operator
965 return operator
.bl_idname
== "EXPORT_SCENE_OT_gltf"
967 def draw_header(self
, context
):
968 sfile
= context
.space_data
969 operator
= sfile
.active_operator
970 self
.layout
.prop(operator
, "export_morph", text
="")
972 def draw(self
, context
):
974 layout
.use_property_split
= True
975 layout
.use_property_decorate
= False # No animation.
977 sfile
= context
.space_data
978 operator
= sfile
.active_operator
980 layout
.active
= operator
.export_morph
982 layout
.prop(operator
, 'export_morph_normal')
983 col
= layout
.column()
984 col
.active
= operator
.export_morph_normal
985 col
.prop(operator
, 'export_morph_tangent')
988 class GLTF_PT_export_animation_skinning(bpy
.types
.Panel
):
989 bl_space_type
= 'FILE_BROWSER'
990 bl_region_type
= 'TOOL_PROPS'
991 bl_label
= "Skinning"
992 bl_parent_id
= "GLTF_PT_export_animation"
993 bl_options
= {'DEFAULT_CLOSED'}
996 def poll(cls
, context
):
997 sfile
= context
.space_data
998 operator
= sfile
.active_operator
1000 return operator
.bl_idname
== "EXPORT_SCENE_OT_gltf"
1002 def draw_header(self
, context
):
1003 sfile
= context
.space_data
1004 operator
= sfile
.active_operator
1005 self
.layout
.prop(operator
, "export_skins", text
="")
1007 def draw(self
, context
):
1008 layout
= self
.layout
1009 layout
.use_property_split
= True
1010 layout
.use_property_decorate
= False # No animation.
1012 sfile
= context
.space_data
1013 operator
= sfile
.active_operator
1015 layout
.active
= operator
.export_skins
1016 layout
.prop(operator
, 'export_all_influences')
1019 row
.active
= operator
.export_force_sampling
1020 row
.prop(operator
, 'export_def_bones')
1021 if operator
.export_force_sampling
is False and operator
.export_def_bones
is True:
1022 layout
.label(text
="Export only deformation bones is not possible when not sampling animation")
1024 class GLTF_PT_export_user_extensions(bpy
.types
.Panel
):
1025 bl_space_type
= 'FILE_BROWSER'
1026 bl_region_type
= 'TOOL_PROPS'
1027 bl_label
= "Exporter Extensions"
1028 bl_parent_id
= "FILE_PT_operator"
1029 bl_options
= {'DEFAULT_CLOSED'}
1032 def poll(cls
, context
):
1033 sfile
= context
.space_data
1034 operator
= sfile
.active_operator
1036 return operator
.bl_idname
== "EXPORT_SCENE_OT_gltf" and operator
.has_active_exporter_extensions
1038 def draw(self
, context
):
1039 layout
= self
.layout
1040 layout
.use_property_split
= True
1041 layout
.use_property_decorate
= False # No animation.
1043 class GLTF_PT_import_user_extensions(bpy
.types
.Panel
):
1044 bl_space_type
= 'FILE_BROWSER'
1045 bl_region_type
= 'TOOL_PROPS'
1046 bl_label
= "Importer Extensions"
1047 bl_parent_id
= "FILE_PT_operator"
1048 bl_options
= {'DEFAULT_CLOSED'}
1051 def poll(cls
, context
):
1052 sfile
= context
.space_data
1053 operator
= sfile
.active_operator
1054 return operator
.bl_idname
== "IMPORT_SCENE_OT_gltf" and operator
.has_active_importer_extensions
1056 def draw(self
, context
):
1057 layout
= self
.layout
1058 layout
.use_property_split
= True
1059 layout
.use_property_decorate
= False # No animation.
1061 class ExportGLTF2(bpy
.types
.Operator
, ExportGLTF2_Base
, ExportHelper
):
1062 """Export scene as glTF 2.0 file"""
1063 bl_idname
= 'export_scene.gltf'
1064 bl_label
= 'Export glTF 2.0'
1068 filter_glob
: StringProperty(default
='*.glb;*.gltf', options
={'HIDDEN'})
1071 def menu_func_export(self
, context
):
1072 self
.layout
.operator(ExportGLTF2
.bl_idname
, text
='glTF 2.0 (.glb/.gltf)')
1075 class ImportGLTF2(Operator
, ImportHelper
):
1076 """Load a glTF 2.0 file"""
1077 bl_idname
= 'import_scene.gltf'
1078 bl_label
= 'Import glTF 2.0'
1079 bl_options
= {'REGISTER', 'UNDO'}
1081 filter_glob
: StringProperty(default
="*.glb;*.gltf", options
={'HIDDEN'})
1083 files
: CollectionProperty(
1085 type=bpy
.types
.OperatorFileListElement
,
1088 loglevel
: IntProperty(
1090 description
="Log Level")
1092 import_pack_images
: BoolProperty(
1094 description
='Pack all images into .blend file',
1098 merge_vertices
: BoolProperty(
1099 name
='Merge Vertices',
1101 'The glTF format requires discontinuous normals, UVs, and '
1102 'other vertex attributes to be stored as separate vertices, '
1103 'as required for rendering on typical graphics hardware. '
1104 'This option attempts to combine co-located vertices where possible. '
1105 'Currently cannot combine verts with different normals'
1110 import_shading
: EnumProperty(
1112 items
=(("NORMALS", "Use Normal Data", ""),
1113 ("FLAT", "Flat Shading", ""),
1114 ("SMOOTH", "Smooth Shading", "")),
1115 description
="How normals are computed during import",
1118 bone_heuristic
: EnumProperty(
1121 ("BLENDER", "Blender (best for re-importing)",
1122 "Good for re-importing glTFs exported from Blender. "
1123 "Bone tips are placed on their local +Y axis (in glTF space)"),
1124 ("TEMPERANCE", "Temperance (average)",
1125 "Decent all-around strategy. "
1126 "A bone with one child has its tip placed on the local axis "
1127 "closest to its child"),
1128 ("FORTUNE", "Fortune (may look better, less accurate)",
1129 "Might look better than Temperance, but also might have errors. "
1130 "A bone with one child has its tip placed at its child's root. "
1131 "Non-uniform scalings may get messed up though, so beware"),
1133 description
="Heuristic for placing bones. Tries to make bones pretty",
1134 default
="TEMPERANCE",
1137 guess_original_bind_pose
: BoolProperty(
1138 name
='Guess Original Bind Pose',
1140 'Try to guess the original bind pose for skinned meshes from '
1141 'the inverse bind matrices. '
1142 'When off, use default/rest pose as bind pose'
1147 def draw(self
, context
):
1148 layout
= self
.layout
1150 layout
.use_property_split
= True
1151 layout
.use_property_decorate
= False # No animation.
1153 layout
.prop(self
, 'import_pack_images')
1154 layout
.prop(self
, 'merge_vertices')
1155 layout
.prop(self
, 'import_shading')
1156 layout
.prop(self
, 'guess_original_bind_pose')
1157 layout
.prop(self
, 'bone_heuristic')
1159 def invoke(self
, context
, event
):
1161 preferences
= bpy
.context
.preferences
1162 for addon_name
in preferences
.addons
.keys():
1164 if hasattr(sys
.modules
[addon_name
], 'glTF2ImportUserExtension') or hasattr(sys
.modules
[addon_name
], 'glTF2ImportUserExtensions'):
1165 importer_extension_panel_unregister_functors
.append(sys
.modules
[addon_name
].register_panel())
1169 self
.has_active_importer_extensions
= len(importer_extension_panel_unregister_functors
) > 0
1170 return ImportHelper
.invoke(self
, context
, event
)
1172 def execute(self
, context
):
1173 return self
.import_gltf2(context
)
1175 def import_gltf2(self
, context
):
1178 self
.set_debug_log()
1179 import_settings
= self
.as_keywords()
1181 user_extensions
= []
1184 preferences
= bpy
.context
.preferences
1185 for addon_name
in preferences
.addons
.keys():
1187 module
= sys
.modules
[addon_name
]
1190 if hasattr(module
, 'glTF2ImportUserExtension'):
1191 extension_ctor
= module
.glTF2ImportUserExtension
1192 user_extensions
.append(extension_ctor())
1193 import_settings
['import_user_extensions'] = user_extensions
1196 # Multiple file import
1198 dirname
= os
.path
.dirname(self
.filepath
)
1199 for file in self
.files
:
1200 path
= os
.path
.join(dirname
, file.name
)
1201 if self
.unit_import(path
, import_settings
) == {'FINISHED'}:
1205 # Single file import
1206 return self
.unit_import(self
.filepath
, import_settings
)
1208 def unit_import(self
, filename
, import_settings
):
1210 from .io
.imp
.gltf2_io_gltf
import glTFImporter
, ImportError
1211 from .blender
.imp
.gltf2_blender_gltf
import BlenderGlTF
1214 gltf_importer
= glTFImporter(filename
, import_settings
)
1215 gltf_importer
.read()
1216 gltf_importer
.checks()
1218 print("Data are loaded, start creating Blender stuff")
1220 start_time
= time
.time()
1221 BlenderGlTF
.create(gltf_importer
)
1222 elapsed_s
= "{:.2f}s".format(time
.time() - start_time
)
1223 print("glTF import finished in " + elapsed_s
)
1225 gltf_importer
.log
.removeHandler(gltf_importer
.log_handler
)
1229 except ImportError as e
:
1230 self
.report({'ERROR'}, e
.args
[0])
1231 return {'CANCELLED'}
1233 def set_debug_log(self
):
1235 if bpy
.app
.debug_value
== 0:
1236 self
.loglevel
= logging
.CRITICAL
1237 elif bpy
.app
.debug_value
== 1:
1238 self
.loglevel
= logging
.ERROR
1239 elif bpy
.app
.debug_value
== 2:
1240 self
.loglevel
= logging
.WARNING
1241 elif bpy
.app
.debug_value
== 3:
1242 self
.loglevel
= logging
.INFO
1244 self
.loglevel
= logging
.NOTSET
1247 def gltf_variant_ui_update(self
, context
):
1248 from .blender
.com
.gltf2_blender_ui
import variant_register
, variant_unregister
1249 if self
.KHR_materials_variants_ui
is True:
1250 # register all needed types
1253 variant_unregister()
1255 class GLTF_AddonPreferences(bpy
.types
.AddonPreferences
):
1256 bl_idname
= __package__
1258 settings_node_ui
: bpy
.props
.BoolProperty(
1260 description
="Displays glTF Material Output node in Shader Editor (Menu Add > Output)"
1263 KHR_materials_variants_ui
: bpy
.props
.BoolProperty(
1265 description
="Displays glTF UI to manage material variants",
1266 update
=gltf_variant_ui_update
1270 def draw(self
, context
):
1271 layout
= self
.layout
1273 row
.prop(self
, "settings_node_ui", text
="Shader Editor Add-ons")
1274 row
.prop(self
, "KHR_materials_variants_ui", text
="Material Variants")
1276 def menu_func_import(self
, context
):
1277 self
.layout
.operator(ImportGLTF2
.bl_idname
, text
='glTF 2.0 (.glb/.gltf)')
1282 GLTF_PT_export_main
,
1283 GLTF_PT_export_include
,
1284 GLTF_PT_export_transform
,
1285 GLTF_PT_export_geometry
,
1286 GLTF_PT_export_geometry_mesh
,
1287 GLTF_PT_export_geometry_material
,
1288 GLTF_PT_export_geometry_original_pbr
,
1289 GLTF_PT_export_geometry_compression
,
1290 GLTF_PT_export_animation
,
1291 GLTF_PT_export_animation_export
,
1292 GLTF_PT_export_animation_shapekeys
,
1293 GLTF_PT_export_animation_skinning
,
1294 GLTF_PT_export_user_extensions
,
1296 GLTF_PT_import_user_extensions
,
1297 GLTF_AddonPreferences
1302 import io_scene_gltf2
.blender
.com
.gltf2_blender_ui
as blender_ui
1304 bpy
.utils
.register_class(c
)
1305 # bpy.utils.register_module(__name__)
1307 blender_ui
.register()
1308 if bpy
.context
.preferences
.addons
['io_scene_gltf2'].preferences
.KHR_materials_variants_ui
is True:
1309 blender_ui
.variant_register()
1311 # add to the export / import menu
1312 bpy
.types
.TOPBAR_MT_file_export
.append(menu_func_export
)
1313 bpy
.types
.TOPBAR_MT_file_import
.append(menu_func_import
)
1317 import io_scene_gltf2
.blender
.com
.gltf2_blender_ui
as blender_ui
1318 blender_ui
.unregister()
1319 if bpy
.context
.preferences
.addons
['io_scene_gltf2'].preferences
.KHR_materials_variants_ui
is True:
1320 blender_ui
.variant_unregister()
1323 bpy
.utils
.unregister_class(c
)
1324 for f
in exporter_extension_panel_unregister_functors
:
1326 exporter_extension_panel_unregister_functors
.clear()
1328 for f
in importer_extension_panel_unregister_functors
:
1330 importer_extension_panel_unregister_functors
.clear()
1332 # bpy.utils.unregister_module(__name__)
1334 # remove from the export / import menu
1335 bpy
.types
.TOPBAR_MT_file_export
.remove(menu_func_export
)
1336 bpy
.types
.TOPBAR_MT_file_import
.remove(menu_func_import
)